From 14bb9c923d8c967e8ce114a868923e9566be51e5 Mon Sep 17 00:00:00 2001 From: Thomas Tsou Date: Wed, 16 Apr 2014 23:10:12 -0400 Subject: 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 --- Transceiver52M/Transceiver.cpp | 40 +++++++++++++++++++++++-- Transceiver52M/Transceiver.h | 7 ++++- Transceiver52M/UHDDevice.cpp | 50 +++++++++++++++++++++++-------- Transceiver52M/radioDevice.h | 3 ++ Transceiver52M/radioInterface.cpp | 4 +++ Transceiver52M/radioInterface.h | 3 ++ Transceiver52M/radioVector.cpp | 28 +++++++++++++---- Transceiver52M/radioVector.h | 7 +++-- Transceiver52M/sch.c | 63 +++++++++++++++++++++++++++++++++++++++ Transceiver52M/sch.h | 2 ++ 10 files changed, 182 insertions(+), 25 deletions(-) diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp index c3446f8..60be8d2 100644 --- a/Transceiver52M/Transceiver.cpp +++ b/Transceiver52M/Transceiver.cpp @@ -45,10 +45,11 @@ using namespace GSM; /* 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), - mode(Transceiver::TRX_MODE_OFF) + : 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; @@ -56,6 +57,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; @@ -452,6 +454,34 @@ bool Transceiver::decodeSCH(SoftVector *burst, GSM::Time *time) 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 @@ -614,6 +644,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI, /* 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) { @@ -635,11 +666,14 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI, 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; } diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h index 170af77..8f3e47b 100644 --- a/Transceiver52M/Transceiver.h +++ b/Transceiver52M/Transceiver.h @@ -80,7 +80,11 @@ 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; @@ -156,6 +160,7 @@ private: complex &, float &toa); bool decodeSCH(SoftVector *burst, GSM::Time *time); + bool correctFCCH(TransceiverState *state, signalVector *burst); /** Detect normal bursts */ bool detectTSC(TransceiverState *state, diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp index e943e73..3b9012a 100644 --- a/Transceiver52M/UHDDevice.cpp +++ b/Transceiver52M/UHDDevice.cpp @@ -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 tx_gains, rx_gains; - std::vector tx_freqs, rx_freqs; + std::vector tx_freqs, rx_freqs; size_t tx_spp, rx_spp; bool started; @@ -1004,7 +1010,7 @@ bool uhd_device::updateAlignment(TIMESTAMP timestamp) uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) { double rf_spread, rf_freq; - std::vector freqs; + std::vector freqs; uhd::tune_request_t treq(freq); if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) { @@ -1023,17 +1029,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 +1056,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 +1074,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 +1101,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 +1132,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 +1142,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() diff --git a/Transceiver52M/radioDevice.h b/Transceiver52M/radioDevice.h index 6273bcc..c77869e 100644 --- a/Transceiver52M/radioDevice.h +++ b/Transceiver52M/radioDevice.h @@ -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; diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp index 317d2b4..75266dd 100644 --- a/Transceiver52M/radioInterface.cpp +++ b/Transceiver52M/radioInterface.cpp @@ -172,6 +172,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() { diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h index 7798e5c..ebf8f38 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -111,6 +111,9 @@ public: /** 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); diff --git a/Transceiver52M/radioVector.cpp b/Transceiver52M/radioVector.cpp index 2e3af9d..ea354b4 100644 --- a/Transceiver52M/radioVector.cpp +++ b/Transceiver52M/radioVector.cpp @@ -74,25 +74,31 @@ bool radioVector::setVector(signalVector *vector, size_t chan) return true; } -noiseVector::noiseVector(size_t size) - : std::vector(size), itr(0) +avgVector::avgVector(size_t max) + : std::vector(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; diff --git a/Transceiver52M/radioVector.h b/Transceiver52M/radioVector.h index 0566123..7e236b6 100644 --- a/Transceiver52M/radioVector.h +++ b/Transceiver52M/radioVector.h @@ -46,14 +46,17 @@ private: GSM::Time mTime; }; -class noiseVector : std::vector { +class avgVector : std::vector { 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 { }; diff --git a/Transceiver52M/sch.c b/Transceiver52M/sch.c index 3cc2232..67db259 100644 --- a/Transceiver52M/sch.c +++ b/Transceiver52M/sch.c @@ -62,6 +62,9 @@ const struct osmo_crc16gen_code gsm0503_sch_crc10 = { #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; @@ -154,3 +157,63 @@ int gsm_sch_decode(uint8_t *info, sbit_t *data) 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; + } +} diff --git a/Transceiver52M/sch.h b/Transceiver52M/sch.h index 1e31968..ec2d0e0 100644 --- a/Transceiver52M/sch.h +++ b/Transceiver52M/sch.h @@ -19,6 +19,8 @@ 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_ */ -- cgit v1.2.3