Files
openbts/GPRS/FEC.cpp
2014-07-16 23:57:22 +02:00

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
};