/* * Copyright 2008, 2014 Free Software Foundation, Inc. * Copyright 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. */ #define LOG_GROUP LogGroup::GSM // Can set Log.Level.GSM for debugging #include #include "GSMTransfer.h" #include "GSML3Message.h" //#include "Globals.h" using namespace std; using namespace GSM; ostream& GSM::operator<<(ostream& os, const L2Frame& frame) { os << LOGVAR2("primitive",frame.primitive()) <MTI()) <<"("<hex(os); os << "))"; } ostream& GSM::operator<<(ostream& os, const L3Frame& frame) { frame.text(os); return os; } ostream& GSM::operator<<(ostream& os, const L3Frame* frame) { if (frame) { os << *frame; } else { os << "(null L3Frame)"; } return os; } ostream& GSM::operator<<(ostream& os, const L2Header::FrameFormat val) { switch (val) { case L2Header::FmtA: os << "A"; break; case L2Header::FmtB: os << "B"; break; case L2Header::FmtBbis: os << "Bbis"; break; case L2Header::FmtBter: os << "Bter"; break; case L2Header::FmtB4: os << "B4"; break; case L2Header::FmtC: os << "C"; break; default: os << "?" << (int)val << "?"; } return os; } size_t N201(ChannelType wChanType, L2Header::FrameFormat wFormat) { // Number of payload bytes in the L2Frame. // GSM 04.06 5.8.3 switch (wFormat) { case L2Header::FmtA: case L2Header::FmtB: switch (wChanType) { case SACCHType: return 18; case FACCHType: case SDCCHType: return 20; default: abort(); } case L2Header::FmtBbis: // We count L2 pseudolength as part of the header. switch (wChanType) { case BCCHType: case CCCHType: return 22; default: abort(); } case L2Header::FmtBter: switch (wChanType) { case SACCHType: return 21; case FACCHType: case SDCCHType: return 23; default: abort(); } case L2Header::FmtB4: switch (wChanType) { case SACCHType: return 19; default: abort(); } default: abort(); } } size_t L2Header::bitsNeeded() const { // number of BITS to encode the header // GSM 04.06 2.1 switch (mFormat) { case FmtA: case FmtB: return 3*8; case FmtBbis: return 8; case FmtBter: return 0; case FmtB4: return 2*8; case FmtC: return 0; default: abort(); } } size_t L2Header::write(L2Frame& frame) const { size_t wp=0; switch (mFormat) { case FmtA: case FmtB: mAddress.write(frame,wp); mControl.write(frame,wp); mLength.write(frame,wp); break; case FmtBbis: mLength.write(frame,wp); break; case FmtBter: break; case FmtB4: mAddress.write(frame,wp); mControl.write(frame,wp); break; case FmtC: break; default: assert(0); } return wp; } void L2Control::write(L2Frame& frame, size_t& wp) const { // GSM 04.06 Table 3 switch (mFormat) { case IFormat: { frame.writeField(wp,mNR,3); frame.writeField(wp,mPF,1); frame.writeField(wp,mNS,3); frame.writeField(wp,0,1); break; } case SFormat:{ frame.writeField(wp,mNR,3); frame.writeField(wp,mPF,1); frame.writeField(wp,mSBits,2); frame.writeField(wp,1,2); break; } case UFormat:{ const unsigned U1 = mUBits >> 2; const unsigned U2 = mUBits & 0x03; frame.writeField(wp,U1,3); frame.writeField(wp,mPF,1); frame.writeField(wp,U2,2); frame.writeField(wp,3,2); break; } default: abort(); } } void L2Length::write(L2Frame& frame, size_t& wp) const { // GSM 04.06 3.6 assert(mL<64); frame.writeField(wp,mL,6); frame.writeField(wp,mM,1); frame.writeField(wp,1,1); } void L2Frame::idleFill() { // GSM 04.06 2.2 static const int pattern[8] = {0,0,1,0,1,0,1,1}; for (size_t i=0; isize()); bits.copyTo(*this); } L2Frame::L2Frame(const L2Header& header, const BitVector& l3, bool noran) :BitVector(23*8),mPrimitive(L2_DATA) { idleFill(); //printf("header.bitsNeeded=%d l3.size=%d this.size=%d\n", //header.bitsNeeded(),l3.size(),this->size()); assert((header.bitsNeeded()+l3.size())<=this->size()); size_t wp = header.write(*this); l3.copyToSegment(*this,wp); // FIXME - figure out why randomizeFiller doesn't like the "noran" headers if (gConfig.getBool("GSM.Cipher.ScrambleFiller") && !noran) randomizeFiller(header); } L2Frame::L2Frame(const L2Header& header) :BitVector(23*8),mPrimitive(L2_DATA) { idleFill(); header.write(*this); if (gConfig.getBool("GSM.Cipher.ScrambleFiller")) randomizeFiller(header); } unsigned L2Frame::SAPI() const { // Get the service access point indicator, // assuming frame format A or B. // See GSM 04.06 2.1, 2.3, 3.2. // This assumes MSB-first field packing. // (pat) If the frame is a primitive, the size is 0 and sapi is not saved in the L2Frame. // The L3Frame will be rebound with the correct SAPI in L2LAPDm. return size()>8 ? peekField(3,3) : SAPIUndefined; } unsigned L2Frame::LPD() const { // This assumes MSB-first field packing. // See GSM 04.06 3.2, GSM 04.12 3.3.1. return peekField(1,2); } L2Control::ControlFormat L2Frame::controlFormat() const { // Determine the LAPDm frame type. // See GSM 04.06 Table 3. // The control field is in the second octet. if (bit(8+7)==0) return L2Control::IFormat; if (bit(8+6)==0) return L2Control::SFormat; return L2Control::UFormat; } L2Control::FrameType L2Frame::UFrameType() const { // Determine U-frame command type. // GSM 04.06 Table 4 (in section 3.8.1, obviously) // TODO -- This would be more efficient as an array. char upper = peekField(8+0,3); char lower = peekField(8+4,2); char uBits = upper<<2 | lower; switch (uBits) { case 0x07: return L2Control::SABMFrame; case 0x03: return L2Control::DMFrame; // (disconnect mode) case 0x00: return L2Control::UIFrame; // (unnumbered information) case 0x08: return L2Control::DISCFrame; // (disconnect) case 0x0c: return L2Control::UAFrame; // (unnumbered acknowledge) default: return L2Control::BogusFrame; } } L2Control::FrameType L2Frame::SFrameType() const { // Determine S-frame command type. // GSM 04.06 Table 4. char sBits = peekField(8+4,2); static const L2Control::FrameType type[] = { L2Control::RRFrame, L2Control::RNRFrame, L2Control::REJFrame, L2Control::BogusFrame }; return type[(int)sBits]; } const L2Frame& GSM::L2IdleFrame() { static volatile bool init = false; static L2Frame idleFrame; if (!init) { init = true; // GSM 04.06 5.4.2.3. // As sent by the network. idleFrame.fillField(8*0,3,8); // address idleFrame.fillField(8*1,3,8); // control idleFrame.fillField(8*2,1,8); // length if (gConfig.getBool("GSM.Cipher.ScrambleFiller")) idleFrame.randomizeFiller(8*4); } return idleFrame; } ostream& GSM::operator<<(ostream& os, const TxBurst& ts) { os << "time=" << ts.time() << " data=(" << (const BitVector&)ts << ")" ; return os; } ostream& GSM::operator<<(ostream& os, const RxBurst& ts) { os << "time=" << ts.time(); os << " RSSI=" << ts.RSSI() << " timing=" << ts.timingError(); os << " data=(" << (const SoftVector&)ts << ")" ; return os; } // We put this in the .cpp file to avoid a circular dependency. TxBurst::TxBurst(const RxBurst& rx) :BitVector((const BitVector&)rx),mTime(rx.time()) {} // We put this in the .cpp file to avoid a circular dependency. RxBurst::RxBurst(const TxBurst& source, float wTimingError, int wRSSI) :SoftVector((const BitVector&) source),mTime(source.time()), mTimingError(wTimingError),mRSSI(wRSSI) { } // (pat 1-2014) Compute the SNR of an RxBurst that is a GSM "normal burst", ie, // one used for TCH/FACCH rather than for RACCH or other. // For this case we ignore the tail bits and training bits and consider only the data bits. float RxBurst::getNormalSNR() const { SoftVector chunk1(this->segment(3,58)); // 57 data bits + stealing bit. float snr1 = chunk1.getSNR(); SoftVector chunk2(this->segment(87,58)); // stealing bit + 57 data bits. float snr2 = chunk2.getSNR(); assert(! chunk1.isOwner()); // Make sure the stupid SoftVector class really returned an alias. return (snr1 + snr2) / 2.0; } // Methods for the L2 address field. void L2Address::write(L2Frame& frame, size_t& wp) const { // GSM 04.06 3.2, 3.3 frame.writeField(wp,0,1); // spare bit frame.writeField(wp,mLPD,2); frame.writeField(wp,mSAPI,3); frame.writeField(wp,mCR,1); frame.writeField(wp,1,1); // no extension } ostream& GSM::operator<<(ostream& os, const L2Address& address) { os << "SAPI=" << address.SAPI() << " CR=" << address.CR(); return os; } ostream& GSM::operator<<(ostream& os,L2Control::ControlFormat format) { switch (format) { case L2Control::IFormat: os << "I"; break; case L2Control::SFormat: os << "S"; break; case L2Control::UFormat: os << "U"; break; default: os << "?" << (int)format << "?"; break; } return os; } ostream& GSM::operator<<(ostream& os, const L2Control& control) { os << "fmt=" << control.format() << " "; switch (control.format()) { case L2Control::IFormat: os << "NR=" << control.NR() << " PF=" << control.PF() << " NS=" << control.NS(); break; case L2Control::SFormat: os << "NR=" << control.NR() << " PF=" << control.PF(); os << hex << " Sbits=0x" << control.SBits() << dec; break; case L2Control::UFormat: os << "PF=" << control.PF(); os << hex << " Ubits=0x" << control.UBits() << dec; break; default: os << "??"; } return os; } ostream& GSM::operator<<(ostream& os, const L2Length& length) { os << "L=" << length.L() << " M=" << length.M(); return os; } ostream& GSM::operator<<(ostream& os, const L2Header& header) { os << "fmt=" << header.format(); os << " addr=(" << header.address() << ")"; os << " cntrl=(" << header.control() << ")"; os << " len=(" << header.length() << ")"; return os; } ostream& GSM::operator<<(ostream& os, L2Control::FrameType cmd) { switch (cmd) { case L2Control::UIFrame: os << "UI"; break; case L2Control::SABMFrame: os << "SABM"; break; case L2Control::UAFrame: os << "UA"; break; case L2Control::DMFrame: os << "DM"; break; case L2Control::DISCFrame: os << "DISC"; break; case L2Control::RRFrame: os << "RR"; break; case L2Control::RNRFrame: os << "RNR"; break; case L2Control::REJFrame: os << "REJ"; break; case L2Control::IFrame: os << "I"; break; default: os << "?" << (int)cmd << "?"; } return os; } #define CASENAME_OS(x) case x: os << #x; break; ostream& GSM::operator<<(ostream& os, Primitive prim) { switch (prim) { CASENAME_OS(L2_DATA) CASENAME_OS(L3_DATA) CASENAME_OS(L3_DATA_CONFIRM) CASENAME_OS(L3_UNIT_DATA) CASENAME_OS(L3_ESTABLISH_REQUEST) CASENAME_OS(L3_ESTABLISH_INDICATION) CASENAME_OS(L3_ESTABLISH_CONFIRM) CASENAME_OS(L3_RELEASE_REQUEST) CASENAME_OS(L3_RELEASE_CONFIRM) CASENAME_OS(L3_HARDRELEASE_REQUEST) CASENAME_OS(MDL_ERROR_INDICATION) CASENAME_OS(L3_RELEASE_INDICATION) CASENAME_OS(PH_CONNECT) CASENAME_OS(HANDOVER_ACCESS) /** case ESTABLISH: os << "ESTABLISH"; break; case RELEASE: os << "RELEASE"; break; case DATA: os << "DATA"; break; case UNIT_DATA: os << "UNIT_DATA"; break; case ERROR: os << "ERROR"; break; case HARDRELEASE: os << "HARDRELEASE"; break; case HANDOVER_ACCESS: os << "HANDOVER_ACCESS"; break; **/ //old:os << "?" << (int)prim << "?"; default: os << "(unrecognized primitive:" << (int)prim << ")"; } return os; } const char *GSM::SAPI2Text(SAPI_t sapi) { switch (sapi) { case SAPI0: return "SAPI0"; case SAPI3: return "SAPI3"; case SAPI0Sacch: return "SAPI0-Sacch"; case SAPI3Sacch: return "SAPI3-Sacch"; case SAPIUndefined: return "SAPIUndefined"; default: return "SAP-Invalid!"; } } ostream& GSM::operator<<(ostream& os, SAPI_t sapi) { os << SAPI2Text(sapi); return os; } void L3Frame::f3init() { mTimestamp = timef(); } L3Frame::L3Frame(const L3Message& msg, Primitive wPrimitive, SAPI_t wSapi) :BitVector(msg.bitsNeeded()),mPrimitive(wPrimitive),mSapi(wSapi), mL2Length(msg.l2Length()) { f3init(); msg.write(*this); } L3Frame::L3Frame(SAPI_t sapi,const char* hexString) :mPrimitive(L3_DATA),mSapi(sapi) { f3init(); size_t len = strlen(hexString); mL2Length = len/2; resize(len*4); size_t wp=0; for (size_t i=0; i9) v = c - 'a' + 10; writeField(wp,v,4); } } // (pat) 9-8-2014 removed, unused. //L3Frame::L3Frame(const char* binary, size_t len) // :mPrimitive(L3_DATA),mSapi(SAPIUndefined) //{ // f3init(); // mL2Length = len; // resize(len*8); // size_t wp=0; // for (size_t i=0; ibegin(); for (int i = headerSizeBits(mMode), n = result->size(); n > 0; i++, n--) { *rp++ = getBit(i); } } // vim: ts=4 sw=4