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>
This commit is contained in:
parent
f31e4bb089
commit
c7f36c282a
|
@ -56,7 +56,8 @@ COMMON_SOURCES = \
|
|||
radioClock.cpp \
|
||||
sigProcLib.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp
|
||||
Transceiver.cpp \
|
||||
sch.c
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
|
@ -87,7 +88,9 @@ osmo_trx_LDADD = \
|
|||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA) \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
#include "Transceiver.h"
|
||||
#include <Logger.h>
|
||||
|
||||
extern "C" {
|
||||
#include "sch.h"
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
@ -405,6 +409,50 @@ bool Transceiver::detectSCH(TransceiverState *state,
|
|||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Detect normal burst training sequence midamble. Update equalization
|
||||
* state information and channel estimate if necessary. Equalization
|
||||
|
@ -491,19 +539,26 @@ 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);
|
||||
|
||||
switch (state->mode) {
|
||||
case TRX_MODE_MS_ACQUIRE:
|
||||
type = SCH;
|
||||
break;
|
||||
case TRX_MODE_MS_TRACK:
|
||||
if (!gsm_sch_check_fn(burst_time.FN()))
|
||||
goto release;
|
||||
type = SCH;
|
||||
break;
|
||||
case TRX_MODE_BTS:
|
||||
if ((type == TSC) || (type == RACH))
|
||||
break;
|
||||
|
@ -534,7 +589,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||
|
||||
/* Detect normal or RACH bursts */
|
||||
if (type == TSC)
|
||||
success = detectTSC(state, *burst, amp, toa, time);
|
||||
success = detectTSC(state, *burst, amp, toa, burst_time);
|
||||
else if (type == RACH)
|
||||
success = detectRACH(state, *burst, amp, toa);
|
||||
else if (type == SCH)
|
||||
|
@ -551,10 +606,32 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||
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)) {
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
|
|
|
@ -155,6 +155,8 @@ private:
|
|||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
bool decodeSCH(SoftVector *burst, GSM::Time *time);
|
||||
|
||||
/** Detect normal bursts */
|
||||
bool detectTSC(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
|
@ -238,6 +240,7 @@ public:
|
|||
TRX_MODE_OFF,
|
||||
TRX_MODE_BTS,
|
||||
TRX_MODE_MS_ACQUIRE,
|
||||
TRX_MODE_MS_TRACK,
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
|
@ -886,7 +886,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
|
||||
|
|
|
@ -29,6 +29,35 @@ void RadioClock::set(const GSM::Time& wTime)
|
|||
mLock.unlock();
|
||||
}
|
||||
|
||||
void RadioClock::adjust(GSM::Time& wOffset)
|
||||
{
|
||||
int tn_diff, fn_diff = 0;
|
||||
|
||||
mLock.lock();
|
||||
|
||||
/* Modulo TN adustment */
|
||||
tn_diff = mClock.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 += mClock.FN() + wOffset.FN();
|
||||
if (fn_diff < 0)
|
||||
fn_diff += GSM::gHyperframe;
|
||||
else if ((unsigned) fn_diff >= GSM::gHyperframe)
|
||||
fn_diff = fn_diff - GSM::gHyperframe;
|
||||
|
||||
mClock = GSM::Time(fn_diff, tn_diff);
|
||||
updateSignal.signal();
|
||||
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void RadioClock::incTN()
|
||||
{
|
||||
mLock.lock();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
class RadioClock {
|
||||
public:
|
||||
void set(const GSM::Time& wTime);
|
||||
void adjust(GSM::Time &wOffset);
|
||||
void incTN();
|
||||
GSM::Time get();
|
||||
void wait();
|
||||
|
|
|
@ -157,6 +157,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);
|
||||
|
|
|
@ -102,6 +102,9 @@ 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);
|
||||
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
#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)
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#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);
|
||||
|
||||
int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
|
||||
|
||||
#endif /* _SCH_H_ */
|
|
@ -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])
|
||||
|
|
Loading…
Reference in New Issue