334 lines
14 KiB
C++
334 lines
14 KiB
C++
/*
|
|
* OpenBTS provides an open source alternative to legacy telco protocols and
|
|
* traditionally complex, proprietary hardware systems.
|
|
*
|
|
* Copyright 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.
|
|
*/
|
|
|
|
#ifndef UMTSPHCH_H
|
|
#define UMTSPHCH_H
|
|
#include "Threads.h"
|
|
#include "UMTSCommon.h"
|
|
#include "TRXManager.h"
|
|
|
|
namespace ASN {
|
|
struct UL_DPCH_Info;
|
|
struct UL_ChannelRequirement;
|
|
struct DL_DPCH_InfoCommon;
|
|
struct DL_CommonInformation;
|
|
struct DL_DPCH_InfoPerRL;
|
|
struct DL_InformationPerRL_List;
|
|
};
|
|
|
|
//#define RELEASE99
|
|
|
|
namespace UMTS {
|
|
class DCHFEC;
|
|
unsigned getDlRadioFrameSize(PhChType chtype, unsigned sf);
|
|
|
|
// Note: This duplicates slot information that Harvind has added elsewhere.
|
|
// 3GPP 25.211
|
|
// table 6: [page 17] Random-access [aka PRACH] message data fields.
|
|
// PRACH is different because the control and data parts are transmitted
|
|
// on two carriers simultaneously; control always uses slot0.
|
|
// table 18: [page 36] Secondary CCPCH fields.
|
|
// table 1+2 [page 11] uplink DBDCH, DPCCH fields.
|
|
// table 11: [page 23] downlink DPDCH and DPCCH fields.
|
|
struct SlotFormat {
|
|
int mSlotFormat; // The index in this table; redundant - must match location in table.
|
|
// int mBitRate; // We dont need to keep this.
|
|
int mSF; // We dont need to keep this, but it is a handy comment.
|
|
// int mBitsPerFrame; // This is always bitsperslot*15.
|
|
int mBitsPerSlot;
|
|
int mNData1; // may be data1+data2.
|
|
int mNData2; // if no data2 in table, then just 0.
|
|
int mNPilot;
|
|
int mPilotIndex; // Index into sDlPilotBitPatternTable;
|
|
int mNTfci; // Number of raw tfci bits in the slot.
|
|
int mNTpc; // transmit power control, not used for CCPCH, always 0.
|
|
|
|
// Currently the SlotForamt is used only by layer2, so I threw away the data1/data2 info and fbi,
|
|
// but I put those in the tables so it is trivial to add in here.
|
|
// transmitted slots per radio frame is always 15 because we do not use fractional.
|
|
// Guess L2 doesnt care about TPC either, but whatever.
|
|
//int mNData1;
|
|
//int mNData2; // not used for CCPCH, always 0.
|
|
//int mNFbi; // uplink dpcch only. layer1 might be interested in this, but not layer2.
|
|
};
|
|
|
|
extern SlotFormat SlotInfoPrachControl[1];
|
|
extern SlotFormat SlotInfoPrachData[4];
|
|
extern SlotFormat SlotInfoSccpch[18];
|
|
extern SlotFormat SlotInfoUplinkDpcch[5];
|
|
extern SlotFormat SlotInfoUplinkDpdch[7];
|
|
extern SlotFormat SlotInfoDownlinkDch[17];
|
|
extern SlotFormat SlotInfoDownlinkDchREL99[17];
|
|
|
|
#if 0
|
|
// This class was defined in UMTSCommon.h but was unused, and the TrCHFEC classes
|
|
// redefined all these parameters using different names.
|
|
// I based the PhCh class on the TrCHFEC version, since that was in use, and this class was not.
|
|
// Here is the class preserved for posterity.
|
|
class PhyChanDesc {
|
|
|
|
private:
|
|
|
|
unsigned int mScramblingCodeIndex;
|
|
unsigned int mSpreadingCodeIndex;
|
|
unsigned int mSpreadingFactor;
|
|
PhyChanBranch mBranch;
|
|
|
|
public:
|
|
|
|
PhyChanDesc(unsigned int wScramblingCodeIndex, unsigned int wSpreadingCodeIndex,
|
|
unsigned int wSpreadingFactor, PhyChanBranch wBranch):
|
|
mScramblingCodeIndex(wScramblingCodeIndex),
|
|
mSpreadingCodeIndex(wSpreadingCodeIndex),
|
|
mSpreadingFactor(wSpreadingFactor),
|
|
mBranch(wBranch) {};
|
|
|
|
unsigned int scramblingCode() { return mScramblingCodeIndex;}
|
|
|
|
unsigned int spreadingCode() { return mSpreadingCodeIndex; }
|
|
|
|
unsigned int spreadingFactor() { return mSpreadingFactor; }
|
|
|
|
PhyChanBranch branch() { return mBranch; }
|
|
|
|
friend bool operator<(const PhyChanDesc& a, const PhyChanDesc& b) {
|
|
if (a.mScramblingCodeIndex < b.mScramblingCodeIndex) return true;
|
|
if (a.mScramblingCodeIndex > b.mScramblingCodeIndex) return false;
|
|
if (a.mSpreadingCodeIndex < b.mSpreadingCodeIndex) return true;
|
|
if (a.mSpreadingCodeIndex > b.mSpreadingCodeIndex) return false;
|
|
if (a.mSpreadingFactor < b.mSpreadingFactor) return true;
|
|
if (a.mSpreadingFactor > b.mSpreadingFactor) return false;
|
|
if (a.mBranch < b.mBranch) return true;
|
|
if (a.mBranch > b.mBranch) return false;
|
|
return false;
|
|
}
|
|
|
|
};
|
|
#endif
|
|
|
|
// This is a physical channel in the ChannelTree.
|
|
// The primiary CPICH (sync) and PCCPCH (beacon) channels are reserved and non-programmable.
|
|
// The others are generally associated with a SlotFormat from one of the tables.
|
|
class PhCh
|
|
{
|
|
public:
|
|
|
|
protected:
|
|
PhChType mPhChType; ///< physical channel type
|
|
// (pat) I suspect we are going to use assymetric SF on the SF=4 DCH, so I separated them.
|
|
unsigned mDlSF; ///< downlink spreading factor
|
|
unsigned mDlSFLog2; ///< log 2 of downlink spreading factor
|
|
unsigned mUlSF; ///< uplink spreading factor
|
|
unsigned mSpCode; ///< downlink spreading code (pat) aka channel code, in the range 0..(SF-1)
|
|
unsigned mSrCode; ///< uplink scrambling code
|
|
ARFCNManager *mRadio;
|
|
// Uplink puncturing limit expressed as percent in the range 40 to 100.
|
|
// From sql "UMTS.Uplink.Puncturing.Limit"; default 100 (no puncturing); bounded if out of range.
|
|
int mUlPuncturingLimit;
|
|
bool mAllocated; // Used by the ChannelTree
|
|
// For SCCPCH and DPDCH we will save the downlink slot format here.
|
|
SlotFormat *mDlSlot;
|
|
|
|
// For uplink DPCCH, need slot format to determine pilot, TFCI, TPC, and FBI locations
|
|
SlotFormat *mUlDPCCH;
|
|
|
|
// For uplink channels there are two additional slot formats required for data and control parts.
|
|
// The data slot format has no additional information for anyone.
|
|
// The control slot format has no info needed by L2, and is probably a constant for all channels anyway,
|
|
// so I left it out of here, although I did transcribe the data into the SlotFormat tables
|
|
// in case anyone wants it.
|
|
|
|
public:
|
|
// Bidirectional channel requires two SF and uplink scrambling code:
|
|
PhCh(PhChType chType,
|
|
unsigned wDlSF, // downlink spreading factor
|
|
unsigned wSpCode, // downlink spreading [aka channel] code
|
|
unsigned wUlSf, // maximum uplink spreading factor; the actual SF varies for each TFC.
|
|
unsigned wSrCode, // uplink scrambling code
|
|
ARFCNManager *wRadio);
|
|
|
|
SlotFormat *getDlSlot() {
|
|
// This method should not be used for uplink channels (CPICH or PCCPCH or PRACH.)
|
|
assert(mPhChType == SCCPCHType || mPhChType == DPDCHType);
|
|
return mDlSlot;
|
|
}
|
|
|
|
SlotFormat *getUlDPCCH() {
|
|
assert(mPhChType == SCCPCHType || mPhChType == DPDCHType);
|
|
return mUlDPCCH;
|
|
}
|
|
|
|
// ASN interface:
|
|
void toAsnUL_DPCH_Info(ASN::UL_DPCH_Info *iep);
|
|
ASN::UL_ChannelRequirement *toAsnUL_ChannelRequirement();
|
|
ASN::DL_DPCH_InfoCommon * toAsnDL_DPCH_InfoCommon();
|
|
ASN::DL_CommonInformation * toAsnDL_CommonInformation();
|
|
ASN::DL_DPCH_InfoPerRL *toAsnDL_DPCH_InfoPerRL();
|
|
ASN::DL_InformationPerRL_List *toAsnDL_InformationPerRL_List();
|
|
|
|
// Dont forget to multiply this by TTI.
|
|
unsigned getDlRadioFrameSize();
|
|
unsigned getUlRadioFrameSize();
|
|
|
|
PhChType phChType() const { return mPhChType; }
|
|
bool isDch() const { return mPhChType == DPDCHType; }
|
|
bool isRach() const { return mPhChType == PRACHType; }
|
|
unsigned getUlSF() const { return mUlSF; }
|
|
unsigned getDlSF() const { return mDlSF; }
|
|
unsigned getDlSFLog2() const { return mDlSFLog2; }
|
|
unsigned SpCode() const { return mSpCode; } // old name
|
|
unsigned getSpCode() const { return mSpCode; }
|
|
unsigned SrCode() const { return mSrCode; } // old name
|
|
unsigned getSrCode() const { return mSrCode; }
|
|
ARFCNManager *getRadio() const { return mRadio; }
|
|
|
|
// Uplink uses 2 bit tfci on both RACH and DCH.
|
|
unsigned getUlNumTfciBits() { return 2; }
|
|
unsigned getDlNumTfciBits() { return mDlSlot->mNTfci; }
|
|
|
|
|
|
/**@name Ganged actions. */
|
|
// NOTE: These are used by the ChannelTree to allocate the underlying
|
|
// physical channel, which operations must be atomic.
|
|
// The parent classes (eg: DCHFEC) have their own unrelated open() routines.
|
|
// The final parent close() routine must call phChClose to release the
|
|
// physical channel back tothe pool.
|
|
//@{
|
|
void phChOpen() { mAllocated = true; }
|
|
void phChClose() { mAllocated = false; }
|
|
//@}
|
|
bool phChAllocated() { return mAllocated; }
|
|
};
|
|
|
|
// Downlink only channel has spreading factor and spreading [channel] code
|
|
struct PhChDownlink: PhCh {
|
|
PhChDownlink(PhChType chType, unsigned wSF, unsigned wSpCode,ARFCNManager *wRadio):
|
|
PhCh(chType,wSF,wSpCode,0,0,wRadio) {}
|
|
};
|
|
|
|
// Uplink only channel has scrambling code but no spreading factor.
|
|
// The uplink does not need an ARFCNManager pointer, so we set it to 0
|
|
struct PhChUplink: PhCh {
|
|
PhChUplink(PhChType chType, unsigned wSF, unsigned wUplinkScramblingCode):
|
|
PhCh(chType,0,0,wSF,wUplinkScramblingCode,(ARFCNManager*)0) {}
|
|
};
|
|
|
|
|
|
// An element in the channel tree.
|
|
// The channel allocation indication is not here,
|
|
// it is inside the DCHFEC class accessed by active() method.
|
|
struct ChannelTreeElt
|
|
{
|
|
bool mReserved; // This channel is reserved for something other than DCH.
|
|
bool mAlsoReserved; // This channel is above a reserved channel, so you cant use it either.
|
|
DCHFEC *mDch; // The DPDCH, although we could put the other PhChs in here too. (SCCPCH, PCCPCH, etc)
|
|
bool available(bool checkOnlyReserved);
|
|
bool active(void);
|
|
ChannelTreeElt() : mReserved(0), mDch(0) {}
|
|
};
|
|
|
|
|
|
// The ChannelTree's primary purpose is to allocate DCH channels
|
|
// from the pool for a SF chosen to meet a bandwidth criteria.
|
|
// DESIGN:
|
|
// The channels are in a tree, where each level is called a tier,
|
|
// with 4 channels at tier 0 (SF=4) and 256 channels at tier 7 (SF=256).
|
|
// We assume that we are going to allocate DCH physical channel objects to populate
|
|
// the entire tree on startup by the chPopulate() method.
|
|
//
|
|
// CHANNEL ALLOCATION:
|
|
// Use the chChooseByBW() or chChooseBySF() methods to allocate a DCH channel.
|
|
// It is dynamic, so you can mix and match SF, no restrictions except what is intrinsic.
|
|
// There is no data stored in this structure about which channels are active,
|
|
// rather it calls the phChAllocated() method from the channel each time.
|
|
// So when a channel is deallocated, nothing is done in this tree at all.
|
|
// The chChoose functions currently open the channel before returning to make sure
|
|
// there is no race between two threads trying to allocate channels simultaneously.
|
|
// Dont know if that is possible because the callers dont yet exist :-)
|
|
// It is the callers responsibility to close the channel when finished.
|
|
//
|
|
// RESERVATIONS:
|
|
// Each physical channel that is used for some purpose other than a DCH
|
|
// must be reserved in the tree by chReserve() prior to calling chPopulate()
|
|
// I added chReserve() calls to the RACH and FACH classes, but make darned sure
|
|
// you also call it for PICH, or whatever.
|
|
// And I quote, from 25.213 5.2.1:
|
|
// "The channelisation code for the Primary CPICH is fixed to C(ch,256,0)
|
|
// "and the channelisation code for the Primary CCPCH is fixed to C(ch,256,1).
|
|
// "The channelisation codes for all other physical channels are assigned by UTRAN."
|
|
// That implies that channel 0 is unusable in all tiers of the tree.
|
|
// Additionally, there are going to be some SCCPCH (for FACH and PCH),
|
|
// then we will fill the rest of the tree with DPDCH.
|
|
//
|
|
// QUALITY OF SERVICE:
|
|
// The bandwidth criteria comes from the QoS [Quality of Service] IE
|
|
// in the L3 PDP Context Setup message.
|
|
// In GPRS, I never saw anything but 'best effort' so we may have to
|
|
// map 'best effort' to something else, probably minimal effort.
|
|
// The bandwidth criteria in the QoS includes mean and peak throughputs,
|
|
// and an astonishing amount of other largely irrelevant information that
|
|
// mostly has to do with allocating capacity upstream.
|
|
// I assume we are only interested in peak throughput.
|
|
// The Peak bandwidth is discreetized into 9 classes: 1, 2, 4, 8, 16, 32, 64, 128 and 256 KB/s.
|
|
// (That's KiloBytes/s, not Kilobits/s.) This does not exactly match the available bandwidth for
|
|
// our 7 available SF (SF=4 to SF=256) so we are going to be a little sloppy about that and
|
|
// just say that 256KB/s maps to SF=4, and so on down, so the bottom three tiers (1,2,4KB/s)
|
|
// all map to a SF=256 channel with a nominal bandwidth of 30kbs = 3.75KB/s
|
|
class ChannelTree
|
|
{
|
|
Mutex mChLock; // channel choosing must be atomic.
|
|
public:
|
|
typedef int Tier; // In the range 0..(sNumTiers-1) for SF=4 to SF=256.
|
|
// Use 'int' because we have loops for (...; tier >= 0; tier--)
|
|
static const int sNumTiers = 7; // seven tree tiers for SF=4 to SF=256.
|
|
|
|
private:
|
|
ChannelTreeElt *mTree[sNumTiers]; // The tree itself is a pyramidal matrix.
|
|
|
|
// These are internal functions of chChooseByTier and chReserve:
|
|
bool isTierFreeUpward(Tier tier,unsigned chcode, bool checkOnlyReserved, Tier *badtier, unsigned *badcode);
|
|
bool isTierFreeDownward(Tier tier,unsigned startcode, unsigned width, bool checkOnlyReserved, Tier *badtier, unsigned *badcode);
|
|
void chConflict(Tier t1,unsigned ch1,Tier t2,unsigned ch2);
|
|
DCHFEC *chChooseByTier(Tier tier); // Choose a DCH specified by SF expressed as a Tier.
|
|
|
|
public:
|
|
ChannelTree();
|
|
static unsigned sf2tier(unsigned sf); // Return the tree tier for a given SF.
|
|
static unsigned tier2sf(Tier tier); // Return the SF for a tree tier (0..7).
|
|
static Tier bw2tier(unsigned KBps, bool guaranteed); // Return the tier needed for specified KBytes/s, approximately.
|
|
DCHFEC *chChooseByBW(unsigned KBps); // Choose one of the DCH channels by bandwidth in KBytes/s.
|
|
DCHFEC *chChooseBySF(unsigned sf); // Choose a DCH specified by SF.
|
|
|
|
// Is this exact ch reserved? This does not check above and below in the ChannelTree and is used
|
|
// only to assert that we have correctly reserved a channel previously.
|
|
bool isReserved(unsigned sf, unsigned code) { return mTree[sf2tier(sf)][code].mReserved; }
|
|
|
|
// Permanently reserve the the specified channel in the tree for this channel.
|
|
void chReserve(unsigned sf,unsigned chcode);
|
|
// Populate the tree with DCH channels.
|
|
// Call after reserving dedicated channels with chReserve()
|
|
void chPopulate(ARFCNManager *downstream);
|
|
|
|
void chTest(std::ostream &os);
|
|
void chTestAlloc(int sf, int cnt, std::ostream &os);
|
|
void chTestFree(int sf, int cnt, std::ostream &os);
|
|
friend std::ostream& operator<<(std::ostream& os, const ChannelTree&);
|
|
};
|
|
std::ostream& operator<<(std::ostream& os, const ChannelTree&);
|
|
|
|
extern ChannelTree gChannelTree; // And here it is.
|
|
|
|
}; // namespace UMTS
|
|
#endif
|