diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index d0a20f4f..2255382f 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -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 diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp index c6042eb7..c3446f82 100644 --- a/Transceiver52M/Transceiver.cpp +++ b/Transceiver52M/Transceiver.cpp @@ -25,6 +25,10 @@ #include "Transceiver.h" #include +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); diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h index c167519f..170af773 100644 --- a/Transceiver52M/Transceiver.h +++ b/Transceiver52M/Transceiver.h @@ -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: diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp index bdeb1b57..e943e73d 100644 --- a/Transceiver52M/UHDDevice.cpp +++ b/Transceiver52M/UHDDevice.cpp @@ -886,7 +886,7 @@ int uhd_device::readSamples(std::vector &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 diff --git a/Transceiver52M/radioClock.cpp b/Transceiver52M/radioClock.cpp index 710018a4..d9d5229f 100644 --- a/Transceiver52M/radioClock.cpp +++ b/Transceiver52M/radioClock.cpp @@ -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(); diff --git a/Transceiver52M/radioClock.h b/Transceiver52M/radioClock.h index 9c35c447..4ce84163 100644 --- a/Transceiver52M/radioClock.h +++ b/Transceiver52M/radioClock.h @@ -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(); diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp index d55761ac..317d2b4c 100644 --- a/Transceiver52M/radioInterface.cpp +++ b/Transceiver52M/radioInterface.cpp @@ -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); diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h index 36bc6eaf..7798e5c0 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -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); diff --git a/Transceiver52M/sch.c b/Transceiver52M/sch.c new file mode 100644 index 00000000..3cc22325 --- /dev/null +++ b/Transceiver52M/sch.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/Transceiver52M/sch.h b/Transceiver52M/sch.h new file mode 100644 index 00000000..1e31968a --- /dev/null +++ b/Transceiver52M/sch.h @@ -0,0 +1,24 @@ +#ifndef _SCH_H_ +#define _SCH_H_ + +#include + +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_ */ diff --git a/configure.ac b/configure.ac index ae4ea3bd..263486ef 100644 --- a/configure.ac +++ b/configure.ac @@ -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])