mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
920 lines
31 KiB
C++
920 lines
31 KiB
C++
/*
|
|
* Copyright 2011, 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::GPRS // Can set Log.Level.GPRS for debugging
|
|
|
|
#include "GPRSInternal.h"
|
|
#include "RLCMessages.h"
|
|
#include "RLCEngine.h"
|
|
#include "FEC.h"
|
|
#include "GSMTAPDump.h"
|
|
#include "../TransceiverRAD1/Transceiver.h" // For Transceiver::IGPRS
|
|
#define FEC_DEBUG 0
|
|
|
|
namespace GPRS {
|
|
static BitVector *decodeLowSide(const RxBurst &inBurst, int B, GprsDecoder &decoder, ChannelCodingType *ccPtr);
|
|
//static int sFecDebug = 1;
|
|
|
|
//static Mutex testlock; Did not help.
|
|
|
|
const int GPRSUSFEncoding[8] = {
|
|
// from table at GSM05.03 sec 5.1.4.2, specified in octal.
|
|
// 3 bits in, 12 bits out.
|
|
// Note that the table is defined as encoding bits 0,1,2 of usf,
|
|
// which is the post-byte-swapped version, not the original usf.
|
|
00000, // 000 000 000 000
|
|
00335, // 000 011 011 101
|
|
01566, // 001 101 110 110
|
|
01653, // 001 110 101 011
|
|
06413, // 110 100 001 011
|
|
06726, // 110 111 010 110
|
|
07175, // 111 001 111 101
|
|
07240 // 111 010 100 000
|
|
};
|
|
|
|
|
|
// Do the reverse encoding on usf, and return the reversed usf,
|
|
// ie, the returned usf is byte-swapped.
|
|
static int decodeUSF(SoftVector &mC)
|
|
{
|
|
// TODO: Make this more robust.
|
|
// Update: No dont bother, should always be zero anyway.
|
|
return (mC.bit(0)<<2) | (mC.bit(6)<<1) | mC.bit(4);
|
|
}
|
|
|
|
ARFCNManager *PDCHCommon::getRadio() { return mchParent->mchOldFec->getRadio(); }
|
|
unsigned PDCHCommon::ARFCN() { return mchParent->mchOldFec->ARFCN(); }
|
|
unsigned PDCHCommon::CN() { return mchParent->mchOldFec->CN(); }
|
|
unsigned PDCHCommon::TN() { return mchParent->mchOldFec->TN(); }
|
|
PDCHL1Uplink *PDCHCommon::uplink() { return mchParent->mchUplink; }
|
|
PDCHL1Downlink *PDCHCommon::downlink() { return mchParent->mchDownlink; }
|
|
PDCHL1FEC *PDCHCommon::parent() { return mchParent; }
|
|
void PDCHL1FEC::debug_test() { /*printf("dt\n"); devassert(*mReservations[3]._vptr != NULL);*/ }
|
|
L1Decoder* PDCHCommon::getDecoder() const { return mchParent->mchOldFec->decoder(); }
|
|
void PDCHCommon::countGoodFrame() { getDecoder()->countGoodFrame(); }
|
|
void PDCHCommon::countBadFrame() { getDecoder()->countBadFrame(); }
|
|
float PDCHCommon::FER() const { return getDecoder()->FER(); }
|
|
|
|
// Print out the USFs bracketing bsn on either side.
|
|
const char *PDCHCommon::getAnsweringUsfText(char *buf,RLCBSN_t bsn)
|
|
{
|
|
PDCHL1FEC *pdch = parent();
|
|
sprintf(buf," AnsweringUsf=%d %d [%d] %d %d",
|
|
pdch->getUsf(bsn-2),pdch->getUsf(bsn-1), pdch->getUsf(bsn),pdch->getUsf(bsn+1),pdch->getUsf(bsn+2));
|
|
return buf;
|
|
}
|
|
|
|
// Must return true if ch1 is before ch2.
|
|
bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2)
|
|
{
|
|
if (ch1->ARFCN() < ch2->ARFCN()) return true;
|
|
if (ch1->ARFCN() > ch2->ARFCN()) return false;
|
|
if (ch1->TN() < ch2->TN()) return true;
|
|
return false;
|
|
}
|
|
|
|
void PDCHL1FEC::mchStart() {
|
|
getRadio()->setSlot(TN(),Transceiver::IGPRS);
|
|
// Load up the GPRS filler idle burst tables in the transceiver.
|
|
// We could use any consecutive bsn, but lets use ones around the current time
|
|
// just to make sure they get through in case someone is triaging somewhere.
|
|
// Sending all 12 blocks is 2x overkill because the modulus in Transceiver::setModulus
|
|
// for type IGPRS is set the same as type I which is only 26, not 52.
|
|
RLCBSN_t bsn = FrameNumber2BSN(gBTS.time().FN()) + 1;
|
|
for (int i = 0; i < 12; i++, bsn = bsn + 1) {
|
|
GPRSLOG(1) <<"sendIdleFrame"<<LOGVAR2("TN",TN())<<LOGVAR(bsn)<<LOGVAR(i);
|
|
mchDownlink->sendIdleFrame(bsn);
|
|
}
|
|
mchOldFec->setGPRS(true,this);
|
|
debug_test();
|
|
}
|
|
void PDCHL1FEC::mchStop() {
|
|
getRadio()->setSlot(TN(),Transceiver::I);
|
|
mchOldFec->setGPRS(false,NULL);
|
|
}
|
|
|
|
PDCHL1FEC::PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan) :
|
|
PDCHCommon(this), mchOldFec(wlogchan->debugGetL1()), mchLogChan(wlogchan), mchTFIs(gTFIs)
|
|
{
|
|
// Warning: These initializations use some of the variables in the init list above.
|
|
mchUplink = new PDCHL1Uplink(this);
|
|
mchDownlink = new PDCHL1Downlink(this);
|
|
Stats.countPDCH++;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch)
|
|
{
|
|
os << (ch ? ch->shortId() : "PDCH#(null)");
|
|
return os;
|
|
}
|
|
|
|
#if 0
|
|
PDCHL1FEC::PDCHL1FEC()
|
|
: PDCHCommon(this)
|
|
{
|
|
mchUplink = new PDCHL1Uplink(this);
|
|
mchDownlink = new PDCHL1Downlink(this);
|
|
mchOldFec = NULL;
|
|
mchLogChan = NULL;
|
|
mchTFIs = gTFIs;
|
|
//mchOpen();
|
|
}
|
|
// We dont really need to remember the LogicalChannel, but maybe we'll need it in the future.
|
|
void PDCHL1FEC::mchOpen(TCHFACCHLogicalChannel *wlogchan)
|
|
{
|
|
// setGPRS has two affects: getTCH will consider the channel in-use;
|
|
// and bursts start being delivered to us.
|
|
// Note that the getTCH function normally doesnt even pay attention to whether
|
|
// the channel is 'open' or not; it calls recyclable() that checks timers
|
|
// and reuses the chan if they have expired.
|
|
mchLogChan = wlogchan;
|
|
devassert(mchLogChan->inUseByGPRS());
|
|
mchOldFec = mchLogChan->debugGetL1();
|
|
//mchReady = true; // finally
|
|
|
|
}
|
|
#endif
|
|
|
|
PDCHL1FEC::~PDCHL1FEC() {
|
|
gL2MAC.macForgetCh(this);
|
|
delete mchUplink;
|
|
delete mchDownlink;
|
|
}
|
|
|
|
|
|
void PDCHL1FEC::mchDump(std::ostream&os, bool verbose)
|
|
{
|
|
os << " PDCH"<<LOGVAR2("ARFCN", ARFCN())
|
|
<< LOGVAR2("TN",TN()) << LOGVAR2("FER",fmtfloat2(100.0*FER())) << "%" << "\n";
|
|
if (verbose) {
|
|
os <<"\t"; dumpReservations(os);
|
|
os <<"\t"; usfDump(os);
|
|
os <<"\t"; mchTFIs->tfiDump(os);
|
|
}
|
|
}
|
|
|
|
//void PDCHL1Downlink::rollForward()
|
|
//{
|
|
// // Calculate the TDMA paramters for the next transmission.
|
|
// // This implements GSM 05.02 Clause 7 for the transmit side.
|
|
// mchPrevWriteTime = mchNextWriteTime;
|
|
// mchTotalBursts++;
|
|
// mchNextWriteTime.rollForward(mchMapping.frameMapping(mchTotalBursts),mchMapping.repeatLength());
|
|
//}
|
|
|
|
#if FEC_DEBUG
|
|
GprsDecoder debugDecoder;
|
|
#endif
|
|
|
|
// Send the specified BitVector at the specified block time.
|
|
void PDCHL1Downlink::transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags)
|
|
{
|
|
parent()->debug_test();
|
|
// Format the bits into the bursts.
|
|
// GSM 05.03 4.1.5, 05.02 5.2.3
|
|
// NO! Dont do a wait here. The MAC serviceloop does this for all channels.
|
|
// waitToSend(); // Don't get too far ahead of the clock.
|
|
|
|
ARFCNManager *radio = getRadio();
|
|
if (!radio) {
|
|
// For some testing, we might not have a radio connected.
|
|
// That's OK, as long as we know it.
|
|
GLOG(INFO) << "XCCHL1Encoder with no radio, dumping frames";
|
|
return;
|
|
}
|
|
|
|
int fn = bsn.FN();
|
|
int tn = TN();
|
|
|
|
for (int qi=0,B=0; B<4; B++) {
|
|
Time nextWriteTime(fn,tn | transceiverflags);
|
|
mchBurst.time(nextWriteTime);
|
|
// Copy in the "encrypted" bits, GSM 05.03 4.1.5, 05.02 5.2.3.
|
|
//OBJLOG(DEBUG) << "transmit mI["<<B<<"]=" << mI[B];
|
|
mI[B].segment(0,57).copyToSegment(mchBurst,3);
|
|
mI[B].segment(57,57).copyToSegment(mchBurst,88);
|
|
mchBurst.Hl(qbits[qi++]);
|
|
mchBurst.Hu(qbits[qi++]);
|
|
// Send it to the radio.
|
|
//OBJLOG(DEBUG) << "transmit mchBurst=" << mchBurst;
|
|
if (gConfig.getBool("Control.GSMTAP.GPRS")) {
|
|
// Send to GSMTAP.
|
|
gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(),
|
|
TDMA_PDCH,
|
|
false, // not SACCH
|
|
false, // this is a downlink
|
|
mchBurst,
|
|
GSMTAP_TYPE_UM_BURST);
|
|
}
|
|
#if FEC_DEBUG
|
|
if (1) {
|
|
// Try decoding the frame we just encoded to see if it is correct.
|
|
devassert(mchBurst.size() == gSlotLen);
|
|
RxBurst rxb(mchBurst);
|
|
ChannelCodingType cc;
|
|
decodeLowSide(rxb, B, debugDecoder, &cc);
|
|
}
|
|
#endif
|
|
radio->writeHighSideTx(mchBurst,"GPRS");
|
|
fn++; // This cannot overflow because it is within an RLC block.
|
|
}
|
|
}
|
|
|
|
static GSM::TypeAndOffset frame2GsmTapType(BitVector &frame)
|
|
{
|
|
switch (frame.peekField(0,2)) { // Mac control field.
|
|
case MACPayloadType::RLCControl:
|
|
return TDMA_PACCH;
|
|
case MACPayloadType::RLCData:
|
|
default:
|
|
return TDMA_PDCH;
|
|
}
|
|
}
|
|
|
|
void PDCHL1Downlink::send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle)
|
|
{
|
|
if (!idle && gConfig.getBool("Control.GSMTAP.GPRS")) {
|
|
// Send to GSMTAP.
|
|
gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(),
|
|
frame2GsmTapType(frame),
|
|
false, // not SACCH
|
|
false, // this is a downlink
|
|
frame); // The data.
|
|
}
|
|
|
|
switch (encoding) {
|
|
case ChannelCodingCS1:
|
|
// Process the 184 bit (23 byte) frame, leave result in mI.
|
|
//mchCS1Enc.encodeFrame41(frame,0);
|
|
//transmit(gBSNNext,mchCS1Enc.mI,qCS1,0);
|
|
mchEnc.encodeCS1(frame);
|
|
transmit(gBSNNext,mchEnc.mI,qCS1,0);
|
|
break;
|
|
case ChannelCodingCS4:
|
|
//std::cout << "WARNING: Using CS4\n";
|
|
// This did not help the 3105/3101 errors:
|
|
//mchCS4Enc.initCS4(); // DEBUG TEST!! Didnt help.
|
|
//mchCS4Enc.encodeCS4(frame); // Result left in mI[].
|
|
//transmit(gBSNNext,mchCS4Enc.mI,qCS4,0);
|
|
mchEnc.encodeCS4(frame); // Result left in mI[].
|
|
transmit(gBSNNext,mchEnc.mI,qCS4,0);
|
|
break;
|
|
default:
|
|
LOG(ERR) << "unrecognized GPRS channel coding " << (int)encoding;
|
|
devassert(0);
|
|
}
|
|
}
|
|
|
|
|
|
// Return true if we send a block on the downlink.
|
|
bool PDCHL1Downlink::send1DataFrame( //SVGDBG
|
|
RLCDownEngine *engdown,
|
|
RLCDownlinkDataBlock *block, // block to send.
|
|
int makeres, // 0 = no res, 1 = optional res, 2 = required res.
|
|
MsgTransactionType mttype, // Type of reservation
|
|
unsigned *pcounter)
|
|
{
|
|
//ScopedLock lock(testlock);
|
|
TBF *tbf = engdown->getTBF();
|
|
if (! setMACFields(block,mchParent,tbf,makeres,mttype,pcounter)) { return false; }
|
|
// The rest of the RLC header is already set, but we did not know the tfi
|
|
// when we created the RLCDownlinkDataBlocks (because tbf not yet attached)
|
|
// so set tfi now that we know. Update 8-2012: Above comment is stale because we
|
|
// make the RLCDownlinkBlocks on the fly now.
|
|
block->mTFI = tbf->mtTFI;
|
|
// block->mPR = 1; // DEBUG test; made no diff.
|
|
|
|
tbf->talkedDown();
|
|
|
|
BitVector tobits = block->getBitVector(); // tobits deallocated when this function exits.
|
|
if (block->mChannelCoding == 0) { devassert(tobits.size() == 184); }
|
|
if (GPRSDebug & 1) {
|
|
RLCBlockReservation *res = mchParent->getReservation(gBSNNext);
|
|
std::ostringstream sshdr;
|
|
block->text(sshdr,false); //block->RLCDownlinkDataBlockHeader::text(sshdr);
|
|
ByteVector content(tobits);
|
|
GPRSLOG(1) << "send1DataFrame "<<parent()<<" "<<tbf<<LOGVAR(tbf->mtExpectedAckBSN)
|
|
<< " "<<sshdr.str()
|
|
<<" "<<(res ? res->str() : "")
|
|
<< LOGVAR2("content",content);
|
|
//<< " enc="<<tbf->mtChannelCoding <<" "<<os.str() << "\nbits:" <<bits.str();
|
|
//<<" " <<block->str() <<"\nbits:" <<tobits.hexstr();
|
|
}
|
|
#if FEC_DEBUG
|
|
BitVector copybits; copybits.clone(tobits);
|
|
#endif
|
|
send1Frame(tobits,block->mChannelCoding,0);
|
|
#if FEC_DEBUG
|
|
BitVector *result = debugDecoder.getResult();
|
|
devassert(result);
|
|
devassert(copybits == tobits);
|
|
if (result && !(*result == tobits)) {
|
|
int diffbit = -1;
|
|
char thing[500];
|
|
for (int i = 0; i < (int)result->size(); i++) {
|
|
thing[i] = '-';
|
|
if (result->bit(i) != tobits.bit(i)) {
|
|
if (diffbit == -1) diffbit = i;
|
|
thing[i] = '0' + result->bit(i);
|
|
}
|
|
}
|
|
thing[result->size()] = 0;
|
|
GPRSLOG(1) <<"encoding error" <<LOGVAR2("cs",(int)debugDecoder.getCS())
|
|
<<LOGVAR(diffbit)
|
|
<<LOGVAR2("in:size",tobits.size()) <<LOGVAR2("out:size",result->size())
|
|
<<"\n"<<tobits
|
|
<<"\n"<<*result
|
|
<<"\n"<<thing;
|
|
} else {
|
|
//GPRSLOG(1) <<"encoding ok" <<LOGVAR2("cs",(int)debugDecoder.getCS());
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Send the idle frame at specified Transceiver::SET_FILLER_FRAME)
|
|
// Do not call send1msgframe, because it would try to set MAC fields.
|
|
void PDCHL1Downlink::sendIdleFrame(RLCBSN_t bsn)
|
|
{
|
|
RLCMsgPacketDownlinkDummyControlBlock *msg = new RLCMsgPacketDownlinkDummyControlBlock();
|
|
devassert(msg->mUSF == 0);
|
|
BitVector tobits(RLCBlockSizeInBits[ChannelCodingCS1]);
|
|
msg->write(tobits);
|
|
delete msg;
|
|
//mchCS1Enc.encodeFrame41(tobits,0);
|
|
//transmit(bsn,mchCS1Enc.mI,qCS1,Transceiver::SET_FILLER_FRAME);
|
|
mchEnc.encodeCS1(tobits);
|
|
transmit(bsn,mchEnc.mI,qCS1,Transceiver::SET_FILLER_FRAME);
|
|
}
|
|
|
|
void PDCHL1Downlink::bugFixIdleFrame()
|
|
{
|
|
// DEBUG: We are only using this function to fix this problem for now.
|
|
if (gFixIdleFrame) {
|
|
// For this debug purpose, the mssage is sent on the next frame
|
|
// TODO: debug purpose only! This only works for one channel!
|
|
//Time tnext(gBSNNext.FN());
|
|
//gBTS.clock().wait(tnext);
|
|
}
|
|
|
|
// Did we make it in time?
|
|
{
|
|
Time tnow = gBTS.time();
|
|
int fn = tnow.FN();
|
|
int mfn = (fn / 13); // how many 13-multiframes
|
|
int rem = (fn - (mfn*13)); // how many blocks within the last multiframe.
|
|
int tbsn = mfn * 3 + ((rem==12) ? 2 : (rem/4));
|
|
GPRSLOG(2) <<"idleframe"<<LOGVAR(fn)<<LOGVAR(tbsn)<<LOGVAR(rem);
|
|
}
|
|
|
|
/***
|
|
if (mchIdleFrame.size() == 0) {
|
|
RLCMsgPacketDownlinkDummyControlBlock *dummymsg = new RLCMsgPacketDownlinkDummyControlBlock();
|
|
mchIdleFrame.set(BitVector(RLCBlockSizeInBits[ChannelCodingCS1]));
|
|
dummymsg->write(mchIdleFrame);
|
|
delete dummymsg;
|
|
}
|
|
send1Frame(mchIdleFrame,ChannelCodingCS1,true);
|
|
***/
|
|
}
|
|
|
|
// Return true if we send a block on the downlink.
|
|
bool PDCHL1Downlink::send1MsgFrame(
|
|
TBF *tbf, // The TBF sending the message, or NULL for an idle frame.
|
|
RLCDownlinkMessage *msg, // The message.
|
|
int makeres, // 0 = no res, 1 = optional res, 2 = required res.
|
|
MsgTransactionType mttype, // Type of reservation
|
|
unsigned *pcounter) // If non-null, incremented if a reservation is made.
|
|
{
|
|
if (! setMACFields(msg,mchParent,msg->mTBF,makeres,mttype,pcounter)) {
|
|
delete msg; // oh well.
|
|
return false; // This allows some other tbf to try to use this downlink block.
|
|
}
|
|
|
|
bool dummy = msg->mMessageType == RLCDownlinkMessage::PacketDownlinkDummyControlBlock;
|
|
bool idle = dummy && msg->isMacUnused();
|
|
if (idle && 0 == gConfig.getNum("GPRS.SendIdleFrames")) {
|
|
delete msg; // Let the transceiver send an idle frame.
|
|
return false; // This return value will not be checked.
|
|
}
|
|
|
|
if (tbf) { tbf->talkedDown(); }
|
|
|
|
// Convert to a BitVector. Messages always use CS-1 encoding.
|
|
BitVector tobits(RLCBlockSizeInBits[ChannelCodingCS1]);
|
|
msg->write(tobits);
|
|
// The possible downlink debug things we want to see are:
|
|
// 2: Only non-dummy messages.
|
|
// 32: include messages with non-idle MAC header, means mUSF or mSP.
|
|
// 1024: all messages including dummy ones.
|
|
if (GPRSDebug) {
|
|
if ((!dummy && (GPRSDebug&2)) || (!idle && (GPRSDebug&32)) || (GPRSDebug&1024)) {
|
|
ByteVector content(tobits);
|
|
GPRSLOG(2|32|1024) << "send1MsgFrame "<<parent()
|
|
<<" "<<msg->mTBF<< " "<<msg->str()
|
|
<< " " <<LOGVAR2("content",content);
|
|
// The res is unrelated to the message, and confusing, so dont print it:
|
|
//<<" "<<(res ? res->str() : "");
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// The below is what went out in release 3.0:
|
|
if (GPRSDebug & (1|32)) {
|
|
//RLCBlockReservation *res = mchParent->getReservation(gBSNNext);
|
|
//std::ostringstream ssres;
|
|
//if (res) ssres << res;
|
|
if (! idle || (GPRSDebug & 1024)) {
|
|
//ostringstream bits;
|
|
//tobits.hex(bits);
|
|
//GPRSLOG(1) << "send1MsgFrame "<<msg->mTBF<< " "<<msg->str() << "\nbits:"<<tobits.hexstr();
|
|
ByteVector content(tobits);
|
|
GPRSLOG(1) << "send1MsgFrame "<<parent()<<" "<<msg->mTBF<< " "<<msg->str() <<" "
|
|
<< LOGVAR2("content",content);
|
|
// This res is unrelated to the message, and confusing, so dont print it:
|
|
//<<" "<<(res ? res->str() : "");
|
|
} else if (msg->mUSF) {
|
|
GPRSLOG(32) << "send1MsgFrame "<<parent()<<" "<<msg->mTBF<< " "<<msg->str() <<" ";
|
|
//<<" "<<(res ? res->str() : "");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
delete msg;
|
|
send1Frame(tobits,ChannelCodingCS1,idle);
|
|
return true;
|
|
}
|
|
|
|
|
|
void PDCHL1Downlink::initBursts(L1FEC *oldfec)
|
|
{
|
|
// unused: mchFillerBurst = GSM::TxBurst(GSM::gDummyBurst); // TODO: This may not be correct.
|
|
// Should probably be RLC Dummy control message.
|
|
|
|
// Set up the training sequence since they'll be the same for all bursts.
|
|
// training sequence, GSM 05.02 5.2.3
|
|
// (pat) Set from the BSC color-code from the original GSM channel.
|
|
GSM::gTrainingSequence[oldfec->TSC()].copyToSegment(mchBurst,61);
|
|
}
|
|
|
|
|
|
// Determine CS from the qbits.
|
|
ChannelCodingType GprsDecoder::getCS()
|
|
{
|
|
// TODO: Make this more robust.
|
|
// Currently we only support CS1 or CS4, so just look at the first bit.
|
|
if (qbits[0]) {
|
|
return ChannelCodingCS1;
|
|
} else {
|
|
return ChannelCodingCS4;
|
|
}
|
|
}
|
|
|
|
BitVector *GprsDecoder::getResult()
|
|
{
|
|
switch (getCS()) {
|
|
case ChannelCodingCS4:
|
|
return &mD_CS4;
|
|
case ChannelCodingCS1:
|
|
return &mD;
|
|
default: devassert(0); // Others not supported yet.
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bool GprsDecoder::decodeCS4()
|
|
{
|
|
// Incoming data is in SoftVector mC(456) and has already been deinterleaved.
|
|
// Convert the SoftVector directly into bits: data + parity:
|
|
// The first 12 bits need to be reconverted to 3 bits of usf.
|
|
// Yes, they do this even on uplink, where there is no usf 5.03 sec 5.1.
|
|
// Parity is run on the remaining 447 (=456-12+3) bits, which consists
|
|
// of 424 of useful data, 7 bits of zeros, and 16 bits of parity.
|
|
unsigned reverseUsf = decodeUSF(mC);
|
|
mDP_CS4.fillField(0,reverseUsf,3);
|
|
// We are grubbing into the arrays. TODO: move this into the classes somewhere.
|
|
float *in = mC.begin() + 12;
|
|
char *out = mDP_CS4.begin() + 3;
|
|
for (int i = 12; i < 456; i++) {
|
|
*out++ = *in++ > 0.5 ? 1 : 0;
|
|
}
|
|
BitVector parity(mDP_CS4.segment(440-12+3,16));
|
|
parity.invert();
|
|
unsigned syndrome = mBlockCoder_CS4.syndrome(mDP_CS4);
|
|
// Result is in mD_CS4.
|
|
return (syndrome==0);
|
|
}
|
|
|
|
// Process the 184 bit frame, starting at offset, add parity, encode.
|
|
// Result is left in mI, representing 4 radio bursts.
|
|
void GprsEncoder::encodeCS1(const BitVector &src)
|
|
{
|
|
encodeFrame41(src,0);
|
|
}
|
|
|
|
static BitVector mCcopy;
|
|
void GprsEncoder::encodeCS4(const BitVector &src)
|
|
{
|
|
//if (sFecDebug) GPRSLOG(1) <<"encodeCS4 src\n"<<src;
|
|
src.copyToSegment(mD_CS4,0,53*8);
|
|
//if (sFecDebug) GPRSLOG(1) <<"encodeCS4 mD_CS4\n"<<mD_CS4;
|
|
// mC.zero(); // DEBUG TEST!! Did not help.
|
|
mD_CS4.fillField(53*8,0,7); // zero out 7 spare bits.
|
|
mD_CS4.LSB8MSB(); // Ignores the last incomplete byte of 7 zero bits.
|
|
//if (sFecDebug) GPRSLOG(1) <<"mC before parity\n"<<mC;
|
|
// Parity is computed on original D before doing the USF translation above.
|
|
mBlockCoder_CS4.writeParityWord(mD_CS4,mP_CS4);
|
|
// Note that usf has been moved to the first three bits by the byte swapping above,
|
|
// so when we write the 12 bits of GPRSUSFEncoding for usf into mC, it will overwrite
|
|
// the original 3 parity bits.
|
|
int reverseUsf = mD_CS4.peekField(0,3);
|
|
// mU overwrites the first 3 bits of mD within mC.
|
|
mU_CS4.fillField(0,GPRS::GPRSUSFEncoding[reverseUsf],12);
|
|
// Result is left in mC.
|
|
devassert(mC.peekField(0,12) == (unsigned) GPRS::GPRSUSFEncoding[reverseUsf]);
|
|
devassert(mC.peekField(433,7) == 0); // unused bits not modified.
|
|
//if (sFecDebug) GPRSLOG(1) <<"mC before interleave\n"<<mC;
|
|
|
|
interleave41(); // Interleaves mC into mI.
|
|
}
|
|
|
|
|
|
// Return decoded frame if success and B == 3, otherwise NULL.
|
|
static BitVector *decodeLowSide(const RxBurst &inBurst, int B, GprsDecoder &decoder, ChannelCodingType *ccPtr)
|
|
{
|
|
inBurst.data1().copyToSegment(decoder.mI[B],0);
|
|
inBurst.data2().copyToSegment(decoder.mI[B],57);
|
|
// Save the stealing bits:
|
|
// TODO: Save these as floats and do a correlation to pick the encoding.
|
|
decoder.qbits[2*B] = inBurst.Hl();
|
|
decoder.qbits[2*B+1] = inBurst.Hu();
|
|
|
|
if (B != 3) { return NULL; }
|
|
|
|
decoder.deinterleave();
|
|
|
|
bool success;
|
|
BitVector *result;
|
|
switch ((*ccPtr = decoder.getCS())) {
|
|
case ChannelCodingCS4:
|
|
success = decoder.decodeCS4();
|
|
LOG(DEBUG) << "CS-4 success=" << success;
|
|
result = &decoder.mD_CS4;
|
|
break;
|
|
case ChannelCodingCS1:
|
|
success = decoder.decode();
|
|
LOG(DEBUG) << "CS-1 success=" << success;
|
|
result = &decoder.mD;
|
|
break;
|
|
default: devassert(0); // Others not supported yet.
|
|
return NULL;
|
|
}
|
|
|
|
if (success) {
|
|
result->LSB8MSB();
|
|
return result;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#if 0 // Moved to SoftVector
|
|
// 1 is perfect and 0 means all the bits were 0.5
|
|
static float getEnergy(const SoftVector &vec,float &low)
|
|
{
|
|
int len = vec.size();
|
|
avg = 0; low = 1;
|
|
for (int i = 0; i < len; i++) {
|
|
float bit = vec[i];
|
|
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
|
if (energy < low) low = energy;
|
|
avg += energy/len;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// WARNING: This func runs in a separate thread.
|
|
void PDCHL1Uplink::writeLowSideRx(const RxBurst &inBurst)
|
|
{
|
|
float low, avg = inBurst.getEnergy(&low);
|
|
//if (avg > 0.7) { OBJLOG(DEBUG) << "PDCHL1Uplink " << inBurst; }
|
|
|
|
//ScopedLock lock(testlock);
|
|
int burstfn = inBurst.time().FN();
|
|
int mfn = (burstfn / 13); // how many 13-multiframes
|
|
int rem = (burstfn - (mfn*13)); // how many blocks within the last multiframe.
|
|
int B = rem % 4;
|
|
|
|
if (avg > 0.5) { GPRSLOG(256) << "FEC:"<<LOGVAR(B)<<" "<<inBurst<<LOGVAR(avg); }
|
|
|
|
ChannelCodingType cc;
|
|
BitVector *result = decodeLowSide(inBurst,B,mchCS14Dec,&cc);
|
|
|
|
if (B == 3) {
|
|
int burst_fn=burstfn-3; // First fn in rlc block.
|
|
RLCBSN_t bsn = FrameNumber2BSN(burst_fn);
|
|
|
|
if (GPRSDebug) {
|
|
PDCHL1FEC *pdch = parent();
|
|
short *qbits = mchCS14Dec.qbits;
|
|
BitVector cshead(mchCS14Dec.mC.head(12).sliced());
|
|
|
|
RLCBlockReservation *res = mchParent->getReservation(bsn);
|
|
int thisUsf = pdch->getUsf(bsn-2);
|
|
// If we miss a reservation or usf, print it:
|
|
int missedRes = avg>0.4 && !result && (res||thisUsf);
|
|
if (missedRes || (GPRSDebug & (result?4:256))) {
|
|
std::ostringstream ss;
|
|
char buf[30];
|
|
ss <<"writeLowSideRx "<<parent()
|
|
<<(result?" === good" : "=== bad")
|
|
<< (res?" res:" : "") <<(res ? res->str() : "")
|
|
//<<LOGVAR(cshead)
|
|
//<<LOGVAR2("cs",(int)mchCS14Dec.getCS())
|
|
<<LOGVAR(cc)
|
|
<<LOGVAR2("revusf",decodeUSF(mchCS14Dec.mC))
|
|
<<LOGVAR(burst_fn)<<LOGVAR(bsn)
|
|
<<LOGVAR2("RSSI",inBurst.RSSI()) <<LOGVAR2("TE",inBurst.timingError())
|
|
// But lets print out the USFs bracketing this on either side.
|
|
<<getAnsweringUsfText(buf,bsn)
|
|
//<<" AnsweringUsf="<<pdch->getUsf(bsn-2)<<" "<<pdch->getUsf(bsn-1)
|
|
//<<" ["<<pdch->getUsf(bsn)<<"] "<<pdch->getUsf(bsn+1)<<" "<<pdch->getUsf(bsn+2)
|
|
<<" qbits="<<qbits[0]<<qbits[1]<<qbits[2]<<qbits[3]
|
|
<<qbits[4]<<qbits[5]<<qbits[6]<<qbits[7]
|
|
<<LOGVAR(low)<<LOGVAR(avg)
|
|
;
|
|
if (missedRes) {
|
|
for (int i = 0; i < 4; i++) {
|
|
// There was an unanswered reservation or usf.
|
|
avg = mchCS14Dec.mI[i].getEnergy(&low);
|
|
GPRSLOG(1) << "energy["<<i<<"]:"<<LOGVAR(avg)<<LOGVAR(low)<<" "
|
|
<<mchCS14Dec.mI[i];
|
|
}
|
|
}
|
|
GLOG(DEBUG)<<ss.str();
|
|
// Make sure we see a decoder failure if it reoccurs.
|
|
if (missedRes) std::cout <<ss.str() <<"\n";
|
|
}
|
|
} // if GPRSDebug
|
|
|
|
if (result) {
|
|
// Check clock skew for debugging purposes.
|
|
static int cnt = 0;
|
|
if (bsn >= gBSNNext-1) {
|
|
if (cnt++ % 32 == 0) {
|
|
GLOG(ERR) << "Incoming burst at frame:"<<burst_fn
|
|
<<" is not sufficiently ahead of clock:"<<gBSNNext.FN();
|
|
if (GPRSDebug) {
|
|
std::cout << "Incoming burst at frame:"<<burst_fn
|
|
<<" is not sufficiently ahead of clock:"<<gBSNNext.FN()<<"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
countGoodFrame();
|
|
|
|
// The four frame radio block has been decoded and is in mD.
|
|
if (gConfig.getBool("Control.GSMTAP.GPRS")) {
|
|
// Send to GSMTAP. Untested.
|
|
gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(), //GSM::TDMA_PACCH,
|
|
frame2GsmTapType(*result),
|
|
false, // not SACCH
|
|
true, // this is an uplink.
|
|
*result); // The data.
|
|
}
|
|
|
|
mchUplinkData.write(new RLCRawBlock(bsn,*result,inBurst.RSSI(),inBurst.timingError(),cc));
|
|
} else {
|
|
countBadFrame();
|
|
}
|
|
} else {
|
|
// We dont have a full 4 bursts yet, and we rarely care about these
|
|
// intermediate results, but here is a way to see them:
|
|
GPRSLOG(64) <<"writeLowSideRx "<<parent()<<LOGVAR(burstfn)<<LOGVAR(B)
|
|
<<" RSSI=" <<inBurst.RSSI() << " timing=" << inBurst.timingError();
|
|
}
|
|
}
|
|
|
|
// This is an entry point from other directories.
|
|
// Just bind the inburst with the associated channel and call the routine above.
|
|
void GPRSWriteLowSideRx(const GSM::RxBurst& inBurst, PDCHL1FEC*pdch)
|
|
{
|
|
pdch->uplink()->writeLowSideRx(inBurst);
|
|
}
|
|
|
|
|
|
// Transfer one RLCBlock, which is four frames, to the radio layer.
|
|
// Copied from XXCHL1Encoder::transmit()
|
|
// void PDCHL1Downlink::transmit(mI)
|
|
// See XCCHL1Encoder:transmit(mI)
|
|
|
|
// Code duplicated almost verbatim from L1Encoder.
|
|
// Inability to share code embedded in a method is one of the problems
|
|
// with object oriented programming.
|
|
#if 0
|
|
void PDCHL1Downlink::mchResync()
|
|
{
|
|
// If the encoder's clock is far from the current BTS clock,
|
|
// get it caught up to something reasonable.
|
|
Time now = gBTS.time();
|
|
int32_t delta = mchNextWriteTime-now;
|
|
GPRSLOG(8) << "PDCHL1Downlink" <<LOGVAR(mchNextWriteTime)
|
|
<<LOGVAR(now)<< LOGVAR(delta);
|
|
if ((delta<0) || (delta>(51*26))) {
|
|
mchNextWriteTime = now;
|
|
//mchNextWriteTime.TN(now.TN()); // unneeded?
|
|
mchNextWriteTime.rollForward(mchMapping.frameMapping(mchTotalBursts),mchMapping.repeatLength());
|
|
GPRSLOG(2) <<"PDCHL1Downlink RESYNC" << LOGVAR(mchNextWriteTime) << LOGVAR(now);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// Dispatch an RLC block on this downlink.
|
|
// This must run once for every Radio Block (4 TDMA frames or so) sent.
|
|
// It should be kept only as far enough ahead of the physical layer so that it never stalls.
|
|
// Based on: TCHFACCHL1Encoder::dispatch()
|
|
void PDCHL1Downlink::dlService()
|
|
{
|
|
// Get right with the system clock.
|
|
// NO: mchResync();
|
|
static int debugCntTotal = 0, debugCntDummy = 0;
|
|
debugCntTotal++;
|
|
if ((GPRSDebug&512) || debugCntTotal % 1024 == 0) {
|
|
GPRSLOG(2) << "dlService sent total="<<debugCntTotal<<" dummy="<<debugCntDummy <<" "<<this->parent();
|
|
}
|
|
|
|
// If gFixIdleFrame only send blocks on the even BSNs,
|
|
// and send idle frames on the odd BSNs, to make SURE that the
|
|
// RRBP and USF fields are cleared.
|
|
// This means that only the odd uplink BSNs will be used for uplink data.
|
|
// I also modified makeReservationInt to make the RRBP uplink reservations
|
|
// only on even frames, since they will be clear of data, but
|
|
// that is overkill for debugging.
|
|
// For reservations all we care is that the downlink RRBP field
|
|
// occurs only on even frames -
|
|
// the associated uplink block will be 3-7 blocks downtime from that,
|
|
// so could land on either even or odd frames.
|
|
if (gFixIdleFrame && (gBSNNext & 1)) { return; }
|
|
|
|
// I think we have to check active because the radio can get turned on/off
|
|
// completely without our knowledge.
|
|
// If this happens, the TBFs will expire, so we should probably destroy
|
|
// the entire GPRS machinery and start over when we get turned back on.
|
|
//if (! active()) {
|
|
// mNextWriteTime += 52; // Wait for a PCH multiframe.
|
|
// gBTS.clock().wait(mNextWriteTime);
|
|
// return;
|
|
//}
|
|
|
|
|
|
// Look for a data block to send.
|
|
// We did not queue these up in advance because the data that the engine
|
|
// wants to send may change every time it receives an ack/nack message.
|
|
//TBFList_t &list = gL2MAC.macTBFs;
|
|
//TBFList_t::iterator itr = list.begin(), e = list.end();
|
|
//for ( ; itr != e; itr++) {
|
|
//TBF *tbf = *itr;
|
|
TBF *tbf;
|
|
TBFList_t::iterator itr;
|
|
for (RListIterator<TBF*> itrl(gL2MAC.macTBFs); itrl.next(tbf,itr); ) {
|
|
if (!tbf->canUseDownlink(this)) {
|
|
GPRSLOG(4) <<"dlService"<<tbf<<" state "<<tbf->mtGetState()
|
|
<<" reqch:"<<tbf->mtMS->msPacch
|
|
<< " can not use downlink:"<<this->parent();
|
|
continue;
|
|
}
|
|
TBFState::type oldstate = tbf->mtGetState();
|
|
|
|
if (tbf->mtServiceDownlink(this)) {
|
|
GPRSLOG(2) <<"dlService"<<tbf<<LOGVAR(oldstate)<<" state="<<tbf->mtGetState()
|
|
<<" reqch:"<<tbf->mtMS->msPacch
|
|
<<" using ch:"<<this->parent();
|
|
// Move this tbf to end of the list so we may service someone else next time.
|
|
// TODO: If the tbf is using extended dynamic uplink, all ganged
|
|
// uplink channels are reserved at once, and so we are not sharing
|
|
// the other ganged uplinks with other TBFs that want to use them unless
|
|
// those TBFs also share this channel.
|
|
gL2MAC.macTBFs.erase(itr);
|
|
gL2MAC.macTBFs.push_back(tbf);
|
|
if (gFixIdleFrame) { bugFixIdleFrame(); }
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If nothing else, send a dummy message.
|
|
// We have to allocate it because we allocate all messages.
|
|
// Note that this message will have the MAC header fields USF and RRBP set by send1MsgFrame.
|
|
RLCMsgPacketDownlinkDummyControlBlock *dummymsg = new RLCMsgPacketDownlinkDummyControlBlock();
|
|
send1MsgFrame(NULL,dummymsg,0,MsgTransNone,NULL);
|
|
debugCntDummy++;
|
|
|
|
if (gFixIdleFrame) { bugFixIdleFrame(); }
|
|
}
|
|
|
|
|
|
// This is just a pass-through to TFIList.
|
|
void PDCHCommon::setTFITBF(int tfi, RLCDir::type dir, TBF *tbf)
|
|
{
|
|
mchParent->mchTFIs->setTFITBF(tfi,dir,tbf);
|
|
}
|
|
|
|
TBF *PDCHCommon::getTFITBF(int tfi, RLCDirType dir)
|
|
{
|
|
TBF *tbf = mchParent->mchTFIs->getTFITBF(dir,tfi);
|
|
if (tbf == NULL) {
|
|
// Somehow we lost track of this tbf. Maybe the timers are set wrong,
|
|
// and we dumped it before the MS gave up on it.
|
|
GLOG(ERR) << "GPRS Radio Block Data Block with unrecognized tfi: " << tfi << " dir:"<<dir;
|
|
return NULL;
|
|
}
|
|
return tbf;
|
|
}
|
|
|
|
// Return the RF Power Control alpha and gamma value.
|
|
// ---------- From GSM05.08 10.2.1 ----------
|
|
// MS RF output power Pch is:
|
|
// Pch = min(Gamma0 - GammaCh - Alpha * (C + 48), PMAX)
|
|
// Alpha is a system param or optionally sent to MS in RLC messages (we dont.)
|
|
// C represents CCCH power measured inside the MS.
|
|
// Gamma0 = 39 dBm for GSM400, GSM900, GSM850, or 36 dBm for DCS1800 and PCS1900
|
|
// PMAX is MS_TXPWR_MAX_CCH if no PBCCH exists (our case.)
|
|
// ------------------------------------------
|
|
// The alpha value represents how much the MS should turn down its output
|
|
// power in response to its own measurement of CCCH input power.
|
|
// This alpha value is sent in the Packet Uplink/Downlink Assignment messages.
|
|
// The gamma value is subtracted from the max power.
|
|
// Basically, the BTS can take complete control of power by setting alpha to 0
|
|
// and using gamma, or let the MS take complete control by setting gamma to 0 and alpha to 1,
|
|
// or anything in between.
|
|
// TODO: Once we start talking to the MS, it will be RACHing to us on a regular basis,
|
|
// and will also be reporting lost bursts, so we could be fiddling with the power parameters
|
|
// on a per-MS basis. However, this function returns the default gamma alpha
|
|
// to be used for the initial single-block downlink assignment when we dont
|
|
// know what MS we are talking to yet.
|
|
// BEGINCONFIG
|
|
// 'GPRS.MS.Power.Alpha',10,1,0,'MS power control parameter; see GSM 05.08 10.2.1'
|
|
// 'GPRS.MS.Power.Gamma',31,1,0,'MS power control parameter; see GSM 05.08 10.2.1'
|
|
// ENDCONFIG
|
|
int GetPowerAlpha()
|
|
{
|
|
// God only knows what this should be.
|
|
int alpha = gConfig.getNum("GPRS.MS.Power.Alpha");
|
|
// The value runs 0..10 representing increments of 10%
|
|
return RN_BOUND(alpha,0,10); // Bound to allowed values.
|
|
}
|
|
|
|
int GetPowerGamma()
|
|
{
|
|
// 0 means full power and let the MS control power.
|
|
int gamma = gConfig.getNum("GPRS.MS.Power.Gamma");
|
|
return RN_BOUND(gamma,0,31); // Bound to allowed values, 5 bits.
|
|
}
|
|
|
|
int GetTimingAdvance(float timingError)
|
|
{
|
|
int initialTA = (int)(timingError + 0.5F);
|
|
initialTA = RN_BOUND(initialTA,0,62); // why 62, not 63?
|
|
return initialTA;
|
|
}
|
|
|
|
#if 0
|
|
// Turn on this PDCHL1
|
|
// Unused function: This is how you would reprogram TRXManager.
|
|
void PDCHL1FEC::snarf()
|
|
{
|
|
assert(! encoder()->active());
|
|
assert(! decoder()->active());
|
|
// TODO: Wait until an appropriate time, lock everything in sight before doing this.
|
|
ARFCNManager*radio = encoder()->getRadio();
|
|
// Connect to the radio now.
|
|
mPDTCH.downstream(radio);
|
|
mPTCCH.downstream(radio);
|
|
mPDIdle.downstream(radio);
|
|
}
|
|
|
|
void PDCHL1FEC::unsnarf()
|
|
{
|
|
assert(mlogchan);
|
|
ARFCNManager*radio = encoder()->getRadio();
|
|
// TODO: Wait until an appropriate time, lock everything in sight before doing this.
|
|
mlogchan->downstream(radio);
|
|
// TODO: This is not thread safe. It would not work either,
|
|
// because the TRXManager function asserts that nobody got the channel earlier.
|
|
// Can we hook the channel inside the logical channel?
|
|
gBTS.addTCH(mlogchan);
|
|
}
|
|
#endif
|
|
};
|