mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +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) << ")";
 | 
						|
}
 | 
						|
};
 |