/* * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. */ #ifndef GSML1FEC_H #define GSML1FEC_H #include "Threads.h" #include #include "BitVector.h" #include "GSMCommon.h" #include "GSMTransfer.h" #include "GSMTDMA.h" #include "GSM610Tables.h" class ARFCNManager; namespace GSM { /* forward refs */ class GSMConfig; class SAPMux; class L1FEC; class L1Encoder; class L1Decoder; class GeneratorL1Encoder; class SACCHL1Encoder; class SACCHL1Decoder; class SACCHL1FEC; class TrafficTranscoder; /* Naming convention for bit vectors follows GSM 05.03 Section 2.2. d[k] data u[k] data bits after first encoding step c[k] data bits after second encoding step i[B][k] interleaved data bits e[B][k] bits in a burst */ /** Abstract class for L1 encoders. In most subclasses, writeHighSide() drives the processing. */ class L1Encoder { protected: ARFCNManager *mDownstream; TxBurst mBurst; ///< a preformatted burst template TxBurst mFillerBurst; ///< the filler burst for this channel /**@name Config items that don't change. */ //@{ const TDMAMapping& mMapping; ///< multiplexing description unsigned mTN; ///< timeslot number to use unsigned mTSC; ///< training sequence for this channel L1FEC *mParent; ///< a containing L1FEC, if any //@} /**@name Multithread access control and data shared across threads. */ //@{ mutable Mutex mLock; //@} /**@ Internal state. */ //@{ unsigned mTotalBursts; ///< total bursts sent since last open() GSM::Time mPrevWriteTime; ///< timestamp of pervious generated burst GSM::Time mNextWriteTime; ///< timestamp of next generated burst volatile bool mRunning; ///< true while the service loop is running bool mActive; ///< true between open() and close() //@} ViterbiR2O4 mVCoder; ///< nearly all GSM channels use the same convolutional code public: /** The basic encoder constructor. @param wTN TDMA timeslot number. @param wMapping TDMA mapping onto the timeslot -- MUST PERSIST. @param wParent The containing L1FEC, for sibling access -- may be NULL. */ L1Encoder(unsigned wTN, const TDMAMapping& wMapping, L1FEC *wParent); virtual ~L1Encoder() {} /** Set the transceiver pointer. */ virtual void downstream(ARFCNManager *wDownstream) { assert(mDownstream==NULL); // Don't call this twice. mDownstream=wDownstream; } /**@name Accessors. */ //@{ const TDMAMapping& mapping() const { return mMapping; } /**@name Components of the channel description. */ //@{ unsigned TN() const { return mTN; } unsigned TSC() const { return mTSC; } unsigned ARFCN() const; ///< this comes from mDownstream TypeAndOffset typeAndOffset() const; ///< this comes from mMapping //@} //@} /** Close the channel after blocking for flush. */ virtual void close(); /** Open the channel for a new transaction. */ virtual void open(); /** Returns true if the channel is in use by a transaction. For broadcast and unicast channels this is always true. For dedicated channels, this is taken from the sibling deocder. */ virtual bool active() const; /** Process pending L2 frames and/or generate filler and enqueue the resulting timeslots. This method may block briefly, up to about 1/2 second. This method is meaningless for some suclasses. */ virtual void writeHighSide(const L2Frame&) { assert(0); } /** Start the service loop thread, if there is one. */ virtual void start() { mRunning=true; } protected: /** Roll write times forward to the next positions. */ void rollForward(); /** Return pointer to paired L1 decoder, if any. */ virtual L1Decoder* sibling(); /** Return pointer to paired L1 decoder, if any. */ virtual const L1Decoder* sibling() const; /** Make sure we're consistent with the current clock. */ void resync(); /** Block until the BTS clock catches up to mPrevWriteTime. */ void waitToSend() const; /** Send the idle filling pattern, if any. The default is a dummy burst. */ virtual void sendIdleFill(); }; /** An abstract class for L1 decoders. writeLowSide() drives the processing. */ class L1Decoder { protected: SAPMux * mUpstream; /**@name Mutex-controlled state information. */ //@{ mutable Mutex mLock; ///< access control /**@name Timers from GSM 04.08 11.1.2 */ //@{ Z100Timer mT3101; ///< timer for new channels Z100Timer mT3109; ///< timer for existing channels Z100Timer mT3111; ///< timer for reuse of a closed channel //@} bool mActive; ///< true between open() and close() //@} /**@name Atomic volatiles, no mutex. */ // Yes, I realize we're violating our own rules here. -- DAB //@{ volatile bool mRunning; ///< true if all required service threads are started volatile float mFER; ///< current FER estimate static const int mFERMemory=20; ///< FER decay time, in frames //@} /**@name Parameters fixed by the constructor, not requiring mutex protection. */ //@{ unsigned mTN; ///< timeslot number const TDMAMapping& mMapping; ///< demux parameters L1FEC* mParent; ///< a containing L1 processor, if any //@} ViterbiR2O4 mVCoder; ///< nearly all GSM channels use the same convolutional code public: /** Constructor for an L1Decoder. @param wTN The timeslot to decode on. @param wMapping Demux parameters, MUST BE PERSISTENT. @param wParent The containing L1FEC, for sibling access. */ L1Decoder(unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent) :mUpstream(NULL), mT3101(T3101ms),mT3109(T3109ms),mT3111(T3111ms), mActive(false), mRunning(false), mFER(0.0F), mTN(wTN),mMapping(wMapping),mParent(wParent) { // Start T3101 so that the channel will // become recyclable soon. mT3101.set(); } virtual ~L1Decoder() {} /** Clear the decoder for a new transaction. Start T3101, stop the others. */ virtual void open(); /** Call this at the end of a tranaction. Start T3111, stop the others. */ virtual void close(); /** Returns true if the channel is in use for a transaction. Returns true if T3111 is not active. */ bool active() const; /** Return true if any timer is expired. */ bool recyclable() const; /** Connect the upstream SAPMux and L2. */ void upstream(SAPMux * wUpstream) { assert(mUpstream==NULL); // Only call this once. mUpstream=wUpstream; } /** Total frame error rate since last open(). */ float FER() const { return mFER; } /** Return the multiplexing parameters. */ const TDMAMapping& mapping() const { return mMapping; } /** Accept an RxBurst and process it into the deinterleaver. */ virtual void writeLowSide(const RxBurst&) = 0; /**@name Components of the channel description. */ //@{ unsigned TN() const { return mTN; } unsigned ARFCN() const; ///< this comes from mUpstream TypeAndOffset typeAndOffset() const; ///< this comes from mMapping //@} protected: virtual L1FEC* parent() { return mParent; } /** Return pointer to paired L1 encoder, if any. */ virtual L1Encoder* sibling(); /** Return pointer to paired L1 encoder, if any. */ virtual const L1Encoder* sibling() const; /** Mark the decoder as started. */ virtual void start() { mRunning=true; } void countGoodFrame(); void countBadFrame(); }; /** The L1FEC encapsulates an encoder and decoder. */ class L1FEC { protected: L1Encoder* mEncoder; L1Decoder* mDecoder; public: /** The L1FEC constructor is over-ridden for different channel types. But the default has no encoder or decoder. */ L1FEC():mEncoder(NULL),mDecoder(NULL) {} /** This is no-op because these channels should not be destroyed. */ virtual ~L1FEC() {}; /** Send in an RxBurst for decoding. */ void writeLowSide(const RxBurst& burst) { assert(mDecoder); mDecoder->writeLowSide(burst); } /** Send in an L2Frame for encoding and transmission. */ void writeHighSide(const L2Frame& frame) { assert(mEncoder); mEncoder->writeHighSide(frame); } /** Attach L1 to a downstream radio. */ void downstream(ARFCNManager*); /** Attach L1 to an upstream SAPI mux and L2. */ void upstream(SAPMux* mux) { if (mDecoder) mDecoder->upstream(mux); } /**@name Ganged actions. */ //@{ void open(); void close(); //@} /**@name Pass-through actions. */ //@{ TypeAndOffset typeAndOffset() const { assert(mEncoder); return mEncoder->typeAndOffset(); } unsigned ARFCN() const { assert(mEncoder); return mEncoder->ARFCN(); } unsigned TN() const { assert(mEncoder); return mEncoder->TN(); } unsigned TSC() const { assert(mEncoder); return mEncoder->TSC(); } float FER() const { assert(mDecoder); return mDecoder->FER(); } bool recyclable() const { assert(mDecoder); return mDecoder->recyclable(); } bool active() const; const TDMAMapping& txMapping() const { assert(mEncoder); return mEncoder->mapping(); } const TDMAMapping& rcvMapping() const { assert(mEncoder); return mDecoder->mapping(); } //@} L1Decoder* decoder() { return mDecoder; } L1Encoder* encoder() { return mEncoder; } }; /** The TestL1FEC does loopbacks at each end. */ class TestL1FEC : public L1FEC { private: SAPMux * mUpstream; ARFCNManager* mDownstream; public: void writeLowSide(const RxBurst&); void writeHighSide(const L2Frame&); void downstream(ARFCNManager *wDownstream) { mDownstream=wDownstream; } void upstream(SAPMux * wUpstream){ mUpstream=wUpstream;} }; /** L1 decoder for Random Access (RACH). */ class RACHL1Decoder : public L1Decoder { private: /**@name FEC state. */ //@{ Parity mParity; ///< block coder BitVector mU; ///< u[], as per GSM 05.03 2.2 BitVector mD; ///< d[], as per GSM 05.03 2.2 //@} // The RACH channel uses an internal FIFO, // because the channel allocation process might block // and we don't want to block the radio receive thread. RxBurstFIFO mQ; ///< a FIFO to decouple the rx thread Thread mServiceThread; ///< a thread to process the FIFO public: RACHL1Decoder(const TDMAMapping &wMapping, L1FEC *wParent) :L1Decoder(0,wMapping,wParent), mParity(0x06f,6,8),mU(18),mD(mU.head(8)) { } /** Start the service thread. */ void start(); /** Decode the burst and call the channel allocator. */ void writeLowSide(const RxBurst&); /** A loop to watch the FIFO. */ void serviceLoop(); /** A "C" calling interface for pthreads. */ friend void *RACHL1DecoderServiceLoopAdapter(RACHL1Decoder*); }; void *RACHL1DecoderServiceLoopAdapter(RACHL1Decoder*); /** Abstract L1 decoder for most control channels -- GSM 05.03 4.1 */ class XCCHL1Decoder : public L1Decoder { protected: /**@name FEC state. */ //@{ Parity mBlockCoder; SoftVector mI[4]; ///< i[][], as per GSM 05.03 2.2 SoftVector mC; ///< c[], as per GSM 05.03 2.2 BitVector mU; ///< u[], as per GSM 05.03 2.2 BitVector mP; ///< p[], as per GSM 05.03 2.2 BitVector mDP; ///< d[]:p[] (data & parity) BitVector mD; ///< d[], as per GSM 05.03 2.2 //@} GSM::Time mReadTime; ///< timestamp of the first burst unsigned mRSSIHistory[4]; public: XCCHL1Decoder(unsigned wTN, const TDMAMapping& wMapping, L1FEC *wParent); protected: /** Offset to the start of the L2 header. */ virtual unsigned headerOffset() const { return 0; } /** The channel type. */ virtual ChannelType channelType() const = 0; /** Accept a timeslot for processing and drive data up the chain. */ virtual void writeLowSide(const RxBurst&); /** Accept a new timeslot for processing and save it in i[]. This virtual method works for all block-interleaved channels (xCCHs). A different method is needed for diagonally-interleaved channels (TCHs). @return true if a new frame is ready for deinterleaving. */ virtual bool processBurst(const RxBurst&); /** Deinterleave the i[] to c[]. This virtual method works for all block-interleaved channels (xCCHs). A different method is needed for diagonally-interleaved channels (TCHs). */ virtual void deinterleave(); /** Decode the frame and send it upstream. Includes LSB-MSB reversal within each octet. @return True if frame passed parity check. */ bool decode(); /** Finish off a properly-received L2Frame in mU and send it up to L2. */ virtual void handleGoodFrame(); }; /** L1 decoder for the SDCCH. */ class SDCCHL1Decoder : public XCCHL1Decoder { public: SDCCHL1Decoder( unsigned wTN, const TDMAMapping& wMapping, L1FEC *wParent) :XCCHL1Decoder(wTN,wMapping,wParent) { } ChannelType channelType() const { return SDCCHType; } }; /** L1 decoder for the SACCH. Like any other control channel, but with hooks for power/timing control. */ class SACCHL1Decoder : public XCCHL1Decoder { private: SACCHL1FEC *mSACCHParent; unsigned mRSSICounter; volatile float mRSSI[4]; ///< RSSI history , dB wrt full scale volatile float mTimingError[4]; ///< Timing error histoty in symbol mutable volatile bool mPhyNew; ///< a flag to prevent phy params from being read twice volatile int mActualMSPower; ///< actual MS tx power in dBm volatile int mActualMSTiming; ///< actual MS tx timing advance in symbols public: SACCHL1Decoder( unsigned wTN, const TDMAMapping& wMapping, SACCHL1FEC *wParent) :XCCHL1Decoder(wTN,wMapping,(L1FEC*)wParent), mSACCHParent(wParent), mRSSICounter(0) { for (int i=0; i<4; i++) mRSSI[i]=0.0F; } ChannelType channelType() const { return SACCHType; } int actualMSPower() const { return mActualMSPower; } int actualMSTiming() const { return mActualMSTiming; } /** Override open() to set physical parameters with reasonable defaults. */ void open(); /** Override processBurst to catch the physical parameters. */ bool processBurst(const RxBurst&); /** Set pyshical parameters for initialization. */ void setPhy(float wRSSI, float wTimingError); void setPhy(const SACCHL1Decoder& other); /** RSSI of most recent received burst, in dB wrt full scale. */ float RSSI() const; /** Timing error of most recent received burst, symbol units. Positive is late; negative is early. */ float timingError() const; /** Return true if the physical parameters are fresh. */ bool phyNew() const { return mPhyNew; } protected: SACCHL1FEC *SACCHParent() { return mSACCHParent; } SACCHL1Encoder* SACCHSibling(); /** This is a wrapper on handleGoodFrame that processes the physical header. */ void handleGoodFrame(); unsigned headerOffset() const { return 16; } }; /** L1 encoder used for many control channels -- mostly from GSM 05.03 4.1 */ class XCCHL1Encoder : public L1Encoder { protected: /**@name FEC signal processing state. */ //@{ Parity mBlockCoder; ///< block coder for this channel BitVector mI[4]; ///< i[][], as per GSM 05.03 2.2 BitVector mC; ///< c[], as per GSM 05.03 2.2 BitVector mU; ///< u[], as per GSM 05.03 2.2 BitVector mD; ///< d[], as per GSM 05.03 2.2 BitVector mP; ///< p[], as per GSM 05.03 2.2 //@} public: XCCHL1Encoder( unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent); protected: /** Process pending incoming messages. */ virtual void writeHighSide(const L2Frame&); /** Offset from the start of mU to the start of the L2 frame. */ virtual unsigned headerOffset() const { return 0; } /** Send a single L2 frame. */ virtual void sendFrame(const L2Frame&); /** Encode u[] to c[]. Includes LSB-MSB reversal within each octet. */ void encode(); /** Interleave c[] to i[]. GSM 05.03 4.1.4. */ virtual void interleave(); /** Format i[] into timeslots and send them down for transmission. Set stealing flags assuming a control channel. Also updates mWriteTime. GSM 05.03 4.1.5, 05.02 5.2.3. */ virtual void transmit(); }; /** L1 encoder used for full rate TCH and FACCH -- mostry from GSM 05.03 3.1 and 4.2 */ class TCHFACCHL1Encoder : public XCCHL1Encoder { private: bool mPreviousFACCH; ///< A copy of the previous stealing flag state. size_t mOffset; ///< Current deinterleaving offset. BitVector mI[8]; ///< deinterleaving history, 8 blocks instead of 4 BitVector mTCHU; ///< u[], but for traffic BitVector mTCHD; ///< d[], but for traffic BitVector mClass1_c; ///< the class 1 part of taffic c[] BitVector mClass1A_d; ///< the class 1A part of taffic d[] BitVector mClass2_d; ///< the class 2 part of d[] BitVector mFillerC; ///< copy of previous c[] for filling dead time Parity mTCHParity; VocoderFrameFIFO mSpeechQ; ///< input queue for speech frames L2FrameFIFO mL2Q; ///< input queue for L2 FACCH frames Thread mEncoderThread; friend void TCHFACCHL1EncoderRoutine( TCHFACCHL1Encoder * encoder ); public: TCHFACCHL1Encoder(unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent); /** Enqueue a traffic frame for transmission. */ void sendTCH(const unsigned char *frame) { mSpeechQ.write(new VocoderFrame(frame)); } /** Extend open() to set up semaphores. */ void open(); protected: /** Interleave c[] to i[]. GSM 05.03 4.1.4. */ virtual void interleave(int blockOffset); /** Encode a FACCH and enqueue it for transmission. */ void sendFrame(const L2Frame&); /** dispatch called in a while loop. process reading transcoder and fifo to interleave and send. */ void dispatch(); /** Will start the dispatch thread. */ void start(); /** Encode a vocoder frame into c[]. */ void encodeTCH(const VocoderFrame& vFrame); }; /** The C adapter for pthreads. */ void TCHFACCHL1EncoderRoutine( TCHFACCHL1Encoder * encoder ); /** L1 decoder used for full rate TCH and FACCH -- mostly from GSM 05.03 3.1 and 4.2 */ class TCHFACCHL1Decoder : public XCCHL1Decoder { protected: SoftVector mI[8]; ///< deinterleaving history, 8 blocks instead of 4 BitVector mTCHU; ///< u[] (uncoded) in the spec BitVector mTCHD; ///< d[] (data) in the spec SoftVector mClass1_c; ///< the class 1 part of c[] BitVector mClass1A_d; ///< the class 1A part of d[] SoftVector mClass2_c; ///< the class 2 part of c[] VocoderFrame mVFrame; ///< unpacking buffer for vocoder frame unsigned char mPrevGoodFrame[33]; ///< previous good frame. Parity mTCHParity; InterthreadQueue mSpeechQ; ///< output queue for speech frames public: TCHFACCHL1Decoder( unsigned wTN, const TDMAMapping& wMapping, L1FEC *wParent); ChannelType channelType() const { return FACCHType; } /** TCH/FACCH has a special-case writeLowSide. */ void writeLowSide(const RxBurst& inBurst); /** Unlike other DCCHs, TCH/FACCH process burst calls deinterleave, decode, handleGoodFrame. */ bool processBurst( const RxBurst& ); /** Deinterleave i[] to c[]. */ void deinterleave(int blockOffset ); void replaceFACCH( int blockOffset ); /** Decode a traffic frame from TCHI[] and enqueue it. Return true if there's a good frame. */ bool decodeTCH(bool stolen); /** Receive a traffic frame. Non-blocking. Returns NULL if queue is dry. Caller is responsible for deleting the returned array. */ unsigned char *recvTCH() { return mSpeechQ.read(0); } /** Return count of internally-queued traffic frames. */ unsigned queueSize() const { return mSpeechQ.size(); } /** Return true if the uplink is dead. */ bool uplinkLost() const; }; /** This is base class for output-only encoders. These all have very thin L2/L3 and are driven by a clock instead of a FIFO. */ class GeneratorL1Encoder : public L1Encoder { private: Thread mSendThread; public: GeneratorL1Encoder( unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent) :L1Encoder(wTN,wMapping,wParent) { } void start(); protected: /** The generate method actually produces output bursts. */ virtual void generate() =0; /** The core service loop calls generate repeatedly. */ void serviceLoop(); /** Provide a C interface for pthreads. */ friend void *GeneratorL1EncoderServiceLoopAdapter(GeneratorL1Encoder*); }; void *GeneratorL1EncoderServiceLoopAdapter(GeneratorL1Encoder*); /** The L1 encoder for the sync channel (SCH). The SCH sends out an encoding of the current BTS clock. GSM 05.03 4.7. */ class SCHL1Encoder : public GeneratorL1Encoder { private: Parity mBlockCoder; ///< block parity coder BitVector mU; ///< u[], as per GSM 05.03 2.2 BitVector mE; ///< e[], as per GSM 05.03 2.2 BitVector mD; ///< d[], as per GSM 05.03 2.2 BitVector mP; ///< p[], as per GSM 05.03 2.2 BitVector mE1; ///< first half of e[] BitVector mE2; ///< second half of e[] public: SCHL1Encoder(L1FEC* wParent); protected: void generate(); }; /** The L1 encoder for the frequency correction channel (FCCH). The FCCH just sends bursts of zeroes at set points in the TDMA pattern. See GSM 05.02 5.2.4. */ class FCCHL1Encoder : public GeneratorL1Encoder { public: FCCHL1Encoder(L1FEC *wParent); protected: void generate(); }; /** L1 encoder for repeating non-dedicated control channels (BCCH). This have generator-like drive loops, but xCCH-like FEC. */ class NDCCHL1Encoder : public XCCHL1Encoder { protected: Thread mSendThread; public: NDCCHL1Encoder( unsigned wTN, const TDMAMapping& wMapping, L1FEC *wParent) :XCCHL1Encoder(wTN, wMapping, wParent) { } void start(); protected: virtual void generate() =0; /** The core service loop. */ void serviceLoop(); friend void *NDCCHL1EncoderServiceLoopAdapter(NDCCHL1Encoder*); }; void *NDCCHL1EncoderServiceLoopAdapter(NDCCHL1Encoder*); /** L1 encoder for the BCCH has generator filling behavior but xCCH-like FEC. */ class BCCHL1Encoder : public NDCCHL1Encoder { public: BCCHL1Encoder(L1FEC *wParent) :NDCCHL1Encoder(0,gBCCHMapping,wParent) {} private: void generate(); }; /** L1 decoder for the SACCH. Like any other control channel, but with hooks for power/timing control. The SI5 and SI5 generation will be handled in higher layers. */ class SACCHL1Encoder : public XCCHL1Encoder { private: SACCHL1FEC *mSACCHParent; /**@name Physical header, GSM 04.04 6, 7.1, 7.2 */ //@{ volatile float mOrderedMSPower; ///< ordered MS tx power level, dBm volatile float mOrderedMSTiming; ///< ordered MS timing advance in symbols //@} public: SACCHL1Encoder(unsigned wTN, const TDMAMapping& wMapping, SACCHL1FEC *wParent); void orderedMSPower(int power) { mOrderedMSPower = power; } void orderedMSTiming(int timing) { mOrderedMSTiming = timing; } void setPhy(const SACCHL1Encoder&); void setPhy(float RSSI, float timingError); /** Override open() to initialize power and timing. */ void open(); //bool active() const { return true; } protected: SACCHL1FEC *SACCHParent() { return mSACCHParent; } SACCHL1Decoder *SACCHSibling(); unsigned headerOffset() const { return 16; } /** A warpper to send an L2 frame with a physical header. */ virtual void sendFrame(const L2Frame&); }; /** The Common Control Channel (CCCH). Carries the AGCH, NCH, PCH. */ class CCCHL1Encoder : public XCCHL1Encoder { public: CCCHL1Encoder(const TDMAMapping& wMapping, L1FEC* wParent) :XCCHL1Encoder(0,wMapping,wParent) {} }; class SDCCHL1FEC : public L1FEC { public: SDCCHL1FEC( unsigned wTN, const MappingPair& wMapping) :L1FEC() { mEncoder = new XCCHL1Encoder(wTN,wMapping.downlink(),this); mDecoder = new SDCCHL1Decoder(wTN,wMapping.uplink(),this); } }; class TCHFACCHL1FEC : public L1FEC { protected: TCHFACCHL1Decoder * mTCHDecoder; TCHFACCHL1Encoder * mTCHEncoder; public: TCHFACCHL1FEC( unsigned wTN, const MappingPair& wMapping) :L1FEC() { mTCHEncoder = new TCHFACCHL1Encoder( wTN, wMapping.downlink(), this ); mEncoder = mTCHEncoder; mTCHDecoder = new TCHFACCHL1Decoder( wTN, wMapping.uplink(), this ); mDecoder = mTCHDecoder; } /** Send a traffic frame. */ void sendTCH(const unsigned char * frame) { assert(mTCHEncoder); mTCHEncoder->sendTCH(frame); } /** Receive a traffic frame. Returns a pointer that must be deleted by calls. Non-blocking. Returns NULL is no data available. */ unsigned char* recvTCH() { assert(mTCHDecoder); return mTCHDecoder->recvTCH(); } unsigned queueSize() const { assert(mTCHDecoder); return mTCHDecoder->queueSize(); } bool radioFailure() const { assert(mTCHDecoder); return mTCHDecoder->uplinkLost(); } }; class SACCHL1FEC : public L1FEC { private: SACCHL1Decoder *mSACCHDecoder; SACCHL1Encoder *mSACCHEncoder; public: SACCHL1FEC( unsigned wTN, const MappingPair& wMapping) :L1FEC() { mSACCHEncoder = new SACCHL1Encoder(wTN,wMapping.downlink(),this); mEncoder = mSACCHEncoder; mSACCHDecoder = new SACCHL1Decoder(wTN,wMapping.uplink(),this); mDecoder = mSACCHDecoder; } SACCHL1Decoder *decoder() { return mSACCHDecoder; } SACCHL1Encoder *encoder() { return mSACCHEncoder; } /**@name Physical parameter access. */ //@{ float RSSI() const { return mSACCHDecoder->RSSI(); } float timingError() const { return mSACCHDecoder->timingError(); } int actualMSPower() const { return mSACCHDecoder->actualMSPower(); } int actualMSTiming() const { return mSACCHDecoder->actualMSTiming(); } void setPhy(const SACCHL1FEC&); virtual void setPhy(float RSSI, float timingError); //@} }; class LoopbackL1FEC : public L1FEC { public: LoopbackL1FEC(unsigned wTN) :L1FEC() { mEncoder = new XCCHL1Encoder(wTN,gLoopbackTestFullMapping,this); mDecoder = new SDCCHL1Decoder(wTN,gLoopbackTestFullMapping,this); } }; /** The common control channel (CCCH). */ class CCCHL1FEC : public L1FEC { public: CCCHL1FEC(const TDMAMapping& wMapping) :L1FEC() { mEncoder = new CCCHL1Encoder(wMapping,this); } }; /** A subclass for channels that have L2 and L3 so thin that they are handled as special cases. These are all broadcast and unicast channels. */ class NDCCHL1FEC : public L1FEC { public: NDCCHL1FEC():L1FEC() {} void upstream(SAPMux*){ assert(0);} }; class FCCHL1FEC : public NDCCHL1FEC { public: FCCHL1FEC():NDCCHL1FEC() { mEncoder = new FCCHL1Encoder(this); } }; class RACHL1FEC : public NDCCHL1FEC { public: RACHL1FEC(const TDMAMapping& wMapping) :NDCCHL1FEC() { mDecoder = new RACHL1Decoder(wMapping,this); } }; class SCHL1FEC : public NDCCHL1FEC { public: SCHL1FEC():NDCCHL1FEC() { mEncoder = new SCHL1Encoder(this); } }; class BCCHL1FEC : public NDCCHL1FEC { public: BCCHL1FEC():NDCCHL1FEC() { mEncoder = new BCCHL1Encoder(this); } }; }; // namespace GSM #endif // vim: ts=4 sw=4