607 lines
18 KiB
C++
607 lines
18 KiB
C++
/**@file Objects for transferring between UMTS layers (frames, blocks, etc.) */
|
|
|
|
/*
|
|
* OpenBTS provides an open source alternative to legacy telco protocols and
|
|
* traditionally complex, proprietary hardware systems.
|
|
*
|
|
* Copyright 2011, 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero General
|
|
* Public License version 3. See the COPYING and NOTICE files in the main
|
|
* directory for licensing information.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
*/
|
|
|
|
#ifndef UMTSTRANSFER_H
|
|
#define UMTSTRANSFER_H
|
|
|
|
#include <BitVector.h>
|
|
#include <ByteVector.h>
|
|
#include "UMTSCommon.h"
|
|
#include "UMTSCodes.h"
|
|
#include "URRCDefs.h"
|
|
|
|
|
|
namespace UMTS {
|
|
|
|
class UEInfo;
|
|
|
|
/** The size of the BCH tranport block used for SIBs. */
|
|
static const unsigned sSIBTrBlockSize = 246;
|
|
|
|
|
|
/**
|
|
Interlayer primitives.
|
|
RLC, MAC, PHY, etc. are implied by context.
|
|
We don't provide the full req-conf-ind-ack handshake because we
|
|
don't always need it in such a tighly integrated system, so
|
|
our primitive set is simple.
|
|
*/
|
|
enum Primitive {
|
|
ESTABLISH, ///< channel establihsment
|
|
RELEASE, ///< normal channel release
|
|
DATA, ///< multiframe data transfer
|
|
UNIT_DATA, ///< datagram-type data transfer
|
|
STATUS, ///< channel status
|
|
CONFIG, ///< configuration control
|
|
ERROR, ///< channel error
|
|
HARDRELEASE ///< forced release after an assignment
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
Class to represent a pre-spread tx slot.
|
|
*/
|
|
class TxBitsBurst : public BitVector {
|
|
|
|
protected:
|
|
|
|
unsigned mSF; ///< spreading factor
|
|
unsigned mLog2SF; ///< log2 of spreading factor
|
|
unsigned mCodeIndex; ///< code number, i.e. index into code tree
|
|
Time mTime; ///< the time when this burst is to be transmitted
|
|
bool mDCH; ///< indicates if burst is a DCH (true) or CCH (false)
|
|
bool mAICH;
|
|
bool mRightJustified; ///< bits should be right justified w.r.t slot boundary
|
|
|
|
public:
|
|
|
|
TxBitsBurst(size_t wSF, size_t wCodeIndex, const Time& wTime, bool wDCH, bool wRightJustified=true)
|
|
:BitVector(gSlotLen/wSF),
|
|
mSF(wSF),mCodeIndex(wCodeIndex),
|
|
mTime(wTime),mDCH(wDCH),mRightJustified(wRightJustified)
|
|
{
|
|
mLog2SF = 0;
|
|
while (wSF > 1) {mLog2SF++; wSF = wSF >> 1;}
|
|
mAICH = false;
|
|
}
|
|
|
|
/** Create a TxBurst by copying from an existing BitVector. */
|
|
TxBitsBurst(const BitVector& bits, size_t wSF, size_t wCodeIndex, const Time& wTime, bool wRightJustified=false);
|
|
|
|
unsigned SF() const { return mSF; }
|
|
|
|
unsigned log2SF() const { return mLog2SF; }
|
|
|
|
unsigned codeIndex() const { return mCodeIndex; }
|
|
|
|
const Time& time() const { return mTime; }
|
|
|
|
bool DCH() const { return mDCH; }
|
|
|
|
void DCH(bool wDCH) {mDCH = wDCH;}
|
|
|
|
bool AICH() const {return mAICH;}
|
|
|
|
void AICH(bool wAICH) {mAICH = wAICH;}
|
|
|
|
bool rightJustified() const { return mRightJustified; }
|
|
|
|
std::ostream& text(std::ostream&os) const;
|
|
friend std::ostream& operator<<(std::ostream& os, const TxBitsBurst&tbb);
|
|
friend std::ostream& operator<<(std::ostream& os, const TxBitsBurst*ptbb);
|
|
|
|
|
|
/** comparison operator, used for sorting */
|
|
bool operator>(const TxBitsBurst& other) const {return mTime > other.mTime;}
|
|
|
|
};
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TxBitsBurst&);
|
|
std::ostream& operator<<(std::ostream& os, const TxBitsBurst*);
|
|
|
|
|
|
|
|
|
|
class RxChipsBurst;
|
|
|
|
/**
|
|
Class to represent a post-de-spread received burst with soft decoding.
|
|
This class represents only the I-part or Q-part, not a full complex signal.
|
|
*/
|
|
class RxBitsBurst : public SoftVector {
|
|
|
|
protected:
|
|
|
|
// pat says: Do we need the spreading factor in here? I'm not using it.
|
|
unsigned mSFI; ///< spreading factor index (log2 of the SF)
|
|
Time mTime; // Must be provided, or havoc will ensue.
|
|
float mTimingError; // Pats todo: accumulate this in the UEInfo struct.
|
|
float mRSSI; // Pats todo: accumulate this in the UEInfo struct.
|
|
public:
|
|
// (pats comment) There are a maximum of two tfcibits in each burst,
|
|
// the exact number is determined by the slot format chosen from the many
|
|
// options in UMTSPhCh.cpp. Then there are 15 (gFrameSlot) bursts
|
|
// in each radio frame, making a total of 30 (gUlTfciSize) tfci bits/radio frame,
|
|
// then the 30 bits are condensed down using sTfciCodes or findTfci
|
|
// to a small TFCI code (usually length 2, 4, or 8, we dont use 10)
|
|
// that are necessary to choose the TFC, which then specifies the TF for
|
|
// all TrCh simultaneously.
|
|
float mTfciBits[2]; // The (max) two tfcibits from this burst, in the range 0..1.
|
|
|
|
|
|
/** Wrap an RxDataBurst around an existing float array. */
|
|
RxBitsBurst(size_t wSFI, float* bits, const UMTS::Time &wTime, float wTimingError, int wRSSI)
|
|
:SoftVector(bits,gSlotLen/(1<<wSFI)),mSFI(wSFI),mTime(wTime),
|
|
mTimingError(wTimingError),mRSSI(wRSSI)
|
|
{
|
|
mTfciBits[0] = -1; // Mark as invalid so we can detect that.
|
|
}
|
|
|
|
RxBitsBurst(size_t wSFI, float* bits, size_t sz, const UMTS::Time &wTime, float wTimingError, int wRSSI)
|
|
:SoftVector(bits,sz),mSFI(wSFI),mTime(wTime),
|
|
mTimingError(wTimingError),mRSSI(wRSSI)
|
|
{
|
|
mTfciBits[0] = -1; // Mark as invalid so we can detect that.
|
|
}
|
|
|
|
unsigned SFI() const { return mSFI; }
|
|
unsigned SF() const { return 1<<mSFI; }
|
|
|
|
Time time() const { return mTime; }
|
|
|
|
void time(const Time& wTime) { mTime = wTime; }
|
|
|
|
float RSSI() const { return mRSSI; }
|
|
|
|
float timingError() const { return mTimingError; }
|
|
|
|
/** Given chips and a spreading sequence, despread this received bit vector. */
|
|
void despread(const char* code, const RxChipsBurst& source);
|
|
|
|
/** Apply a scrambling code. */
|
|
void descramble(const char* code);
|
|
|
|
friend std::ostream& operator<<(std::ostream& os, const RxBitsBurst&);
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const RxBitsBurst&);
|
|
|
|
/**
|
|
Class to represent a pre-de-spread received burst with soft decoding.
|
|
This class represents only the I-part or Q-part, not a full complex signal.
|
|
*/
|
|
class RxChipsBurst : public SoftVector {
|
|
|
|
protected:
|
|
|
|
Time mTime;
|
|
float mTimingError;
|
|
float mRSSI;
|
|
|
|
public:
|
|
|
|
/** Wrap an RxChipsBurst around an existing float array. */
|
|
RxChipsBurst(float* chips, size_t wSz, const Time &wTime, float wTimingError, int wRSSI)
|
|
:SoftVector(chips,wSz),mTime(wTime),
|
|
mTimingError(wTimingError),mRSSI(wRSSI)
|
|
{ }
|
|
|
|
Time time() const { return mTime; }
|
|
|
|
void time(const Time& wTime) { mTime = wTime; }
|
|
|
|
float RSSI() const { return mRSSI; }
|
|
|
|
float timingError() const { return mTimingError; }
|
|
|
|
friend std::ostream& operator<<(std::ostream& os, const RxChipsBurst&);
|
|
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const RxChipsBurst&);
|
|
|
|
|
|
|
|
|
|
/**
|
|
The transport block is passed on the simple L1/MAC interface,
|
|
which supports only one TrCh per L1 connection per RadioFrame.
|
|
This class is over-ridden in the MAC engine to aid in TransportBlock creation,
|
|
but only the information here is needed to be passed on the L1/MAC interface.
|
|
*/
|
|
class TransportBlock : public BitVector {
|
|
|
|
UMTS::Time mTime; ///< the time when this block was receive or is to be transmitted.
|
|
public:
|
|
bool mScheduled; ///< if false, ignore mTime
|
|
// (pat) No, the tfci does not go in the TB, it goes in the TBS.
|
|
//unsigned mTfci; ///< the TFCI [Transport Format Combination Index] assigned by MAC.
|
|
std::string mDescr; ///< Optional description of what is in it.
|
|
|
|
|
|
TransportBlock(const BitVector& bits)
|
|
:BitVector(bits),mScheduled(false)//,mTfci(0)
|
|
{ }
|
|
|
|
TransportBlock(const BitVector& bits, const UMTS::Time& wTime)
|
|
:BitVector(bits),
|
|
mTime(wTime),mScheduled(true)//,mTfci(0)
|
|
{ }
|
|
|
|
TransportBlock(size_t sz)
|
|
:BitVector(sz),mScheduled(false)//,mTfci(0)
|
|
{ }
|
|
|
|
TransportBlock(size_t sz, const UMTS::Time& wTime)
|
|
:BitVector(sz),
|
|
mTime(wTime),mScheduled(true)//,mTfci(0)
|
|
{ }
|
|
|
|
TransportBlock(const TransportBlock& block, const UMTS::Time& wTime)
|
|
:BitVector(block),
|
|
mTime(wTime),mScheduled(true)//,mTfci(0)
|
|
{ }
|
|
|
|
void setSchedule(unsigned framenum) { mTime = Time(framenum); mScheduled = true; }
|
|
|
|
bool scheduled() const { return mScheduled;}
|
|
|
|
const UMTS::Time& time() const { assert(mScheduled); return mTime; }
|
|
void time(const UMTS::Time& wTime) { mTime = wTime; mScheduled=true; }
|
|
|
|
void encodeParity(Parity& parity, BitVector& bBits) const;
|
|
|
|
friend std::ostream& operator<<(std::ostream& os, const TransportBlock&);
|
|
friend std::ostream& operator<<(std::ostream& os, const TransportBlock*);
|
|
void text(std::ostream &os) const;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TransportBlock&);
|
|
std::ostream& operator<<(std::ostream& os, const TransportBlock*);
|
|
|
|
// The MacTbs is passed on the L1/MAC interface, when it is necessary to support
|
|
// multiple Trch or multiple blocks per RadioFrame.
|
|
// MAC has already encoded the logical channels so all that is left here is
|
|
// the TransportBlock data per TrCh, and the selected TFC to be used by L1.
|
|
// FYI: UMTS has four important flow identifiers:
|
|
// o RAB-id is the CN level identifier.
|
|
// For PS [packet switched] channels it is in the range 5..15
|
|
// o RB-id is the RRC level identifier.
|
|
// RB-id 1-4 are reserved as SRB1 - SRB4 [Signaling Radio Bearer] for communication with RRC.
|
|
// SRB0 is not an official channel in that no RB-id will ever be 0,
|
|
// but it used for CCCH messages that do not have an RB-id assigned yet.
|
|
// The other RB-id can be assigned by RRC. Each RAB-id has one or more RB-id assigned,
|
|
// called RAB subflows.
|
|
// o The logical channel id is used by the MAC header to distinguish RB-ids on the PHY interface.
|
|
// o The TrCh distinguishes Transport Channels, of course.
|
|
// Each RB-id sub-flow can be assigned either a unique TrCh with no mac multiplexing,
|
|
// or unique logical channel ids for mac multiplexing on a shared TrCh.
|
|
// We currently use the RB-id and the logical channel id, and for CS channels,
|
|
// we use one-to-one mapping of RAB-id onto RB-id, ie, RAB 5-15 are RB 15.
|
|
// For voice channels, there should be just one RAB-id which will use sub-flows RB5,6,7
|
|
// for the AMR data.
|
|
// Notes:
|
|
// DCCH carries SRB1,2,3,4, and DTCH carries RB5 for IP data, or RB5,6,7 for voice data.
|
|
// From 25.331 [RRC] 8.5.21 it looks to me like DTCH and DCCH need to both be on
|
|
// the same physical channel, either DCH or RACH/FACH (more precisely, PRACH/SCCPCH.)
|
|
// Therefore, either the lower layer is going to have to support multiple TrCh or
|
|
// the TFCS will have to specify just one TrCh at a time and let MAC do the switching.
|
|
class RrcTfc;
|
|
class MacTbs : public virtual RrcDefs
|
|
{ public:
|
|
RrcTfc *mTfc; // The TFC selected by MAC.
|
|
UMTS::Time mTime; // time MacTbs to be transmitted, used when number of blocks is zero
|
|
// The transport channels here are indexed starting at 0,
|
|
// which is the transport channel number minus 1.
|
|
struct TbList {
|
|
unsigned mNumTb;
|
|
TransportBlock *mTb[maxTbPerTrCh];
|
|
TbList() : mNumTb(0) { memset(mTb,0,sizeof(mTb)); }
|
|
void addTb1(TransportBlock *tb) { assert(mNumTb < maxTbPerTrCh); mTb[mNumTb++] = tb; }
|
|
} mTrChTb[maxTrCh];
|
|
|
|
// Add a transport block for the specified TrCh.
|
|
// Note that we used 0 based TrCh id here.
|
|
void addTb(TransportBlock *tb, TrChId tcid = 0) { mTrChTb[tcid].addTb1(tb); }
|
|
|
|
unsigned getNumTb(TrChId tcid = 0) { return mTrChTb[tcid].mNumTb; }
|
|
|
|
void clear(void) {
|
|
for (unsigned i = 0; i < maxTrCh; i ++) {
|
|
for (unsigned j = 0; j < mTrChTb[i].mNumTb; j++) delete mTrChTb[i].mTb[j];
|
|
}
|
|
}
|
|
|
|
// Return the specified TB or null.
|
|
TransportBlock *getTB(unsigned tbid, TrChId tcid) const {
|
|
assert(tcid < maxTrCh);
|
|
assert(tbid < maxTbPerTrCh);
|
|
return tbid < mTrChTb[tcid].mNumTb ? mTrChTb[tcid].mTb[tbid] : NULL;
|
|
}
|
|
|
|
// Assuming there is only one TransportBlock in this TBS, return it, or NULL.
|
|
TransportBlock *getOneTB() const { return getTB(0,0); }
|
|
|
|
MacTbs(RrcTfc *wTfc) : mTfc(wTfc) { }
|
|
|
|
~MacTbs() {
|
|
/*for (int i = 0; i < maxTrCh; i++) {
|
|
for (int j = 0; j < mTrChTb[i].mNumTb; j++) delete mTrChTb[i].mTb[j];
|
|
}*/
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if 0 // DABs start on this:
|
|
|
|
class MACSDU : public BitVector {
|
|
|
|
public:
|
|
|
|
MACSDU(const BitVector& bits):BitVector(bits) {}
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
(DAB) The MAC-PDU is passed between the MAC and the RLC.
|
|
From 3GPP 25.321 9.1.2: A MAC PDU consists of an optional
|
|
MAC header and a MAC Service Data Unit (MAC SDU), see figure 9.1.2.1.
|
|
Both the MAC header and the MAC SDU are of variable size. The
|
|
content and the size of the MAC header depends on the type of the
|
|
logical channel, and in some cases none of the parameters in the MAC
|
|
header are needed. The size of the MAC-SDU depends on the size of
|
|
the RLC-PDU, which is defined during the setup procedure.
|
|
*/
|
|
class MACPDU : public BitVector {
|
|
|
|
public:
|
|
|
|
// This only applies to FACH.
|
|
// If there is a FACH_MACPDU class, they should probably be there instead of here.
|
|
// The size of this field depends on the channel type and the data itself.
|
|
virtual size_t TCTFBase() const { return 0; }
|
|
virtual size_t TCTFSize() const =0;
|
|
virtual unsigned TCTF() const { return peekField(TCTFBase(),TCTFSize()); }
|
|
|
|
// The size of this field depend on the channel type.
|
|
virtual size_t UEIdTypeBase() const { return TCTFBase()+TCTFSize(); }
|
|
virtual size_t UEIdTypeSize() const =0;
|
|
virtual unsigned UEIdType() const { return peekField(UEIdBase(),UEIdSize()); }
|
|
|
|
// The size of this field depends on the value of UEIdType().
|
|
virtual size_t UEIdBase() const { return TCTFBase()+TCTFSize(); }
|
|
virtual size_t UEIdSize() const;
|
|
virtual unsigned UEId() const { return peekField(UEIdBase(),UEIdSize()); }
|
|
|
|
virtual size_t CTBase() const { return UEIdBase() + UEIdSize(); }
|
|
virtual size_t CTSize() const { return 4; }
|
|
virtual unsigned CT() const { return peekField(CTBase(),CTSize()); }
|
|
|
|
size_t SDUBase() const { return CTBase()+CTSize(); }
|
|
const MACSDU SDU() const { return MACSDU(tail(SDUBase())); }
|
|
};
|
|
|
|
|
|
/** MAC PDU with SDU only. */
|
|
class SimpleMACPDU : public MACPDU {
|
|
|
|
public:
|
|
|
|
size_t TCHFSize() const { return 0; }
|
|
unsigned TCTF() const { assert(0); }
|
|
|
|
size_t UEIdTypeSize() const { return 0; }
|
|
unsigned UEIdType() { assert(0); }
|
|
|
|
size_t UEIdSize() const { return 0; }
|
|
unsigned UEId() { assert(0); }
|
|
|
|
size_t CTSize() const { return 0; }
|
|
unsigned CT() { assert(0); }
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A full MAC PDU as seen on the FACH. */
|
|
class FACH_MACPDU : public MACPDU {
|
|
|
|
public:
|
|
|
|
size_t TCTFSize() const;
|
|
|
|
size_t UEIdTypeSize() const { return 2; }
|
|
};
|
|
|
|
|
|
/** A MAC PDU with TCTF+SDU only, as seen on the FACH. */
|
|
class FACH_TCTFOnlyMACPDU : public FACH_MACPDU {
|
|
|
|
public:
|
|
|
|
size_t UEIdTypeSize() const { return 0; }
|
|
unsigned UEIdType() { assert(0); }
|
|
|
|
size_t UEIdSize() const { return 0; }
|
|
unsigned UEId() { assert(0); }
|
|
|
|
size_t CTSize() const { return 0; }
|
|
unsigned CT() { assert(0); }
|
|
};
|
|
|
|
|
|
// (pat) Having an alternate format PDU is kind of irrelevant on DCH.
|
|
// When using DCH, the primary information needed is the transport channel
|
|
// so we can identify the UE, and that is not in the MACPDU.
|
|
/** 3GPP 25.321 9.2.1.1 case (a) */
|
|
class DTCH_DCH_MACPDU : public SimpleMACPDU { };
|
|
class DCCH_DCH_MACPDU : public SimpleMACPDU { };
|
|
// We dont support case (b)
|
|
/** 3GPP 25.321 9.2.1.1 case (c) */
|
|
class DTCH_FACH_MACPDU : public FACH_MACPDU { };
|
|
class DCCH_FACH_MACPDU : public FACH_MACPDU { };
|
|
|
|
// These are davids originals, which I didnt want to delete.
|
|
// But I dont know what DTCCH & DCCCH are - maybe deprecated channel names?
|
|
// And the case (b) is not right for either case b or c, so I am fixing, above
|
|
/** 3GPP 25.321 9.2.1.1 case (a) */
|
|
class DTCCH_DCH_MACPDU : public SimpleMACPDU { };
|
|
class DTCH_DCH_MACPDU : public DTCCH_DCH_MACPDU { };
|
|
class DCCCH_DCH_MACPDU : public SimpleMACPDU { };
|
|
class DCCH_DCH_MACPDU : public DCCCH_DCH_MACPDU { };
|
|
|
|
/** 3GPP 25.321 9.2.1.1 case (b) */
|
|
// (pat) This is not right for any of the cases, so I changed:
|
|
class DTCCH_FACH_MACPDU : public FACH_TCTFOnlyMACPDU { };
|
|
class DTCH_FACH_MACPDU : public DTCCH_DCH_MACPDU { };
|
|
class DCCH_FACH_MACPDU : public DCCH_DCH_MACPDU { };
|
|
|
|
/** 3GPP 25.321 9.2.1.2
|
|
(pat) There is no header because the UE must look
|
|
inside the message for the gigantic IE to determine ownership.
|
|
*/
|
|
class BCCH_BCH_MACPDU : public SimpleMACPDU { };
|
|
|
|
/** 3GPP 25.321 9.2.1.2 */
|
|
class BCCH_FACH_MACPDU : public FACH_TCTFOnlyMACPDU { };
|
|
|
|
/** 3GPP 25.321 9.2.1.3 */
|
|
class PCCH_PCH_MACPDU : public SimpleMACPDU { };
|
|
|
|
/** 3GPP 25.321 9.2.1.4 */
|
|
class CCCH_FACCH_MACPDU : public FACH_TCTFOnlyMACPDU { };
|
|
|
|
class RLCSDU : public BitVector {
|
|
public:
|
|
RLCSDU(const BitVector& bits):BitVector(bits) { };
|
|
};
|
|
|
|
|
|
/**
|
|
The RLC-PDU is passed between the RLC and upper layers.
|
|
3GPP 25.322
|
|
*/
|
|
class RLCPDU : public BitVector {
|
|
public:
|
|
RLCPDU(const BitVector& bits):BitVector(bits) { };
|
|
};
|
|
|
|
|
|
/** RLC control PDUs 3GPP 9.2.1.5-9.2.1.7 */
|
|
class ControlRLCPDU : public RLCPDU {
|
|
|
|
public:
|
|
|
|
ControlRLCPDU(const BitVector& bits):RLCPDU(bits) { }
|
|
|
|
unsigned DC() const { return peekField(0,1); }
|
|
unsigned type() const { return peekField(2,3); }
|
|
|
|
};
|
|
|
|
|
|
class STATUS_RLCPDU : public ControlRLCPDU {
|
|
|
|
public:
|
|
|
|
STATUS_RLCPDU(const BitVector& bits):ControlRLCPDU(bits) { }
|
|
|
|
unsigned RSN() const { return peekField(4,1); }
|
|
unsigned R1() const { return peekField(5,3); }
|
|
|
|
};
|
|
|
|
|
|
|
|
/** RLC data PDUs */
|
|
class DataRLCPDU : public BitVector {
|
|
|
|
public:
|
|
|
|
size_t SNBase() const { return 0; }
|
|
virtual size_t SNLen() const { assert(0); }
|
|
|
|
virtual size_t LIBase() const { return SNBase()+SNLen(); }
|
|
virtual size_t LILen() const;
|
|
virtual unsigned LI() const;
|
|
|
|
virtual size_t SDUBase() const =0;
|
|
virtual size_t SDULen() const =0;
|
|
virtual const RLCSDU SDU() const { return RLCSDU(segment(SDUBase(),SDULen())); }
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Transparent data, 3GPP 25.322 9.2.1.2 */
|
|
class TMD_RLCPDU : public DataRLCPDU {
|
|
|
|
public:
|
|
|
|
size_t SDUBase() const { return 0; }
|
|
size_t SDULen() const { return size(); }
|
|
};
|
|
|
|
|
|
/** Unacknowledged mode data, 3Gpp 25.332 9.2.1.3 */
|
|
class UMD_RLCPDU : public DataRLCPDU {
|
|
|
|
public:
|
|
|
|
size_t SNLen() const { return 8; }
|
|
unsigned SN() const { return peekField(SNBase(),7); }
|
|
|
|
size_t SDUBase() const { return LIBase()+LILen(); }
|
|
size_t SDULen() const { return LI()*8; }
|
|
};
|
|
|
|
/** Acknowledged mode data, 3Gpp 25.332 9.2.1.4 */
|
|
class AMD_RLCPDU : public DataRLCPDU {
|
|
|
|
public:
|
|
|
|
size_t SNLen() const { return 16; }
|
|
unsigned DC() const { return peekField(SNBase(),1); }
|
|
unsigned SN() const { return peekField(SNBase()+1,12); }
|
|
unsigned P() const { return peekField(SNBase()+13,1); }
|
|
unsigned HE() const { return peekField(SNBase()+14,2); }
|
|
|
|
size_t SDUBase() const { return LIBase()+LILen(); }
|
|
size_t SDULen() const { return LI()*8; }
|
|
|
|
size_t piggybackBase() const { return SDUBase() + SDULen(); }
|
|
const STATUS_RLCPDU piggyback() const { return tail(piggybackBase()); }
|
|
};
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace UMTS
|
|
|
|
|
|
#endif
|