/* * Copyright 2011, 2014 Range Networks, Inc. * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /**@file GPRS L1 radio channels, from GSM 05.02 and 05.03. */ #ifndef GPRSL1FEC_H #define GPRSL1FEC_H #include #include // for gBTS #include #include // for TxBurst #include // for TCHFACCHLogicalChannel #include "MAC.h" using namespace GSM; namespace GPRS { class TBF; // GSM05.03 sec 5.1.4 re GPRS CS-4 says: 16 bit parity with generator: D16 + D12 + D5 + 1, static const unsigned long sCS4Generator = (1<<16) + (1<<12) + (1<<5) + 1; class PDCHL1FEC; class PDCHCommon { public: PDCHL1FEC *mchParent; PDCHCommon(PDCHL1FEC*wParent) { mchParent = wParent; } ARFCNManager *getRadio(); unsigned TN(); unsigned ARFCN(); unsigned CN(); L1Decoder *getDecoder() const; float FER() const; void countGoodFrame(); void countBadFrame(); PDCHL1Uplink *uplink(); PDCHL1Downlink *downlink(); PDCHL1FEC *parent(); // If called from mchParent, returns itself. TBF *getTFITBF(int tfi, RLCDirType dir); void setTFITBF(int tfi, RLCDirType dir, TBF *TBF); const char *getAnsweringUsfText(char *buf, RLCBSN_t bsn); // Printable USFs around BSN for debugging char *shortId() { // Return a short printable id for this channel. static char buf[20]; sprintf(buf,"PDCH#%u:%u",getRadio()->ARFCN(),TN()); return buf; } }; // For gprs channels, should we allocate them using getTCH(), // which returns a TCHFACCHLogicalChannel which we have no use for, // or should we get dedicated GPRS channels directly from TRXManager, // which currently does not allow this. // Answer: We are going to make the logical channels tri-state (inactive, RRactive, GPRSactive), // and use getTCH to get them. // There is one of these classes for each GPRS Data Channel, PDTCH. // Downstream it attaches to a single Physical channel in L1FEC via mchEncoder and mchDecoder. // TODO: I did this wrong. This should be for a single ARFCN, but multiple // upstream/downstream timeslots. class PDCHL1FEC : public L1UplinkReservation, public PDCHCommon, public USFList { public: PDCHL1Downlink *mchDownlink; PDCHL1Uplink *mchUplink; L1FEC *mchOldFec; // The GSM TCH channel that this GPRS channel took over; // it has the channel parameters. // Temporary: GPRS will not use anything in this LogicalChannel class, and we dont want // the extra class hanging around, but currently the only way to dynamically // allocate physical channels is via the associated logical channel. TCHFACCHLogicalChannel *mchLogChan; public: // The TFIs are a 5 bit handle for TBFs. The USFs are a 3 bit handle for uplink TBFs. TFIList *mchTFIs;// Points to the global TFIList. Someday will point to the per-ARFCN TFIList. void debug_test(); PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan); // Release the radio channel. // GSM will start using it immediately. TODO: Do we want to set a timer // so it is not reused immediately? ~PDCHL1FEC(); // Attach this GPRS channel to the specified GSM channel. //void mchOpen(TCHFACCHLogicalChannel *wlogchan); void mchStart(); void mchStop(); void mchDump(std::ostream&os,bool verbose); // Return a description of PDTCH, which is the only one we care about. // (We dont care about the associated SDCCH, whose frame is used in GPRS for // continuous timing advance.) // The packet channel description is the same as channel description except: // From GSM04.08 10.5.25 table 10.5.2.25a table 10.5.58, and I quote: // "The Channel type field (5 bit) shall be ignored by the receiver and // all bits treated as spare. For backward compatibility // reasons, the sender shall set the spare bits to binary '00001'." // This doesnt matter in the slightest, because the typeAndOffset would // have been TCHF_0 whose enum value is 1 anyway. L3ChannelDescription packetChannelDescription() { L1FEC *lf = mchOldFec; return L3ChannelDescription((TypeAndOffset) 1, lf->TN(), lf->TSC(), lf->ARFCN()); } }; std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch); // For CS-1 decoding, just uses SharedL1Decoder. // For CS-4 decoding: Uses the SharedL1Decoder through deinterleaving into mC. class GprsDecoder : public SharedL1Decoder { Parity mBlockCoder_CS4; BitVector mDP_CS4; public: BitVector mD_CS4; short qbits[8]; ChannelCodingType getCS(); // Determine CS from the qbits. BitVector *getResult(); GprsDecoder() : mBlockCoder_CS4(sCS4Generator,16,431+16), mDP_CS4(431+16), mD_CS4(mDP_CS4.head(424)) {} bool decodeCS4(); const char* descriptiveString() const { return "GprsDecoder"; } // not very useful. }; // CS-4 has 431 input data bits, which are always 424 real data bits (53 bytes) // plus 7 unused bits that are set to 0, to make 431 data bits. // The first 3 bits are usf encoded to 12 bits, to yield 440 bits. // Then 16 bit parity bits yields 456 bits. class GprsEncoder : public SharedL1Encoder { Parity mBlockCoder_CS4; public: // Uses SharedL1Encoder::mC for result vector // Uses SharedL1Encoder::mI for the 4-way interleaved result vector. BitVector mP_CS4; // alias for parity part of mC BitVector mU_CS4; // alias for usf part of mC BitVector mD_CS4; // assembly area for parity. GprsEncoder() : SharedL1Encoder(), mBlockCoder_CS4(sCS4Generator,16,431+16), mP_CS4(mC.segment(440,16)), mU_CS4(mC.segment(0,12)), mD_CS4(mC.segment(12-3,431)) {} void encodeCS4(const BitVector&src); void encodeCS1(const BitVector &src); // would be nice to add "GPRS"; should be at init. const char* descriptiveString() const { return "GprsEncoder"; } }; class PDCHL1Uplink : public PDCHCommon { protected: #if GPRS_ENCODER //SharedL1Decoder mchCS1Dec; GprsDecoder mchCS14Dec; #else // This case does not compile yet. L1Decoder mchCS1Dec; #endif public: static const RLCDirType mchDir = RLCDir::Up; // The uplink queue: // There will typically only be one guy on here, and we could probably dispense // with the queue, but it is safer to do it this way to avoid thread problems. // InterthreadQueue template adds "*" so it is really a queue of BitVector* InterthreadQueue mchUplinkData; PDCHL1Uplink(PDCHL1FEC *wParent) : PDCHCommon(wParent) { } ~PDCHL1Uplink() {} void writeLowSideRx(const RxBurst &inBurst); // TODO: This needs to be per-MS. // void setPhy(float wRSSI, float TimingError) { // This function is inapplicable to packet channels, which have multiple // MS listening to the same channel. //assert(0); //} }; // One of these for each PDCH (physical channel), attached to L1FEC. // Accepts Radio Blocks from anybody. // Based on SACCHL1Encoder and TCHFACCHL1Encoder // This does on-demand sending of RLCBlocks down to the physical channel. // We wait until the last minute so we can encode uplink assignments in the blocks // as close as possible to the present time. // TODO: When we support different encodings we may have to base this on L1Encoder directly // and copy a bunch of routines from XCCHL1Encoder? static const int qCS1[8] = { 1,1,1,1,1,1,1,1 }; static const int qCS2[8] = { 1,1,0,0,1,0,0,0 }; // GSM05.03 sec 5.1.2.5 static const int qCS3[8] = { 0,0,1,0,0,0,0,1 }; // GSM05.03 sec 5.1.3.5 static const int qCS4[8] = { 0,0,0,1,0,1,1,0 }; // GSM0503 sec5.1.4.5; magically identifies CS-4. class PDCHL1Downlink : public PDCHCommon { protected: #if GPRS_ENCODER GprsEncoder mchEnc; //GSM::SharedL1Encoder mchCS1Enc; //GSM::SharedL1Encoder mchCS4Enc; #else GSM::L1Encoder mchCS1Enc; #endif TxBurst mchBurst; ///< a preformatted burst template //TxBurst mchFillerBurst; // unused ///< the filler burst for this channel int mchTotalBursts; //GSM::Time mchNextWriteTime, mchPrevWriteTime; const TDMAMapping& mchMapping; BitVector mchIdleFrame; // The mDownlinkData is used only for control messages, which can stack up. //InterthreadQueue mchDownlinkMsgQ; public: static const RLCDirType mchDir = RLCDir::Up; void initBursts(L1FEC*); PDCHL1Downlink(PDCHL1FEC *wParent) : PDCHCommon(wParent), //mchCS1Enc(ChannelCodingCS1), #if GPRS_ENCODER //mchCS4Enc(ChannelCodingCS4), #endif mchTotalBursts(0), mchMapping(wParent->mchOldFec->encoder()->mapping()), mchIdleFrame((size_t)0) { initBursts(wParent->mchOldFec); } ~PDCHL1Downlink() {} // Enqueue a downlink message. We dont use this for downlink data - those // are sent by calling the RLCEngine when this queue is empty. //void enqueueMsg(RLCDownlinkMessage *); // The PDCH must feed the radio on time. This is the routine that does it. void dlService(); void transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags); //void rollForward(); //void mchResync(); int findNeedyUSF(); // Send the L2Frame down to the radio now. void send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle); bool send1DataFrame(RLCDownEngine *tbfdown, RLCDownlinkDataBlock *block, int makeres,MsgTransactionType mttype,unsigned *pcounter); bool send1MsgFrame(TBF *tbf,RLCDownlinkMessage *msg, int makeres, MsgTransactionType mttype,unsigned *pcounter); void sendIdleFrame(RLCBSN_t bsn); void bugFixIdleFrame(); }; extern bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2); }; // namespace GPRS #endif