mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 05:13:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			9.5 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.
 | 
						|
*/
 | 
						|
 | 
						|
/**@file GPRS L1 radio channels, from GSM 05.02 and 05.03. */
 | 
						|
 | 
						|
#ifndef GPRSL1FEC_H
 | 
						|
#define GPRSL1FEC_H
 | 
						|
 | 
						|
#include <GSMCommon.h>
 | 
						|
#include <GSMConfig.h>		// for gBTS
 | 
						|
#include <GSML1FEC.h>
 | 
						|
#include <GSMTransfer.h>	// for TxBurst
 | 
						|
#include <GSMLogicalChannel.h> // for TCHFACCHLogicalChannel
 | 
						|
#include "MAC.h"
 | 
						|
using namespace GSM;
 | 
						|
namespace GPRS {
 | 
						|
class TBF;
 | 
						|
 | 
						|
// GSM05.03 sec 5.1.4 re GPRS CS-4 says: 16 bit parity with generator: D16 + D12 + D5 + 1,
 | 
						|
static const unsigned long sCS4Generator = (1<<16) + (1<<12) + (1<<5) + 1;
 | 
						|
 | 
						|
class PDCHL1FEC;
 | 
						|
class PDCHCommon
 | 
						|
{
 | 
						|
	public:
 | 
						|
	PDCHL1FEC *mchParent;
 | 
						|
 | 
						|
	PDCHCommon(PDCHL1FEC*wParent) { mchParent = wParent; }
 | 
						|
	ARFCNManager *getRadio();
 | 
						|
	unsigned TN();
 | 
						|
	unsigned ARFCN();
 | 
						|
	unsigned CN();
 | 
						|
	L1Decoder *getDecoder() const;
 | 
						|
	float FER() const;
 | 
						|
	void countGoodFrame();
 | 
						|
	void countBadFrame();
 | 
						|
	PDCHL1Uplink *uplink();
 | 
						|
	PDCHL1Downlink *downlink();
 | 
						|
	PDCHL1FEC *parent();	// If called from mchParent, returns itself.
 | 
						|
	TBF *getTFITBF(int tfi, RLCDirType dir);
 | 
						|
	void setTFITBF(int tfi, RLCDirType dir, TBF *TBF);
 | 
						|
	
 | 
						|
	const char *getAnsweringUsfText(char *buf, RLCBSN_t bsn); // Printable USFs around BSN for debugging
 | 
						|
 | 
						|
	char *shortId() {	// Return a short printable id for this channel.
 | 
						|
		static char buf[20];
 | 
						|
		sprintf(buf,"PDCH#%u:%u",getRadio()->ARFCN(),TN());
 | 
						|
		return buf;
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// For gprs channels, should we allocate them using getTCH(),
 | 
						|
// which returns a TCHFACCHLogicalChannel which we have no use for,
 | 
						|
// or should we get dedicated GPRS channels directly from TRXManager,
 | 
						|
// which currently does not allow this.
 | 
						|
// Answer: We are going to make the logical channels tri-state (inactive, RRactive, GPRSactive),
 | 
						|
// and use getTCH to get them.
 | 
						|
 | 
						|
// There is one of these classes for each GPRS Data Channel, PDTCH.
 | 
						|
// Downstream it attaches to a single Physical channel in L1FEC via mchEncoder and mchDecoder.
 | 
						|
// TODO: I did this wrong.  This should be for a single ARFCN, but multiple
 | 
						|
// upstream/downstream timeslots.
 | 
						|
class PDCHL1FEC :
 | 
						|
	public L1UplinkReservation,
 | 
						|
	public PDCHCommon,
 | 
						|
	public USFList
 | 
						|
{
 | 
						|
	public:
 | 
						|
	PDCHL1Downlink *mchDownlink;
 | 
						|
	PDCHL1Uplink *mchUplink;
 | 
						|
 | 
						|
	L1FEC *mchOldFec;		// The GSM TCH channel that this GPRS channel took over;
 | 
						|
						// it has the channel parameters.
 | 
						|
 | 
						|
	// Temporary: GPRS will not use anything in this LogicalChannel class, and we dont want
 | 
						|
	// the extra class hanging around, but currently the only way to dynamically
 | 
						|
	// allocate physical channels is via the associated logical channel.
 | 
						|
	TCHFACCHLogicalChannel *mchLogChan;
 | 
						|
 | 
						|
	public:
 | 
						|
	// The TFIs are a 5 bit handle for TBFs.  The USFs are a 3 bit handle for uplink TBFs.
 | 
						|
	TFIList *mchTFIs;// Points to the global TFIList.  Someday will point to the per-ARFCN TFIList.
 | 
						|
 | 
						|
	void debug_test();
 | 
						|
 | 
						|
	PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan);
 | 
						|
 | 
						|
	// Release the radio channel.
 | 
						|
	// GSM will start using it immediately.  TODO: Do we want to set a timer
 | 
						|
	// so it is not reused immediately?
 | 
						|
	~PDCHL1FEC();
 | 
						|
 | 
						|
	// Attach this GPRS channel to the specified GSM channel.
 | 
						|
	//void mchOpen(TCHFACCHLogicalChannel *wlogchan);
 | 
						|
 | 
						|
	void mchStart();
 | 
						|
	void mchStop();
 | 
						|
	void mchDump(std::ostream&os,bool verbose);
 | 
						|
 | 
						|
	// Return a description of PDTCH, which is the only one we care about.
 | 
						|
	// (We dont care about the associated SDCCH, whose frame is used in GPRS for
 | 
						|
	// continuous timing advance.)
 | 
						|
	// The packet channel description is the same as channel description except:
 | 
						|
	// From GSM04.08 10.5.25 table 10.5.2.25a table 10.5.58, and I quote:
 | 
						|
	// "The Channel type field (5 bit) shall be ignored by the receiver and
 | 
						|
	// all bits treated as spare. For backward compatibility
 | 
						|
	// reasons, the sender shall set the spare bits to binary '00001'."
 | 
						|
	// This doesnt matter in the slightest, because the typeAndOffset would
 | 
						|
	// have been TCHF_0 whose enum value is 1 anyway.
 | 
						|
	L3ChannelDescription packetChannelDescription()
 | 
						|
	{
 | 
						|
		L1FEC *lf = mchOldFec;
 | 
						|
		return L3ChannelDescription((TypeAndOffset) 1, lf->TN(), lf->TSC(), lf->ARFCN());
 | 
						|
	}
 | 
						|
};
 | 
						|
std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch);
 | 
						|
 | 
						|
// For CS-1 decoding, just uses SharedL1Decoder.
 | 
						|
// For CS-4 decoding: Uses the SharedL1Decoder through deinterleaving into mC.
 | 
						|
class GprsDecoder : public SharedL1Decoder
 | 
						|
{
 | 
						|
	Parity mBlockCoder_CS4;
 | 
						|
	BitVector mDP_CS4;
 | 
						|
	public:
 | 
						|
	BitVector mD_CS4;
 | 
						|
	short qbits[8];
 | 
						|
	ChannelCodingType getCS();	// Determine CS from the qbits.
 | 
						|
	BitVector *getResult();
 | 
						|
	GprsDecoder() :
 | 
						|
		mBlockCoder_CS4(sCS4Generator,16,431+16),
 | 
						|
		mDP_CS4(431+16),
 | 
						|
		mD_CS4(mDP_CS4.head(424))
 | 
						|
		{}
 | 
						|
	bool decodeCS4();
 | 
						|
	const char* descriptiveString() const { return "GprsDecoder"; }	// not very useful.
 | 
						|
};
 | 
						|
 | 
						|
// CS-4 has 431 input data bits, which are always 424 real data bits (53 bytes)
 | 
						|
// plus 7 unused bits that are set to 0, to make 431 data bits.
 | 
						|
// The first 3 bits are usf encoded to 12 bits, to yield 440 bits.
 | 
						|
// Then 16 bit parity bits yields 456 bits.
 | 
						|
class GprsEncoder : public SharedL1Encoder
 | 
						|
{
 | 
						|
	Parity mBlockCoder_CS4;
 | 
						|
	public:
 | 
						|
	// Uses SharedL1Encoder::mC for result vector
 | 
						|
	// Uses SharedL1Encoder::mI for the 4-way interleaved result vector.
 | 
						|
	BitVector mP_CS4;	// alias for parity part of mC
 | 
						|
	BitVector mU_CS4;	// alias for usf part of mC
 | 
						|
	BitVector mD_CS4;	// assembly area for parity.
 | 
						|
	GprsEncoder() :
 | 
						|
		SharedL1Encoder(),
 | 
						|
		mBlockCoder_CS4(sCS4Generator,16,431+16),
 | 
						|
		mP_CS4(mC.segment(440,16)),
 | 
						|
		mU_CS4(mC.segment(0,12)),
 | 
						|
		mD_CS4(mC.segment(12-3,431))
 | 
						|
		{}
 | 
						|
	void encodeCS4(const BitVector&src);
 | 
						|
	void encodeCS1(const BitVector &src);
 | 
						|
	// would be nice to add "GPRS"; should be at init.
 | 
						|
	const char* descriptiveString() const { return "GprsEncoder"; }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class PDCHL1Uplink : public PDCHCommon
 | 
						|
{
 | 
						|
	protected:
 | 
						|
#if GPRS_ENCODER
 | 
						|
	//SharedL1Decoder mchCS1Dec;
 | 
						|
	GprsDecoder mchCS14Dec;
 | 
						|
#else	// This case does not compile yet.
 | 
						|
	L1Decoder mchCS1Dec;
 | 
						|
#endif
 | 
						|
 | 
						|
	public:
 | 
						|
	static const RLCDirType mchDir = RLCDir::Up;
 | 
						|
	// The uplink queue:
 | 
						|
	// There will typically only be one guy on here, and we could probably dispense
 | 
						|
	// with the queue, but it is safer to do it this way to avoid thread problems.
 | 
						|
	// InterthreadQueue template adds "*" so it is really a queue of BitVector*
 | 
						|
	InterthreadQueue<RLCRawBlock> mchUplinkData;
 | 
						|
 | 
						|
	PDCHL1Uplink(PDCHL1FEC *wParent) : PDCHCommon(wParent) { }
 | 
						|
 | 
						|
	~PDCHL1Uplink() {}
 | 
						|
 | 
						|
	void writeLowSideRx(const RxBurst &inBurst);
 | 
						|
 | 
						|
	// TODO: This needs to be per-MS.
 | 
						|
	// void setPhy(float wRSSI, float TimingError) {
 | 
						|
		// This function is inapplicable to packet channels, which have multiple
 | 
						|
		// MS listening to the same channel.
 | 
						|
		//assert(0);
 | 
						|
	//}
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
// One of these for each PDCH (physical channel), attached to L1FEC.
 | 
						|
// Accepts Radio Blocks from anybody.
 | 
						|
// Based on SACCHL1Encoder and TCHFACCHL1Encoder
 | 
						|
// This does on-demand sending of RLCBlocks down to the physical channel.
 | 
						|
// We wait until the last minute so we can encode uplink assignments in the blocks
 | 
						|
// as close as possible to the present time.
 | 
						|
// TODO: When we support different encodings we may have to base this on L1Encoder directly
 | 
						|
// and copy a bunch of routines from XCCHL1Encoder?
 | 
						|
static const int qCS1[8] = { 1,1,1,1,1,1,1,1 };
 | 
						|
static const int qCS2[8] = { 1,1,0,0,1,0,0,0 }; // GSM05.03 sec 5.1.2.5
 | 
						|
static const int qCS3[8] = { 0,0,1,0,0,0,0,1 }; // GSM05.03 sec 5.1.3.5
 | 
						|
static const int qCS4[8] = { 0,0,0,1,0,1,1,0 }; // GSM0503 sec5.1.4.5; magically identifies CS-4.
 | 
						|
 | 
						|
class PDCHL1Downlink : public PDCHCommon
 | 
						|
{
 | 
						|
	protected:
 | 
						|
#if GPRS_ENCODER
 | 
						|
	GprsEncoder mchEnc;
 | 
						|
	//GSM::SharedL1Encoder mchCS1Enc;
 | 
						|
	//GSM::SharedL1Encoder mchCS4Enc;
 | 
						|
#else
 | 
						|
	GSM::L1Encoder mchCS1Enc;
 | 
						|
#endif
 | 
						|
	TxBurst mchBurst;					///< a preformatted burst template
 | 
						|
	//TxBurst mchFillerBurst;			// unused ///< the filler burst for this channel
 | 
						|
	int mchTotalBursts;
 | 
						|
	//GSM::Time mchNextWriteTime, mchPrevWriteTime;
 | 
						|
	const TDMAMapping& mchMapping;
 | 
						|
	BitVector mchIdleFrame;
 | 
						|
 | 
						|
	// The mDownlinkData is used only for control messages, which can stack up.
 | 
						|
	//InterthreadQueue<RLCDownlinkMessage> mchDownlinkMsgQ;
 | 
						|
 | 
						|
	public:
 | 
						|
	static const RLCDirType mchDir = RLCDir::Up;
 | 
						|
 | 
						|
	void initBursts(L1FEC*);
 | 
						|
	PDCHL1Downlink(PDCHL1FEC *wParent) :
 | 
						|
		PDCHCommon(wParent),
 | 
						|
		//mchCS1Enc(ChannelCodingCS1),
 | 
						|
#if GPRS_ENCODER
 | 
						|
		//mchCS4Enc(ChannelCodingCS4),
 | 
						|
#endif
 | 
						|
		mchTotalBursts(0),
 | 
						|
		mchMapping(wParent->mchOldFec->encoder()->mapping()),
 | 
						|
		mchIdleFrame((size_t)0)
 | 
						|
	{
 | 
						|
	 	initBursts(wParent->mchOldFec);
 | 
						|
	}
 | 
						|
 | 
						|
	~PDCHL1Downlink() {}
 | 
						|
 | 
						|
	// Enqueue a downlink message.  We dont use this for downlink data - those
 | 
						|
	// are sent by calling the RLCEngine when this queue is empty.
 | 
						|
	//void enqueueMsg(RLCDownlinkMessage *);
 | 
						|
	// The PDCH must feed the radio on time.  This is the routine that does it.
 | 
						|
	void dlService();
 | 
						|
	void transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags);
 | 
						|
	//void rollForward();
 | 
						|
	//void mchResync();
 | 
						|
	int findNeedyUSF();
 | 
						|
 | 
						|
	// Send the L2Frame down to the radio now.
 | 
						|
	void send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle);
 | 
						|
	bool send1DataFrame(RLCDownEngine *tbfdown, RLCDownlinkDataBlock *block, int makeres,MsgTransactionType mttype,unsigned *pcounter);
 | 
						|
	bool send1MsgFrame(TBF *tbf,RLCDownlinkMessage *msg, int makeres, MsgTransactionType mttype,unsigned *pcounter);
 | 
						|
	void sendIdleFrame(RLCBSN_t bsn);
 | 
						|
	void bugFixIdleFrame();
 | 
						|
};
 | 
						|
 | 
						|
extern bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2);
 | 
						|
 | 
						|
}; // namespace GPRS
 | 
						|
 | 
						|
 | 
						|
#endif
 |