| /* |
| * Copyright 2008 Free Software Foundation, Inc. |
| * |
| * This software is distributed under the terms of the GNU Public License. |
| * See the COPYING file in the main directory for details. |
| * |
| * This use of this software may be subject to additional restrictions. |
| * See the LEGAL file in the main directory for details. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| */ |
| |
| #include "radioInterface.h" |
| #include "Interthread.h" |
| #include "GSMCommon.h" |
| #include "Sockets.h" |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| |
| class Transceiver; |
| |
| /** Channel descriptor for transceiver object and channel number pair */ |
| struct TransceiverChannel { |
| TransceiverChannel(Transceiver *trx, int num) |
| { |
| this->trx = trx; |
| this->num = num; |
| } |
| |
| ~TransceiverChannel() |
| { |
| } |
| |
| Transceiver *trx; |
| size_t num; |
| }; |
| |
| /** Internal transceiver state variables */ |
| struct TransceiverState { |
| TransceiverState(); |
| ~TransceiverState(); |
| |
| /* Initialize a multiframe slot in the filler table */ |
| bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); |
| |
| int chanType[8]; |
| |
| /* Last timestamp of each timeslot's channel estimate */ |
| GSM::Time chanEstimateTime[8]; |
| |
| /* The filler table */ |
| signalVector *fillerTable[102][8]; |
| int fillerModulus[8]; |
| bool mRetrans; |
| |
| /* Most recent channel estimate of all timeslots */ |
| signalVector *chanResponse[8]; |
| |
| /* Most recent DFE feedback filter of all timeslots */ |
| signalVector *DFEForward[8]; |
| signalVector *DFEFeedback[8]; |
| |
| /* Most recent SNR, timing, and channel amplitude estimates */ |
| float SNRestimate[8]; |
| float chanRespOffset[8]; |
| complex chanRespAmplitude[8]; |
| |
| /* Received noise energy levels */ |
| float mNoiseLev; |
| noiseVector mNoises; |
| |
| /* Shadowed downlink attenuation */ |
| int mPower; |
| }; |
| |
| /** The Transceiver class, responsible for physical layer of basestation */ |
| class Transceiver { |
| public: |
| /** Transceiver constructor |
| @param wBasePort base port number of UDP sockets |
| @param TRXAddress IP address of the TRX manager, as a string |
| @param wSPS number of samples per GSM symbol |
| @param wTransmitLatency initial setting of transmit latency |
| @param radioInterface associated radioInterface object |
| */ |
| Transceiver(int wBasePort, |
| const char *TRXAddress, |
| size_t tx_sps, size_t rx_sps, size_t chans, |
| GSM::Time wTransmitLatency, |
| RadioInterface *wRadioInterface, |
| double wRssiOffset); |
| |
| /** Destructor */ |
| ~Transceiver(); |
| |
| /** Start the control loop */ |
| bool init(int filler, size_t rtsc, unsigned rach_delay); |
| |
| /** attach the radioInterface receive FIFO */ |
| bool receiveFIFO(VectorFIFO *wFIFO, size_t chan) |
| { |
| if (chan >= mReceiveFIFO.size()) |
| return false; |
| |
| mReceiveFIFO[chan] = wFIFO; |
| return true; |
| } |
| |
| /** accessor for number of channels */ |
| size_t numChans() const { return mChans; }; |
| |
| /** Codes for channel combinations */ |
| typedef enum { |
| FILL, ///< Channel is transmitted, but unused |
| I, ///< TCH/FS |
| II, ///< TCH/HS, idle every other slot |
| III, ///< TCH/HS |
| IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH |
| V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4 |
| VI, ///< CCCH+BCCH, uplink RACH |
| VII, ///< SDCCH/8 + SACCH/8 |
| VIII, ///< TCH/F + FACCH/F + SACCH/M |
| IX, ///< TCH/F + SACCH/M |
| X, ///< TCH/FD + SACCH/MD |
| XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH |
| XII, ///< PCCCH+PDTCH+PACCH+PTCCH |
| XIII, ///< PDTCH+PACCH+PTCCH |
| NONE, ///< Channel is inactive, default |
| LOOPBACK ///< similar go VII, used in loopback testing |
| } ChannelCombination; |
| |
| /** Codes for burst types of received bursts*/ |
| typedef enum { |
| OFF, ///< timeslot is off |
| TSC, ///< timeslot should contain a normal burst |
| RACH, ///< timeslot should contain an access burst |
| EDGE, ///< timeslot should contain an EDGE burst |
| IDLE ///< timeslot is an idle (or dummy) burst |
| } CorrType; |
| |
| enum FillerType { |
| FILLER_DUMMY, |
| FILLER_ZERO, |
| FILLER_NORM_RAND, |
| FILLER_EDGE_RAND, |
| FILLER_ACCESS_RAND, |
| }; |
| |
| private: |
| int mBasePort; |
| std::string mAddr; |
| |
| std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core |
| std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core |
| UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core |
| |
| std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core |
| std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts |
| |
| std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO |
| Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO |
| Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO |
| std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core |
| std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core |
| |
| GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock |
| GSM::Time mLatencyUpdateTime; ///< last time latency was updated |
| GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO |
| GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core |
| |
| RadioInterface *mRadioInterface; ///< associated radioInterface object |
| double txFullScale; ///< full scale input to radio |
| double rxFullScale; ///< full scale output to radio |
| |
| double rssiOffset; ///< RSSI to dBm conversion offset |
| |
| /** modulate and add a burst to the transmit queue */ |
| void addRadioVector(size_t chan, BitVector &bits, |
| int RSSI, GSM::Time &wTime); |
| |
| /** Update filler table */ |
| void updateFillerTable(size_t chan, radioVector *burst); |
| |
| /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */ |
| void pushRadioVector(GSM::Time &nowTime); |
| |
| /** Pull and demodulate a burst from the receive FIFO */ |
| SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid, |
| double &timingOffset, double &noise, |
| size_t chan = 0); |
| |
| /** Set modulus for specific timeslot */ |
| void setModulus(size_t timeslot, size_t chan); |
| |
| /** return the expected burst type for the specified timestamp */ |
| CorrType expectedCorrType(GSM::Time currTime, size_t chan); |
| |
| /** send messages over the clock socket */ |
| void writeClockInterface(void); |
| |
| /** Detectbursts */ |
| int detectBurst(signalVector &burst, |
| complex &, float &toa, CorrType type); |
| |
| /** Demodulate burst and output soft bits */ |
| SoftVector *demodulate(signalVector &burst, |
| complex amp, float toa, CorrType type); |
| |
| int mSPSTx; ///< number of samples per Tx symbol |
| int mSPSRx; ///< number of samples per Rx symbol |
| size_t mChans; |
| |
| bool mOn; ///< flag to indicate that transceiver is powered on |
| bool mHandover[8][8]; ///< expect handover to the timeslot/subslot |
| double mTxFreq; ///< the transmit frequency |
| double mRxFreq; ///< the receive frequency |
| unsigned mTSC; ///< the midamble sequence code |
| unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH) |
| unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts |
| unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk |
| |
| std::vector<TransceiverState> mStates; |
| |
| /** Start and stop I/O threads through the control socket API */ |
| bool start(); |
| void stop(); |
| |
| /** Protect destructor accessable stop call */ |
| Mutex mLock; |
| |
| protected: |
| /** drive lower receive I/O and burst generation */ |
| void driveReceiveRadio(); |
| |
| /** drive demodulation of GSM bursts */ |
| void driveReceiveFIFO(size_t chan); |
| |
| /** drive transmission of GSM bursts */ |
| void driveTxFIFO(); |
| |
| /** drive handling of control messages from GSM core */ |
| void driveControl(size_t chan); |
| |
| /** |
| drive modulation and sorting of GSM bursts from GSM core |
| @return true if a burst was transferred successfully |
| */ |
| bool driveTxPriorityQueue(size_t chan); |
| |
| friend void *RxUpperLoopAdapter(TransceiverChannel *); |
| |
| friend void *TxUpperLoopAdapter(TransceiverChannel *); |
| |
| friend void *RxLowerLoopAdapter(Transceiver *); |
| |
| friend void *TxLowerLoopAdapter(Transceiver *); |
| |
| friend void *ControlServiceLoopAdapter(TransceiverChannel *); |
| |
| |
| void reset(); |
| |
| /** set priority on current thread */ |
| void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); } |
| |
| void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm, |
| double rssi, double noise, double toa); |
| }; |
| |
| void *RxUpperLoopAdapter(TransceiverChannel *); |
| |
| /** Main drive threads */ |
| void *RxLowerLoopAdapter(Transceiver *); |
| void *TxLowerLoopAdapter(Transceiver *); |
| |
| /** control message handler thread loop */ |
| void *ControlServiceLoopAdapter(TransceiverChannel *); |
| |
| /** transmit queueing thread loop */ |
| void *TxUpperLoopAdapter(TransceiverChannel *); |
| |