mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-10-23 08:22:00 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
774a06369a | ||
|
94ce835050 | ||
|
09befd7a06 | ||
|
1303376ad1 | ||
|
25021dfe5a | ||
|
e287598e6b | ||
|
98b1af896c | ||
|
14bb9c923d | ||
|
c7f36c282a | ||
|
f31e4bb089 | ||
|
1189019c30 |
@@ -45,6 +45,8 @@ const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000
|
||||
|
||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||
|
||||
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
|
||||
|
||||
|
||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||
{
|
||||
|
@@ -53,6 +53,8 @@ extern const BitVector gDummyBurst;
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequence;
|
||||
|
||||
/** Synchronization burst sync sequence */
|
||||
extern const BitVector gSCHSynchSequence;
|
||||
|
||||
/**@name Modulus operations for frame numbers. */
|
||||
//@{
|
||||
|
@@ -56,7 +56,8 @@ COMMON_SOURCES = \
|
||||
radioClock.cpp \
|
||||
sigProcLib.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp
|
||||
Transceiver.cpp \
|
||||
sch.c
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
@@ -87,7 +88,9 @@ osmo_trx_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA) \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
|
@@ -25,6 +25,10 @@
|
||||
#include "Transceiver.h"
|
||||
#include <Logger.h>
|
||||
|
||||
extern "C" {
|
||||
#include "sch.h"
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@@ -39,11 +43,16 @@ using namespace GSM;
|
||||
# define USB_LATENCY_MIN 1,1
|
||||
#endif
|
||||
|
||||
/* Clock indication interval in frames */
|
||||
#define CLK_IND_INTERVAL 100
|
||||
|
||||
/* Number of running values use in noise average */
|
||||
#define NOISE_CNT 20
|
||||
#define FREQ_CNT 20
|
||||
|
||||
TransceiverState::TransceiverState()
|
||||
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
|
||||
: mRetrans(false), mNoiseLev(0.0),
|
||||
mNoises(NOISE_CNT), mFreqOffsets(FREQ_CNT), mode(Transceiver::TRX_MODE_OFF)
|
||||
{
|
||||
for (int i = 0; i < 8; i++) {
|
||||
chanType[i] = Transceiver::NONE;
|
||||
@@ -51,6 +60,7 @@ TransceiverState::TransceiverState()
|
||||
chanResponse[i] = NULL;
|
||||
DFEForward[i] = NULL;
|
||||
DFEFeedback[i] = NULL;
|
||||
prevFrame[i] = NULL;
|
||||
|
||||
for (int n = 0; n < 102; n++)
|
||||
fillerTable[n][i] = NULL;
|
||||
@@ -91,12 +101,12 @@ Transceiver::Transceiver(int wBasePort,
|
||||
: mBasePort(wBasePort), mAddr(TRXAddress),
|
||||
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
|
||||
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
|
||||
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
|
||||
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0),
|
||||
mBSIC(-1)
|
||||
{
|
||||
GSM::Time startTime(random() % gHyperframe,0);
|
||||
|
||||
mRxLowerLoopThread = new Thread(32768);
|
||||
mTxLowerLoopThread = new Thread(32768);
|
||||
mLowerLoopThread = new Thread(32768);
|
||||
|
||||
mTransmitDeadlineClock = startTime;
|
||||
mLastClockUpdateTime = startTime;
|
||||
@@ -105,6 +115,9 @@ Transceiver::Transceiver(int wBasePort,
|
||||
|
||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
mRxSlotMask[i] = 0;
|
||||
}
|
||||
|
||||
Transceiver::~Transceiver()
|
||||
@@ -194,6 +207,9 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
||||
return;
|
||||
}
|
||||
|
||||
if (mStates[0].mode != TRX_MODE_BTS)
|
||||
return;
|
||||
|
||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
||||
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
||||
|
||||
@@ -221,7 +237,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
radioVector *burst;
|
||||
TransceiverState *state;
|
||||
std::vector<signalVector *> bursts(mChans);
|
||||
std::vector<bool> zeros(mChans);
|
||||
std::vector<bool> zeros(mChans, false);
|
||||
std::vector<bool> filler(mChans, true);
|
||||
|
||||
for (size_t i = 0; i < mChans; i ++) {
|
||||
@@ -238,7 +254,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
modFN = nowTime.FN() % state->fillerModulus[TN];
|
||||
|
||||
bursts[i] = state->fillerTable[modFN][TN];
|
||||
zeros[i] = state->chanType[TN] == NONE;
|
||||
if (state->mode == TRX_MODE_BTS)
|
||||
zeros[i] = state->chanType[TN] == NONE;
|
||||
|
||||
if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
|
||||
bursts[i] = burst->getVector();
|
||||
@@ -299,6 +316,19 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
unsigned burstTN = currTime.TN();
|
||||
unsigned burstFN = currTime.FN();
|
||||
|
||||
if (state->mode == TRX_MODE_MS_TRACK) {
|
||||
/* 102 modulus case currently unhandled */
|
||||
if (state->fillerModulus[burstTN] > 52)
|
||||
return OFF;
|
||||
|
||||
int modFN = burstFN % state->fillerModulus[burstTN];
|
||||
unsigned long long reg = (unsigned long long) 1 << modFN;
|
||||
if (reg & mRxSlotMask[burstTN])
|
||||
return TSC;
|
||||
else
|
||||
return OFF;
|
||||
}
|
||||
|
||||
switch (state->chanType[burstTN]) {
|
||||
case NONE:
|
||||
return OFF;
|
||||
@@ -376,6 +406,108 @@ bool Transceiver::detectRACH(TransceiverState *state,
|
||||
return detectRACHBurst(burst, threshold, mSPSRx, &, &toa);
|
||||
}
|
||||
|
||||
/* Detect SCH synchronization sequence within a burst */
|
||||
bool Transceiver::detectSCH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa)
|
||||
{
|
||||
int shift, full;;
|
||||
float mag, threshold = 7.0;
|
||||
|
||||
full = (state->mode == TRX_MODE_MS_TRACK) ?
|
||||
SCH_DETECT_NARROW : SCH_DETECT_FULL;
|
||||
|
||||
if (!detectSCHBurst(burst, threshold, mSPSRx, &, &toa, full))
|
||||
return false;
|
||||
|
||||
std::cout << "SCH : Timing offset " << toa << " symbols" << std::endl;
|
||||
|
||||
mag = fabsf(toa);
|
||||
if (mag < 1.0f)
|
||||
return true;
|
||||
|
||||
shift = (int) (mag / 2.0f);
|
||||
if (!shift)
|
||||
shift++;
|
||||
|
||||
mRadioInterface->applyOffset(toa > 0 ? shift : -shift);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define SCH_BIT_SCALE 64
|
||||
|
||||
/* Decode SCH burst */
|
||||
bool Transceiver::decodeSCH(SoftVector *burst, GSM::Time *time)
|
||||
{
|
||||
int fn;
|
||||
struct sch_info sch;
|
||||
ubit_t info[GSM_SCH_INFO_LEN];
|
||||
sbit_t data[GSM_SCH_CODED_LEN];
|
||||
|
||||
if (burst->size() < 156) {
|
||||
std::cout << "Invalid SCH burst length" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
float_to_sbit(&(*burst)[3], &data[0], SCH_BIT_SCALE, 39);
|
||||
float_to_sbit(&(*burst)[106], &data[39], SCH_BIT_SCALE, 39);
|
||||
|
||||
if (!gsm_sch_decode(info, data)) {
|
||||
gsm_sch_parse(info, &sch);
|
||||
|
||||
mBSIC = sch.bsic;
|
||||
|
||||
std::cout << "SCH : Decoded values" << std::endl;
|
||||
std::cout << " BSIC: " << sch.bsic << std::endl;
|
||||
std::cout << " T1 : " << sch.t1 << std::endl;
|
||||
std::cout << " T2 : " << sch.t2 << std::endl;
|
||||
std::cout << " T3p : " << sch.t3p << std::endl;
|
||||
std::cout << " FN : " << gsm_sch_to_fn(&sch) << std::endl;
|
||||
|
||||
fn = gsm_sch_to_fn(&sch);
|
||||
if (fn < 0) {
|
||||
std::cout << "SCH : Failed to convert FN " << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
time->FN(fn);
|
||||
time->TN(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define FCCH_OFFSET_LIMIT 2e3
|
||||
#define FCCH_ADJUST_LIMIT 20.0
|
||||
|
||||
/* Apply FCCH frequency correction */
|
||||
bool Transceiver::correctFCCH(TransceiverState *state, signalVector *burst)
|
||||
{
|
||||
double offset, avg;
|
||||
|
||||
if (!burst)
|
||||
return false;
|
||||
|
||||
offset = gsm_fcch_offset((float *) burst->begin(), burst->size());
|
||||
if (offset > FCCH_OFFSET_LIMIT)
|
||||
return false;
|
||||
|
||||
state->mFreqOffsets.insert(offset);
|
||||
avg = state->mFreqOffsets.avg();
|
||||
|
||||
if (state->mFreqOffsets.full())
|
||||
std::cout << "FCCH: Frequency offset " << avg << " Hz" << std::endl;
|
||||
|
||||
if (state->mFreqOffsets.full() && (fabs(avg) > FCCH_ADJUST_LIMIT)) {
|
||||
mRadioInterface->tuneRxOffset(-avg);
|
||||
state->mFreqOffsets.reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect normal burst training sequence midamble. Update equalization
|
||||
* state information and channel estimate if necessary. Equalization
|
||||
@@ -462,18 +594,33 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
||||
SoftVector *bits = NULL;
|
||||
TransceiverState *state = &mStates[chan];
|
||||
|
||||
GSM::Time sch_time, burst_time, diff_time;
|
||||
|
||||
/* Blocking FIFO read */
|
||||
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
||||
if (!radio_burst)
|
||||
return NULL;
|
||||
|
||||
/* Set time and determine correlation type */
|
||||
GSM::Time time = radio_burst->getTime();
|
||||
CorrType type = expectedCorrType(time, chan);
|
||||
burst_time = radio_burst->getTime();
|
||||
CorrType type = expectedCorrType(burst_time, chan);
|
||||
|
||||
if ((type == OFF) || (type == IDLE)) {
|
||||
delete radio_burst;
|
||||
return NULL;
|
||||
switch (state->mode) {
|
||||
case TRX_MODE_MS_ACQUIRE:
|
||||
type = SCH;
|
||||
break;
|
||||
case TRX_MODE_MS_TRACK:
|
||||
if (gsm_sch_check_fn(burst_time.FN()))
|
||||
type = SCH;
|
||||
else if (type == OFF)
|
||||
goto release;
|
||||
break;
|
||||
case TRX_MODE_BTS:
|
||||
if ((type == TSC) || (type == RACH))
|
||||
break;
|
||||
case TRX_MODE_OFF:
|
||||
default:
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Select the diversity channel with highest energy */
|
||||
@@ -488,8 +635,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
||||
|
||||
if (max_i < 0) {
|
||||
LOG(ALERT) << "Received empty burst";
|
||||
delete radio_burst;
|
||||
return NULL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Average noise on diversity paths and update global levels */
|
||||
@@ -499,30 +645,66 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
||||
|
||||
/* Detect normal or RACH bursts */
|
||||
if (type == TSC)
|
||||
success = detectTSC(state, *burst, amp, toa, time);
|
||||
else
|
||||
success = detectTSC(state, *burst, amp, toa, burst_time);
|
||||
else if (type == RACH)
|
||||
success = detectRACH(state, *burst, amp, toa);
|
||||
else if (type == SCH)
|
||||
success = detectSCH(state, *burst, amp, toa);
|
||||
else
|
||||
success = false;
|
||||
|
||||
if (!success) {
|
||||
state->mNoises.insert(avg);
|
||||
delete radio_burst;
|
||||
return NULL;
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Demodulate and set output info */
|
||||
if (equalize && (type != TSC))
|
||||
equalize = false;
|
||||
|
||||
if (avg - state->mNoiseLev > 0.0)
|
||||
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
||||
/* Ignore noise threshold on MS mode for now */
|
||||
if ((type == SCH) || (avg - state->mNoiseLev > 0.0))
|
||||
bits = demodulate(state, *burst, amp, toa,
|
||||
burst_time.TN(), equalize);
|
||||
|
||||
wTime = time;
|
||||
/* MS: Decode SCH and adjust GSM clock */
|
||||
if ((state->mode == TRX_MODE_MS_ACQUIRE) ||
|
||||
(state->mode == TRX_MODE_MS_TRACK)) {
|
||||
correctFCCH(state, state->prevFrame[burst_time.TN()]->getVector());
|
||||
|
||||
if (decodeSCH(bits, &sch_time)) {
|
||||
if (state->mode == TRX_MODE_MS_ACQUIRE) {
|
||||
diff_time = GSM::Time(sch_time.FN() - burst_time.FN(),
|
||||
-burst_time.TN());
|
||||
mRadioInterface->adjustClock(diff_time);
|
||||
mTransmitDeadlineClock = RadioClock::adjust(
|
||||
mTransmitDeadlineClock,
|
||||
diff_time);
|
||||
state->mode = TRX_MODE_MS_TRACK;
|
||||
|
||||
std::cout << "SCH : Locking GSM clock " << std::endl;
|
||||
} else {
|
||||
std::cout << "SCH : Read SCH at FN " << burst_time.FN()
|
||||
<< " FN51 " << burst_time.FN() % 51 << std::endl;
|
||||
}
|
||||
}
|
||||
goto release;
|
||||
}
|
||||
|
||||
wTime = burst_time;
|
||||
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
|
||||
timingOffset = (int) round(toa * 256.0 / mSPSRx);
|
||||
|
||||
delete radio_burst;
|
||||
delete state->prevFrame[burst_time.TN()];
|
||||
state->prevFrame[burst_time.TN()] = radio_burst;
|
||||
|
||||
return bits;
|
||||
|
||||
release:
|
||||
delete state->prevFrame[burst_time.TN()];
|
||||
state->prevFrame[burst_time.TN()] = radio_burst;
|
||||
delete bits;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Transceiver::start()
|
||||
@@ -589,10 +771,8 @@ void Transceiver::driveControl(size_t chan)
|
||||
mRadioInterface->start();
|
||||
|
||||
// Start radio interface threads.
|
||||
mTxLowerLoopThread->start((void * (*)(void*))
|
||||
TxLowerLoopAdapter,(void*) this);
|
||||
mRxLowerLoopThread->start((void * (*)(void*))
|
||||
RxLowerLoopAdapter,(void*) this);
|
||||
mLowerLoopThread->start((void * (*)(void*))
|
||||
LowerLoopAdapter,(void*) this);
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||
@@ -695,6 +875,12 @@ void Transceiver::driveControl(size_t chan)
|
||||
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(command,"GETBSIC")) {
|
||||
if (mBSIC < 0)
|
||||
sprintf(response, "RSP GETBSIC 1");
|
||||
else
|
||||
sprintf(response, "RSP GETBSIC 0 %d", mBSIC);
|
||||
}
|
||||
else if (strcmp(command,"SETSLOT")==0) {
|
||||
// set TSC
|
||||
int corrCode;
|
||||
@@ -708,7 +894,21 @@ void Transceiver::driveControl(size_t chan)
|
||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||
setModulus(timeslot, chan);
|
||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
||||
|
||||
}
|
||||
else if (!strcmp(command,"SETRXMASK")) {
|
||||
int slot;
|
||||
unsigned long long mask;
|
||||
sscanf(buffer,"%3s %s %d 0x%llx", cmdcheck, command, &slot, &mask);
|
||||
if ((slot < 0) || (slot > 7)) {
|
||||
sprintf(response, "RSP SETRXMASK 1");
|
||||
} else {
|
||||
mRxSlotMask[slot] = mask;
|
||||
sprintf(response, "RSP SETRXMASK 0 %d 0x%llx", slot, mask);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(command, "SYNC")) {
|
||||
mStates[0].mode = TRX_MODE_MS_ACQUIRE;
|
||||
sprintf(response,"RSP SYNC 0");
|
||||
}
|
||||
else {
|
||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
||||
@@ -734,15 +934,6 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
for (int i = 0; i < 4; i++)
|
||||
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
||||
|
||||
// periodically update GSM core clock
|
||||
LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
|
||||
<< " mLastClockUpdateTime " << mLastClockUpdateTime;
|
||||
|
||||
if (!chan) {
|
||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||
writeClockInterface();
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||
|
||||
int RSSI = (int) buffer[5];
|
||||
@@ -848,7 +1039,13 @@ void Transceiver::driveTxFIFO()
|
||||
}
|
||||
// time to push burst to transmit FIFO
|
||||
pushRadioVector(mTransmitDeadlineClock);
|
||||
|
||||
mTransmitDeadlineClock.incTN();
|
||||
|
||||
if (!mTransmitDeadlineClock.TN() &&
|
||||
!(mTransmitDeadlineClock.FN() % CLK_IND_INTERVAL)) {
|
||||
writeClockInterface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,22 +1084,12 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||
void *LowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
transceiver->setPriority(0.45);
|
||||
|
||||
while (1) {
|
||||
transceiver->driveReceiveRadio();
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
transceiver->setPriority(0.44);
|
||||
|
||||
while (1) {
|
||||
transceiver->driveTxFIFO();
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
@@ -80,7 +80,14 @@ struct TransceiverState {
|
||||
|
||||
/* Received noise energy levels */
|
||||
float mNoiseLev;
|
||||
noiseVector mNoises;
|
||||
avgVector mNoises;
|
||||
avgVector mFreqOffsets;
|
||||
|
||||
/* Store pointers to previous frame */
|
||||
radioVector *prevFrame[8];
|
||||
|
||||
/* Transceiver mode */
|
||||
int mode;
|
||||
};
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
@@ -99,8 +106,7 @@ private:
|
||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
|
||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
||||
Thread *mLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||
|
||||
@@ -116,6 +122,7 @@ private:
|
||||
OFF, ///< timeslot is off
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
SCH, ///< timeslot should contain a SCH burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
} CorrType;
|
||||
|
||||
@@ -147,6 +154,13 @@ private:
|
||||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
bool detectSCH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
bool decodeSCH(SoftVector *burst, GSM::Time *time);
|
||||
bool correctFCCH(TransceiverState *state, signalVector *burst);
|
||||
|
||||
/** Detect normal bursts */
|
||||
bool detectTSC(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
@@ -167,7 +181,9 @@ private:
|
||||
double mRxFreq; ///< the receive frequency
|
||||
int mPower; ///< the transmit power in dB
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
|
||||
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
|
||||
int mBSIC; ///< MS - detected BSIC
|
||||
|
||||
std::vector<TransceiverState> mStates;
|
||||
|
||||
@@ -226,6 +242,13 @@ public:
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
enum {
|
||||
TRX_MODE_OFF,
|
||||
TRX_MODE_BTS,
|
||||
TRX_MODE_MS_ACQUIRE,
|
||||
TRX_MODE_MS_TRACK,
|
||||
};
|
||||
|
||||
protected:
|
||||
/** drive lower receive I/O and burst generation */
|
||||
void driveReceiveRadio();
|
||||
@@ -249,9 +272,7 @@ protected:
|
||||
|
||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
||||
friend void *LowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
@@ -266,8 +287,7 @@ protected:
|
||||
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** Main drive threads */
|
||||
void *RxLowerLoopAdapter(Transceiver *);
|
||||
void *TxLowerLoopAdapter(Transceiver *);
|
||||
void *LowerLoopAdapter(Transceiver *);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
@@ -58,6 +58,11 @@ struct uhd_dev_offset {
|
||||
const std::string desc;
|
||||
};
|
||||
|
||||
struct tune_result {
|
||||
uhd::tune_result_t uhd;
|
||||
double freq;
|
||||
};
|
||||
|
||||
/*
|
||||
* Tx / Rx sample offset values. In a perfect world, there is no group delay
|
||||
* though analog components, and behaviour through digital filters exactly
|
||||
@@ -282,6 +287,7 @@ public:
|
||||
|
||||
bool setTxFreq(double wFreq, size_t chan);
|
||||
bool setRxFreq(double wFreq, size_t chan);
|
||||
bool setRxOffset(double wOffset, size_t chan);
|
||||
|
||||
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
|
||||
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
||||
@@ -332,7 +338,7 @@ private:
|
||||
double offset;
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
std::vector<double> tx_freqs, rx_freqs;
|
||||
std::vector<tune_result> tx_freqs, rx_freqs;
|
||||
size_t tx_spp, rx_spp;
|
||||
|
||||
bool started;
|
||||
@@ -886,7 +892,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
if (rc < 0) {
|
||||
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
||||
LOG(ERR) << rx_buffers[0]->str_status();
|
||||
return 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
// Create vector buffer
|
||||
@@ -996,15 +1002,17 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
return num_smpls;
|
||||
}
|
||||
|
||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
|
||||
bool uhd_device::updateAlignment(TIMESTAMP)
|
||||
{
|
||||
aligned = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
{
|
||||
double rf_spread, rf_freq;
|
||||
std::vector<double> freqs;
|
||||
std::vector<tune_result> freqs;
|
||||
uhd::tune_request_t treq(freq);
|
||||
|
||||
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
|
||||
@@ -1023,17 +1031,17 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
freqs = rx_freqs;
|
||||
|
||||
/* Tune directly if other channel isn't tuned */
|
||||
if (freqs[!chan] < 10.0)
|
||||
if (freqs[!chan].freq < 10.0)
|
||||
return treq;
|
||||
|
||||
/* Find center frequency between channels */
|
||||
rf_spread = fabs(freqs[!chan] - freq);
|
||||
rf_spread = fabs(freqs[!chan].freq - freq);
|
||||
if (rf_spread > B2XX_CLK_RT) {
|
||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||
return treq;
|
||||
}
|
||||
|
||||
rf_freq = (freqs[!chan] + freq) / 2.0f;
|
||||
rf_freq = (freqs[!chan].freq + freq) / 2.0f;
|
||||
|
||||
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.target_freq = freq;
|
||||
@@ -1050,10 +1058,12 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
||||
|
||||
if (tx) {
|
||||
tres = usrp_dev->set_tx_freq(treq, chan);
|
||||
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
|
||||
tx_freqs[chan].uhd = tres;
|
||||
tx_freqs[chan].freq = usrp_dev->get_tx_freq(chan);
|
||||
} else {
|
||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
||||
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
|
||||
rx_freqs[chan].uhd = tres;
|
||||
rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
|
||||
}
|
||||
LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
|
||||
@@ -1066,13 +1076,15 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
||||
*/
|
||||
if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
|
||||
if (tx) {
|
||||
treq = select_freq(tx_freqs[!chan], !chan, true);
|
||||
treq = select_freq(tx_freqs[!chan].freq, !chan, true);
|
||||
tres = usrp_dev->set_tx_freq(treq, !chan);
|
||||
tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
|
||||
tx_freqs[!chan].uhd = tres;
|
||||
tx_freqs[!chan].freq = usrp_dev->get_tx_freq(!chan);
|
||||
} else {
|
||||
treq = select_freq(rx_freqs[!chan], !chan, false);
|
||||
treq = select_freq(rx_freqs[!chan].freq, !chan, false);
|
||||
tres = usrp_dev->set_rx_freq(treq, !chan);
|
||||
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
|
||||
rx_freqs[!chan].uhd = tres;
|
||||
rx_freqs[!chan].freq = usrp_dev->get_rx_freq(!chan);
|
||||
|
||||
}
|
||||
LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
@@ -1091,6 +1103,20 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
||||
return set_freq(wFreq, chan, true);
|
||||
}
|
||||
|
||||
bool uhd_device::setRxOffset(double wOffset, size_t chan)
|
||||
{
|
||||
uhd::tune_result_t tres;
|
||||
uhd::tune_request_t treq(rx_freqs[chan].freq - wOffset);
|
||||
|
||||
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.rf_freq = rx_freqs[chan].uhd.actual_rf_freq;
|
||||
|
||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
||||
rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
if (chan >= rx_freqs.size()) {
|
||||
@@ -1108,7 +1134,7 @@ double uhd_device::getTxFreq(size_t chan)
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return tx_freqs[chan];
|
||||
return tx_freqs[chan].freq;
|
||||
}
|
||||
|
||||
double uhd_device::getRxFreq(size_t chan)
|
||||
@@ -1118,7 +1144,7 @@ double uhd_device::getRxFreq(size_t chan)
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return rx_freqs[chan];
|
||||
return rx_freqs[chan].freq;
|
||||
}
|
||||
|
||||
bool uhd_device::recv_async_msg()
|
||||
|
@@ -68,6 +68,7 @@ struct trx_config {
|
||||
bool extref;
|
||||
bool filler;
|
||||
bool diversity;
|
||||
bool ms;
|
||||
double offset;
|
||||
};
|
||||
|
||||
@@ -118,7 +119,7 @@ bool testConfig()
|
||||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr;
|
||||
std::string refstr, fillstr, divstr, msstr;
|
||||
|
||||
if (!testConfig())
|
||||
return false;
|
||||
@@ -167,6 +168,7 @@ bool trx_setup_config(struct trx_config *config)
|
||||
refstr = config->extref ? "Enabled" : "Disabled";
|
||||
fillstr = config->filler ? "Enabled" : "Disabled";
|
||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||
msstr = config->ms ? "Enabled" : "Disabled";
|
||||
|
||||
std::ostringstream ost("");
|
||||
ost << "Config Settings" << std::endl;
|
||||
@@ -179,6 +181,7 @@ bool trx_setup_config(struct trx_config *config)
|
||||
ost << " External Reference...... " << refstr << std::endl;
|
||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||
ost << " Diversity............... " << divstr << std::endl;
|
||||
ost << " MS Mode................. " << msstr << std::endl;
|
||||
ost << " Tuning offset........... " << config->offset << std::endl;
|
||||
std::cout << ost << std::endl;
|
||||
|
||||
@@ -196,10 +199,22 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
RadioDevice *usrp, int type)
|
||||
{
|
||||
RadioInterface *radio = NULL;
|
||||
size_t div = 1;
|
||||
int offset = 3;
|
||||
|
||||
if (config->ms) {
|
||||
if (type != RadioDevice::NORMAL) {
|
||||
LOG(ALERT) << "Unsupported configuration";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
offset *= -1;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, config->sps, config->chans);
|
||||
radio = new RadioInterface(usrp, config->sps,
|
||||
config->chans, div, offset);
|
||||
break;
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
@@ -286,6 +301,7 @@ static void print_help()
|
||||
" -s Samples-per-symbol (1 or 4)\n"
|
||||
" -c Number of ARFCN channels (default=1)\n"
|
||||
" -f Enable C0 filler table\n"
|
||||
" -m Enable MS mode\n"
|
||||
" -o Set baseband frequency offset (default=auto)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
@@ -300,9 +316,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->extref = false;
|
||||
config->filler = false;
|
||||
config->diversity = false;
|
||||
config->ms = false;
|
||||
config->offset = 0.0;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -1) {
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:ms:")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
@@ -335,6 +352,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
case 'o':
|
||||
config->offset = atof(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
config->ms = true;
|
||||
break;
|
||||
case 's':
|
||||
config->sps = atoi(optarg);
|
||||
if ((config->sps != 1) && (config->sps != 4)) {
|
||||
|
@@ -29,6 +29,40 @@ void RadioClock::set(const GSM::Time& wTime)
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
GSM::Time RadioClock::adjust(GSM::Time &wBase, GSM::Time &wOffset)
|
||||
{
|
||||
int tn_diff, fn_diff = 0;
|
||||
|
||||
/* Modulo TN adustment */
|
||||
tn_diff = wBase.TN() + wOffset.TN();
|
||||
if (tn_diff < 0) {
|
||||
tn_diff += 8;
|
||||
fn_diff--;
|
||||
} else if (tn_diff >= 8) {
|
||||
tn_diff -= 8;
|
||||
fn_diff++;
|
||||
}
|
||||
|
||||
/* Modulo FN adjustment */
|
||||
fn_diff += wBase.FN() + wOffset.FN();
|
||||
if (fn_diff < 0)
|
||||
fn_diff += GSM::gHyperframe;
|
||||
else if ((unsigned) fn_diff >= GSM::gHyperframe)
|
||||
fn_diff = fn_diff - GSM::gHyperframe;
|
||||
|
||||
return GSM::Time(fn_diff, tn_diff);
|
||||
}
|
||||
|
||||
void RadioClock::adjust(GSM::Time& wOffset)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
mClock = adjust(mClock, wOffset);
|
||||
updateSignal.signal();
|
||||
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void RadioClock::incTN()
|
||||
{
|
||||
mLock.lock();
|
||||
|
@@ -26,7 +26,10 @@
|
||||
|
||||
class RadioClock {
|
||||
public:
|
||||
static GSM::Time adjust(GSM::Time &base, GSM::Time &offset);
|
||||
|
||||
void set(const GSM::Time& wTime);
|
||||
void adjust(GSM::Time &wOffset);
|
||||
void incTN();
|
||||
GSM::Time get();
|
||||
void wait();
|
||||
|
@@ -91,6 +91,9 @@ class RadioDevice {
|
||||
/** Set the receiver frequency */
|
||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
|
||||
|
||||
/** Adjust the receiver offset */
|
||||
virtual bool setRxOffset(double wOffset, size_t chan = 0) = 0;
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
virtual TIMESTAMP initialWriteTimestamp(void)=0;
|
||||
|
||||
|
@@ -38,7 +38,8 @@ RadioInterface::RadioInterface(RadioDevice *wRadio,
|
||||
int wReceiveOffset, GSM::Time wStartTime)
|
||||
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
|
||||
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
|
||||
receiveOffset(wReceiveOffset), mOn(false)
|
||||
receiveOffset(wReceiveOffset), shiftOffset(0), shiftUpdate(false),
|
||||
mOn(false)
|
||||
{
|
||||
mClock.set(wStartTime);
|
||||
}
|
||||
@@ -157,6 +158,11 @@ int RadioInterface::unRadioifyVector(float *floatVector,
|
||||
return newVector.size();
|
||||
}
|
||||
|
||||
void RadioInterface::adjustClock(GSM::Time &offset)
|
||||
{
|
||||
mClock.adjust(offset);
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneTx(double freq, size_t chan)
|
||||
{
|
||||
return mRadio->setTxFreq(freq, chan);
|
||||
@@ -167,6 +173,10 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
||||
return mRadio->setRxFreq(freq, chan);
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneRxOffset(double offset, size_t chan)
|
||||
{
|
||||
return mRadio->setRxOffset(offset, chan);
|
||||
}
|
||||
|
||||
void RadioInterface::start()
|
||||
{
|
||||
@@ -228,7 +238,11 @@ bool RadioInterface::driveReceiveRadio()
|
||||
pullBuffer();
|
||||
|
||||
GSM::Time rcvClock = mClock.get();
|
||||
rcvClock.decTN(receiveOffset);
|
||||
if (receiveOffset < 0)
|
||||
rcvClock.incTN(-receiveOffset);
|
||||
else
|
||||
rcvClock.decTN(receiveOffset);
|
||||
|
||||
unsigned tN = rcvClock.TN();
|
||||
int recvSz = recvCursor;
|
||||
int readSz = 0;
|
||||
@@ -292,6 +306,12 @@ bool RadioInterface::isUnderrun()
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RadioInterface::applyOffset(int offset)
|
||||
{
|
||||
shiftOffset += offset;
|
||||
shiftUpdate = true;
|
||||
}
|
||||
|
||||
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
||||
{
|
||||
if (chan >= mReceiveFIFO.size())
|
||||
@@ -330,7 +350,7 @@ void RadioInterface::pullBuffer()
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
CHUNK,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
readTimestamp + shiftOffset,
|
||||
&local_underrun);
|
||||
if (num_recv != CHUNK) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
@@ -365,11 +385,16 @@ void RadioInterface::pushBuffer()
|
||||
powerScaling[i], 2 * sendCursor);
|
||||
}
|
||||
|
||||
if (shiftUpdate) {
|
||||
mRadio->updateAlignment(0);
|
||||
shiftUpdate = false;
|
||||
}
|
||||
|
||||
/* Send the all samples in the send buffer */
|
||||
num_sent = mRadio->writeSamples(convertSendBuffer,
|
||||
sendCursor,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
writeTimestamp + mSPSTx * shiftOffset);
|
||||
writeTimestamp += num_sent;
|
||||
sendCursor = 0;
|
||||
}
|
||||
|
@@ -56,7 +56,8 @@ protected:
|
||||
RadioClock mClock; ///< the basestation clock!
|
||||
|
||||
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
|
||||
|
||||
int shiftOffset;
|
||||
bool shiftUpdate;
|
||||
bool mOn; ///< indicates radio is on
|
||||
|
||||
private:
|
||||
@@ -94,6 +95,7 @@ public:
|
||||
|
||||
/** check for underrun, resets underrun value */
|
||||
bool isUnderrun();
|
||||
void applyOffset(int offset);
|
||||
|
||||
/** return the receive FIFO */
|
||||
VectorFIFO* receiveFIFO(size_t chan = 0);
|
||||
@@ -101,12 +103,18 @@ public:
|
||||
/** return the basestation clock */
|
||||
RadioClock* getClock(void) { return &mClock;};
|
||||
|
||||
/** apply an offset to the main clock */
|
||||
void adjustClock(GSM::Time &offset);
|
||||
|
||||
/** set transmit frequency */
|
||||
bool tuneTx(double freq, size_t chan = 0);
|
||||
|
||||
/** set receive frequency */
|
||||
virtual bool tuneRx(double freq, size_t chan = 0);
|
||||
|
||||
/** set frequency correction */
|
||||
virtual bool tuneRxOffset(double offset, size_t chan = 0);
|
||||
|
||||
/** set receive gain */
|
||||
double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
|
@@ -189,7 +189,7 @@ void RadioInterfaceDiversity::pullBuffer()
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
resamp_outchunk,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
readTimestamp + shiftOffset,
|
||||
&local_underrun);
|
||||
if ((size_t) num != resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num;
|
||||
|
@@ -188,7 +188,7 @@ void RadioInterfaceResamp::pullBuffer()
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
resamp_outchunk,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
readTimestamp + shiftOffset,
|
||||
&local_underrun);
|
||||
if (num_recv != (int) resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
|
@@ -74,25 +74,31 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
|
||||
return true;
|
||||
}
|
||||
|
||||
noiseVector::noiseVector(size_t size)
|
||||
: std::vector<float>(size), itr(0)
|
||||
avgVector::avgVector(size_t max)
|
||||
: std::vector<float>(0), itr(0)
|
||||
{
|
||||
this->max = max;
|
||||
}
|
||||
|
||||
float noiseVector::avg() const
|
||||
float avgVector::avg() const
|
||||
{
|
||||
float val = 0.0;
|
||||
|
||||
if (!size())
|
||||
return 0.0f;
|
||||
|
||||
for (size_t i = 0; i < size(); i++)
|
||||
val += (*this)[i];
|
||||
|
||||
return val / (float) size();
|
||||
}
|
||||
|
||||
bool noiseVector::insert(float val)
|
||||
bool avgVector::insert(float val)
|
||||
{
|
||||
if (!size())
|
||||
return false;
|
||||
if (size() < max) {
|
||||
push_back(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itr >= this->size())
|
||||
itr = 0;
|
||||
@@ -102,6 +108,16 @@ bool noiseVector::insert(float val)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool avgVector::full() const
|
||||
{
|
||||
return size() >= max;
|
||||
}
|
||||
|
||||
void avgVector::reset()
|
||||
{
|
||||
resize(0);
|
||||
}
|
||||
|
||||
GSM::Time VectorQueue::nextTime() const
|
||||
{
|
||||
GSM::Time retVal;
|
||||
|
@@ -46,14 +46,17 @@ private:
|
||||
GSM::Time mTime;
|
||||
};
|
||||
|
||||
class noiseVector : std::vector<float> {
|
||||
class avgVector : std::vector<float> {
|
||||
public:
|
||||
noiseVector(size_t size = 0);
|
||||
avgVector(size_t size = 0);
|
||||
bool insert(float val);
|
||||
bool full() const;
|
||||
float avg() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
size_t itr;
|
||||
size_t max;
|
||||
};
|
||||
|
||||
class VectorFIFO : public InterthreadQueue<radioVector> { };
|
||||
|
219
Transceiver52M/sch.c
Normal file
219
Transceiver52M/sch.c
Normal file
@@ -0,0 +1,219 @@
|
||||
#include <complex.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/conv.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/crcgen.h>
|
||||
|
||||
#include "sch.h"
|
||||
|
||||
/* GSM 04.08, 9.1.30 Synchronization channel information */
|
||||
struct sch_packed_info {
|
||||
ubit_t t1_hi[2];
|
||||
ubit_t bsic[6];
|
||||
ubit_t t1_md[8];
|
||||
ubit_t t3p_hi[2];
|
||||
ubit_t t2[5];
|
||||
ubit_t t1_lo[1];
|
||||
ubit_t t3p_lo[1];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sch_burst {
|
||||
sbit_t tail0[3];
|
||||
sbit_t data0[39];
|
||||
sbit_t etsc[64];
|
||||
sbit_t data1[39];
|
||||
sbit_t tail1[3];
|
||||
sbit_t guard[8];
|
||||
} __attribute__((packed));
|
||||
|
||||
static const uint8_t sch_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
|
||||
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t sch_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const struct osmo_conv_code gsm_conv_sch = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = GSM_SCH_UNCODED_LEN,
|
||||
.next_output = sch_next_output,
|
||||
.next_state = sch_next_state,
|
||||
};
|
||||
|
||||
const struct osmo_crc16gen_code gsm0503_sch_crc10 = {
|
||||
.bits = 10,
|
||||
.poly = 0x175,
|
||||
.init = 0x000,
|
||||
.remainder = 0x3ff,
|
||||
};
|
||||
|
||||
#define GSM_MAX_BURST_LEN 157
|
||||
#define GSM_SYM_RATE (1625e3 / 6)
|
||||
|
||||
/* Pre-generated FCCH measurement tone */
|
||||
static complex float fcch_ref[GSM_MAX_BURST_LEN];
|
||||
|
||||
int float_to_sbit(const float *in, sbit_t *out, float scale, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
out[i] = (in[i] - 0.5f) * scale;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if FN contains a SCH burst */
|
||||
int gsm_sch_check_fn(int fn)
|
||||
{
|
||||
int fn51 = fn % 51;
|
||||
|
||||
switch (fn51) {
|
||||
case 1:
|
||||
case 11:
|
||||
case 21:
|
||||
case 31:
|
||||
case 41:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SCH (T1, T2, T3p) to full FN value */
|
||||
int gsm_sch_to_fn(struct sch_info *sch)
|
||||
{
|
||||
int t1 = sch->t1;
|
||||
int t2 = sch->t2;
|
||||
int t3p = sch->t3p;
|
||||
|
||||
if ((t1 < 0) || (t2 < 0) || (t3p < 0))
|
||||
return -1;
|
||||
int tt;
|
||||
int t3 = t3p * 10 + 1;
|
||||
|
||||
if (t3 < t2)
|
||||
tt = (t3 + 26) - t2;
|
||||
else
|
||||
tt = (t3 - t2) % 26;
|
||||
|
||||
return t1 * 51 * 26 + tt * 51 + t3;
|
||||
}
|
||||
|
||||
/* Parse encoded SCH message */
|
||||
int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
|
||||
{
|
||||
struct sch_packed_info *p = (struct sch_packed_info *) info;
|
||||
|
||||
desc->bsic = (p->bsic[0] << 0) | (p->bsic[1] << 1) |
|
||||
(p->bsic[2] << 2) | (p->bsic[3] << 3) |
|
||||
(p->bsic[4] << 4);
|
||||
|
||||
desc->t1 = (p->t1_lo[0] << 0) | (p->t1_md[0] << 1) |
|
||||
(p->t1_md[1] << 2) | (p->t1_md[2] << 3) |
|
||||
(p->t1_md[3] << 4) | (p->t1_md[4] << 5) |
|
||||
(p->t1_md[5] << 6) | (p->t1_md[6] << 7) |
|
||||
(p->t1_md[7] << 8) | (p->t1_hi[0] << 9) |
|
||||
(p->t1_hi[1] << 10);
|
||||
|
||||
desc->t2 = (p->t2[0] << 0) | (p->t2[1] << 1) |
|
||||
(p->t2[2] << 2) | (p->t2[3] << 3) |
|
||||
(p->t2[4] << 4);
|
||||
|
||||
desc->t3p = (p->t3p_lo[0] << 0) | (p->t3p_hi[0] << 1) |
|
||||
(p->t3p_hi[1] << 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* From osmo-bts */
|
||||
int gsm_sch_decode(uint8_t *info, sbit_t *data)
|
||||
{
|
||||
int rc;
|
||||
ubit_t uncoded[GSM_SCH_UNCODED_LEN];
|
||||
|
||||
osmo_conv_decode(&gsm_conv_sch, data, uncoded);
|
||||
|
||||
rc = osmo_crc16gen_check_bits(&gsm0503_sch_crc10,
|
||||
uncoded, GSM_SCH_INFO_LEN,
|
||||
uncoded + GSM_SCH_INFO_LEN);
|
||||
if (rc)
|
||||
return -1;
|
||||
|
||||
memcpy(info, uncoded, GSM_SCH_INFO_LEN * sizeof(ubit_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FCCH_TAIL_BITS_LEN 3
|
||||
#define FCCH_DATA_LEN 142
|
||||
|
||||
/* Compute FCCH frequency offset */
|
||||
double gsm_fcch_offset(float *burst, int len)
|
||||
{
|
||||
int i, start, end;
|
||||
float a, b, c, d, ang, avg = 0.0f;
|
||||
double freq;
|
||||
|
||||
if (len > GSM_MAX_BURST_LEN)
|
||||
len = GSM_MAX_BURST_LEN;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
a = burst[2 * i + 0];
|
||||
b = burst[2 * i + 1];
|
||||
c = crealf(fcch_ref[i]);
|
||||
d = cimagf(fcch_ref[i]);
|
||||
|
||||
burst[2 * i + 0] = a * c - b * d;
|
||||
burst[2 * i + 1] = a * d + b * c;
|
||||
}
|
||||
|
||||
start = FCCH_TAIL_BITS_LEN;
|
||||
end = start + FCCH_DATA_LEN;
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
a = cargf(burst[2 * (i - 1) + 0] +
|
||||
burst[2 * (i - 1) + 1] * I);
|
||||
b = cargf(burst[2 * i + 0] +
|
||||
burst[2 * i + 1] * I);
|
||||
|
||||
ang = b - a;
|
||||
|
||||
if (ang > M_PI)
|
||||
ang -= 2 * M_PI;
|
||||
else if (ang < -M_PI)
|
||||
ang += 2 * M_PI;
|
||||
|
||||
avg += ang;
|
||||
}
|
||||
|
||||
avg /= (float) (end - start);
|
||||
freq = avg / (2 * M_PI) * GSM_SYM_RATE;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/* Generate FCCH measurement tone */
|
||||
static __attribute__((constructor)) void init()
|
||||
{
|
||||
int i;
|
||||
double freq = 0.25;
|
||||
|
||||
for (i = 0; i < GSM_MAX_BURST_LEN; i++) {
|
||||
fcch_ref[i] = sin(2 * M_PI * freq * (double) i) +
|
||||
cos(2 * M_PI * freq * (double) i) * I;
|
||||
}
|
||||
}
|
26
Transceiver52M/sch.h
Normal file
26
Transceiver52M/sch.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _SCH_H_
|
||||
#define _SCH_H_
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
struct sch_info {
|
||||
int bsic;
|
||||
int t1;
|
||||
int t2;
|
||||
int t3p;
|
||||
};
|
||||
|
||||
#define GSM_SCH_INFO_LEN 25
|
||||
#define GSM_SCH_UNCODED_LEN 35
|
||||
#define GSM_SCH_CODED_LEN 78
|
||||
|
||||
int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
|
||||
int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
|
||||
int gsm_sch_to_fn(struct sch_info *sch);
|
||||
int gsm_sch_check_fn(int fn);
|
||||
|
||||
double gsm_fcch_offset(float *burst, int len);
|
||||
|
||||
int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
|
||||
|
||||
#endif /* _SCH_H_ */
|
@@ -78,6 +78,7 @@ struct CorrelationSequence {
|
||||
|
||||
signalVector *sequence;
|
||||
void *buffer;
|
||||
void *history;
|
||||
float toa;
|
||||
complex gain;
|
||||
};
|
||||
@@ -111,6 +112,7 @@ struct PulseSequence {
|
||||
|
||||
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||
CorrelationSequence *gRACHSequence = NULL;
|
||||
CorrelationSequence *gSCHSequence = NULL;
|
||||
PulseSequence *GSMPulse = NULL;
|
||||
PulseSequence *GSMPulse1 = NULL;
|
||||
|
||||
@@ -131,6 +133,7 @@ void sigProcLibDestroy()
|
||||
delete GMSKRotation1;
|
||||
delete GMSKReverseRotation1;
|
||||
delete gRACHSequence;
|
||||
delete gSCHSequence;
|
||||
delete GSMPulse;
|
||||
delete GSMPulse1;
|
||||
|
||||
@@ -139,6 +142,7 @@ void sigProcLibDestroy()
|
||||
GMSKReverseRotationN = NULL;
|
||||
GMSKReverseRotation1 = NULL;
|
||||
gRACHSequence = NULL;
|
||||
gSCHSequence = NULL;
|
||||
GSMPulse = NULL;
|
||||
GSMPulse1 = NULL;
|
||||
}
|
||||
@@ -395,8 +399,10 @@ signalVector *convolve(const signalVector *x,
|
||||
break;
|
||||
case CUSTOM:
|
||||
if (start < h->size() - 1) {
|
||||
head = h->size() - start;
|
||||
append = true;
|
||||
if (x->getStart() < h->size() - 1) {
|
||||
head = h->size() - start;
|
||||
append = true;
|
||||
}
|
||||
}
|
||||
if (start + len > x->size()) {
|
||||
tail = start + len - x->size();
|
||||
@@ -1274,6 +1280,69 @@ release:
|
||||
return status;
|
||||
}
|
||||
|
||||
bool generateSCHSequence(int sps)
|
||||
{
|
||||
bool status = true;
|
||||
float toa;
|
||||
complex *data = NULL;
|
||||
signalVector *autocorr = NULL;
|
||||
signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
|
||||
|
||||
delete gSCHSequence;
|
||||
|
||||
seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
|
||||
if (!seq0)
|
||||
return false;
|
||||
|
||||
seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
|
||||
if (!seq1) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
conjugateVector(*seq1);
|
||||
|
||||
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
|
||||
data = (complex *) convolve_h_alloc(seq1->size());
|
||||
_seq1 = new signalVector(data, 0, seq1->size());
|
||||
_seq1->setAligned(true);
|
||||
memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));
|
||||
|
||||
autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
|
||||
if (!autocorr) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
gSCHSequence = new CorrelationSequence;
|
||||
gSCHSequence->sequence = _seq1;
|
||||
gSCHSequence->buffer = data;
|
||||
gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
|
||||
gSCHSequence->history = new complex[_seq1->size()];
|
||||
|
||||
/* For 1 sps only
|
||||
* (Half of correlation length - 1) + midpoint of pulse shaping filer
|
||||
* 20.5 = (64 / 2 - 1) + 1.5
|
||||
*/
|
||||
if (sps == 1)
|
||||
gSCHSequence->toa = toa - 32.5;
|
||||
else
|
||||
gSCHSequence->toa = 0.0;
|
||||
|
||||
release:
|
||||
delete autocorr;
|
||||
delete seq0;
|
||||
delete seq1;
|
||||
|
||||
if (!status) {
|
||||
delete _seq1;
|
||||
free(data);
|
||||
gSCHSequence = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static float computePeakRatio(signalVector *corr,
|
||||
int sps, float toa, complex amp)
|
||||
{
|
||||
@@ -1424,6 +1493,71 @@ int detectRACHBurst(signalVector &rxBurst,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int detectSCHBurst(signalVector &burst,
|
||||
float thresh,
|
||||
int sps,
|
||||
complex *amp,
|
||||
float *toa, int state)
|
||||
{
|
||||
int rc, start, target, head, tail, len;
|
||||
float _toa;
|
||||
complex _amp;
|
||||
signalVector *corr, *_burst;
|
||||
CorrelationSequence *sync;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return -1;
|
||||
|
||||
target = 3 + 39 + 64;
|
||||
|
||||
switch (state) {
|
||||
case SCH_DETECT_NARROW:
|
||||
head = 4;
|
||||
tail = 4;
|
||||
break;
|
||||
case SCH_DETECT_FULL:
|
||||
default:
|
||||
head = target - 1;
|
||||
tail = 39 + 3 + 9;
|
||||
break;
|
||||
}
|
||||
|
||||
start = (target - head) * sps - 1;
|
||||
len = (head + tail) * sps;
|
||||
sync = gSCHSequence;
|
||||
corr = new signalVector(len);
|
||||
|
||||
_burst = new signalVector(burst, sync->sequence->size(), 5);
|
||||
|
||||
memcpy(_burst->begin() - sync->sequence->size(), sync->history,
|
||||
sync->sequence->size() * sizeof(complex));
|
||||
|
||||
memcpy(sync->history, &burst.begin()[burst.size() - sync->sequence->size()],
|
||||
sync->sequence->size() * sizeof(complex));
|
||||
|
||||
rc = detectBurst(*_burst, *corr, sync,
|
||||
thresh, sps, &_amp, &_toa, start, len);
|
||||
delete corr;
|
||||
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
} else if (!rc) {
|
||||
if (amp)
|
||||
*amp = 0.0f;
|
||||
if (toa)
|
||||
*toa = 0.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Subtract forward search bits from delay */
|
||||
if (toa)
|
||||
*toa = _toa - head * sps;
|
||||
if (amp)
|
||||
*amp = _amp;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal burst detection
|
||||
*
|
||||
@@ -1717,6 +1851,11 @@ bool sigProcLibSetup(int sps)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!generateSCHSequence(1)) {
|
||||
sigProcLibDestroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
generateDelayFilters();
|
||||
|
||||
return true;
|
||||
|
@@ -158,6 +158,7 @@ bool generateMidamble(int sps, int tsc);
|
||||
@return Success.
|
||||
*/
|
||||
bool generateRACHSequence(int sps);
|
||||
bool generateSCHSequence(int sps);
|
||||
|
||||
/**
|
||||
Energy detector, checks to see if received burst energy is above a threshold.
|
||||
@@ -187,6 +188,17 @@ int detectRACHBurst(signalVector &rxBurst,
|
||||
complex *amplitude,
|
||||
float* TOA);
|
||||
|
||||
enum {
|
||||
SCH_DETECT_FULL,
|
||||
SCH_DETECT_NARROW,
|
||||
};
|
||||
|
||||
int detectSCHBurst(signalVector &rxBurst,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex *amplitude,
|
||||
float* TOA, int state);
|
||||
|
||||
/**
|
||||
Normal burst correlator, detector, channel estimator.
|
||||
@param rxBurst The received GSM burst of interest.
|
||||
|
@@ -58,6 +58,9 @@ AC_TYPE_SIZE_T
|
||||
AC_HEADER_TIME
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
dnl checks for libraries
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
|
||||
|
||||
AC_ARG_WITH(usrp1, [
|
||||
AS_HELP_STRING([--with-usrp1],
|
||||
[enable USRP1 gnuradio based transceiver])
|
||||
|
Reference in New Issue
Block a user