OpenBTS-UMTS/UMTS/UMTSL1FEC.cpp

1174 lines
38 KiB
C++

/**@file L1 TrCH/PhCH FEC declarations for UMTS. */
/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#include "UMTSL1FEC.h"
#include "MACEngine.h"
#include <assert.h>
#include <Configuration.h>
#include <Logger.h>
#include "UMTSConfig.h"
#include "URRCTrCh.h"
#include "URRC.h"
#include "RateMatch.h"
using namespace std;
namespace UMTS {
int gFecTestMode = 0;
extern ConfigurationTable gConfig;
DCHListType gActiveDCH;
#if 0 // unused
/** From 3GPP 25.211 Table 11, 15-slot formats only */
unsigned SlotFormatDnNData1[] = {
0, 0, 2, 2, // 0-3
2, 2, 2, 2, // 4-7
6, 6, 6, 6, // 8-11
12, 28, 56, 120, // 12-15
248 // 16
};
/** From 3GPP 25.211 Table 11, 15-slot formats only */
unsigned SlotFormatDnNData2[] = {
4, 2, 14, 12, // 0-3
12, 10, 8, 6, // 4-7
28, 26, 24, 22, // 8-11
48, 112, 232, 488, // 12-15
1000 // 16
};
/** From 3GPP 25.211 Table 11, 15-slot formats only */
unsigned SlotFormatDnTPC[] = {
2, 2, 2, 2, // 0-3
2, 2, 2, 2, // 4-7
2, 2, 2, 2, // 8-11
4, 4, 8, 8, // 12-15
8 // 16
};
/** From 3GPP 25.211 Table 11, 15-slot formats only */
unsigned SlotFormatDnTFCI[] = {
0, 2, 2, 2, // 0-3
0, 2, 0, 2, // 4-7
0, 2, 0, 2, // 8-11
8, 8, 8, 8, // 12-15
16 // 16
};
/** From 3GPP 25.211 Table 11, 15-slot formats only */
unsigned SlotFormatDnPilot[] = {
4, 4, 2, 4, // 0-3
4, 4, 8, 8, // 4-7
4, 4, 8, 8, // 8-11
8, 8, 16, 16, // 12-15
16 // 16
};
#endif
TrCHFECDecoder::TrCHFECDecoder(TrCHFEC* wParent,FecProgInfo &fpi):
TrCHFECBase(wParent,fpi),
mUpstream(NULL)
#if OLD_CONTROL_IF
, mRunning(false),
mFER(0.0F),
mAssignmentTimer(10000),
mReleaseTimer(10000)
#endif
{
unsigned frameSize = gFrameLen / SF();
mD = new SoftVector(frameSize);
mHDI = new SoftVector(frameSize);
mDSlotIndex = 0;
unsigned nrf = fpi.getNumRadioFrames();// number of radio frames per tti
//mDTti = new SoftVector(frameSize * nrf);
// mRM and mDTti are after rate-matching:
mRM = new SoftVector(fpi.mCodedBkSz/nrf);
mDTti = new SoftVector(fpi.mCodedBkSz);
mDTtiIndex = 0;
rateMatchComputeUlEini(fpi.mCodedBkSz/nrf,mRadioFrameSz,fpi.getTTICode(),mEini);
}
#if OLD_CONTROL_IF
bool TrCHFECDecoder::recyclable() const
{
ScopedLock lock(mLock);
if (mAssignmentTimer.expired()) return true;
if (mReleaseTimer.expired()) return true;
return false;
}
#endif
PhCh * TrCHFECBase::getPhCh() const
{
assert(mParent);
return mParent->mPhCh;
}
unsigned FecProgInfo::getNumRadioFrames() const
{
return TTICode2NumFrames(getTTICode());
}
FecProgInfoSimple::FecProgInfoSimple(unsigned wSF,TTICodes wTTICode,unsigned wPB,
unsigned wRadioFrameSz) :
FecProgInfo(wSF,wTTICode,wPB,wRadioFrameSz, false)
{
assert(mPB == 16); // Required for the simple channels.
// assume no rate matching, convolutional coding, and compute TB size.
mCodedBkSz = mRadioFrameSz * getNumRadioFrames();
mTBSz = RrcDefs::R2DecodedSize(mCodedBkSz) - mPB;
}
// This class is used to init the FecProgInfo for a full support rach/fach/dch.
// Assumes convolutional coding.
// TODO: Need a different constructor for Turbo-coded.
FecProgInfoInit::FecProgInfoInit(unsigned wSF,TTICodes wTTICode,unsigned wPB,
unsigned wRadioFrameSz, unsigned wTBSz, bool wTurbo) :
FecProgInfo(wSF,wTTICode,wPB,wRadioFrameSz, wTurbo)
{
assert(mPB == 24 || mPB == 16 || mPB == 12 || mPB == 8);
assert(wTBSz != 0);
mTBSz = wTBSz; // Determined by RRC.
if (wTurbo) {
mCodedBkSz = RrcDefs::TurboEncodedSize(mTBSz + mPB, &mCodeBkSz);
mFillBits = RrcDefs::TurboDecodedSize(mCodedBkSz) - (mTBSz+mPB);
} else {
mCodedBkSz = RrcDefs::R2EncodedSize(mTBSz + mPB, &mCodeBkSz);
mFillBits = RrcDefs::R2DecodedSize(mCodedBkSz) - (mTBSz+mPB);
}
printf("ProgInfo: %u %u %u %u %u\n",wSF,wRadioFrameSz,wTBSz,(unsigned)mCodedBkSz,(unsigned)mFillBits);
}
TrCHFECDecoder* TrCHFECEncoder::sibling()
{
assert(mParent);
return mParent->decoder();
}
const TrCHFECDecoder* TrCHFECEncoder::sibling() const
{
assert(mParent);
return mParent->decoder();
}
TrCHFECEncoder* TrCHFECDecoder::sibling()
{
assert(mParent);
return mParent->encoder();
}
const TrCHFECEncoder* TrCHFECDecoder::sibling() const
{
assert(mParent);
return mParent->encoder();
}
void TrCHFECEncoder::open()
{
mTotalBursts = 0;
mPrevWriteTime = 0;
mNextWriteTime = gNodeB.clock().get();
mActive = true;
}
void TrCHFECEncoder::close()
{
mActive = false;
}
void TrCHFECDecoder::open()
{
#if OLD_CONTROL_IF
mActive = true;
mAssignmentTimer.set();
mReleaseTimer.reset();
#else
controlOpen();
#endif
}
void TrCHFECDecoder::close(bool hardRelease)
{
#if OLD_CONTROL_IF
mActive = false;
mReleaseTimer.set();
// TODO UMTS - If hardRelease, we need to force-expire that timer.
#else
controlClose(hardRelease);
#endif
}
// Incoming frame is the result of 25.212 4.2.6 Radio Frame Segmentation.
void TrCHFECEncoder::sendFrame(BitVector& frame, unsigned tfci)
{
//TODO: This test now fails, correctly, due to rate matching.
//assert(frame.size()%gFrameSlots == 0);
BitVector h = frame.alias();
// 25.212 4.2.8 TrCh Multiplexing.
// (pat) We only have one TrCh, so nothing to do.
// 25.212 4.2.9 Insertion of Discontinuous Transmission (DTX) Indicators.
// Since we only either the max. size of possible transport format combinatiors or transmit nothing, DTX is not needed.
// (pat) This adds empty bits to fill up the radio frames.
// The description is complicated by various types of compressed frames that we
// will never support, but it is basically just right-padding with emptiness.
// 25.212 4.2.10 Physical Channel Segmentation.
// "When more than one PhCH is used..." OK, can stop reading right there.
// 25.212 4.2.11 Second Interleaving.
// (pat) Number of columns fixed at 30, and number of rows is the minimum that will work.
// The padding will only occur when supporting multiple TrCh, because the
// radio frame is a multiple 150 which is divisible by 30.
const unsigned C2 = 30; // Number of columns;
unsigned hsize = h.size();
unsigned rows = (hsize + (C2 - 1))/C2;
int padding = (C2 * rows) - hsize;
assert(padding >= 0);
unsigned Ysize = hsize + padding; // Y is defined in 4.2.11 as padded interleave buf
BitVector Yout(Ysize);
if (padding == 0) {
h.interleavingNP(C2, TrCHConsts::inter2Perm, Yout);
} else {
// Must pre-pad and post-strip bits.
// The post-interleave pad bits are spread all over; easiest way to get rid
// of them is to use a special marker value for the padding.
const char padval = 4; // We will pad with padbits set to this value.
BitVector Yin(Ysize); // Temporary buffer
h.copyTo(Yin);
memset(Yin.begin()+hsize,padval,padding); // Add the padding.
Yin.interleavingNP(C2, TrCHConsts::inter2Perm, Yout);
// Strip out the padding bits.
char *yp = Yout.begin(), *yend = Yout.end();
for (char *cp = yp; cp < yend; cp++) {
if (*cp != padval) { *yp++ = *cp; }
}
assert(yp == Yin.begin() + hsize);
}
BitVector U(Yout.head(hsize));
#if USE_OLD_FEC // This makes assumptions about RACHFEC so does not compile with the new code.
if (gFecTestMode == 2) {
gNodeB.mRachFec->decoder()->writeLowSide2(U);
return;
}
#endif
// 25.212 4.2.12 Physical Channel Mapping.
// "In compressed mode..." OK, can stop reading right there.
// 25.212 4.3 Transport Format Detection Based on TFCI.
// 25.212 4.3.3 Coding of TFCI.
// (pat) We have to pre-encode the TFCI to go in the radio slots.
// This encoding is static based only on on the tfci to be sent,
// so it is done once on startup in class TrCHConsts,
// and now we just index into it with the incoming tfci.
assert(tfci < TrCHConsts::sMaxTfci);
//tfci = 0;
uint32_t tfciCode = TrCHConsts::sTfciCodes[tfci];
// (pat) Okey Dokey, finished with spec 25.212.
// Now we move on to another fine and wonderful spec:
// 25.211: "Physical channels and mapping of transport channels onto physical channels (FDD)"
PhCh *phch = getPhCh();
PhChType chType = phch->phChType();
assert(chType == DPDCHType || chType == SCCPCHType || chType == PCCPCHType);
size_t dataSlotSize = U.size() / gFrameSlots;
// NOTE: PCCPCHType sends only 18 bits per slot, not 20.
if (chType == PCCPCHType) {
// The PCCPCH radio slot contains: | Tx Off | Data (always 18 bits) |
for (unsigned i=0; i<gFrameSlots; i++) {
// (pat) Before I added the BitVector copy constructor, this allocated
// a new Vector via constructor: Vector(const Vector<char>&other)
const BitVector slotBits = U.segment(i*dataSlotSize,dataSlotSize);
TxBitsBurst *out = new TxBitsBurst(
slotBits,
SF(), SpCode(), mNextWriteTime.slot(i),true
);
RN_MEMLOG(TxBitsBurst,out);
phch->getRadio()->writeHighSide(out);
}
} else {
int radioFrameSize = 2*(gFrameLen / SF());
BitVector radioFrame(radioFrameSize);
int radioSlotSize = radioFrameSize/15;
SlotFormat *dlslot = phch->getDlSlot();
const unsigned ndata1 = dlslot->mNData1;
const unsigned ndata2 = dlslot->mNData2;
const unsigned ntpc = dlslot->mNTpc;
const unsigned ntfci = dlslot->mNTfci;
const unsigned tfciMask = (1<<ntfci)-1;
const unsigned npilot = dlslot->mNPilot;
const unsigned pi = dlslot->mPilotIndex;
// Double check this against channel parameters.
assert(dataSlotSize == ndata1+ndata2);
BitVector radioSlotBits(radioSlotSize);
for (unsigned s=0; s<gFrameSlots; s++) {
const unsigned dataStart = s*dataSlotSize;
size_t wp = 0;
unsigned int tpcField = 0;
// 25.211 4.3.5.1: Mapping of TFCI word in normal [non-compressed] mode:
// The 32 TFCI code bits are stuck in the slots LSB first,
// wrapping around to be reused as much as needed.
switch (chType) {
case SCCPCHType:
//cout << "TFCI: " << tfci << " code: " << (tfciCode&tfciMask);
// The SCCPCH radio slot contains: | TFCI | Data | Pilot |
radioSlotBits.writeFieldReversed(wp,tfciCode&tfciMask,ntfci);
U.segment(dataStart,ndata1).copyToSegment(radioSlotBits,wp,ndata1);
wp += ndata1;
radioSlotBits.fillField(wp, TrCHConsts::sDlPilotBitPattern[pi][s], npilot);
// There is no data2 for SCCPCH.
break;
case DPDCHType:
// The DCH radio slot contains:
// Release 4 or later: | Data1 | TPC | TFCI | Data2 | Pilot |
// Early versions of R'99: | TFCI | Data1 | TPC | Data2 | Pilot |
#ifdef RELEASE99 // defined in UMTSPhCh.cpp
radioSlotBits.writeFieldReversed(wp,tfciCode&tfciMask,ntfci);
#endif
if (ndata1 > 0) {
U.segment(dataStart,ndata1).copyToSegment(radioSlotBits,wp,ndata1);
wp += ndata1;
}
// Lower layers are going to fill in TPC, we hope.
// Toggle tpc bits between all ones and all zeros to keep phone at same tx power
// TODO: Will need to do better power control later
tpcField = ((1 << ntpc)-1); // * ( (s+mNextWriteTime.FN()) % 2);
radioSlotBits.writeField(wp,tpcField,ntpc);
#ifndef RELEASE99
radioSlotBits.writeFieldReversed(wp,tfciCode&tfciMask,ntfci);
#endif
if (ndata2 > 0) {
U.segment(dataStart+ndata1,ndata2).copyToSegment(radioSlotBits,wp,ndata2);
wp += ndata2;
}
radioSlotBits.fillField(wp, TrCHConsts::sDlPilotBitPattern[pi][s], npilot);
break;
default: assert(0);
}
// rotate tfciCode by ntfci bits. It is unsigned, which helps.
tfciCode = (tfciCode>>ntfci) | (tfciCode<<(32-ntfci));
TxBitsBurst *out = new TxBitsBurst(radioSlotBits, SF(), SpCode(), mNextWriteTime.slot(s));
RN_MEMLOG(TxBitsBurst,out);
phch->getRadio()->writeHighSide(out);
}
}
mPrevWriteTime = mNextWriteTime;
++mNextWriteTime; // The ++ does not matter for BCH because the TBs have scheduled() set, which overrides mNextWriteTime.
}
#if 0
//void TrCHConsts::initPilotBitPatterns()
//{
// // 25.211 5.3.2 Table 12: Pilot bit patterns for downlink DPCCH with Npilot = 2, 4, 8 and 16
// //
// // Npilot=2 Npilot=4 Npilot=8 Npilot=16
// //Symbol 0 0 1 0 1 2 3 0 1 2 3 4 5 6 7
// // #
// //Slot #0 11 11 11 11 11 11 10 11 11 11 10 11 11 11 10
// // 1 00 11 00 11 00 11 10 11 00 11 10 11 11 11 00
// // 2 01 11 01 11 01 11 01 11 01 11 01 11 10 11 00
// // 3 00 11 00 11 00 11 00 11 00 11 00 11 01 11 10
// // 4 10 11 10 11 10 11 01 11 10 11 01 11 11 11 11
// // 5 11 11 11 11 11 11 10 11 11 11 10 11 01 11 01
// // 6 11 11 11 11 11 11 00 11 11 11 00 11 10 11 11
// // 7 10 11 10 11 10 11 00 11 10 11 00 11 10 11 00
// // 8 01 11 01 11 01 11 10 11 01 11 10 11 00 11 11
// // 9 11 11 11 11 11 11 11 11 11 11 11 11 00 11 11
// // 10 01 11 01 11 01 11 01 11 01 11 01 11 11 11 10
// // 11 10 11 10 11 10 11 11 11 10 11 11 11 00 11 10
// // 12 10 11 10 11 10 11 00 11 10 11 00 11 01 11 01
// // 13 00 11 00 11 00 11 11 11 00 11 11 11 00 11 00
// // 14 00 11 00 11 00 11 11 11 00 11 11 11 10 11 01
//
// // These are from columns 1,3,5,7 for nPilot==16; all the others columns are just copies.
// // Table 19: Pilot Symbol Pattern for SCCPCH is identical.
// static uint8_t sDlPilotBitPatternTable[4][gFrameSlots] = {
// { 3, 0, 1, 0, 2, 3, 3, 2, 1, 3, 1, 2, 2, 0, 0 }, // column 1
// { 2, 2, 1, 0, 1, 2, 0, 0, 2, 3, 1, 3, 0, 3, 3 }, // column 3
// { 3, 3, 2, 1, 3, 1, 2, 2, 0, 0, 3, 0, 1, 0, 2 }, // column 5
// { 2, 0, 0, 2, 3, 1, 3, 0, 3, 3, 2, 2, 1, 0, 1 } // column 7
// };
//
// for (unsigned slot = 0; slot < gFrameSlots; slot++) {
// uint16_t col1 = sDlPilotBitPatternTable[0][slot];
// uint16_t col3 = sDlPilotBitPatternTable[1][slot];
// uint16_t col5 = sDlPilotBitPatternTable[2][slot];
// uint16_t col7 = sDlPilotBitPatternTable[3][slot];
// uint16_t pat;
// // Npilot=2
// sDlPilotBitPattern[0][slot] = col1;
// // Npilot=4
// sDlPilotBitPattern[1][slot] = pat = (3<<2)|col1;
// // Npilot=8
// sDlPilotBitPattern[2][slot] = pat = (pat<<4)|(3<<2)|col3;
// // Npilot=16
// sDlPilotBitPattern[3][slot] = (pat<<8)|(3<<6)|(col5<<4)|(3<<2)|col7;
// }
//}
//
//
// // 25.212 4.2.11 table 7: Inter-Column permutation pattern for 2nd interleaving:
//const char TrCHConsts::inter2Perm[30] = {0,20,10,5,15,25,3,13,23,8,18,28,1,11,21,
// 6,16,26,4,14,24,19,9,29,12,2,7,22,27,17};
//
// // (pat) 25.212 4.3.3 table 8: Magic for TFCI code encoding.
// // The codes are 32 words of 10 bits each, but I am generating it
// // form the original binary table in the spec (below)
// // to avoid any transcription errors.
//const bool TrCHConsts::reedMullerTable[32][10] = {
// // i Mi,0 Mi,1 Mi,2 Mi,3 Mi,4 Mi,5 Mi,6 Mi,7 Mi,8 Mi,9
// /*0*/ { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
// /*1*/ { 0, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
// /*2*/ { 1, 1, 0, 0, 0, 1, 0, 0, 0, 1 },
// /*3*/ { 0, 0, 1, 0, 0, 1, 1, 0, 1, 1 },
// /*4*/ { 1, 0, 1, 0, 0, 1, 0, 0, 0, 1 },
// /*5*/ { 0, 1, 1, 0, 0, 1, 0, 0, 1, 0 },
// /*6*/ { 1, 1, 1, 0, 0, 1, 0, 1, 0, 0 },
// /*7*/ { 0, 0, 0, 1, 0, 1, 0, 1, 1, 0 },
// /*8*/ { 1, 0, 0, 1, 0, 1, 1, 1, 1, 0 },
// /*9*/ { 0, 1, 0, 1, 0, 1, 1, 0, 1, 1 },
// /*10*/ { 1, 1, 0, 1, 0, 1, 0, 0, 1, 1 },
// /*11*/ { 0, 0, 1, 1, 0, 1, 0, 1, 1, 0 },
// /*12*/ { 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 },
// /*13*/ { 0, 1, 1, 1, 0, 1, 1, 0, 0, 1 },
// /*14*/ { 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
// /*15*/ { 1, 0, 0, 0, 1, 1, 1, 1, 0, 0 },
// /*16*/ { 0, 1, 0, 0, 1, 1, 1, 1, 0, 1 },
// /*17*/ { 1, 1, 0, 0, 1, 1, 1, 0, 1, 0 },
// /*18*/ { 0, 0, 1, 0, 1, 1, 0, 1, 1, 1 },
// /*19*/ { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1 },
// /*20*/ { 0, 1, 1, 0, 1, 1, 0, 0, 1, 1 },
// /*21*/ { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1 },
// /*22*/ { 0, 0, 0, 1, 1, 1, 0, 1, 0, 0 },
// /*23*/ { 1, 0, 0, 1, 1, 1, 1, 1, 0, 1 },
// /*24*/ { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0 },
// /*25*/ { 1, 1, 0, 1, 1, 1, 1, 0, 0, 1 },
// /*26*/ { 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 },
// /*27*/ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
// /*28*/ { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
// /*29*/ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
// /*30*/ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
// /*31*/ { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }
// };
//
//void TrCHConsts::initTfciCodes()
//{
// // Pre-compute the tfci code for each possible tfci.
// // Implements 4.3.3 verbatim.
// // Yes, I realize we could turn the table sideways and do this quickly,
// // but it is only done once, ever.
// for (unsigned tfci = 0; tfci < sMaxTfci; tfci++) {
// uint32_t result = 0;
// for (unsigned i = 0; i <= 31; i++) {
// unsigned bi = 0;
// for (unsigned n = 0; n <= 9; n++) {
// unsigned an = (tfci >> n) & 1; // a0 is the lsb of tfci.
// bi += (an & reedMullerTable[i][n]); // b0 is the lsb of the result.
// }
// result |= ((bi&1) << i);
// }
// sTfciCodes[tfci] = result;
// if (tfci < 4) printf("TFCI[%d] = 0x%x\n",tfci,sTfciCodes[tfci]);
// }
//}
//
//// This is the wonderfully redundant redundant C++ way to declare declare declare static members.
//bool TrCHConsts::oneTimeInit = false;
//uint16_t TrCHConsts::sDlPilotBitPattern[4][15];
//uint32_t TrCHConsts::sTfciCodes[sMaxTfci]; // Table for up to 8 bit tfci, plenty for us.
//
//TrCHConsts::TrCHConsts(TTICodes wTTImsDiv10Log2)
// :mTTImsDiv10Log2(wTTImsDiv10Log2)
//{
// static int inter1Columns[4] = {
// 1, // TTI = 10ms
// 2, // TTI = 20ms
// 4, // TTI = 40ms
// 8 // TTI = 80ms
// };
//
// // 25.212 4.2.5.2 table 4: Inter-Column permutation pattern for 1st interleaving:
// static char inter1Perm[4][8] = {
// {0}, // TTI = 10ms
// {0, 1}, // TTI = 20ms
// {0, 2, 1, 3}, // TTI = 40ms
// {0, 4, 2, 6, 1, 5, 3, 7} // TTI = 80ms
// };
//
//
// /**
// static uint16_t reedMullerCode[32]; // They are 10 bits long.
// for (unsigned w = 0; w < 32; w++) {
// unsigned code = 0;
// for (unsigned b = 0; b < 10; b++) {
// code = (code << 1) | !!reedMullerTable[w][b];
// }
// ReedMullerCode[w] = code;
// }
// **/
//
// if (!oneTimeInit) {
// oneTimeInit = true;
// initPilotBitPatterns();
// initTfciCodes();
// }
//
// mInter1Columns = inter1Columns[mTTImsDiv10Log2];
// mInter1Perm = inter1Perm[mTTImsDiv10Log2];
//}
//
//// In uplink each slot has 2 TFCI bits which are concatenated to form 30 bits,
//// from which we attempt to retrieve the original tfci.
//unsigned findTfci(SoftVector &radioTfciBits, unsigned numTfcis)
//{
// assert(radioTfciBits.size() == 30);
// float *bits = radioTfciBits.begin();
//
// unsigned bestTfci = 0;
// float bestMatch = 0;
// assert(numTfcis <= TrCHConsts::sMaxTfci);
// for (unsigned tfci = 0; tfci < numTfcis; tfci++) {
// uint32_t tfciCode = TrCHConsts::sTfciCodes[tfci];
// float thisMatch = 0;
// for (unsigned b = 0; b < 30; b++) {
// // The bits are transmitted in the slots LSB first, so start with LSB of tfciCode.
// unsigned wantbit = tfciCode & 1; tfciCode >>= 1;
// float havebit = RN_BOUND(bits[b],0.0,1.0);
// if (wantbit) {
// thisMatch += havebit;
// } else {
// thisMatch += 1.0 - havebit;
// }
// }
// // A perfect match would be 30.
// if (thisMatch > bestMatch) {
// bestMatch = thisMatch;
// bestTfci = tfci;
// }
// }
// return bestTfci; // TODO: Regardless of how poor it is?
//}
#endif
// parity - 25.212, 4.2.1
void getParity(const BitVector &in, BitVector &parity)
{
int L = parity.size();
if (in.size() > 0) {
uint64_t gcrc;
if (L == 24) {
gcrc = TrCHConsts::mgcrc24;
} else if (L == 16) {
gcrc = TrCHConsts::mgcrc16;
} else if (L == 12) {
gcrc = TrCHConsts::mgcrc12;
} else if (L == 8) {
gcrc = TrCHConsts::mgcrc8;
} else if (L == 0) {
// no parity bits to add
} else {
assert(0);
}
if (L != 0) {
Parity p(gcrc, L, L + in.size());
p.writeParityWord(in, parity);
parity.reverse();
parity.invert(); // undo inversion done by parity generator
}
} else {
parity.fill(0);
}
}
void TrCHFECEncoder::writeHighSide(const TransportBlock &tblock)
{
const BitVector a = tblock.alias();
//PhCh *phch = getPhCh();
//PhChType chType = phch->phChType();
/*if (chType==SCCPCHType) {
LOG(NOTICE) << "SCCPCH TrCHFECEncoder input " << a.size() << " " << a.hexstr();
LOG(NOTICE) << tblock.size() << " " << trBkSz();
}*/
LOG(DEBUG) << "TrCHFECEncoder input " <<tblock;
assert(tblock.size() == trBkSz());
// parity - 25.212, 4.2.1
BitVector b(a.size() + getPB());
a.copyTo(b);
BitVector parityOfA = b.segment(a.size(), getPB());
getParity(a, parityOfA);
//OBJLOG(DEBUG) << "with parity " << b.size() << " " << b;
if (gFecTestMode) {
std::cout<<"writeHighSide"<<LOGBV(a)<<LOGBV(parityOfA)<<"\n";
}
// 24.212 4.2.2.1 Transport Block Concatenation.
// not yet
BitVector c; // The result after convolutional encoding.
unsigned Z = getZ();
if (b.size() <= Z) {
// No code block segmentation required.
BitVector o = b.alias();
// 24.212 4.2.3 Channel Coding.
// convolutional coding - 25.212, 4.2.3.1
if (isTurbo()) {
c = BitVector(3 * o.size() + 12);
encode(o,c);
} else {
BitVector in(b.size() + 8);
c = BitVector(2 * in.size());
o.copyTo(in);
in.fill(0, b.size(), 8);
encode(in,c);
}
} else {
// 24.212 4.2.2.2 Code Block Segmentation.
// 25.212 4.2.3.3 concatenation of encoded blocks
// We just encode the blocks directly into the concatenated output c.
// This code is for convolutional coding only!!
unsigned Xi = b.size(); // number of input bits
unsigned Ci = (Xi+ Z-1)/ Z; // number of code blocks.
unsigned Ki = (Xi+Ci-1)/Ci; // number of bits per block.
unsigned Yi = Ci * Ki - Xi; // number of filler bits.
// Unnecessary assertion used as a warning that rate-matching
// may be required. The code below works fine, but the RRC
// does not currently take these Yi filler bits into account
// and may have botched the TB size calculation if Yi is non-zero.
// Take this assertion out if you are sure.
// (pat) 6-19-2012: Updated TrChConfig::configDchPS to take Yi into
// account and removed this assertion. We should still assert
// elsewhere that any rate-matching is downward only.
// assert(Yi == 0);
const unsigned csize = isTurbo() ? 3*Ki+12 : 2*Ki+16;
c = BitVector(Ci * csize);
BitVector in(Ki+8);
for (unsigned r = 0; r < Ci; r++) {
if (Yi && (r == 0)) {
in.fill(0,0,Yi); // First block has Yi filler bits.
b.segment(0,Ki-Yi).copyToSegment(in,Yi);
} else {
b.segment(r*Ki-Yi,Ki).copyToSegment(in,0);
}
// 24.212 4.2.3 Channel Coding,
// convolutional coding - 25.212, 4.2.3.1
// And implicit concatenation of encoded blocks.
BitVector csegment = c.segment(r*csize,csize);
if (!isTurbo()) {
in.fill(0,Ki,8);
encode(in,csegment);
} else {
BitVector inTrunc = in.segment(0,Ki);
encode(inTrunc,csegment);
}
}
}
// 25.212 4.2.4 Radio Frame Size Equalization
// (pat) 25.212 4.2.4 and I quote:
// "Radio frame size equalisation is only performed in the UL."
// (pat) And that is because we use DTX instead of rate-matching in DL.
BitVector g = c.alias();
// Rate-matching
// The RRC insures that codedBkSz < mRadioFrameSz, ie, we will never use puncturing, ever,
// for any TF [Transport Format]
unsigned outsize = this->mRadioFrameSz*this->getNumRadioFrames();
unsigned insize = this->mCodedBkSz;
BitVector h(outsize);
if (insize != outsize)
rateMatchFunc<char>(g,h,1);
else
g.copyTo(h);
// not doing insertion of DTX (pat) doesnt go here?
// 4.2.9
//BitVector h; // This is 3-valued, but for now, just pad with zeros.
// Since we only either the max. size of possible transport format combinatiors or transmit nothing, DTX is not needed.
/*if (insize == outsize) {
h = frame;
} else {
h = BitVector(outsize);
frame.copyTo(h);
h.tail(frame.size()).zero();
}*/
// first interleave - 25.212, 4.2.5
BitVector q(h.size());
h.interleavingNP(inter1Columns(), inter1Perm(), q);
//LOG(DEBUG) << "interleaved " <<LOGVAR2("descr",tblock.mDescr) << " "<< q.str();
LOG(DEBUG) << "interleaved " << q.str();
#if USE_OLD_FEC // This makes assumptions about RACHFEC so does not compile with the new code.
if (gFecTestMode == 1) {
// (pat) For testing, send it back up through the decoder.
// Jumper around the radio frame segmentation for now.
// We are assuming it is RACH/FACH for now.
SoftVector qdebug(q); // Convert to SoftVector.
gNodeB.mRachFec->decoder()->writeLowSide3(qdebug);
return;
}
#endif
// radio frame segmentation - 25.212, 4.2.6
// inter1Columns is the same as the number of 10 ms radio frames in the TTI.
const int frames = inter1Columns();
const int frameSize = q.size() / frames;
assert(q.size() % frameSize == 0);
if (tblock.scheduled()) mNextWriteTime = tblock.time();
for (int i = 0; i < frames; i++) {
BitVector seg = q.segment(i * frameSize, frameSize);
sendFrame(seg,0 /*tblock.mTfci*/);
}
}
// (pat) I assume the incoming burst is a single-slot data burst.
// Note that uplink separates data and control info, so the data burst is
// nothing but data - the control burst contains the pilot and tfci bits.
// This function just adds filler bursts for missing slot data.
void TrCHFECDecoder::l1WriteLowSide(const RxBitsBurst &burst)
{
// TODO: Enable these assertions.
//assert(burst.mTfciBits[0] >= 0 && burst.mTfciBits[0] <= 1);
//assert(burst.mTfciBits[1] >= 0 && burst.mTfciBits[1] <= 1);
const SoftVector f = burst.alias();
// not doing rate matching
SoftVector e = f.alias();
// (pat) Why are we inserting filler bursts going upstream?
// They will fail the parity check (hopefully!) and writeLowSide1 will just throw them away?
// Answer: Because the radio frame un-segmentation needs the right number of
// bursts or it will become permanently desynchronized.
// We cannot use the burst time to synchronize because incoming RACH bursts are not
// synchronized to the main radio clock; we just get 15/30 in a row starting anywhere.
// Also, maybe he hopes the convolutional decoder will span the data.
// This assumes frame boundary is at TN=0, and TTI boundary is at FN=0
unsigned expectedTTISlotIndex = mDTtiIndex*gFrameSlots+mDSlotIndex;
unsigned receivedTTISlotIndex = burst.time().TN();
unsigned numFramesTTI = TTICode2NumFrames(getTTICode());
unsigned receivedTTIIndex = ((unsigned) burst.time().FN() % numFramesTTI);
receivedTTISlotIndex += (receivedTTIIndex*gFrameSlots);
if (receivedTTISlotIndex <= expectedTTISlotIndex) {
mDSlotIndex = burst.time().TN();
mDTtiIndex = receivedTTIIndex;
expectedTTISlotIndex = receivedTTISlotIndex;
}
//LOG(INFO) << "time: " << burst.time() << " slot: " << mDSlotIndex << " frame " << mDTtiIndex;
SoftVector fillerBurst;
bool first = true;
while (receivedTTISlotIndex > expectedTTISlotIndex) {
if (first) {
fillerBurst.resize(burst.size());
for (unsigned i = 0; i < burst.size(); i++)
fillerBurst[i] = 0.5f + 0.0001*(2.0*(float) (random() & 0x01)-1.0);
first = false;
}
float garbageTfci[2] = { 1.0, 1.0 };
writeLowSide1(fillerBurst, garbageTfci);
expectedTTISlotIndex++;
}
writeLowSide1(e,burst.mTfciBits);
}
// (pat) Accumulate slots into one radio frame in mD.
void TrCHFECDecoder::writeLowSide1(const SoftVector &e, const float tfcibits[2])
{
//OBJLOG(INFO) << "TrCHFECDecoder input " << e.size() << " " << e;
// (pat) If TTI != 10ms, we just concatenate radio frames until we have them all.
// (pat) So accumulate the incoming fuzzy bits in mD until it is full.
// radio frame segmentation - 25.212, 4.2.6
unsigned frameSize = gFrameLen / SF();
unsigned slotSize = frameSize/gFrameSlots;
assert(e.size() == slotSize);
e.copyToSegment(*mD,mDSlotIndex*slotSize);
if (++mDSlotIndex < gFrameSlots) {return;}
mDSlotIndex = 0; // prep for next Radio Frame.
// (pat) Previous code did alot of copying things around:
//if (mD->size() == frameSize) mD->resize(0);
//SoftVector *tmp = mD;
//mD = new SoftVector(*mD, e);
//delete tmp;
//if (mD->size() < frameSize) return;
assert(mD->size() == frameSize);
//OBJLOG(INFO) << "concatenated " << mD->size() << " " << *mD;
writeLowSide2(*mD);
}
// The input here is one radio-frame, ie, 15 slots worth.
void TrCHFECDecoder::writeLowSide2(const SoftVector &frame)
{
unsigned frameSize = gFrameLen / SF();
assert(frame.size() == frameSize);
// 25.212 4.2.12 Physical Channel Mapping.
// "In compressed mode..." Nope.
// TODO: These vectors can be allocated statically at initialization time.
// 25.212 4.2.11 Second Interleaving.
const_cast<SoftVector&>(frame).deInterleavingNP(30, TrCHConsts::inter2Perm, *mHDI);
assert(mHDI->size() == frameSize);
//OBJLOG(INFO) << "2nd deinterleaved " << mHDI->size() << " " << *mHDI;
// 25.212 4.2.10 Physical Channel Segmentation.
// "When more than one PhCh is used..." Nope.
// 25.212 4.2.8 TrCh (De-)Multiplexing.
// Not yet.
// 25.212 4.2.7 Rate Matching.
unsigned insize = this->mRadioFrameSz;
unsigned outsize = this->mCodedBkSz/this->getNumRadioFrames();
assert(mHDI->size() == insize);
SoftVector *result = mHDI;
if (insize != outsize) {
result = mRM;
rateMatchFunc<float>(*mHDI,*mRM,this->mEini[mDTtiIndex]);
}
OBJLOG(INFO) << "insize: " << insize << "outsize: " << outsize;
OBJLOG(INFO) << "rate-unmatched" << result->size() << " " << *result;
// 25.212 4.2.6 Radio Frame Segmentation.
// If not using TTI=10ms, At this point we have to reaccumulate the complete TTI data.
TTICodes tticode = getTTICode();
if (tticode == TTI10ms) { writeLowSide3(*result); return; }
// Accumulate one ttis worth of Radio Frames into mDTti.
result->copyToSegment(*mDTti,mDTtiIndex*outsize);
mDTtiIndex++;
unsigned numFramesPerTti = getNumRadioFrames();
if (mDTtiIndex < numFramesPerTti) {return;}
mDTtiIndex = 0; // prep for next TTI
writeLowSide3(*mDTti);
}
// Input is post-radio-frame-segmentation, which means input is the accumulation
// of 1, 2, 4, 8 radio frames based on the TTI=10,20,40,80
void TrCHFECDecoder::writeLowSide3(const SoftVector &dataTti)
{
//OBJLOG(INFO) << "concatenated TTI: " << dataTti.size() << " " << dataTti;
// first interleave - 25.212, 4.2.5
SoftVector t(dataTti.size());
const_cast<SoftVector&>(dataTti).deInterleavingNP(inter1Columns(), inter1Perm(), t);
OBJLOG(INFO) << "deinterleaved " << t.size() << " " << t;
// radio frame equalization - 25.212, 4.2.4
SoftVector c = t.alias();
// FIXME -- This stuff assumes a rate-1/2 coder.
//assert(c.size() <= 2 * getZ());
BitVector b; // The result
if (0) {
// old code does not handle concatenation/segmentation.
BitVector o(c.size() / 2);
decode(c,o);
//OBJLOG(INFO) << "unconvoluted " << o.size() << " " << o;
// 24.212 4.2.2 Transport Block Concatenation and Code Block Segmentation.
// concatenation - 25.212, 4.2.2.1
// segmentation - 25.212, 4.2.2.2
// nothing to do but remove 8 bits of fill
//BitVector b(o.size() - 8);
b = BitVector(o.size() - 8);
o.copyToSegment(b, 0, o.size() - 8);
} else {
// convolutional coding - 25.212, 4.2.3.1
// concatenation of encoded blocks - 25.212, 4.2.3.3
unsigned Zenc = isTurbo() ? (3*getZ()+12) : (2*getZ() + 16); // encoded size of Z.
unsigned Ci = (c.size() + Zenc-1)/Zenc; // number of coded blocks.
unsigned Kienc = c.size()/Ci; // number of encoded bits per coded block.
unsigned Ki = isTurbo() ? ((Kienc-12)/3) : (Kienc/2 - 8); // number of unencoded bits per coded block.
unsigned numFillBits = fillBits(); // number of filler bits in first coded block.
assert(Kienc * Ci == c.size());
b = BitVector(Ci*Ki - numFillBits);
BitVector o1(isTurbo() ? Ki : (Ki+8));
//printf("%u %u %u %u\n",Zenc,Ci,Kienc,Ki);
for (unsigned r = 0; r < Ci; r++) {
decode(c.segment(r*Kienc,Kienc),o1);
if (numFillBits && (r == 0)) {// skip first fillBits, they aren't data
o1.segmentCopyTo(b,numFillBits,Ki-numFillBits);
}
else
o1.copyToSegment(b,r*Ki-numFillBits,Ki);
}
}
OBJLOG(INFO) << "de-filled " << b.size() << " " << b;
OBJLOG(INFO) << "de-filled last 100: " << b.segment(b.size()-100,100);
// parity - 25.212, 4.2.1
unsigned pb = getPB();
BitVector a = b.segment(0, b.size() - pb);
BitVector gotParity = b.segment(b.size() - pb, pb);
BitVector expectParity(pb);
BitVector bWithoutParity = b.segment(0, b.size() - pb);
getParity(bWithoutParity, expectParity);
//bool parityOK = true;
//for (int i = 0; i < pb && parityOK; i++) {
// parityOK = expectParity[i] == gotParity[i];
//}
bool parityOK = expectParity == gotParity;
if (gotParity.sum() == 0) parityOK = false;
OBJLOG(INFO) << "parity " << expectParity << " " << gotParity;
if (gFecTestMode) {
std::cout<<"writeLowSide3"<<LOGBV(b) <<LOGBV(gotParity)<<LOGBV(expectParity)<<LOGVAR(parityOK)<<"\n";
}
if (parityOK) {
countGoodFrame();
} else {
countBadFrame();
return;
}
assert(mUpstream);
const TransportBlock tb(a);
LOG(INFO) << "Sending up tb: " << a;
mUpstream->macWriteLowSideTb(tb);
}
#if OLD_CONTROL_IF
void TrCHFECDecoder::countGoodFrame()
{
ScopedLock lock(mLock);
mAssignmentTimer.reset();
static const float a = 1.0F / ((float)mFERMemory);
static const float b = 1.0F - a;
mFER *= b;
OBJLOG(DEBUG) <<"L1Decoder FER=" << mFER;
}
void TrCHFECDecoder::countBadFrame()
{
static const float a = 1.0F / ((float)mFERMemory);
static const float b = 1.0F - a;
mFER = b*mFER + a;
OBJLOG(DEBUG) <<"L1Decoder FER=" << mFER;
}
#endif
// (pat) This is used only for BCH - see macServiceLoop for FACH and DCH.
//void TrCHFECEncoder::waitToSend() const
void TrCHFECEncoder::l1WaitToSend() const
{
// Block until the NodeB clock catches up to the
// mostly recently transmitted burst.
//LOG(INFO) << "mPrevWriteTime: " << mPrevWriteTime << ", " << mNextWriteTime;
// (pat) TODO: Add Transceiver.mTransmitLatency in here.
gNodeB.clock().wait(mPrevWriteTime);
//LOG(NOTICE) << "waitToSend "<<when<<" clock="<<gNodeB.clock().FN();
}
void TrCHFECEncoderLowRate::encode(BitVector& in, BitVector& c)
{
// convolutional coding - 25.212, 4.2.3.1
// concatenation of encoded blocks - 25.212, 4.2.3.3
in.encode(mVCoder, c);
OBJLOG(DEBUG) << "convoluted " << c.str(); // c.size() << " " << c;
}
void TrCHFECDecoderLowRate::decode(const SoftVector& c, BitVector& o)
{
c.decode(mVCoder, o);
OBJLOG(DEBUG) << "unconvoluted " << c.str(); //c.size() << " " << c;
}
void BCHFEC::generate()
{
//printf("BCHFEC::generate\n"); fflush(stdout);
l1WaitToSend();
const TransportBlock *tb = gNodeB.getTxSIB(nextWriteTime().FN());
//printf("BCHFEC::generate calling writeHighSide\n"); fflush(stdout);
//LOG(NOTICE) << "BCH TB.time="<<tb->time() <<" clock="<<gNodeB.clock().FN() <<" t="<< format("%.2f",timef());
l1WriteHighSide(*tb);
}
static void* BCHServiceLoop(BCHFEC* chan)
{
while (true) {
chan->generate();
}
return 0;
}
void BCHFEC::start()
{
mServiceThread.start((void* (*)(void*))BCHServiceLoop, (void*)this);
}
void TrCHFECEncoderTurbo::encode(BitVector& in, BitVector &c)
{
// coding - 25.212, 4.2.3.1
// concatenation of encoded blocks - 25.212, 4.2.3.3
in.encode(mTCoder, c, mInterleaver);
OBJLOG(DEBUG) << "turbo " << c.str(); //c.size() << " " << c;
}
void TrCHFECDecoderTurbo::decode(const SoftVector&c, BitVector &o)
{
// coding - 25.212, 4.2.3.1
// concatenation of encoded blocks - 25.212, 4.2.3.3
c.decode(mTCoder, o, mInterleaver);
OBJLOG(DEBUG) << "turbo " << o.str(); //o.size() << " " << o;
}
#if USE_OLD_DCH
// Create the encoder/decoders for this FEC class from the RRC programming.
void DCHFEC::fecConfig(TrChConfig &config, bool turbo)
{
// Currently we support only one TrCh.
assert(config.ul()->getNumTrCh() == 1);
assert(config.dl()->getNumTrCh() == 1);
// Uplink:
{
RrcTfs *tfs = config.ul()->getTfs(0);
// Currently we do not support TFC, so only one TF can be non-empty.
int ntf = tfs->getNumTF();
int numNonEmpty = 0;
for (int n = 0; n < ntf; n++) {
int numtb = tfs->getNumTB(n);
int tbsize = tfs->getTBSize(n);
if (numtb == 0 || tbsize == 0) continue;
if (numNonEmpty++) {assert(0);} // There are multiple non-empty TF.
assert(numtb == 1);
FecProgInfoInit fpiul(this->getUlSF(),tfs->getTTICode(),tfs->getPB(),
this->getUlRadioFrameSize(), tbsize, turbo);
if (turbo) {
TrCHFECDecoder *decoder = new TrCHFECDecoderTurbo(this,fpiul);
this->setDecoder(decoder);
} else {
TrCHFECDecoder *decoder = new TrCHFECDecoderLowRate(this,fpiul);
this->setDecoder(decoder);
}
}
}
// Downlink:
{
RrcTfs *tfs = config.dl()->getTfs(0);
// Currently we do not support TFC, so only one TF can be non-empty.
int ntf = tfs->getNumTF();
int numNonEmpty = 0;
for (int n = 0; n < ntf; n++) {
int numtb = tfs->getNumTB(n);
int tbsize = tfs->getTBSize(n);
if (numtb == 0 || tbsize == 0) continue;
if (numNonEmpty++) {assert(0);} // There are multiple non-empty TF.
assert(numtb == 1);
FecProgInfoInit fpidl(this->getDlSF(),tfs->getTTICode(),tfs->getPB(),
this->getDlRadioFrameSize(), tbsize, turbo);
if (turbo) {
TrCHFECEncoder *encoder = new TrCHFECEncoderTurbo(this,fpidl);
this->setEncoder(encoder);
} else {
TrCHFECEncoder *encoder = new TrCHFECEncoderLowRate(this,fpidl);
this->setEncoder(encoder);
}
}
}
//this->setDownstream(mRadio); radio pointer moved to PhCh
}
#endif
void DCHFEC::open()
{
ScopedLock lock(gActiveDCH.mLock);
// The DCHFEC was already allocated from the ChannelTree by the caller.
assert(phChAllocated());
#if USE_OLD_DCH
mEncoder->open();
mDecoder->open();
#else
controlOpen();
#endif
gActiveDCH.push_back((DCHFEC*)this);
cout << "Opening DCH" << endl;
}
// TODO: Do we want a time delay here somewhere before reusing the channel?
void DCHFEC::close()
{
printf("waiting to remove...\n");
while (gActiveDCH.inTxUse || gActiveDCH.inRxUse) usleep(1000);
ScopedLock lock(gActiveDCH.mLock);
gActiveDCH.remove((DCHFEC*)this);
printf("removed\n");
#if USE_OLD_DCH
mEncoder->close();
mDecoder->close();
#else
// Somebody needs to deallocate the encoder/decoders.
controlClose();
#endif
mPhCh->phChClose(); // Allows reallocation in the ChannelTree.
}
}; // namespace
// vim: ts=4 sw=4