Compare commits

...

11 Commits
0.2.0 ... ms

Author SHA1 Message Date
Tom Tsou
774a06369a ms: Move clock indications generation to lower drive loop
During BTS operation, active downlink bursts drive the clock indication,
but the flow of bursts is not present in MS mode. Shift the indication
trigger to the lower non-blocking loop with FN mod 100 as the interval.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-10-31 13:26:50 -07:00
Thomas Tsou
94ce835050 ms: Specify SETRXMASK argument with hex mask
Use a 64-bit hex value as the mask specifier because the integer string
is rather unwieldly. Fix 64-bit register handling on the mask check.

Receive burst masking is only available on in MS tracking mode (post SCH
acquisition).

Modulus values greater than 64 remain unsupported.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-08 12:11:21 -07:00
Thomas Tsou
09befd7a06 ms: Add command line option and allow negative Tx/Rx offset
The +/- 3 frame offset for Tx/Rx is setup in the RadioInterface
constructor, so we need to know whether we are operating in MS or BTS
mode at the very beginning.

For MS mode, handle negative frame offsets, which would previously cause
an assertion error.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-07 19:40:54 -07:00
Thomas Tsou
1303376ad1 ms: Enable synchronized uplink bursts
Extend the measured SCH timing offset from the downlink to the uplink
path. In order to absorb the frame timing adjustment and remove the
potential of thread contention during the change, combine the lower
FIFO threads into single drive loop.

Force timing changes through to the UHD interface with stream flags
triggered through the updateAlignment() call.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-07 19:35:25 -07:00
Thomas Tsou
25021dfe5a ms: Add SETRXMASK command
Create 64-bit multiframe masks for each physical slot. These are set
through the SETRXMASK command with first and second arguemnts timeslot
and mask respectively.

Modulus value of 102 is currently not handled and will cause all receive
bursts on that slot to be disabled in MS tracking mode.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 19:16:40 -07:00
Thomas Tsou
e287598e6b ms: Add BSIC request command
The GETBSIC command returns 1 if no SCH has been decoded, and 0 with
the BSIC value if SCH decoding has been successful.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 19:16:20 -07:00
Thomas Tsou
98b1af896c Transceiver52M: Use multiple SCH correlation ranges
Setup two SCH detection ranges - full search and narrow search. Full
search correlates the full burst length to find for SCH detection.
Narrow uses a reduced search range, which is the same as that used for
RACH detection.

Narrow searching is enabled after the SCH is successfully decoded and
the MS is locked to the frame timing.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 10:35:29 -07:00
Thomas Tsou
14bb9c923d Transceiver52M: Add FCCH based frequency correction
Enable frequency detection and correction by buffering the previous
frame to allow FCCH measurement and compensation after frame timing is
locked using the SCH. When the SCH is detected and symbol timing
matched, measure the FCCH burst from one frame prior and compensate by
baseband tuning the DDC on the device. Avoid appying frequency
corrections to the RF portion due to possible tuning delays, which is
not an issue with DDC tuning.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 10:35:29 -07:00
Thomas Tsou
c7f36c282a Transceiver52M: Decode SCH and adjust GSM clock
When in MS acquisition mode, attempt to decode SCH and establish the BTS
frame timing. Lock the transceiver GSM clock to the BTS by adjusting the
clock value by the measured burst-SCH offset.

Add tracking state, TRX_MODE_MS_TRACKING, which continues to detect and
decode the SCH with timing tracking, but only on SCH poitions within the
51 multiframe.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 10:35:29 -07:00
Thomas Tsou
f31e4bb089 Transceiver52M: Setup TRX mode for MS acquisition
On receipt of a SYNC command on the socket interface, set the mode to
TRX_MODE_MS_ACQUIRE, which attempts to synchronize against a remote BTS
signal by detecting the SCH burst and timing offset. The transceiver
defaults to TRX_MODE_OFF, which implies no BTS or MS capability has been
enabled.

There is currently no command to enable the BTS mode. The MS mode also
disables uplink transmission since no such capability is implemented.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 10:35:29 -07:00
Thomas Tsou
1189019c30 Transceiver52M: Add SCH detection capability
Use similar approach for detecting normal and RACH bursts, but apply a
sample shift after detection in order to gradually zero the measured
timing offset. SCH synchronization sequence and setup are added similar
to RACH detection with the main difference, aside being the SCH runs
full length of the burst. History is also added to accommodate full
length burst correlation.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-10-06 10:35:29 -07:00
21 changed files with 843 additions and 92 deletions

View File

@@ -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)
{

View File

@@ -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. */
//@{

View File

@@ -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

View File

@@ -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, &amp, &toa);
}
/* Detect SCH synchronization sequence within a burst */
bool Transceiver::detectSCH(TransceiverState *state,
signalVector &burst,
complex &amp, 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, &amp, &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();
}

View File

@@ -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 &amp, float &toa);
bool detectSCH(TransceiverState *state,
signalVector &burst,
complex &amp, 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 *);

View File

@@ -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()

View File

@@ -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)) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
View 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
View 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_ */

View File

@@ -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;

View File

@@ -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.

View File

@@ -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])