mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-10-26 01:33:34 +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::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||||
|
|
||||||
|
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
|
||||||
|
|
||||||
|
|
||||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ extern const BitVector gDummyBurst;
|
|||||||
/** Random access burst synch. sequence */
|
/** Random access burst synch. sequence */
|
||||||
extern const BitVector gRACHSynchSequence;
|
extern const BitVector gRACHSynchSequence;
|
||||||
|
|
||||||
|
/** Synchronization burst sync sequence */
|
||||||
|
extern const BitVector gSCHSynchSequence;
|
||||||
|
|
||||||
/**@name Modulus operations for frame numbers. */
|
/**@name Modulus operations for frame numbers. */
|
||||||
//@{
|
//@{
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ COMMON_SOURCES = \
|
|||||||
radioClock.cpp \
|
radioClock.cpp \
|
||||||
sigProcLib.cpp \
|
sigProcLib.cpp \
|
||||||
signalVector.cpp \
|
signalVector.cpp \
|
||||||
Transceiver.cpp
|
Transceiver.cpp \
|
||||||
|
sch.c
|
||||||
|
|
||||||
libtransceiver_la_SOURCES = \
|
libtransceiver_la_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
@@ -87,7 +88,9 @@ osmo_trx_LDADD = \
|
|||||||
libtransceiver.la \
|
libtransceiver.la \
|
||||||
$(ARCH_LA) \
|
$(ARCH_LA) \
|
||||||
$(GSM_LA) \
|
$(GSM_LA) \
|
||||||
$(COMMON_LA) $(SQLITE_LA)
|
$(COMMON_LA) \
|
||||||
|
$(SQLITE_LA) \
|
||||||
|
$(LIBOSMOCORE_LIBS)
|
||||||
|
|
||||||
if USRP1
|
if USRP1
|
||||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
#include "Transceiver.h"
|
#include "Transceiver.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "sch.h"
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -39,11 +43,16 @@ using namespace GSM;
|
|||||||
# define USB_LATENCY_MIN 1,1
|
# define USB_LATENCY_MIN 1,1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Clock indication interval in frames */
|
||||||
|
#define CLK_IND_INTERVAL 100
|
||||||
|
|
||||||
/* Number of running values use in noise average */
|
/* Number of running values use in noise average */
|
||||||
#define NOISE_CNT 20
|
#define NOISE_CNT 20
|
||||||
|
#define FREQ_CNT 20
|
||||||
|
|
||||||
TransceiverState::TransceiverState()
|
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++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
chanType[i] = Transceiver::NONE;
|
chanType[i] = Transceiver::NONE;
|
||||||
@@ -51,6 +60,7 @@ TransceiverState::TransceiverState()
|
|||||||
chanResponse[i] = NULL;
|
chanResponse[i] = NULL;
|
||||||
DFEForward[i] = NULL;
|
DFEForward[i] = NULL;
|
||||||
DFEFeedback[i] = NULL;
|
DFEFeedback[i] = NULL;
|
||||||
|
prevFrame[i] = NULL;
|
||||||
|
|
||||||
for (int n = 0; n < 102; n++)
|
for (int n = 0; n < 102; n++)
|
||||||
fillerTable[n][i] = NULL;
|
fillerTable[n][i] = NULL;
|
||||||
@@ -91,12 +101,12 @@ Transceiver::Transceiver(int wBasePort,
|
|||||||
: mBasePort(wBasePort), mAddr(TRXAddress),
|
: mBasePort(wBasePort), mAddr(TRXAddress),
|
||||||
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
|
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
|
||||||
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
|
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);
|
GSM::Time startTime(random() % gHyperframe,0);
|
||||||
|
|
||||||
mRxLowerLoopThread = new Thread(32768);
|
mLowerLoopThread = new Thread(32768);
|
||||||
mTxLowerLoopThread = new Thread(32768);
|
|
||||||
|
|
||||||
mTransmitDeadlineClock = startTime;
|
mTransmitDeadlineClock = startTime;
|
||||||
mLastClockUpdateTime = startTime;
|
mLastClockUpdateTime = startTime;
|
||||||
@@ -105,6 +115,9 @@ Transceiver::Transceiver(int wBasePort,
|
|||||||
|
|
||||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
mRxSlotMask[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::~Transceiver()
|
Transceiver::~Transceiver()
|
||||||
@@ -194,6 +207,9 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mStates[0].mode != TRX_MODE_BTS)
|
||||||
|
return;
|
||||||
|
|
||||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
||||||
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
||||||
|
|
||||||
@@ -221,7 +237,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
|||||||
radioVector *burst;
|
radioVector *burst;
|
||||||
TransceiverState *state;
|
TransceiverState *state;
|
||||||
std::vector<signalVector *> bursts(mChans);
|
std::vector<signalVector *> bursts(mChans);
|
||||||
std::vector<bool> zeros(mChans);
|
std::vector<bool> zeros(mChans, false);
|
||||||
std::vector<bool> filler(mChans, true);
|
std::vector<bool> filler(mChans, true);
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i ++) {
|
for (size_t i = 0; i < mChans; i ++) {
|
||||||
@@ -238,7 +254,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
|||||||
modFN = nowTime.FN() % state->fillerModulus[TN];
|
modFN = nowTime.FN() % state->fillerModulus[TN];
|
||||||
|
|
||||||
bursts[i] = state->fillerTable[modFN][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))) {
|
if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
|
||||||
bursts[i] = burst->getVector();
|
bursts[i] = burst->getVector();
|
||||||
@@ -299,6 +316,19 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
unsigned burstTN = currTime.TN();
|
unsigned burstTN = currTime.TN();
|
||||||
unsigned burstFN = currTime.FN();
|
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]) {
|
switch (state->chanType[burstTN]) {
|
||||||
case NONE:
|
case NONE:
|
||||||
return OFF;
|
return OFF;
|
||||||
@@ -376,6 +406,108 @@ bool Transceiver::detectRACH(TransceiverState *state,
|
|||||||
return detectRACHBurst(burst, threshold, mSPSRx, &, &toa);
|
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
|
* Detect normal burst training sequence midamble. Update equalization
|
||||||
* state information and channel estimate if necessary. Equalization
|
* state information and channel estimate if necessary. Equalization
|
||||||
@@ -462,18 +594,33 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
SoftVector *bits = NULL;
|
SoftVector *bits = NULL;
|
||||||
TransceiverState *state = &mStates[chan];
|
TransceiverState *state = &mStates[chan];
|
||||||
|
|
||||||
|
GSM::Time sch_time, burst_time, diff_time;
|
||||||
|
|
||||||
/* Blocking FIFO read */
|
/* Blocking FIFO read */
|
||||||
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
||||||
if (!radio_burst)
|
if (!radio_burst)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Set time and determine correlation type */
|
/* Set time and determine correlation type */
|
||||||
GSM::Time time = radio_burst->getTime();
|
burst_time = radio_burst->getTime();
|
||||||
CorrType type = expectedCorrType(time, chan);
|
CorrType type = expectedCorrType(burst_time, chan);
|
||||||
|
|
||||||
if ((type == OFF) || (type == IDLE)) {
|
switch (state->mode) {
|
||||||
delete radio_burst;
|
case TRX_MODE_MS_ACQUIRE:
|
||||||
return NULL;
|
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 */
|
/* Select the diversity channel with highest energy */
|
||||||
@@ -488,8 +635,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
|
|
||||||
if (max_i < 0) {
|
if (max_i < 0) {
|
||||||
LOG(ALERT) << "Received empty burst";
|
LOG(ALERT) << "Received empty burst";
|
||||||
delete radio_burst;
|
goto release;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Average noise on diversity paths and update global levels */
|
/* 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 */
|
/* Detect normal or RACH bursts */
|
||||||
if (type == TSC)
|
if (type == TSC)
|
||||||
success = detectTSC(state, *burst, amp, toa, time);
|
success = detectTSC(state, *burst, amp, toa, burst_time);
|
||||||
else
|
else if (type == RACH)
|
||||||
success = detectRACH(state, *burst, amp, toa);
|
success = detectRACH(state, *burst, amp, toa);
|
||||||
|
else if (type == SCH)
|
||||||
|
success = detectSCH(state, *burst, amp, toa);
|
||||||
|
else
|
||||||
|
success = false;
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
state->mNoises.insert(avg);
|
state->mNoises.insert(avg);
|
||||||
delete radio_burst;
|
goto release;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Demodulate and set output info */
|
/* Demodulate and set output info */
|
||||||
if (equalize && (type != TSC))
|
if (equalize && (type != TSC))
|
||||||
equalize = false;
|
equalize = false;
|
||||||
|
|
||||||
if (avg - state->mNoiseLev > 0.0)
|
/* Ignore noise threshold on MS mode for now */
|
||||||
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
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));
|
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
|
||||||
timingOffset = (int) round(toa * 256.0 / mSPSRx);
|
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;
|
return bits;
|
||||||
|
|
||||||
|
release:
|
||||||
|
delete state->prevFrame[burst_time.TN()];
|
||||||
|
state->prevFrame[burst_time.TN()] = radio_burst;
|
||||||
|
delete bits;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::start()
|
void Transceiver::start()
|
||||||
@@ -589,10 +771,8 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
mRadioInterface->start();
|
mRadioInterface->start();
|
||||||
|
|
||||||
// Start radio interface threads.
|
// Start radio interface threads.
|
||||||
mTxLowerLoopThread->start((void * (*)(void*))
|
mLowerLoopThread->start((void * (*)(void*))
|
||||||
TxLowerLoopAdapter,(void*) this);
|
LowerLoopAdapter,(void*) this);
|
||||||
mRxLowerLoopThread->start((void * (*)(void*))
|
|
||||||
RxLowerLoopAdapter,(void*) this);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||||
@@ -695,6 +875,12 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
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) {
|
else if (strcmp(command,"SETSLOT")==0) {
|
||||||
// set TSC
|
// set TSC
|
||||||
int corrCode;
|
int corrCode;
|
||||||
@@ -708,7 +894,21 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||||
setModulus(timeslot, chan);
|
setModulus(timeslot, chan);
|
||||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
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 {
|
else {
|
||||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
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++)
|
for (int i = 0; i < 4; i++)
|
||||||
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
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);
|
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||||
|
|
||||||
int RSSI = (int) buffer[5];
|
int RSSI = (int) buffer[5];
|
||||||
@@ -848,7 +1039,13 @@ void Transceiver::driveTxFIFO()
|
|||||||
}
|
}
|
||||||
// time to push burst to transmit FIFO
|
// time to push burst to transmit FIFO
|
||||||
pushRadioVector(mTransmitDeadlineClock);
|
pushRadioVector(mTransmitDeadlineClock);
|
||||||
|
|
||||||
mTransmitDeadlineClock.incTN();
|
mTransmitDeadlineClock.incTN();
|
||||||
|
|
||||||
|
if (!mTransmitDeadlineClock.TN() &&
|
||||||
|
!(mTransmitDeadlineClock.FN() % CLK_IND_INTERVAL)) {
|
||||||
|
writeClockInterface();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,22 +1084,12 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
void *LowerLoopAdapter(Transceiver *transceiver)
|
||||||
{
|
{
|
||||||
transceiver->setPriority(0.45);
|
transceiver->setPriority(0.45);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
transceiver->driveReceiveRadio();
|
transceiver->driveReceiveRadio();
|
||||||
pthread_testcancel();
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
|
||||||
{
|
|
||||||
transceiver->setPriority(0.44);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
transceiver->driveTxFIFO();
|
transceiver->driveTxFIFO();
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,14 @@ struct TransceiverState {
|
|||||||
|
|
||||||
/* Received noise energy levels */
|
/* Received noise energy levels */
|
||||||
float mNoiseLev;
|
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 */
|
/** 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<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||||
|
|
||||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
Thread *mLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
|
||||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||||
|
|
||||||
@@ -116,6 +122,7 @@ private:
|
|||||||
OFF, ///< timeslot is off
|
OFF, ///< timeslot is off
|
||||||
TSC, ///< timeslot should contain a normal burst
|
TSC, ///< timeslot should contain a normal burst
|
||||||
RACH, ///< timeslot should contain an access burst
|
RACH, ///< timeslot should contain an access burst
|
||||||
|
SCH, ///< timeslot should contain a SCH burst
|
||||||
IDLE ///< timeslot is an idle (or dummy) burst
|
IDLE ///< timeslot is an idle (or dummy) burst
|
||||||
} CorrType;
|
} CorrType;
|
||||||
|
|
||||||
@@ -147,6 +154,13 @@ private:
|
|||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
complex &, float &toa);
|
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 */
|
/** Detect normal bursts */
|
||||||
bool detectTSC(TransceiverState *state,
|
bool detectTSC(TransceiverState *state,
|
||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
@@ -167,7 +181,9 @@ private:
|
|||||||
double mRxFreq; ///< the receive frequency
|
double mRxFreq; ///< the receive frequency
|
||||||
int mPower; ///< the transmit power in dB
|
int mPower; ///< the transmit power in dB
|
||||||
unsigned mTSC; ///< the midamble sequence code
|
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;
|
std::vector<TransceiverState> mStates;
|
||||||
|
|
||||||
@@ -226,6 +242,13 @@ public:
|
|||||||
LOOPBACK ///< similar go VII, used in loopback testing
|
LOOPBACK ///< similar go VII, used in loopback testing
|
||||||
} ChannelCombination;
|
} ChannelCombination;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TRX_MODE_OFF,
|
||||||
|
TRX_MODE_BTS,
|
||||||
|
TRX_MODE_MS_ACQUIRE,
|
||||||
|
TRX_MODE_MS_TRACK,
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** drive lower receive I/O and burst generation */
|
/** drive lower receive I/O and burst generation */
|
||||||
void driveReceiveRadio();
|
void driveReceiveRadio();
|
||||||
@@ -249,9 +272,7 @@ protected:
|
|||||||
|
|
||||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
friend void *LowerLoopAdapter(Transceiver *);
|
||||||
|
|
||||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
|
||||||
|
|
||||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
@@ -266,8 +287,7 @@ protected:
|
|||||||
void *RxUpperLoopAdapter(TransceiverChannel *);
|
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
/** Main drive threads */
|
/** Main drive threads */
|
||||||
void *RxLowerLoopAdapter(Transceiver *);
|
void *LowerLoopAdapter(Transceiver *);
|
||||||
void *TxLowerLoopAdapter(Transceiver *);
|
|
||||||
|
|
||||||
/** control message handler thread loop */
|
/** control message handler thread loop */
|
||||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ struct uhd_dev_offset {
|
|||||||
const std::string desc;
|
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
|
* Tx / Rx sample offset values. In a perfect world, there is no group delay
|
||||||
* though analog components, and behaviour through digital filters exactly
|
* though analog components, and behaviour through digital filters exactly
|
||||||
@@ -282,6 +287,7 @@ public:
|
|||||||
|
|
||||||
bool setTxFreq(double wFreq, size_t chan);
|
bool setTxFreq(double wFreq, size_t chan);
|
||||||
bool setRxFreq(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 initialWriteTimestamp() { return ts_initial * sps; }
|
||||||
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
||||||
@@ -332,7 +338,7 @@ private:
|
|||||||
double offset;
|
double offset;
|
||||||
|
|
||||||
std::vector<double> tx_gains, rx_gains;
|
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;
|
size_t tx_spp, rx_spp;
|
||||||
|
|
||||||
bool started;
|
bool started;
|
||||||
@@ -886,7 +892,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
||||||
LOG(ERR) << rx_buffers[0]->str_status();
|
LOG(ERR) << rx_buffers[0]->str_status();
|
||||||
return 0;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create vector buffer
|
// Create vector buffer
|
||||||
@@ -996,15 +1002,17 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
|||||||
return num_smpls;
|
return num_smpls;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
|
bool uhd_device::updateAlignment(TIMESTAMP)
|
||||||
{
|
{
|
||||||
|
aligned = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||||
{
|
{
|
||||||
double rf_spread, rf_freq;
|
double rf_spread, rf_freq;
|
||||||
std::vector<double> freqs;
|
std::vector<tune_result> freqs;
|
||||||
uhd::tune_request_t treq(freq);
|
uhd::tune_request_t treq(freq);
|
||||||
|
|
||||||
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
|
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;
|
freqs = rx_freqs;
|
||||||
|
|
||||||
/* Tune directly if other channel isn't tuned */
|
/* Tune directly if other channel isn't tuned */
|
||||||
if (freqs[!chan] < 10.0)
|
if (freqs[!chan].freq < 10.0)
|
||||||
return treq;
|
return treq;
|
||||||
|
|
||||||
/* Find center frequency between channels */
|
/* Find center frequency between channels */
|
||||||
rf_spread = fabs(freqs[!chan] - freq);
|
rf_spread = fabs(freqs[!chan].freq - freq);
|
||||||
if (rf_spread > B2XX_CLK_RT) {
|
if (rf_spread > B2XX_CLK_RT) {
|
||||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||||
return treq;
|
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.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||||
treq.target_freq = freq;
|
treq.target_freq = freq;
|
||||||
@@ -1050,10 +1058,12 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
|||||||
|
|
||||||
if (tx) {
|
if (tx) {
|
||||||
tres = usrp_dev->set_tx_freq(treq, chan);
|
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 {
|
} else {
|
||||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
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;
|
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 (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
|
||||||
if (tx) {
|
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);
|
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 {
|
} 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);
|
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;
|
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);
|
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)
|
bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
if (chan >= rx_freqs.size()) {
|
if (chan >= rx_freqs.size()) {
|
||||||
@@ -1108,7 +1134,7 @@ double uhd_device::getTxFreq(size_t chan)
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx_freqs[chan];
|
return tx_freqs[chan].freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_device::getRxFreq(size_t chan)
|
double uhd_device::getRxFreq(size_t chan)
|
||||||
@@ -1118,7 +1144,7 @@ double uhd_device::getRxFreq(size_t chan)
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rx_freqs[chan];
|
return rx_freqs[chan].freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uhd_device::recv_async_msg()
|
bool uhd_device::recv_async_msg()
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ struct trx_config {
|
|||||||
bool extref;
|
bool extref;
|
||||||
bool filler;
|
bool filler;
|
||||||
bool diversity;
|
bool diversity;
|
||||||
|
bool ms;
|
||||||
double offset;
|
double offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ bool testConfig()
|
|||||||
*/
|
*/
|
||||||
bool trx_setup_config(struct trx_config *config)
|
bool trx_setup_config(struct trx_config *config)
|
||||||
{
|
{
|
||||||
std::string refstr, fillstr, divstr;
|
std::string refstr, fillstr, divstr, msstr;
|
||||||
|
|
||||||
if (!testConfig())
|
if (!testConfig())
|
||||||
return false;
|
return false;
|
||||||
@@ -167,6 +168,7 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
refstr = config->extref ? "Enabled" : "Disabled";
|
refstr = config->extref ? "Enabled" : "Disabled";
|
||||||
fillstr = config->filler ? "Enabled" : "Disabled";
|
fillstr = config->filler ? "Enabled" : "Disabled";
|
||||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||||
|
msstr = config->ms ? "Enabled" : "Disabled";
|
||||||
|
|
||||||
std::ostringstream ost("");
|
std::ostringstream ost("");
|
||||||
ost << "Config Settings" << std::endl;
|
ost << "Config Settings" << std::endl;
|
||||||
@@ -179,6 +181,7 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
ost << " External Reference...... " << refstr << std::endl;
|
ost << " External Reference...... " << refstr << std::endl;
|
||||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||||
ost << " Diversity............... " << divstr << std::endl;
|
ost << " Diversity............... " << divstr << std::endl;
|
||||||
|
ost << " MS Mode................. " << msstr << std::endl;
|
||||||
ost << " Tuning offset........... " << config->offset << std::endl;
|
ost << " Tuning offset........... " << config->offset << std::endl;
|
||||||
std::cout << ost << std::endl;
|
std::cout << ost << std::endl;
|
||||||
|
|
||||||
@@ -196,10 +199,22 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
|||||||
RadioDevice *usrp, int type)
|
RadioDevice *usrp, int type)
|
||||||
{
|
{
|
||||||
RadioInterface *radio = NULL;
|
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) {
|
switch (type) {
|
||||||
case RadioDevice::NORMAL:
|
case RadioDevice::NORMAL:
|
||||||
radio = new RadioInterface(usrp, config->sps, config->chans);
|
radio = new RadioInterface(usrp, config->sps,
|
||||||
|
config->chans, div, offset);
|
||||||
break;
|
break;
|
||||||
case RadioDevice::RESAMP_64M:
|
case RadioDevice::RESAMP_64M:
|
||||||
case RadioDevice::RESAMP_100M:
|
case RadioDevice::RESAMP_100M:
|
||||||
@@ -286,6 +301,7 @@ static void print_help()
|
|||||||
" -s Samples-per-symbol (1 or 4)\n"
|
" -s Samples-per-symbol (1 or 4)\n"
|
||||||
" -c Number of ARFCN channels (default=1)\n"
|
" -c Number of ARFCN channels (default=1)\n"
|
||||||
" -f Enable C0 filler table\n"
|
" -f Enable C0 filler table\n"
|
||||||
|
" -m Enable MS mode\n"
|
||||||
" -o Set baseband frequency offset (default=auto)\n",
|
" -o Set baseband frequency offset (default=auto)\n",
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
"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->extref = false;
|
||||||
config->filler = false;
|
config->filler = false;
|
||||||
config->diversity = false;
|
config->diversity = false;
|
||||||
|
config->ms = false;
|
||||||
config->offset = 0.0;
|
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) {
|
switch (option) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help();
|
print_help();
|
||||||
@@ -335,6 +352,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
case 'o':
|
case 'o':
|
||||||
config->offset = atof(optarg);
|
config->offset = atof(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'm':
|
||||||
|
config->ms = true;
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
config->sps = atoi(optarg);
|
config->sps = atoi(optarg);
|
||||||
if ((config->sps != 1) && (config->sps != 4)) {
|
if ((config->sps != 1) && (config->sps != 4)) {
|
||||||
|
|||||||
@@ -29,6 +29,40 @@ void RadioClock::set(const GSM::Time& wTime)
|
|||||||
mLock.unlock();
|
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()
|
void RadioClock::incTN()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
mLock.lock();
|
||||||
|
|||||||
@@ -26,7 +26,10 @@
|
|||||||
|
|
||||||
class RadioClock {
|
class RadioClock {
|
||||||
public:
|
public:
|
||||||
|
static GSM::Time adjust(GSM::Time &base, GSM::Time &offset);
|
||||||
|
|
||||||
void set(const GSM::Time& wTime);
|
void set(const GSM::Time& wTime);
|
||||||
|
void adjust(GSM::Time &wOffset);
|
||||||
void incTN();
|
void incTN();
|
||||||
GSM::Time get();
|
GSM::Time get();
|
||||||
void wait();
|
void wait();
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ class RadioDevice {
|
|||||||
/** Set the receiver frequency */
|
/** Set the receiver frequency */
|
||||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
|
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*/
|
/** Returns the starting write Timestamp*/
|
||||||
virtual TIMESTAMP initialWriteTimestamp(void)=0;
|
virtual TIMESTAMP initialWriteTimestamp(void)=0;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ RadioInterface::RadioInterface(RadioDevice *wRadio,
|
|||||||
int wReceiveOffset, GSM::Time wStartTime)
|
int wReceiveOffset, GSM::Time wStartTime)
|
||||||
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
|
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
|
||||||
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
|
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
|
||||||
receiveOffset(wReceiveOffset), mOn(false)
|
receiveOffset(wReceiveOffset), shiftOffset(0), shiftUpdate(false),
|
||||||
|
mOn(false)
|
||||||
{
|
{
|
||||||
mClock.set(wStartTime);
|
mClock.set(wStartTime);
|
||||||
}
|
}
|
||||||
@@ -157,6 +158,11 @@ int RadioInterface::unRadioifyVector(float *floatVector,
|
|||||||
return newVector.size();
|
return newVector.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioInterface::adjustClock(GSM::Time &offset)
|
||||||
|
{
|
||||||
|
mClock.adjust(offset);
|
||||||
|
}
|
||||||
|
|
||||||
bool RadioInterface::tuneTx(double freq, size_t chan)
|
bool RadioInterface::tuneTx(double freq, size_t chan)
|
||||||
{
|
{
|
||||||
return mRadio->setTxFreq(freq, chan);
|
return mRadio->setTxFreq(freq, chan);
|
||||||
@@ -167,6 +173,10 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
|||||||
return mRadio->setRxFreq(freq, chan);
|
return mRadio->setRxFreq(freq, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RadioInterface::tuneRxOffset(double offset, size_t chan)
|
||||||
|
{
|
||||||
|
return mRadio->setRxOffset(offset, chan);
|
||||||
|
}
|
||||||
|
|
||||||
void RadioInterface::start()
|
void RadioInterface::start()
|
||||||
{
|
{
|
||||||
@@ -228,7 +238,11 @@ bool RadioInterface::driveReceiveRadio()
|
|||||||
pullBuffer();
|
pullBuffer();
|
||||||
|
|
||||||
GSM::Time rcvClock = mClock.get();
|
GSM::Time rcvClock = mClock.get();
|
||||||
rcvClock.decTN(receiveOffset);
|
if (receiveOffset < 0)
|
||||||
|
rcvClock.incTN(-receiveOffset);
|
||||||
|
else
|
||||||
|
rcvClock.decTN(receiveOffset);
|
||||||
|
|
||||||
unsigned tN = rcvClock.TN();
|
unsigned tN = rcvClock.TN();
|
||||||
int recvSz = recvCursor;
|
int recvSz = recvCursor;
|
||||||
int readSz = 0;
|
int readSz = 0;
|
||||||
@@ -292,6 +306,12 @@ bool RadioInterface::isUnderrun()
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioInterface::applyOffset(int offset)
|
||||||
|
{
|
||||||
|
shiftOffset += offset;
|
||||||
|
shiftUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
||||||
{
|
{
|
||||||
if (chan >= mReceiveFIFO.size())
|
if (chan >= mReceiveFIFO.size())
|
||||||
@@ -330,7 +350,7 @@ void RadioInterface::pullBuffer()
|
|||||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||||
CHUNK,
|
CHUNK,
|
||||||
&overrun,
|
&overrun,
|
||||||
readTimestamp,
|
readTimestamp + shiftOffset,
|
||||||
&local_underrun);
|
&local_underrun);
|
||||||
if (num_recv != CHUNK) {
|
if (num_recv != CHUNK) {
|
||||||
LOG(ALERT) << "Receive error " << num_recv;
|
LOG(ALERT) << "Receive error " << num_recv;
|
||||||
@@ -365,11 +385,16 @@ void RadioInterface::pushBuffer()
|
|||||||
powerScaling[i], 2 * sendCursor);
|
powerScaling[i], 2 * sendCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shiftUpdate) {
|
||||||
|
mRadio->updateAlignment(0);
|
||||||
|
shiftUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send the all samples in the send buffer */
|
/* Send the all samples in the send buffer */
|
||||||
num_sent = mRadio->writeSamples(convertSendBuffer,
|
num_sent = mRadio->writeSamples(convertSendBuffer,
|
||||||
sendCursor,
|
sendCursor,
|
||||||
&underrun,
|
&underrun,
|
||||||
writeTimestamp);
|
writeTimestamp + mSPSTx * shiftOffset);
|
||||||
writeTimestamp += num_sent;
|
writeTimestamp += num_sent;
|
||||||
sendCursor = 0;
|
sendCursor = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ protected:
|
|||||||
RadioClock mClock; ///< the basestation clock!
|
RadioClock mClock; ///< the basestation clock!
|
||||||
|
|
||||||
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
|
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
|
||||||
|
int shiftOffset;
|
||||||
|
bool shiftUpdate;
|
||||||
bool mOn; ///< indicates radio is on
|
bool mOn; ///< indicates radio is on
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -94,6 +95,7 @@ public:
|
|||||||
|
|
||||||
/** check for underrun, resets underrun value */
|
/** check for underrun, resets underrun value */
|
||||||
bool isUnderrun();
|
bool isUnderrun();
|
||||||
|
void applyOffset(int offset);
|
||||||
|
|
||||||
/** return the receive FIFO */
|
/** return the receive FIFO */
|
||||||
VectorFIFO* receiveFIFO(size_t chan = 0);
|
VectorFIFO* receiveFIFO(size_t chan = 0);
|
||||||
@@ -101,12 +103,18 @@ public:
|
|||||||
/** return the basestation clock */
|
/** return the basestation clock */
|
||||||
RadioClock* getClock(void) { return &mClock;};
|
RadioClock* getClock(void) { return &mClock;};
|
||||||
|
|
||||||
|
/** apply an offset to the main clock */
|
||||||
|
void adjustClock(GSM::Time &offset);
|
||||||
|
|
||||||
/** set transmit frequency */
|
/** set transmit frequency */
|
||||||
bool tuneTx(double freq, size_t chan = 0);
|
bool tuneTx(double freq, size_t chan = 0);
|
||||||
|
|
||||||
/** set receive frequency */
|
/** set receive frequency */
|
||||||
virtual bool tuneRx(double freq, size_t chan = 0);
|
virtual bool tuneRx(double freq, size_t chan = 0);
|
||||||
|
|
||||||
|
/** set frequency correction */
|
||||||
|
virtual bool tuneRxOffset(double offset, size_t chan = 0);
|
||||||
|
|
||||||
/** set receive gain */
|
/** set receive gain */
|
||||||
double setRxGain(double dB, size_t chan = 0);
|
double setRxGain(double dB, size_t chan = 0);
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ void RadioInterfaceDiversity::pullBuffer()
|
|||||||
num = mRadio->readSamples(convertRecvBuffer,
|
num = mRadio->readSamples(convertRecvBuffer,
|
||||||
resamp_outchunk,
|
resamp_outchunk,
|
||||||
&overrun,
|
&overrun,
|
||||||
readTimestamp,
|
readTimestamp + shiftOffset,
|
||||||
&local_underrun);
|
&local_underrun);
|
||||||
if ((size_t) num != resamp_outchunk) {
|
if ((size_t) num != resamp_outchunk) {
|
||||||
LOG(ALERT) << "Receive error " << num;
|
LOG(ALERT) << "Receive error " << num;
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ void RadioInterfaceResamp::pullBuffer()
|
|||||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||||
resamp_outchunk,
|
resamp_outchunk,
|
||||||
&overrun,
|
&overrun,
|
||||||
readTimestamp,
|
readTimestamp + shiftOffset,
|
||||||
&local_underrun);
|
&local_underrun);
|
||||||
if (num_recv != (int) resamp_outchunk) {
|
if (num_recv != (int) resamp_outchunk) {
|
||||||
LOG(ALERT) << "Receive error " << num_recv;
|
LOG(ALERT) << "Receive error " << num_recv;
|
||||||
|
|||||||
@@ -74,25 +74,31 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
noiseVector::noiseVector(size_t size)
|
avgVector::avgVector(size_t max)
|
||||||
: std::vector<float>(size), itr(0)
|
: std::vector<float>(0), itr(0)
|
||||||
{
|
{
|
||||||
|
this->max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
float noiseVector::avg() const
|
float avgVector::avg() const
|
||||||
{
|
{
|
||||||
float val = 0.0;
|
float val = 0.0;
|
||||||
|
|
||||||
|
if (!size())
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
for (size_t i = 0; i < size(); i++)
|
for (size_t i = 0; i < size(); i++)
|
||||||
val += (*this)[i];
|
val += (*this)[i];
|
||||||
|
|
||||||
return val / (float) size();
|
return val / (float) size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool noiseVector::insert(float val)
|
bool avgVector::insert(float val)
|
||||||
{
|
{
|
||||||
if (!size())
|
if (size() < max) {
|
||||||
return false;
|
push_back(val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (itr >= this->size())
|
if (itr >= this->size())
|
||||||
itr = 0;
|
itr = 0;
|
||||||
@@ -102,6 +108,16 @@ bool noiseVector::insert(float val)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool avgVector::full() const
|
||||||
|
{
|
||||||
|
return size() >= max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void avgVector::reset()
|
||||||
|
{
|
||||||
|
resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
GSM::Time VectorQueue::nextTime() const
|
GSM::Time VectorQueue::nextTime() const
|
||||||
{
|
{
|
||||||
GSM::Time retVal;
|
GSM::Time retVal;
|
||||||
|
|||||||
@@ -46,14 +46,17 @@ private:
|
|||||||
GSM::Time mTime;
|
GSM::Time mTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
class noiseVector : std::vector<float> {
|
class avgVector : std::vector<float> {
|
||||||
public:
|
public:
|
||||||
noiseVector(size_t size = 0);
|
avgVector(size_t size = 0);
|
||||||
bool insert(float val);
|
bool insert(float val);
|
||||||
|
bool full() const;
|
||||||
float avg() const;
|
float avg() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t itr;
|
size_t itr;
|
||||||
|
size_t max;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VectorFIFO : public InterthreadQueue<radioVector> { };
|
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;
|
signalVector *sequence;
|
||||||
void *buffer;
|
void *buffer;
|
||||||
|
void *history;
|
||||||
float toa;
|
float toa;
|
||||||
complex gain;
|
complex gain;
|
||||||
};
|
};
|
||||||
@@ -111,6 +112,7 @@ struct PulseSequence {
|
|||||||
|
|
||||||
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||||
CorrelationSequence *gRACHSequence = NULL;
|
CorrelationSequence *gRACHSequence = NULL;
|
||||||
|
CorrelationSequence *gSCHSequence = NULL;
|
||||||
PulseSequence *GSMPulse = NULL;
|
PulseSequence *GSMPulse = NULL;
|
||||||
PulseSequence *GSMPulse1 = NULL;
|
PulseSequence *GSMPulse1 = NULL;
|
||||||
|
|
||||||
@@ -131,6 +133,7 @@ void sigProcLibDestroy()
|
|||||||
delete GMSKRotation1;
|
delete GMSKRotation1;
|
||||||
delete GMSKReverseRotation1;
|
delete GMSKReverseRotation1;
|
||||||
delete gRACHSequence;
|
delete gRACHSequence;
|
||||||
|
delete gSCHSequence;
|
||||||
delete GSMPulse;
|
delete GSMPulse;
|
||||||
delete GSMPulse1;
|
delete GSMPulse1;
|
||||||
|
|
||||||
@@ -139,6 +142,7 @@ void sigProcLibDestroy()
|
|||||||
GMSKReverseRotationN = NULL;
|
GMSKReverseRotationN = NULL;
|
||||||
GMSKReverseRotation1 = NULL;
|
GMSKReverseRotation1 = NULL;
|
||||||
gRACHSequence = NULL;
|
gRACHSequence = NULL;
|
||||||
|
gSCHSequence = NULL;
|
||||||
GSMPulse = NULL;
|
GSMPulse = NULL;
|
||||||
GSMPulse1 = NULL;
|
GSMPulse1 = NULL;
|
||||||
}
|
}
|
||||||
@@ -395,8 +399,10 @@ signalVector *convolve(const signalVector *x,
|
|||||||
break;
|
break;
|
||||||
case CUSTOM:
|
case CUSTOM:
|
||||||
if (start < h->size() - 1) {
|
if (start < h->size() - 1) {
|
||||||
head = h->size() - start;
|
if (x->getStart() < h->size() - 1) {
|
||||||
append = true;
|
head = h->size() - start;
|
||||||
|
append = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (start + len > x->size()) {
|
if (start + len > x->size()) {
|
||||||
tail = start + len - x->size();
|
tail = start + len - x->size();
|
||||||
@@ -1274,6 +1280,69 @@ release:
|
|||||||
return status;
|
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,
|
static float computePeakRatio(signalVector *corr,
|
||||||
int sps, float toa, complex amp)
|
int sps, float toa, complex amp)
|
||||||
{
|
{
|
||||||
@@ -1424,6 +1493,71 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
return 1;
|
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
|
* Normal burst detection
|
||||||
*
|
*
|
||||||
@@ -1717,6 +1851,11 @@ bool sigProcLibSetup(int sps)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!generateSCHSequence(1)) {
|
||||||
|
sigProcLibDestroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
generateDelayFilters();
|
generateDelayFilters();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ bool generateMidamble(int sps, int tsc);
|
|||||||
@return Success.
|
@return Success.
|
||||||
*/
|
*/
|
||||||
bool generateRACHSequence(int sps);
|
bool generateRACHSequence(int sps);
|
||||||
|
bool generateSCHSequence(int sps);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Energy detector, checks to see if received burst energy is above a threshold.
|
Energy detector, checks to see if received burst energy is above a threshold.
|
||||||
@@ -187,6 +188,17 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
complex *amplitude,
|
complex *amplitude,
|
||||||
float* TOA);
|
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.
|
Normal burst correlator, detector, channel estimator.
|
||||||
@param rxBurst The received GSM burst of interest.
|
@param rxBurst The received GSM burst of interest.
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ AC_TYPE_SIZE_T
|
|||||||
AC_HEADER_TIME
|
AC_HEADER_TIME
|
||||||
AC_C_BIGENDIAN
|
AC_C_BIGENDIAN
|
||||||
|
|
||||||
|
dnl checks for libraries
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
|
||||||
|
|
||||||
AC_ARG_WITH(usrp1, [
|
AC_ARG_WITH(usrp1, [
|
||||||
AS_HELP_STRING([--with-usrp1],
|
AS_HELP_STRING([--with-usrp1],
|
||||||
[enable USRP1 gnuradio based transceiver])
|
[enable USRP1 gnuradio based transceiver])
|
||||||
|
|||||||
Reference in New Issue
Block a user