mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
174 lines
6.0 KiB
C++
174 lines
6.0 KiB
C++
/*
|
|
* Copyright 2014, Range Networks, Inc.
|
|
*
|
|
|
|
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.
|
|
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_GROUP LogGroup::GSM // Can set Log.Level.GSM for debugging
|
|
|
|
#include "GSMRadioResource.h"
|
|
#include "GSMCommon.h"
|
|
#include "GSMConfig.h"
|
|
#include "GSMLogicalChannel.h"
|
|
#include "GSMCCCH.h"
|
|
#include <ControlTransfer.h>
|
|
#include <L3TranEntry.h> // For L3TranEntryId, NewTransactionTable.
|
|
#include <Globals.h>
|
|
|
|
namespace GSM {
|
|
using namespace Control;
|
|
|
|
/**
|
|
Determine the channel type needed. GSM 04.08 9.1.8, Table 9.3 and 9.3a.
|
|
Assumes we do not support call reestablishment.
|
|
@param RA The request reference from the channel request message.
|
|
@return channel type code, undefined if not a supported service
|
|
*/
|
|
ChannelType decodeChannelNeeded(unsigned RA)
|
|
{
|
|
// This code is based on GSM 04.08 Table 9.9. section 9.1.8
|
|
|
|
unsigned RA4 = RA>>4;
|
|
unsigned RA5 = RA>>5;
|
|
|
|
// We use VEA for all emergency calls, regardless of configuration.
|
|
if (RA5 == 0x05) return TCHFType; // emergency call
|
|
|
|
// Answer to paging, Table 9.9a.
|
|
// We don't support TCH/H, so it's wither SDCCH or TCH/F.
|
|
// The spec allows for "SDCCH-only" MS. We won't support that here.
|
|
// FIXME -- So we probably should not use "any channel" in the paging indications.
|
|
if (RA5 == 0x04) return TCHFType; // any channel or any TCH.
|
|
if (RA4 == 0x01) return SDCCHType; // SDCCH
|
|
if (RA4 == 0x02) return TCHFType; // TCH/F
|
|
if (RA4 == 0x03) return TCHFType; // TCH/F
|
|
if ((RA&0xf8) == 0x78 && RA != 0x7f) return PSingleBlock1PhaseType;
|
|
if ((RA&0xf8) == 0x70) return PSingleBlock2PhaseType;
|
|
|
|
int NECI = gConfig.getNum("GSM.CellSelection.NECI");
|
|
if (NECI==0) {
|
|
if (RA5 == 0x07) return SDCCHType; // MOC or SDCCH procedures
|
|
if (RA5 == 0x00) return SDCCHType; // location updating
|
|
} else {
|
|
if (gConfig.getBool("Control.VEA")) {
|
|
// Very Early Assignment
|
|
if (RA5 == 0x07) return TCHFType; // MOC for TCH/F
|
|
if (RA4 == 0x04) return TCHFType; // MOC, TCH/H sufficient
|
|
} else {
|
|
// Early Assignment
|
|
if (RA5 == 0x07) return SDCCHType; // MOC for TCH/F
|
|
if (RA4 == 0x04) return SDCCHType; // MOC, TCH/H sufficient
|
|
}
|
|
if (RA4 == 0x00) return SDCCHType; // location updating
|
|
if (RA4 == 0x01) return SDCCHType; // other procedures on SDCCH
|
|
}
|
|
|
|
// Anything else falls through to here.
|
|
// We are still ignoring data calls, LMU.
|
|
return UndefinedCHType;
|
|
}
|
|
|
|
/** Return true if RA indicates LUR. */
|
|
bool requestingLUR(unsigned RA)
|
|
{
|
|
int NECI = gConfig.getNum("GSM.CellSelection.NECI");
|
|
if (NECI==0) return ((RA>>5) == 0x00);
|
|
else return ((RA>>4) == 0x00);
|
|
}
|
|
|
|
|
|
|
|
// Return a RACH channel request message (what we call RA) for various types of channel requests.
|
|
// The RA is only 8 bits.
|
|
static unsigned createFakeRachRA(FakeRachType rachtype)
|
|
{
|
|
switch (rachtype) {
|
|
default: devassert(0);
|
|
case FakeRachTCH: return 0xe0 | (0x1f & random()); // top 3 bits are 0x7.
|
|
case FakeRachSDCCH: return 0x10 | (0x0f & random()); // top 4 bits are 0001
|
|
case FakeRachLUR: return 0x00 | (0x0f & random()); // top 4 bits are 0000
|
|
case FakeRachGPRS: return 0x70 | (0x7 & random()); // top 5 bits are 01110.
|
|
case FakeRachAnswerToPaging: return 0x80 | (0x1f & random()); // top 3 bits are 100.
|
|
}
|
|
}
|
|
|
|
FakeRachType fakeRachTypeTranslate(const char *name)
|
|
{
|
|
if (strcasestr(name,"tch")) return FakeRachTCH;
|
|
if (strcasestr(name,"sdcch")) return FakeRachSDCCH;
|
|
if (strcasestr(name,"lur")) return FakeRachLUR;
|
|
if (strcasestr(name,"gprs")) return FakeRachGPRS;
|
|
if (strcasestr(name,"pag")) return FakeRachAnswerToPaging;
|
|
LOG(ERR) <<"Unrecognized fake rach type: " <<name;
|
|
return FakeRachTCH; // Make something up.
|
|
}
|
|
|
|
// Enqueue a fake rach of the specified type. Used to test/exercise the CCCH code.
|
|
void createFakeRach(FakeRachType rachtype)
|
|
{
|
|
Time now = gBTS.time();
|
|
AccessGrantResponder(createFakeRachRA(rachtype),now,0,1,0);
|
|
//ChannelRequestRecord *req = new ChannelRequestRecord(createFakeRachRA(rachtype),now,0,1);
|
|
//gBTS.channelRequest(req);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const RachInfo &rach)
|
|
{
|
|
unsigned RA = rach.mRA;
|
|
ChannelType chtype = decodeChannelNeeded(RA);
|
|
os <<LOGVAR(chtype) <<LOGVAR2("lur",requestingLUR(RA))
|
|
<<LOGHEX(RA) <<LOGVAR2("TE",rach.initialTA())<<LOGVAR2("RSSI",rach.RSSI()) <<LOGVAR2("when",rach.mWhen) <<LOGVAR(rach.mTN);
|
|
return os;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const RachInfo *rach) { os << &rach; return os; } // idiotic language
|
|
|
|
|
|
// RR Establishment. GSM 04.08 3.3.1.1.3. The RA bits are defined in 44.018 9.1.8
|
|
// Triage the RACHes, prioritize them, put them on RachQ.
|
|
// TODO: Merge RachInfo with ChannelRequestRecord and pass it in here.
|
|
void AccessGrantResponder(
|
|
unsigned RA, const GSM::Time& when,
|
|
float RSSI, float timingError,
|
|
int TN) // The TN the RACH arrived on. Only non-0 if there are multiple beacon timeslots.
|
|
{
|
|
gReports.incr("OpenBTS.GSM.RR.RACH.TA.All",(int)(timingError));
|
|
gReports.incr("OpenBTS.GSM.RR.RACH.RA.All",RA);
|
|
|
|
// Are we holding off new allocations?
|
|
if (gBTS.btsHold()) {
|
|
LOG(NOTICE) << "ignoring RACH due to BTS hold-off";
|
|
return;
|
|
}
|
|
|
|
{
|
|
// Screen for delay.
|
|
int MaxTA = gConfig.getNum("GSM.MS.TA.Max");
|
|
if (timingError>MaxTA) {
|
|
RachInfo tmpRACH(RA,when,RadData(RSSI,timingError)); // Temporary just so we can print it more easily.
|
|
LOG(NOTICE) << "ignoring RACH burst TE > "<<MaxTA<<":" <<tmpRACH;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int initialTA = (int)(timingError + 0.5F);
|
|
if (initialTA<0) initialTA=0;
|
|
if (initialTA>62) initialTA=62;
|
|
|
|
RachInfo *rip = new RachInfo(RA,when,RadData(RSSI,initialTA),TN);
|
|
LOG(DEBUG) << "Incoming RACH:" <<*rip <<LOGVAR2("now",gBTS.time()) <<LOGVAR2("when%42432",rip->mWhen.FN() % 42432);
|
|
enqueueRach(rip);
|
|
}
|
|
|
|
};
|