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:
Thomas Tsou 2014-04-16 22:24:00 -04:00
parent f31e4bb089
commit c7f36c282a
11 changed files with 313 additions and 9 deletions

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

View File

@ -155,6 +155,8 @@ private:
signalVector &burst,
complex &amp, 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:

View File

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

View File

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

View File

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

View File

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

View File

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

156
Transceiver52M/sch.c Normal file
View File

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

24
Transceiver52M/sch.h Normal file
View File

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

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