mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
379 lines
13 KiB
C++
379 lines
13 KiB
C++
/**@file @brief L3 Radio Resource messages related to GPRS */
|
|
/*
|
|
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
|
* Copyright 2011 Kestrel Signal Processing, 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 <typeinfo>
|
|
#include <iostream>
|
|
|
|
#include "GSML3RRMessages.h"
|
|
#include "../GPRS/GPRSExport.h"
|
|
#include <Logger.h>
|
|
|
|
|
|
namespace GSM {
|
|
|
|
|
|
// GSM 04.60 sec 12.24
|
|
void L3GPRSCellOptions::writeBits(L3Frame& dest, size_t &wp) const
|
|
{
|
|
GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
|
|
dest.writeField(wp,gco.mNMO,2);
|
|
dest.writeField(wp,gco.mT3168Code,3);
|
|
dest.writeField(wp,gco.mT3192Code,3);
|
|
dest.writeField(wp,gco.mDRX_TIMER_MAX,3);
|
|
dest.writeField(wp,gco.mACCESS_BURST_TYPE,1);
|
|
dest.writeField(wp,gco.mCONTROL_ACK_TYPE,1);
|
|
dest.writeField(wp,gco.mBS_CV_MAX,4);
|
|
dest.writeField(wp,0,1); // optional PAN_ fields omitted.
|
|
LOG(INFO)<< "beacon"<<LOGVAR2("NW_EXT_UTBF",gco.mNW_EXT_UTBF);
|
|
if (!gco.mNW_EXT_UTBF) {
|
|
dest.writeField(wp,0,1); // optional extension information omitted
|
|
} else {
|
|
dest.writeField(wp,1,1); // extension information included.
|
|
unsigned extlen = 6; // 6 bits of extension information.
|
|
dest.writeField(wp,extlen,6); // length of extension.
|
|
// R99 extensions:
|
|
dest.writeField(wp,0,1); // No EGPRS.
|
|
dest.writeField(wp,0,1); // No PFC_FEATURE_MODE
|
|
dest.writeField(wp,0,1); // No DTM_SUPPORT
|
|
dest.writeField(wp,0,1); // No BSS_PAGING_COORDINATION
|
|
// Rel-4 extensions:
|
|
// I tried setting CCN to 1 to get the MS to indicate GERAN feature pack I support.
|
|
// CCN is network assisted cell change and is also part of GERAN feature pack I.
|
|
dest.writeField(wp,0,1); // CCN_ACTIVE. CCN described 44.060 5.5.1.1a
|
|
dest.writeField(wp,gco.mNW_EXT_UTBF,1); // Finally.
|
|
// Rel-6 extensions:
|
|
// We dont want any of these, but here they are as documentation.
|
|
//dest.writeField(wp,0,1); // No MULTIPLE_TBF_CAPABILITY
|
|
//dest.writeField(wp,0,1); // No EXT_UTBF_NODATA
|
|
//dest.writeField(wp,0,1); // No DTM_ENHANCEMENTS_CAPABILITY
|
|
//dest.writeField(wp,0,1); // No MBMS procedures
|
|
// End of Rel extensions, we are allowed to truncate here.
|
|
dest.writeField(wp,0,1); // Required spare bit - this is very confusing in the spec.
|
|
}
|
|
}
|
|
|
|
size_t L3GPRSCellOptions::lengthBits() const
|
|
{
|
|
GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
|
|
size_t result = 2+3+3+3+1+1+4+1+1;
|
|
if (gco.mNW_EXT_UTBF) {
|
|
result += 6 + 6 + 1; // 6 bit len + 6 bits of extension + 1 spare bit.
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void L3GPRSCellOptions::text(ostream& os) const
|
|
{
|
|
GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
|
|
os << "NMO=" << gco.mNMO;
|
|
os << " T3168Code=" << gco.mT3168Code;
|
|
os << " T3192Code=" << gco.mT3192Code;
|
|
os << " DRX_TIMER_MAX=" << gco.mDRX_TIMER_MAX;
|
|
os << " ACCESS_BURST_TYPE=" << gco.mACCESS_BURST_TYPE;
|
|
os << " CONTROL_ACK_TYPE=" << gco.mCONTROL_ACK_TYPE;
|
|
os << " BS_CV_MAX=" << gco.mBS_CV_MAX;
|
|
os << LOGVAR2("NW_EXT_UTBF",gco.mNW_EXT_UTBF);
|
|
}
|
|
|
|
|
|
|
|
const char *L3IAPacketAssignment::IAPacketAssignmentTypeText(enum IAPacketAssignmentType type) const
|
|
{
|
|
switch (type) {
|
|
case PacketUplinkAssignUninitialized: return ""; // No rest octets neeeded for this.
|
|
case PacketUplinkAssignFixed: return "Fixed Packet Uplink Assignment";
|
|
case PacketUplinkAssignDynamic: return "Dynamic Packet Uplink Assignment";
|
|
case PacketUplinkAssignSingleBlock: return "Single Block Packet Uplink Assignment";
|
|
case PacketDownlinkAssign: return "Packet Downlink Assignment";
|
|
default:
|
|
LOG(ERR) << "unrecognized packet assignment type code " << (int)type;
|
|
return "??Unknown Assignment Type??";
|
|
}
|
|
}
|
|
|
|
void L3IAPacketAssignment::setPacketUplinkAssignSingleBlock(unsigned TBFStartingTime)
|
|
{
|
|
mPacketAssignmentType = PacketUplinkAssignSingleBlock;
|
|
mTBFStartingTimePresent = true;
|
|
mTBFStartingTime = TBFStartingTime;
|
|
mChannelCodingCommand = 0; // use CS-1; redundant
|
|
}
|
|
|
|
void L3IAPacketAssignment::setPacketUplinkAssignDynamic(unsigned TFI, unsigned CSNum, unsigned USF)
|
|
{
|
|
mPacketAssignmentType = PacketUplinkAssignDynamic;
|
|
mTFIPresent = true;
|
|
mTFIAssignment = TFI;
|
|
mChannelCodingCommand = CSNum; // CS-1, etc.
|
|
mUSFGranularity = 0; // redundant.
|
|
mUSF = USF;
|
|
}
|
|
|
|
void L3IAPacketAssignment::setPacketDownlinkAssign(
|
|
unsigned wTLLI, unsigned wTFI,unsigned wCSNum,
|
|
unsigned wRLCMode,unsigned wTAValid)
|
|
{
|
|
mPacketAssignmentType = PacketDownlinkAssign;
|
|
mTLLI = wTLLI;
|
|
mTFIPresent = true;
|
|
mTFIAssignment = wTFI;
|
|
mChannelCodingCommand = wCSNum; // CS-1, etc.
|
|
mRLCMode = wRLCMode;
|
|
mTAValid = wTAValid; // We provided a valid TimingAdvance.
|
|
}
|
|
|
|
void L3IAPacketAssignment::setPacketPollTime(unsigned wTBFStartingTime)
|
|
{
|
|
mPolling = true;
|
|
mTBFStartingTimePresent = true;
|
|
mTBFStartingTime = wTBFStartingTime;
|
|
}
|
|
|
|
void L3IAPacketAssignment::setPacketUplinkAssignFixed()
|
|
{
|
|
mPacketAssignmentType = PacketUplinkAssignFixed;
|
|
// unimplemented
|
|
assert(0);
|
|
}
|
|
|
|
// We broadcast alpha in the SI13 message, but not gamma.
|
|
// This gives us a chance to over-ride the alpha,gamma for an individual MS,
|
|
// or based on RSSI from the RACH.
|
|
// Dont know if we will use this, but here it is anyway.
|
|
// If this is not called, the default value of gamma == 0 means MS broadcasts at full power,
|
|
// moderated only by the alpha broadcast on SI13.
|
|
void L3IAPacketAssignment::setPacketPowerOptions(unsigned wAlpha, unsigned wGamma)
|
|
{
|
|
mAlpha = wAlpha; mGamma = wGamma; mAlphaPresent = true;
|
|
}
|
|
|
|
// TBF Starting Time GSM 04.08 10.5.2.38
|
|
static void writeStartTime(MsgCommon &dest, unsigned startframe)
|
|
{
|
|
std::ostream*os = dest.getStream(); // non-NULL for text() function.
|
|
|
|
// The names T1, T2, T3 are defined in table 10.5.79
|
|
unsigned T1 = (startframe/1326)%32;
|
|
unsigned T3 = startframe%51;
|
|
unsigned T2 = startframe%26;
|
|
|
|
// Recompute original startframe:
|
|
// Note that T3-T2 may be negative:
|
|
int recomputed = 51 * (((int)T3-(int)T2) % 26) + T3 + 51 * 26 * T1;
|
|
unsigned startframemod = (startframe % 42432);
|
|
|
|
// If we are writing text(), output both the original startframe and
|
|
// the computed T1,T2,T3.
|
|
if (os) {
|
|
// The recomputed time may be a much smaller number than startframe.
|
|
*os << " TBFStartFrame=" <<startframe << "=(" << "T=" <<recomputed;
|
|
}
|
|
|
|
// Note: The fields are written here Most-Significant-Bit first in each byte,
|
|
// then the bytes are reversed in the encoder before being sent to the radio.
|
|
// 7 6 5 4 3 2 1 0
|
|
// [ T1[4:0] ][ T3[5:3] ] Octet 1
|
|
// [ T3[2:0] ][ T2[4:0] ] Octet 2
|
|
dest.writeField(T1,5,"T1p");
|
|
dest.writeField(T3,6,"T3"); // Yes T3 comes before T2.
|
|
dest.writeField(T2,5,"T2");
|
|
|
|
// This just doesnt work, despite the documentation.
|
|
if (os && recomputed != (int)startframemod) {
|
|
*os << " TBF Start Time miscalculation: "
|
|
<<LOGVAR(startframemod) <<"!=" <<LOGVAR(recomputed);
|
|
}
|
|
|
|
if (os) { *os << ")"; }
|
|
}
|
|
|
|
|
|
// (pat) The uplink assignment is always initiated by the MS using a RACH,
|
|
// so the MS is identified by the request reference, and this message
|
|
// does not contain a TLLI.
|
|
void L3IAPacketAssignment::writePacketUplinkAssignment(MsgCommon &dest) const
|
|
{
|
|
// The IA Rest Octets start with some bits to indicate a Packet Uplink Assignment:
|
|
// GSM04.08 sec 10.5.2.16
|
|
dest.writeH();
|
|
dest.writeH();
|
|
dest.write0();
|
|
dest.write0();
|
|
if (mPacketAssignmentType == PacketUplinkAssignFixed ||
|
|
mPacketAssignmentType == PacketUplinkAssignDynamic) {
|
|
dest.write1();
|
|
dest.WRITE_FIELD(mTFIAssignment, 5);
|
|
dest.WRITE_FIELD(mPolling, 1);
|
|
switch (mPacketAssignmentType) {
|
|
case PacketUplinkAssignDynamic:
|
|
dest.write0();
|
|
dest.WRITE_FIELD(mUSF, 3);
|
|
dest.WRITE_FIELD(mUSFGranularity, 1);
|
|
dest.write0(); // No downlink power parameters present.
|
|
// mPowerOption.writePower(dest,0);
|
|
break;
|
|
case PacketUplinkAssignFixed:
|
|
dest.write1();
|
|
dest.WRITE_FIELD(mAllocationBitmapLength, 5);
|
|
if (mAllocationBitmapLength) {
|
|
dest.WRITE_FIELD(mAllocationBitmap, mAllocationBitmapLength);
|
|
}
|
|
dest.write0(); // No downlink power parameters present.
|
|
// mPowerOption.writePower(dest,0);
|
|
break;
|
|
default: assert(0);
|
|
}
|
|
dest.WRITE_FIELD(mChannelCodingCommand, 2);
|
|
dest.WRITE_FIELD(mTLLIBlockChannelCoding, 1);
|
|
if (dest.write01(mAlphaPresent)) { dest.WRITE_FIELD(mAlpha,4); }
|
|
dest.WRITE_FIELD(mGamma,5);
|
|
if (dest.write01(mTimingAdvanceIndexPresent)) {
|
|
dest.WRITE_FIELD(mTimingAdvanceIndex,4);
|
|
}
|
|
if (dest.write01(mTBFStartingTimePresent)) {
|
|
writeStartTime(dest,mTBFStartingTime);
|
|
}
|
|
} else { // single block assignment.
|
|
dest.write0(); // uplink assignment type designator
|
|
if (dest.write01(mAlphaPresent)) { dest.WRITE_FIELD(mAlpha,4); }
|
|
dest.WRITE_FIELD(mGamma,5);
|
|
dest.write0(); dest.write1(); // As per 10.5.2.16 Note 1.
|
|
assert(mTBFStartingTimePresent); // required for single block uplink assignment.
|
|
writeStartTime(dest,mTBFStartingTime);
|
|
dest.writeL(); // No downlink power parameters present.
|
|
//mPowerOption.writePower(dest,1);
|
|
}
|
|
}
|
|
|
|
// (pat) The downlink assignment may be (normally is) initiated by the network,
|
|
// in which case the "request reference" in the Immediate Assignment message
|
|
// is set to an impossible value, and the MS is identified by the TLLI.
|
|
void L3IAPacketAssignment::writePacketDownlinkAssignment(MsgCommon &dest) const
|
|
{
|
|
// The IA Rest Octets start with some bits to indicate a Packet Downlink Assignment:
|
|
// GSM04.08 sec 10.5.2.16
|
|
dest.writeH();
|
|
dest.writeH();
|
|
dest.write0();
|
|
dest.write1();
|
|
dest.writeField(mTLLI,32,"TLLI",tohex);
|
|
if (dest.write01(mTFIPresent)) {
|
|
dest.WRITE_FIELD(mTFIAssignment,5);
|
|
dest.WRITE_FIELD(mRLCMode,1);
|
|
dest.WRITE_OPT_FIELD01(mAlpha,4,mAlphaPresent);
|
|
dest.WRITE_FIELD(mGamma,5);
|
|
dest.WRITE_FIELD(mPolling, 1);
|
|
dest.WRITE_FIELD(mTAValid, 1);
|
|
}
|
|
dest.WRITE_OPT_FIELD01(mTimingAdvanceIndex,4,mTimingAdvanceIndexPresent);
|
|
if (dest.write01(mTBFStartingTimePresent)) {
|
|
writeStartTime(dest,mTBFStartingTime);
|
|
}
|
|
dest.write0(); // No downlink power parameters present.
|
|
dest.writeL(); // No Egprs.
|
|
}
|
|
|
|
void L3IAPacketAssignment::writeIAPacketAssignment(MsgCommon &dest) const
|
|
{
|
|
// (pat) These messages are the rest octets in an Immediate Assignment Message.
|
|
switch (mPacketAssignmentType) {
|
|
case PacketUplinkAssignUninitialized:
|
|
return; // No rest octets neeeded for this.
|
|
case PacketUplinkAssignFixed:
|
|
case PacketUplinkAssignDynamic:
|
|
case PacketUplinkAssignSingleBlock:
|
|
return writePacketUplinkAssignment(dest);
|
|
case PacketDownlinkAssign:
|
|
return writePacketDownlinkAssignment(dest);
|
|
}
|
|
}
|
|
|
|
void L3IAPacketAssignment::writeBits(L3Frame &frame, size_t &wp) const
|
|
{
|
|
MsgCommonWrite tmp(frame,wp);
|
|
writeIAPacketAssignment(tmp);
|
|
wp = tmp.wp;
|
|
}
|
|
|
|
// Return the length of this puppy.
|
|
size_t L3IAPacketAssignment::lengthBits() const
|
|
{
|
|
MsgCommonLength dest;
|
|
writeIAPacketAssignment(dest);
|
|
return dest.wp;
|
|
}
|
|
|
|
// Print a human readable version of this puppy.
|
|
void L3IAPacketAssignment::text(std::ostream& os) const
|
|
{
|
|
if (mPacketAssignmentType != PacketUplinkAssignUninitialized) {
|
|
os << " " << IAPacketAssignmentTypeText(mPacketAssignmentType);
|
|
MsgCommonText dest(os);
|
|
writeIAPacketAssignment(dest);
|
|
}
|
|
}
|
|
|
|
|
|
#if 0 // Currently unused
|
|
void L3GPRSPowerControlParameters::writeBits(L3Frame& dest, size_t &wp) const
|
|
{
|
|
dest.writeField(wp,mAlpha,4);
|
|
// We dont use any of these gamma fields at the moment:
|
|
dest.writeField(wp,0,1); // GAMMA_TM0
|
|
dest.writeField(wp,0,1); // GAMMA_TM1
|
|
dest.writeField(wp,0,1); // GAMMA_TM2
|
|
dest.writeField(wp,0,1); // GAMMA_TM3
|
|
dest.writeField(wp,0,1); // GAMMA_TM4
|
|
dest.writeField(wp,0,1); // GAMMA_TM5
|
|
dest.writeField(wp,0,1); // GAMMA_TM6
|
|
dest.writeField(wp,0,1); // GAMMA_TM7
|
|
}
|
|
|
|
|
|
void L3GPRSPowerControlParameters::text(ostream& os) const
|
|
{
|
|
os << "Alpha=" << mAlpha;
|
|
}
|
|
#endif
|
|
|
|
L3GPRSSI13PowerControlParameters::L3GPRSSI13PowerControlParameters()
|
|
: mAlpha(GPRS::GetPowerAlpha()),
|
|
mTAvgW(gConfig.getNum("GPRS.MS.Power.T_AVG_W")),
|
|
mTAvgT(gConfig.getNum("GPRS.MS.Power.T_AVG_T")),
|
|
mPCMeasChan(0),
|
|
mNAvgI(15) // We dont use this so dont bother putting in sql.
|
|
{}
|
|
|
|
void L3GPRSSI13PowerControlParameters::writeBits(L3Frame& dest, size_t &wp) const
|
|
{
|
|
// TODO: use WRITE_ITEM from MsgBase.h
|
|
dest.writeField(wp,mAlpha,4);
|
|
dest.writeField(wp,mTAvgW,5);
|
|
dest.writeField(wp,mTAvgT,5);
|
|
dest.writeField(wp,mPCMeasChan,1);
|
|
dest.writeField(wp,mNAvgI,4);
|
|
}
|
|
|
|
|
|
void L3GPRSSI13PowerControlParameters::text(ostream& os) const
|
|
{
|
|
os << "(" <<LOGVAR(mAlpha) <<LOGVAR(mTAvgW) <<LOGVAR(mTAvgT)
|
|
<<LOGVAR(mPCMeasChan) <<LOGVAR(mNAvgI) << ")";
|
|
}
|
|
};
|