aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M
diff options
context:
space:
mode:
Diffstat (limited to 'Transceiver52M')
-rw-r--r--Transceiver52M/Makefile.am11
-rw-r--r--Transceiver52M/Transceiver2.cpp1198
-rw-r--r--Transceiver52M/Transceiver2.h258
-rw-r--r--Transceiver52M/device/common/radioDevice.h3
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.cpp71
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.h9
-rw-r--r--Transceiver52M/fbsb.cpp148
-rw-r--r--Transceiver52M/fbsb.h0
-rw-r--r--Transceiver52M/l1if.cpp124
-rw-r--r--Transceiver52M/l1if.h74
-rw-r--r--Transceiver52M/osmo-trx-org.cpp718
-rw-r--r--Transceiver52M/osmo-trx.cpp801
-rw-r--r--Transceiver52M/radioClock.cpp35
-rw-r--r--Transceiver52M/radioClock.h3
-rw-r--r--Transceiver52M/radioInterface.cpp33
-rw-r--r--Transceiver52M/radioInterface.h10
-rw-r--r--Transceiver52M/radioInterfaceMulti.cpp2
-rw-r--r--Transceiver52M/radioInterfaceResamp.cpp2
-rw-r--r--Transceiver52M/radioVector.cpp19
-rw-r--r--Transceiver52M/radioVector.h3
-rw-r--r--Transceiver52M/sch.c299
-rw-r--r--Transceiver52M/sch.h27
-rw-r--r--Transceiver52M/sigProcLib.cpp138
-rw-r--r--Transceiver52M/sigProcLib.h11
24 files changed, 3429 insertions, 568 deletions
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index 7dad159..a205d6b 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -30,6 +30,8 @@ AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_C
noinst_LTLIBRARIES = libtransceiver_common.la
COMMON_SOURCES = \
+ fbsb.cpp \
+ l1if.cpp \
radioInterface.cpp \
radioVector.cpp \
radioClock.cpp \
@@ -37,10 +39,12 @@ COMMON_SOURCES = \
sigProcLib.cpp \
signalVector.cpp \
Transceiver.cpp \
+ Transceiver2.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
- proto_trxd.c
+ proto_trxd.c \
+ sch.c
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -57,6 +61,7 @@ noinst_HEADERS = \
sigProcLib.h \
signalVector.h \
Transceiver.h \
+ Transceiver2.h \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
@@ -68,6 +73,7 @@ COMMON_LDADD = \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) \
+ $(TRXCON_LA) \
$(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
@@ -81,7 +87,8 @@ osmo_trx_uhd_SOURCES = osmo-trx.cpp
osmo_trx_uhd_LDADD = \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
- $(UHD_LIBS)
+ $(UHD_LIBS) \
+ $(TRXCON_LA)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
endif
diff --git a/Transceiver52M/Transceiver2.cpp b/Transceiver52M/Transceiver2.cpp
new file mode 100644
index 0000000..595508e
--- /dev/null
+++ b/Transceiver52M/Transceiver2.cpp
@@ -0,0 +1,1198 @@
+/*
+* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "osmocom/core/bits.h"
+#include <stdio.h>
+#include <Logger.h>
+#include "Transceiver2.h"
+
+extern "C" {
+#include "sch.h"
+}
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "l1if.h"
+
+using namespace GSM;
+
+#define USB_LATENCY_INTRVL 10,0
+
+#if USE_UHD
+# define USB_LATENCY_MIN 6,7
+#else
+# 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), mFreqOffsets(FREQ_CNT), mode(Transceiver2::TRX_MODE_OFF)
+{
+ for (int i = 0; i < 8; i++) {
+ chanType[i] = Transceiver2::NONE;
+ fillerModulus[i] = 26;
+ prevFrame[i] = NULL;
+
+ for (int n = 0; n < 102; n++)
+ fillerTable[n][i] = NULL;
+ }
+}
+
+TransceiverState::~TransceiverState()
+{
+ for (int i = 0; i < 8; i++) {
+ for (int n = 0; n < 102; n++)
+ delete fillerTable[n][i];
+ }
+}
+
+void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
+{
+ signalVector *filler;
+
+ for (int i = 0; i < 102; i++) {
+ if (fill)
+ filler = new signalVector(*burst);
+ else
+ filler = new signalVector(burst->size());
+
+ fillerTable[i][slot] = filler;
+ }
+}
+
+Transceiver2::Transceiver2(int wBasePort,
+ const char *TRXAddress,
+ size_t wSPS, size_t wChans,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface)
+ : rx_sps(4), tx_sps(4), mAddr(TRXAddress),
+ mTransmitLatency(wTransmitLatency),
+ mRadioInterface(wRadioInterface), mChans(wChans),
+ mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0),
+ mBSIC(-1)
+{
+ GSM::Time startTime(random() % gHyperframe,0);
+
+ mLowerLoopThread = new Thread(32768);
+
+ mTransmitDeadlineClock = startTime;
+ mLastClockUpdateTime = startTime;
+ mLatencyUpdateTime = startTime;
+ mRadioInterface->getClock()->set(startTime);
+
+ txFullScale = mRadioInterface->fullScaleInputValue();
+ rxFullScale = mRadioInterface->fullScaleOutputValue();
+
+ for (int i = 0; i < 8; i++)
+ mRxSlotMask[i] = 0;
+}
+
+Transceiver2::~Transceiver2()
+{
+ stop();
+
+ sigProcLibDestroy();
+
+ for (size_t i = 0; i < mChans; i++) {
+ mTxPriorityQueues[i].clear();
+ }
+}
+
+bool Transceiver2::init(bool filler)
+{
+
+ if (!mChans) {
+ LOG(ALERT) << "No channels assigned";
+ return false;
+ }
+
+ if (!sigProcLibSetup()) {
+ LOG(ALERT) << "Failed to initialize signal processing library";
+ return false;
+ }
+
+ mControlServiceLoopThreads.resize(mChans);
+ mTxPriorityQueueServiceLoopThreads.resize(mChans);
+ mRxServiceLoopThreads.resize(mChans);
+
+ mTxPriorityQueues.resize(mChans);
+ mReceiveFIFO.resize(mChans);
+ mStates.resize(mChans);
+
+ /* Filler table retransmissions - support only on channel 0 */
+ if (filler)
+ mStates[0].mRetrans = true;
+
+
+ for (size_t i = 0; i < mChans; i++) {
+ mControlServiceLoopThreads[i] = new Thread(32768);
+ mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
+ mRxServiceLoopThreads[i] = new Thread(32768);
+
+ for (size_t n = 0; n < 8; n++) {
+ // burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), tx_sps);
+ // scaleVector(*burst, txFullScale);
+ // mStates[i].init(n, burst, filler && !i);
+ // delete burst;
+ }
+ }
+
+ return true;
+}
+
+void Transceiver2::addRadioVector(size_t chan, BitVector &bits,
+ int RSSI, GSM::Time &wTime)
+{
+ signalVector *burst;
+ radioVector *radio_burst;
+
+ if (chan >= mTxPriorityQueues.size()) {
+ LOG(ALERT) << "Invalid channel " << chan;
+ return;
+ }
+
+ if (wTime.TN() > 7) {
+ LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
+ return;
+ }
+
+ if (mStates[0].mode != TRX_MODE_BTS)
+ return;
+
+ burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), tx_sps);
+ scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
+
+ radio_burst = new radioVector(wTime, burst);
+
+ mTxPriorityQueues[chan].write(radio_burst);
+}
+
+void Transceiver2::updateFillerTable(size_t chan, radioVector *burst)
+{
+ int TN, modFN;
+ TransceiverState *state = &mStates[chan];
+
+ TN = burst->getTime().TN();
+ modFN = burst->getTime().FN() % state->fillerModulus[TN];
+
+ delete state->fillerTable[modFN][TN];
+ state->fillerTable[modFN][TN] = burst->getVector();
+ burst->setVector(NULL);
+}
+
+void Transceiver2::pushRadioVector(GSM::Time &nowTime)
+{
+ int TN, modFN;
+ radioVector *burst;
+ TransceiverState *state;
+ std::vector<signalVector *> bursts(mChans);
+ std::vector<bool> zeros(mChans, false);
+ std::vector<bool> filler(mChans, true);
+
+ for (size_t i = 0; i < mChans; i ++) {
+ state = &mStates[i];
+
+ while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
+ LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
+ if (state->mRetrans)
+ updateFillerTable(i, burst);
+ delete burst;
+ }
+
+ TN = nowTime.TN();
+ modFN = nowTime.FN() % state->fillerModulus[TN];
+
+ bursts[i] = state->fillerTable[modFN][TN];
+ if (state->mode == TRX_MODE_BTS)
+ zeros[i] = state->chanType[TN] == NONE;
+
+ if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
+ bursts[i] = burst->getVector();
+
+ if (state->mRetrans) {
+ updateFillerTable(i, burst);
+ } else {
+ burst->setVector(NULL);
+ filler[i] = false;
+ }
+
+ delete burst;
+ }
+ }
+
+ mRadioInterface->driveTransmitRadio(bursts, zeros);
+
+ for (size_t i = 0; i < mChans; i++) {
+ if (!filler[i])
+ delete bursts[i];
+ }
+}
+
+void Transceiver2::setModulus(size_t timeslot, size_t chan)
+{
+ TransceiverState *state = &mStates[chan];
+
+ switch (state->chanType[timeslot]) {
+ case NONE:
+ case I:
+ case II:
+ case III:
+ case FILL:
+ state->fillerModulus[timeslot] = 26;
+ break;
+ case IV:
+ case VI:
+ case V:
+ state->fillerModulus[timeslot] = 51;
+ break;
+ //case V:
+ case VII:
+ state->fillerModulus[timeslot] = 102;
+ break;
+ case XIII:
+ state->fillerModulus[timeslot] = 52;
+ break;
+ default:
+ break;
+ }
+}
+
+
+CorrType Transceiver2::expectedCorrType(GSM::Time currTime,
+ size_t chan)
+{
+ TransceiverState *state = &mStates[chan];
+ 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;
+ break;
+ case FILL:
+ return IDLE;
+ break;
+ case I:
+ return TSC;
+ /*if (burstFN % 26 == 25)
+ return IDLE;
+ else
+ return TSC;*/
+ break;
+ case II:
+ return TSC;
+ break;
+ case III:
+ return TSC;
+ break;
+ case IV:
+ case VI:
+ return RACH;
+ break;
+ case V: {
+ int mod51 = burstFN % 51;
+ if ((mod51 <= 36) && (mod51 >= 14))
+ return RACH;
+ else if ((mod51 == 4) || (mod51 == 5))
+ return RACH;
+ else if ((mod51 == 45) || (mod51 == 46))
+ return RACH;
+ else
+ return TSC;
+ break;
+ }
+ case VII:
+ if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
+ return IDLE;
+ else
+ return TSC;
+ break;
+ case XIII: {
+ int mod52 = burstFN % 52;
+ if ((mod52 == 12) || (mod52 == 38))
+ return RACH;
+ else if ((mod52 == 25) || (mod52 == 51))
+ return IDLE;
+ else
+ return TSC;
+ break;
+ }
+ case LOOPBACK:
+ if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
+ return IDLE;
+ else
+ return TSC;
+ break;
+ default:
+ return OFF;
+ break;
+ }
+}
+
+
+/* Detect SCH synchronization sequence within a burst */
+bool Transceiver2::detectSCH(TransceiverState *state,
+ signalVector &burst,
+ struct estim_burst_params *ebp)
+{
+ int shift;
+ sch_detect_type full;
+ float mag, threshold = 5.0;
+
+ full = (state->mode == TRX_MODE_MS_TRACK) ?
+ sch_detect_type::SCH_DETECT_NARROW : sch_detect_type::SCH_DETECT_FULL;
+
+ if (!detectSCHBurst(burst, threshold, rx_sps, full, ebp))
+ return false;
+
+ std::clog << "SCH : Timing offset " << ebp->toa << " symbols" << std::endl;
+
+ mag = fabsf(ebp->toa);
+ if (mag < 1.0f)
+ return true;
+
+ shift = (int) (mag / 2.0f);
+ if (!shift)
+ shift++;
+
+ shift = ebp->toa > 0 ? shift : -shift;
+ std::clog << "SCH : shift -> " << shift << " symbols" << std::endl;
+ mRadioInterface->applyOffset(shift);
+ return false;
+}
+
+#define SCH_BIT_SCALE 64
+
+/* Decode SCH burst */
+bool Transceiver2::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::clog << "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;
+ mTSC = mBSIC & 0x7;
+
+// std::clog << "SCH : Decoded values" << std::endl;
+// std::clog << " BSIC: " << sch.bsic << std::endl;
+// std::clog << " T1 : " << sch.t1 << std::endl;
+// std::clog << " T2 : " << sch.t2 << std::endl;
+// std::clog << " T3p : " << sch.t3p << std::endl;
+// std::clog << " FN : " << gsm_sch_to_fn(&sch) << std::endl;
+
+ fn = gsm_sch_to_fn(&sch);
+ if (fn < 0) {
+ std::clog << "SCH : Failed to convert FN " << std::endl;
+ return false;
+ }
+
+ time->FN(fn);
+ time->TN(0);
+ } else {
+ std::clog << "Invalid SCH decode!!" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+#define FCCH_OFFSET_LIMIT 5e3
+#define FCCH_ADJUST_LIMIT 20.0
+
+/* Apply FCCH frequency correction */
+bool Transceiver2::correctFCCH(TransceiverState *state, signalVector *burst)
+{
+ double offset;
+
+ if (!burst)
+ return false;
+
+ offset = gsm_fcch_offset((float *) burst->begin(), burst->size());
+ //std::cout << "XXXX FCCH: Frequency offset " << offset << " Hz" << std::endl;
+ if (offset > FCCH_OFFSET_LIMIT)
+ return false;
+
+ state->mFreqOffsets.insert(offset);
+
+ if (state->mFreqOffsets.full()) {
+ double avg = state->mFreqOffsets.avg();
+ std::clog << "FCCH: Frequency offset " << avg << " Hz" << std::endl;
+
+ if (fabs(avg) > FCCH_ADJUST_LIMIT) {
+ mRadioInterface->tuneRxOffset(-avg);
+ state->mFreqOffsets.reset();
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Pull bursts from the FIFO and handle according to the slot
+ * and burst correlation type. Equalzation is currently disabled.
+ */
+SoftVector *Transceiver2::pullRadioVector(GSM::Time &wTime, int &RSSI,
+ int &timingOffset, size_t chan) __attribute__((optnone))
+{
+ struct estim_burst_params ebp;
+ int rc;
+ float pow, max = -1.0, avg = 1.0;
+ int max_i = -1;
+ signalVector *burst;
+ 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 */
+ burst_time = radio_burst->getTime();
+
+#if 0
+ if (state->mode == TRX_MODE_MS_ACQUIRE) {
+ switch(fbsb_acq_buf.s.load()) {
+ case fbsb_par::fbsb_state::IDLE:
+ case fbsb_par::fbsb_state::INIT:
+ fbsb_acq_buf.s.store(fbsb_par::fbsb_state::ACQ);
+ case fbsb_par::fbsb_state::ACQ:
+ fbsb_acq_buf.take(radio_burst->getVector()->begin(), radio_burst->getVector()->size(), burst_time);
+
+ if(!fbsb_acq_buf.done()) {
+ delete radio_burst;
+ return nullptr;
+ }
+ fbsb_acq_buf.s.store(fbsb_par::fbsb_state::ACQ_COMPL);
+// break;
+ case fbsb_par::fbsb_state::ACQ_COMPL:
+
+
+ {
+ complex famp = 0;
+ int found_index;
+ auto foundat = fbsb_acq_buf.fcch(&famp, &found_index, false);
+ std::cerr << "@ " << found_index << std::endl;
+ foundat = found_index;
+
+ auto framelen = (3 + 142 + 3 + 8.25); // 1sps!;
+ int searchbegin = 8*framelen-20;
+ int searchend = 10*framelen+20;
+
+ if(famp.abs() < 2000 || foundat+searchend > fbsb_acq_buf.sz()) {
+ mReceiveFIFO[chan]->clear();
+ delete radio_burst;
+ fbsb_acq_buf.reset();
+ fbsb_acq_buf.s.store(fbsb_par::fbsb_state::ACQ);
+ return nullptr;
+ }
+ correctFCCH_raw(state, &fbsb_acq_buf.fbsb_buf[foundat], 98);
+
+ burst = new signalVector(searchend-searchbegin, GSM::gRACHSynchSequence.size());
+// burst = new signalVector(fbsb_acq_buf.fbsb_buf, foundat+searchbegin, searchend-searchbegin);
+ memcpy(burst->begin(), &fbsb_acq_buf.fbsb_buf[foundat+searchbegin], (searchend-searchbegin) * sizeof(complex));
+ success = detectSCH(state, *burst, amp, toa, 0); // will "fail" if sample adjustment is required
+ std::cerr << "###detected sch: " << success << "at toa " << toa << std::endl;
+ mReceiveFIFO[chan]->clear();
+ delete radio_burst;
+
+ if(toa > 0) {
+ int how_many_ts = (found_index+searchbegin)/framelen;
+// int how_many_fn = how_many_ts/8;
+ auto t = fbsb_acq_buf.rcvClock[how_many_ts];
+
+ diff_time = GSM::Time(sch_time.FN() - t.FN(),-t.TN());
+ mRadioInterface->adjustClock(diff_time);
+ mTransmitDeadlineClock = RadioClock::adjust(
+ mTransmitDeadlineClock,
+ diff_time);
+ }
+ if(!success) {
+ fbsb_acq_buf.reset();
+ fbsb_acq_buf.s.store(fbsb_par::fbsb_state::ACQ);
+ }
+ else
+ fbsb_acq_buf.s.store(fbsb_par::fbsb_state::DONE);
+ return nullptr;
+ }
+ case fbsb_par::fbsb_state::DONE:
+ break;
+ default:
+ // fbsb_acq_buf.s = fbsb_par::fbsb_state::WAIT;
+ return nullptr;
+ break;
+ /* no-op */
+ }
+
+ }
+#endif
+ 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()) && burst_time.TN() == 0)
+ type = SCH;
+ else if(burst_time.TN() == 0 && !gsm_fcch_check_fn(burst_time.FN())) // all ts0, but not fcch or sch..
+ type = TSC;
+ 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 */
+ for (size_t i = 0; i < radio_burst->chans(); i++) {
+ float pow = energyDetect(*radio_burst->getVector(i), 20 * rx_sps);
+ if (pow > max) {
+ max = pow;
+ max_i = i;
+ }
+ avg += pow;
+ }
+
+ if (max_i < 0) {
+ LOG(ALERT) << "Received empty burst";
+ goto release;
+ }
+
+ /* Average noise on diversity paths and update global levels */
+ burst = radio_burst->getVector(max_i);
+ avg = sqrt(avg / radio_burst->chans());
+
+
+ /* Detect normal or RACH bursts */
+ if (type == SCH) {
+ rc = detectSCH(state, *burst, &ebp);
+ rc = rc > 0 ? rc : -1;
+ } else {
+ rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, rx_sps, type, mMaxExpectedDelay, &ebp);
+ if(rc > 0) {
+ type = (CorrType)rc;
+ }
+ }
+
+ if (rc < 0)
+ goto release;
+
+
+ /* Ignore noise threshold on MS mode for now */
+ //if ((type == SCH) || (avg - state->mNoiseLev > 0.0))
+ bits = demodAnyBurst(*burst, type, rx_sps, &ebp);
+
+ /* MS: Decode SCH and adjust GSM clock */
+ if ((type != TSC) &&
+ ((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;
+
+ burst_time = sch_time; //sch bits passed up might have a different time, so upper layers complain
+ std::clog << "SCH : Locking GSM clock " << std::endl;
+ } else {
+ //std::clog << "SCH : Read SCH at FN " << burst_time.FN() << " FN51 " << burst_time.FN() % 51 << std::endl;
+ wTime = burst_time;
+ RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
+ timingOffset = (int) round(ebp.toa * 256.0 / rx_sps);
+ return bits;
+ }
+ }
+ else
+ std::clog << "SCH : FAIL!!!!! SCH at FN " << burst_time.FN() << std::endl;
+
+ goto release;
+ }
+
+ wTime = burst_time;
+ RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
+ timingOffset = (int) round(ebp.toa * 256.0 / rx_sps);
+
+ 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 Transceiver2::start()
+{
+ TransceiverChannel *chan;
+
+ for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
+ chan = new TransceiverChannel(this, i);
+ mControlServiceLoopThreads[i]->start((void * (*)(void*))
+ ControlServiceLoopAdapter, (void*) chan);
+ }
+}
+
+void Transceiver2::stop()
+{
+
+ if (!mOn)
+ return;
+
+ LOG(NOTICE) << "Stopping the transceiver";
+ mLowerLoopThread->cancel();
+ mLowerLoopThread->join();
+ delete mLowerLoopThread;
+
+
+ for (size_t i = 0; i < mChans; i++) {
+ mRxServiceLoopThreads[i]->cancel();
+ mTxPriorityQueueServiceLoopThreads[i]->cancel();
+ }
+
+ LOG(INFO) << "Stopping the device";
+ mRadioInterface->stop();
+
+ for (size_t i = 0; i < mChans; i++) {
+ mRxServiceLoopThreads[i]->join();
+ mTxPriorityQueueServiceLoopThreads[i]->join();
+ delete mRxServiceLoopThreads[i];
+ delete mTxPriorityQueueServiceLoopThreads[i];
+
+ mTxPriorityQueues[i].clear();
+ }
+
+ mOn = false;
+ LOG(NOTICE) << "Transceiver stopped";
+}
+
+
+void Transceiver2::reset()
+{
+ for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
+ mTxPriorityQueues[i].clear();
+}
+
+
+void Transceiver2::driveControl(size_t chan)
+{
+ //char response[MAX_PACKET_LENGTH];
+
+ // check control socket
+// char buffer[MAX_PACKET_LENGTH];
+// int msgLen = -1;
+// buffer[0] = '\0';
+
+
+//// msgLen = mCtrlSockets[chan]->read(buffer);
+
+// if (msgLen < 1) {
+// return;
+// }
+
+ auto m = pop_c();
+ if(!m)
+ return;
+
+auto response = (TRX_C*)malloc(sizeof(TRX_C));
+response->cmd[0] = '\0';
+commandhandler(m->cmd, response->cmd, chan);
+free(m);
+std::clog << "response is " << response->cmd << std::endl;
+push_c(response);
+ //mCtrlSockets[chan]->write(response, strlen(response) + 1);
+}
+
+void Transceiver2::commandhandler(char* buffer, char* response, int chan)
+{
+ int MAX_PACKET_LENGTH = TRXC_BUF_SIZE;
+
+ char cmdcheck[4];
+ char command[MAX_PACKET_LENGTH];
+
+
+ sscanf(buffer,"%3s %s",cmdcheck,command);
+
+ if (!chan)
+ writeClockInterface();
+
+ if (strcmp(cmdcheck,"CMD")!=0) {
+ LOG(WARNING) << "bogus message on control interface";
+ return;
+ }
+ std::clog << "command is " << buffer << std::endl << std::flush;
+
+ if (strcmp(command,"MEASURE")==0) {
+ msleep(100);
+ int freq;
+ sscanf(buffer,"%3s %s %d",cmdcheck,command,&freq);
+ sprintf(response,"RSP MEASURE 0 %d -80",freq);
+ }
+ else if (strcmp(command,"ECHO")==0) {
+ msleep(100);
+ sprintf(response,"RSP ECHO 0");
+ }
+ else if (strcmp(command,"POWEROFF")==0) {
+ // turn off transmitter/demod
+ sprintf(response,"RSP POWEROFF 0");
+ }
+ else if (strcmp(command,"POWERON")==0) {
+ // turn on transmitter/demod
+ if (!mTxFreq || !mRxFreq)
+ sprintf(response,"RSP POWERON 1");
+ else {
+ sprintf(response,"RSP POWERON 0");
+ if (!chan && !mOn) {
+ // Prepare for thread start
+ mPower = -20;
+ mRadioInterface->start();
+
+ // Start radio interface threads.
+ mLowerLoopThread->start((void * (*)(void*))
+ LowerLoopAdapter,(void*) this);
+
+ for (size_t i = 0; i < mChans; i++) {
+ TransceiverChannel *chan = new TransceiverChannel(this, i);
+ mRxServiceLoopThreads[i]->start((void * (*)(void*))
+ RxUpperLoopAdapter, (void*) chan);
+
+ // chan = new TransceiverChannel(this, i);
+ // mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
+ // TxUpperLoopAdapter, (void*) chan);
+ }
+
+ writeClockInterface();
+ mOn = true;
+ }
+ }
+ }
+ else if (strcmp(command,"SETMAXDLY")==0) {
+ //set expected maximum time-of-arrival
+ int maxDelay;
+ sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
+ mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
+ sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
+ }
+ else if (strcmp(command,"SETRXGAIN")==0) {
+ //set expected maximum time-of-arrival
+ int newGain;
+ sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
+ newGain = mRadioInterface->setRxGain(newGain, chan);
+ sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
+ }
+ else if (strcmp(command,"NOISELEV")==0) {
+ if (mOn) {
+ float lev = 0;//mStates[chan].mNoiseLev;
+ sprintf(response,"RSP NOISELEV 0 %d",
+ (int) round(20.0 * log10(rxFullScale / lev)));
+ }
+ else {
+ sprintf(response,"RSP NOISELEV 1 0");
+ }
+ }
+ else if (!strcmp(command, "SETPOWER")) {
+ // set output power in dB
+ int dbPwr;
+ sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
+ if (!mOn)
+ sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
+ else {
+ mPower = dbPwr;
+ mRadioInterface->setPowerAttenuation(mPower, chan);
+ sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
+ }
+ }
+ else if (!strcmp(command,"ADJPOWER")) {
+ // adjust power in dB steps
+ int dbStep;
+ sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
+ if (!mOn)
+ sprintf(response, "RSP ADJPOWER 1 %d", mPower);
+ else {
+ mPower += dbStep;
+ mRadioInterface->setPowerAttenuation(mPower, chan);
+ sprintf(response, "RSP ADJPOWER 0 %d", mPower);
+ }
+ }
+ else if (strcmp(command,"RXTUNE")==0) {
+ // tune receiver
+ int freqKhz;
+ sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
+ mRxFreq = freqKhz * 1e3;
+ if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
+ LOG(ALERT) << "RX failed to tune";
+ sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
+ }
+ else
+ sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
+ }
+ else if (strcmp(command,"TXTUNE")==0) {
+ // tune txmtr
+ int freqKhz;
+ sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
+ mTxFreq = freqKhz * 1e3;
+ if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
+ LOG(ALERT) << "TX failed to tune";
+ sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
+ }
+ else
+ sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
+ }
+ else if (!strcmp(command,"SETTSC")) {
+ // set TSC
+ unsigned TSC;
+ sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
+ if (mOn)
+ sprintf(response, "RSP SETTSC 1 %d", TSC);
+ else if (chan && (TSC != mTSC))
+ sprintf(response, "RSP SETTSC 1 %d", TSC);
+ else {
+ mTSC = TSC;
+ //generateMidamble(rx_sps, TSC);
+ 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;
+ int timeslot;
+ sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
+ if ((timeslot < 0) || (timeslot > 7)) {
+ LOG(WARNING) << "bogus message on control interface";
+ sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
+ return;
+ }
+ 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")) {
+ msleep(10);
+ mStates[0].mode = TRX_MODE_MS_ACQUIRE;
+ sprintf(response,"RSP SYNC 0");
+ mMaxExpectedDelay = 10;
+ mRadioInterface->setRxGain(30, 0);
+ msleep(10);
+ }
+ else {
+ LOG(WARNING) << "bogus command " << command << " on control interface.";
+ }
+
+ //mCtrlSockets[chan]->write(response, strlen(response) + 1);
+}
+
+bool Transceiver2::driveTxPriorityQueue(size_t chan)
+{
+ auto burst = pop_d();
+ if(!burst)
+ return true;
+
+ auto currTime = GSM::Time(burst->fn,burst->ts);
+ int RSSI = (int) burst->txlev;
+
+ static BitVector newBurst(gSlotLen);
+ BitVector::iterator itr = newBurst.begin();
+ auto *bufferItr = burst->symbols;
+ while (itr < newBurst.end())
+ *itr++ = *bufferItr++;
+
+ addRadioVector(chan, newBurst, RSSI, currTime);
+ free(burst);
+ return true;
+
+
+
+// char buffer[gSlotLen+50];
+
+// // check data socket
+// size_t msgLen = mDataSockets[chan]->read(buffer);
+
+// if (msgLen!=gSlotLen+1+4+1) {
+// LOG(ERR) << "badly formatted packet on GSM->TRX interface";
+// return false;
+// }
+
+// int timeSlot = (int) buffer[0];
+// uint64_t frameNum = 0;
+// for (int i = 0; i < 4; i++)
+// frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
+
+// LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
+
+// int RSSI = (int) buffer[5];
+// static BitVector newBurst(gSlotLen);
+// BitVector::iterator itr = newBurst.begin();
+// char *bufferItr = buffer+6;
+// while (itr < newBurst.end())
+// *itr++ = *bufferItr++;
+
+// GSM::Time currTime = GSM::Time(frameNum,timeSlot);
+
+// addRadioVector(chan, newBurst, RSSI, currTime);
+
+ return true;
+
+
+}
+
+void Transceiver2::driveReceiveRadio()
+{
+ if (!mRadioInterface->driveReceiveRadio())
+ usleep(100000);
+}
+
+void Transceiver2::driveReceiveFIFO(size_t chan)
+{
+ SoftVector *rxBurst = NULL;
+ int RSSI;
+ int TOA; // in 1/256 of a symbol
+ GSM::Time burstTime;
+
+ rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
+
+ if (rxBurst) {
+ auto response = (trxd_from_trx*)malloc(sizeof(trxd_from_trx));
+#if 0
+ if( !gsm_fcch_check_fn(burstTime.FN()) && !gsm_sch_check_fn(burstTime.FN()))
+ std::cerr << "burst parameters: "
+ << " time: " << burstTime
+ << " RSSI: " << RSSI
+ << " TOA: " << TOA
+ << " bits: " << *rxBurst << std::endl;
+#endif
+ response->ts = burstTime.TN();
+ response->fn = burstTime.FN();
+ response->rssi = RSSI;
+ response->toa = TOA;
+
+ SoftVector::const_iterator burstItr = rxBurst->begin();
+ if(gsm_sch_check_fn(burstTime.FN())) {
+ for (unsigned int i = 0; i < gSlotLen; i++)
+ ((int8_t*)response->symbols)[i] = round(((*burstItr++)-0.5) * 64.0);
+ } else {
+ // invert and fix to +-127 sbits
+ for (int i = 0; i < 148; i++)
+ ((int8_t*)response->symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
+ }
+
+ delete rxBurst;
+ push_d(response);
+
+ }
+}
+
+void Transceiver2::driveTxFIFO()
+{
+
+ /**
+ Features a carefully controlled latency mechanism, to
+ assure that transmit packets arrive at the radio/USRP
+ before they need to be transmitted.
+
+ Deadline clock indicates the burst that needs to be
+ pushed into the FIFO right NOW. If transmit queue does
+ not have a burst, stick in filler data.
+ */
+
+
+ RadioClock *radioClock = (mRadioInterface->getClock());
+
+ if (mOn) {
+ //radioClock->wait(); // wait until clock updates
+ LOG(DEBUG) << "radio clock " << radioClock->get();
+ while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
+ // if underrun, then we're not providing bursts to radio/USRP fast
+ // enough. Need to increase latency by one GSM frame.
+ if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
+ if (mRadioInterface->isUnderrun()) {
+ // only update latency at the defined frame interval
+ if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
+ mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
+ LOG(INFO) << "new latency: " << mTransmitLatency;
+ mLatencyUpdateTime = radioClock->get();
+ }
+ }
+ else {
+ // if underrun hasn't occurred in the last sec (216 frames) drop
+ // transmit latency by a timeslot
+ if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
+ if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
+ mTransmitLatency.decTN();
+ //LOG(INFO) << "reduced latency: " << mTransmitLatency;
+ mLatencyUpdateTime = radioClock->get();
+ }
+ }
+ }
+ }
+ // time to push burst to transmit FIFO
+ pushRadioVector(mTransmitDeadlineClock);
+
+ mTransmitDeadlineClock.incTN();
+
+ if (!mTransmitDeadlineClock.TN() &&
+ !(mTransmitDeadlineClock.FN() % CLK_IND_INTERVAL)) {
+ writeClockInterface();
+ }
+ }
+ }
+
+ radioClock->wait();
+}
+
+
+
+void Transceiver2::writeClockInterface()
+{
+ mLastClockUpdateTime = mTransmitDeadlineClock;
+}
+
+void *RxUpperLoopAdapter(TransceiverChannel *chan)
+{
+ Transceiver2 *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ // trx->setPriority(0.42);
+
+ while (1) {
+ trx->driveReceiveFIFO(num);
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *LowerLoopAdapter(Transceiver2 *transceiver)
+{
+ // transceiver->setPriority(0.45);
+
+ while (1) {
+ transceiver->driveReceiveRadio();
+ //transceiver->driveTxFIFO();
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *ControlServiceLoopAdapter(TransceiverChannel *chan)
+{
+ Transceiver2 *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ while (1) {
+ trx->driveControl(num);
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *TxUpperLoopAdapter(TransceiverChannel *chan)
+{
+ Transceiver2 *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ // trx->setPriority(0.40);
+
+ while (1) {
+ bool stale = false;
+ // Flush the UDP packets until a successful transfer.
+ while (!trx->driveTxPriorityQueue(num)) {
+ stale = true;
+ }
+ if (!num && stale) {
+ // If a packet was stale, remind the GSM stack of the clock.
+ trx->writeClockInterface();
+ }
+ pthread_testcancel();
+ }
+ return NULL;
+}
diff --git a/Transceiver52M/Transceiver2.h b/Transceiver52M/Transceiver2.h
new file mode 100644
index 0000000..e5579f4
--- /dev/null
+++ b/Transceiver52M/Transceiver2.h
@@ -0,0 +1,258 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "radioInterface.h"
+#include "Interthread.h"
+#include "GSMCommon.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern "C" {
+#include <osmocom/core/signal.h>
+#include <osmocom/core/select.h>
+#include "config_defs.h"
+}
+
+class Transceiver2;
+
+/** Channel descriptor for transceiver object and channel number pair */
+struct TransceiverChannel {
+ TransceiverChannel(Transceiver2 *trx, int num)
+ {
+ this->trx = trx;
+ this->num = num;
+ }
+
+ ~TransceiverChannel()
+ {
+ }
+
+ Transceiver2 *trx;
+ size_t num;
+};
+
+/** Internal transceiver state variables */
+struct TransceiverState {
+ TransceiverState();
+ ~TransceiverState();
+
+ /* Initialize a multiframe slot in the filler table */
+ void init(size_t slot, signalVector *burst, bool fill);
+
+ int chanType[8];
+
+ /* The filler table */
+ signalVector *fillerTable[102][8];
+ int fillerModulus[8];
+ bool mRetrans;
+
+ /* Received noise energy levels */
+ noiseVector mFreqOffsets;
+
+ /* Store pointers to previous frame */
+ radioVector *prevFrame[8];
+
+ /* Transceiver mode */
+ int mode;
+};
+
+/** The Transceiver class, responsible for physical layer of basestation */
+class Transceiver2 {
+private:
+
+ int rx_sps, tx_sps;
+ std::string mAddr;
+ GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
+ GSM::Time mLatencyUpdateTime; ///< last time latency was updated
+
+ std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
+ std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
+
+ std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive 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
+
+ GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
+ GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
+
+ RadioInterface *mRadioInterface; ///< associated radioInterface object
+ double txFullScale; ///< full scale input to radio
+ double rxFullScale; ///< full scale output to radio
+
+ /** modulate and add a burst to the transmit queue */
+ void addRadioVector(size_t chan, BitVector &bits,
+ int RSSI, GSM::Time &wTime);
+
+ /** Update filler table */
+ void updateFillerTable(size_t chan, radioVector *burst);
+
+ /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
+ void pushRadioVector(GSM::Time &nowTime);
+
+ /** Pull and demodulate a burst from the receive FIFO */
+ SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
+ int &timingOffset, size_t chan = 0);
+
+ /** Set modulus for specific timeslot */
+ void setModulus(size_t timeslot, size_t chan);
+
+ /** return the expected burst type for the specified timestamp */
+ CorrType expectedCorrType(GSM::Time currTime, size_t chan);
+
+ /** send messages over the clock socket */
+ void writeClockInterface(void);
+
+
+ bool detectSCH(TransceiverState *state,
+ signalVector &burst,
+ struct estim_burst_params *ebp);
+
+ bool decodeSCH(SoftVector *burst, GSM::Time *time);
+ bool correctFCCH(TransceiverState *state, signalVector *burst);
+
+ size_t mChans;
+
+ bool mOn; ///< flag to indicate that transceiver is powered on
+ double mTxFreq; ///< the transmit frequency
+ double mRxFreq; ///< the receive frequency
+ int mPower; ///< the transmit power in dB
+ unsigned mTSC; ///< the midamble sequence code
+ 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;
+
+public:
+
+ /** Transceiver constructor
+ @param wBasePort base port number of UDP sockets
+ @param TRXAddress IP address of the TRX manager, as a string
+ @param wSPS number of samples per GSM symbol
+ @param wTransmitLatency initial setting of transmit latency
+ @param radioInterface associated radioInterface object
+ */
+ Transceiver2(int wBasePort,
+ const char *TRXAddress,
+ size_t wSPS, size_t chans,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface);
+
+ /** Destructor */
+ ~Transceiver2();
+
+ /** start the Transceiver */
+ void start();
+ bool init(bool filler);
+
+ /** attach the radioInterface receive FIFO */
+ bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
+ {
+ if (chan >= mReceiveFIFO.size())
+ return false;
+
+ mReceiveFIFO[chan] = wFIFO;
+ return true;
+ }
+
+ /** accessor for number of channels */
+ size_t numChans() const { return mChans; };
+
+ /** Codes for channel combinations */
+ typedef enum {
+ FILL, ///< Channel is transmitted, but unused
+ I, ///< TCH/FS
+ II, ///< TCH/HS, idle every other slot
+ III, ///< TCH/HS
+ IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
+ V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
+ VI, ///< CCCH+BCCH, uplink RACH
+ VII, ///< SDCCH/8 + SACCH/8
+ VIII, ///< TCH/F + FACCH/F + SACCH/M
+ IX, ///< TCH/F + SACCH/M
+ X, ///< TCH/FD + SACCH/MD
+ XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
+ XII, ///< PCCCH+PDTCH+PACCH+PTCCH
+ XIII, ///< PDTCH+PACCH+PTCCH
+ NONE, ///< Channel is inactive, default
+ LOOPBACK ///< similar go VII, used in loopback testing
+ } ChannelCombination;
+
+ enum {
+ TRX_MODE_OFF,
+ TRX_MODE_BTS,
+ TRX_MODE_MS_ACQUIRE,
+ TRX_MODE_MS_TRACK,
+ };
+
+ void commandhandler(char *buffer, char *response, int chan);
+ void stop();
+protected:
+ /** drive lower receive I/O and burst generation */
+ void driveReceiveRadio();
+
+ /** drive demodulation of GSM bursts */
+ void driveReceiveFIFO(size_t chan);
+
+ /** drive transmission of GSM bursts */
+ void driveTxFIFO();
+
+ /** drive handling of control messages from GSM core */
+ void driveControl(size_t chan);
+
+ /**
+ drive modulation and sorting of GSM bursts from GSM core
+ @return true if a burst was transferred successfully
+ */
+ bool driveTxPriorityQueue(size_t chan);
+
+ friend void *RxUpperLoopAdapter(TransceiverChannel *);
+
+ friend void *TxUpperLoopAdapter(TransceiverChannel *);
+
+ friend void *LowerLoopAdapter(Transceiver2 *);
+
+ friend void *ControlServiceLoopAdapter(TransceiverChannel *);
+
+
+ void reset();
+
+ /** set priority on current thread */
+ //void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
+
+};
+
+void *RxUpperLoopAdapter(TransceiverChannel *);
+
+/** Main drive threads */
+void *LowerLoopAdapter(Transceiver2 *);
+
+/** control message handler thread loop */
+void *ControlServiceLoopAdapter(TransceiverChannel *);
+
+/** transmit queueing thread loop */
+void *TxUpperLoopAdapter(TransceiverChannel *);
+
diff --git a/Transceiver52M/device/common/radioDevice.h b/Transceiver52M/device/common/radioDevice.h
index 3f5da1f..1e19bdc 100644
--- a/Transceiver52M/device/common/radioDevice.h
+++ b/Transceiver52M/device/common/radioDevice.h
@@ -101,6 +101,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/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp
index f109660..d8b969a 100644
--- a/Transceiver52M/device/uhd/UHDDevice.cpp
+++ b/Transceiver52M/device/uhd/UHDDevice.cpp
@@ -557,7 +557,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
#ifdef USE_UHD_3_11
uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
uhd::log::set_log_level(uhd::log::debug);
- uhd::log::set_console_level(uhd::log::off);
+ uhd::log::set_console_level(uhd::log::debug);
uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
#else
uhd::msg::register_handler(&uhd_msg_handler);
@@ -870,7 +870,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
- return 0;
+ return len;
}
// Receive samples from the usrp until we have enough
@@ -975,13 +975,14 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
bool uhd_device::updateAlignment(TIMESTAMP 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 (dev_type == UMTRX) {
@@ -1013,17 +1014,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 > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
LOGC(DDEV, 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;
@@ -1041,11 +1042,13 @@ 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);
str_dir = "Tx";
} 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);
str_dir = "Rx";
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1059,13 +1062,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);
+ tx_freqs[chan].uhd = tres;
+ rx_freqs[!chan].freq = usrp_dev->get_rx_freq(!chan);
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1105,6 +1110,20 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
return 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)
{
uint16_t req_arfcn;
@@ -1116,19 +1135,19 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan)
}
ScopedLock lock(tune_lock);
- req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
- if (req_arfcn == 0xffff) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
- return false;
- }
- if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
- << " Hz (ARFCN " << req_arfcn << " )";
- return false;
- }
+ // req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
+ // if (req_arfcn == 0xffff) {
+ // LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
+ // return false;
+ // }
+ // if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
+ // LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
+ // << " Hz (ARFCN " << req_arfcn << " )";
+ // return false;
+ // }
- if (!set_band(req_band))
- return false;
+ // if (!set_band(req_band))
+ // return false;
return set_freq(wFreq, chan, false);
}
@@ -1140,7 +1159,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)
@@ -1150,7 +1169,7 @@ double uhd_device::getRxFreq(size_t chan)
return 0.0;
}
- return rx_freqs[chan];
+ return rx_freqs[chan].freq;
}
bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
diff --git a/Transceiver52M/device/uhd/UHDDevice.h b/Transceiver52M/device/uhd/UHDDevice.h
index 659fd18..d23911d 100644
--- a/Transceiver52M/device/uhd/UHDDevice.h
+++ b/Transceiver52M/device/uhd/UHDDevice.h
@@ -80,6 +80,12 @@ struct dev_band_desc {
*/
class uhd_device : public RadioDevice {
public:
+
+ struct tune_result {
+ uhd::tune_result_t uhd;
+ double freq;
+ };
+
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chan_num, double offset,
const std::vector<std::string>& tx_paths,
@@ -102,6 +108,7 @@ public:
bool setTxFreq(double wFreq, size_t chan);
bool setRxFreq(double wFreq, size_t chan);
+ bool setRxOffset(double wOffset, size_t chan);
TIMESTAMP initialWriteTimestamp();
TIMESTAMP initialReadTimestamp();
@@ -159,7 +166,7 @@ protected:
double rx_gain_min, rx_gain_max;
std::vector<double> tx_gains, rx_gains;
- std::vector<double> tx_freqs, rx_freqs;
+ std::vector<tune_result> tx_freqs, rx_freqs;
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
enum gsm_band band;
struct dev_band_desc band_desc;
diff --git a/Transceiver52M/fbsb.cpp b/Transceiver52M/fbsb.cpp
new file mode 100644
index 0000000..870b28c
--- /dev/null
+++ b/Transceiver52M/fbsb.cpp
@@ -0,0 +1,148 @@
+#include <atomic>
+#include "radioInterface.h"
+#include "Interthread.h"
+#include "GSMCommon.h"
+//#include "Sockets.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+static const auto tssize = 156.25;
+static const auto framee_to_read = 12;
+static const auto ts_to_read = framee_to_read * 8;
+static const int fbsb_chunk_len = ts_to_read * 2 * tssize; // contiguous fcch+sch guaranteed
+static const auto corr_offset = 0;
+
+struct fbsb_par {
+
+ enum class fbsb_state {
+ IDLE,
+ INIT,
+ ACQ,
+ WAIT,
+ ACQ_COMPL,
+ DONE
+ };
+
+ int pos;
+ int time_idx;
+ std::atomic<fbsb_state> s;
+ GSM::Time rcvClock[ts_to_read];
+ complex fbsb_buf[fbsb_chunk_len+corr_offset];
+ complex conjbuf[fbsb_chunk_len+corr_offset];
+ complex avgbuf[fbsb_chunk_len+corr_offset];
+ void addclk(GSM::Time c) { rcvClock[time_idx] = c; time_idx++; return;}
+public:
+ fbsb_par() : s(fbsb_state::IDLE) {
+ reset();
+ };
+ bool done() {return !(time_idx < ts_to_read);}
+ void take(void* addr, int num_cplx_sps, GSM::Time c) {
+ memcpy(fbsb_buf+pos+corr_offset,addr, num_cplx_sps * sizeof(complex));
+ pos += num_cplx_sps;
+ assert(pos < fbsb_chunk_len);
+ rcvClock[time_idx] = c;
+ time_idx++;
+ }
+ void reset() {pos = 0;
+ time_idx = 0;
+// s = fbsb_state::IDLE;
+ memset(fbsb_buf, 0, sizeof(fbsb_buf));
+ memset(conjbuf, 0, sizeof(conjbuf));
+ memset(avgbuf, 0, sizeof(avgbuf));
+ }
+ int sz () {return fbsb_chunk_len;}
+ int off () {return corr_offset;}
+
+ // from osmotrx sigproc
+ complex fastPeakDetect(complex* rxBurst, float* index, int N)
+ {
+ float val, max = 0.0f;
+ complex amp;
+ int _index = -1;
+
+ for (size_t i = 0; i < N; i++) {
+ val = rxBurst[i].abs();
+ if (val > max) {
+ max = val;
+ _index = i;
+ amp = rxBurst[i];
+ }
+ }
+
+ if (index)
+ *index = (float)_index;
+
+ return amp;
+ }
+
+ void conj_with_lag(complex* in, complex* out, int lag, int offset, int N) {
+ int total_offset = offset + lag;
+ for (int s = 0; s < N; s++)
+ out[s] = in[s + total_offset] * in[s + total_offset - lag].conj();
+ }
+
+ auto ac_sum_with_lag(complex* in, int lag, int offset, int N) {
+ complex v(0,0);
+ auto total_offset = offset + lag;
+ for (auto s = 0; s < N; s++)
+ v += in[s + total_offset] * in[s + total_offset - lag].conj();
+ return atan2(v.imag(), v.real());
+ }
+
+
+ bool running_avg_opt(complex* in, complex* out, int avg_window, int val_to_find, int* idx, int N) {
+ bool found = false;
+ complex avg0 = 0;
+ complex scale(avg_window, avg_window);
+ for (auto i = 0; i < avg_window; i++)
+ avg0 += in[i];
+ out[0] = avg0 / scale;
+
+ //skip first
+ for (auto i = 1; i < N - avg_window; i++) {
+ avg0 += in[i-1] - in[i+avg_window];
+ auto tmp = avg0 / scale;
+ out[i] = tmp;
+ if (!found && tmp.abs() > val_to_find) {
+ found = !found;
+ *idx = i;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int fcch(complex* amp, int* found_idx, bool dump) {
+ conj_with_lag(fbsb_buf, conjbuf, 3, off(), sz());
+
+ running_avg_opt(conjbuf, avgbuf, 48, 1.5e6, found_idx, sz()-off());
+ float pos;
+ auto r = fastPeakDetect(avgbuf, &pos, sz()-off());
+ *found_idx = *found_idx+off();
+ std::cerr << "fcch found at " << pos << " amp: " << r.abs() << std::endl;
+ *amp = r;
+
+ if(dump) {
+ {
+ auto f = fopen("inbuf.cfile", "wb");
+ fwrite(fbsb_buf, sz(), 1, f);
+ fclose(f);
+ }
+ {
+ auto f = fopen("conjbuf.cfile", "wb");
+ fwrite(conjbuf, sz(), 1, f);
+ fclose(f);
+ }
+ {
+ auto f = fopen("schfcch.cfile", "wb");
+ fwrite(avgbuf, sz(), 1, f);
+ fclose(f);
+ }
+ exit(0);
+ }
+ return pos;
+ }
+};
+
diff --git a/Transceiver52M/fbsb.h b/Transceiver52M/fbsb.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Transceiver52M/fbsb.h
diff --git a/Transceiver52M/l1if.cpp b/Transceiver52M/l1if.cpp
new file mode 100644
index 0000000..440f6e7
--- /dev/null
+++ b/Transceiver52M/l1if.cpp
@@ -0,0 +1,124 @@
+#include <mutex>
+#include <queue>
+#include <deque>
+#include <condition_variable>
+#include <iostream>
+
+extern "C" {
+
+#include <unistd.h>
+#include <sys/eventfd.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+}
+#include "l1if.h"
+
+using namespace std;
+using namespace std::chrono_literals;
+
+template<typename Data>
+class spsc_q{
+
+ std::queue<Data> m_q;
+ std::mutex m_mtx;
+ std::condition_variable m_cond;
+ bool killme;
+
+public:
+ spsc_q() : killme{ false } { }
+
+ void push(Data i){
+ std::unique_lock<std::mutex> lock(m_mtx);
+ m_q.push(i);
+ m_cond.notify_one();
+ }
+
+ Data pop(){
+ std::unique_lock<std::mutex> lock(m_mtx);
+ m_cond.wait_for(lock, 100ms, [&](){ return !m_q.empty() || killme; });
+
+ if (killme || m_q.empty()){
+ return {};
+ }
+
+ Data x = m_q.front();
+ m_q.pop();
+
+ return x;
+ }
+
+ void stop(){
+ killme = true;
+ m_cond.notify_all();
+ }
+
+ auto sz() { return m_q.size(); }
+};
+
+
+/*
+ * trxif_from_trx_c <-> push_c
+ * trxif_to_trx_c <-> pop_c
+ * trxif_from_trx_d <-> push_d
+ * trxif_to_trx_d <-> pop_d
+ * ...
+ *
+ *
+ */
+class trxl1if {
+public:
+ spsc_q<TRX_C*> c_to_trx;
+ spsc_q<TRX_C*> c_from_trx;
+
+ spsc_q<trxd_to_trx*> d_to_trx;
+ spsc_q<trxd_from_trx*> d_from_trx;
+
+ struct osmo_fd g_event_ofd_C;
+ struct osmo_fd g_event_ofd_D;
+};
+
+trxl1if trxif;
+
+void push_c(TRX_C* i) {
+ uint64_t one = 1;
+ int rc;
+ trxif.c_from_trx.push(i);
+ std::clog << trxif.c_from_trx.sz() << std::endl;
+ rc = ::write(trxif.g_event_ofd_C.fd, &one, sizeof(one));
+ return;
+};
+TRX_C* pop_c() {
+ return trxif.c_to_trx.pop();
+};
+void push_d(trxd_from_trx* i) {
+ uint64_t one = 1;
+ int rc;
+ trxif.d_from_trx.push(i);
+ rc = ::write(trxif.g_event_ofd_D.fd, &one, sizeof(one));
+ return;
+};
+trxd_to_trx* pop_d() {
+ return trxif.d_to_trx.pop();
+};
+
+extern "C" {
+char* trxif_from_trx_c() {
+ uint64_t one = 1;
+ ::read(trxif.g_event_ofd_C.fd, &one, sizeof(one));
+ return (char*)trxif.c_from_trx.pop();
+}
+void trxif_to_trx_c(char* msg) {
+ trxif.c_to_trx.push((TRX_C*)msg);
+}
+trxd_from_trx* trxif_from_trx_d() {
+ uint64_t one = 1;
+ ::read(trxif.g_event_ofd_D.fd, &one, sizeof(one));
+ return trxif.d_from_trx.pop();
+}
+void trxif_to_trx_d(trxd_to_trx* msg) {
+ trxif.d_to_trx.push(msg);
+}
+struct osmo_fd* get_c_fd() { return &trxif.g_event_ofd_C;}
+struct osmo_fd* get_d_fd() { return &trxif.g_event_ofd_D;}
+}
diff --git a/Transceiver52M/l1if.h b/Transceiver52M/l1if.h
new file mode 100644
index 0000000..d477e47
--- /dev/null
+++ b/Transceiver52M/l1if.h
@@ -0,0 +1,74 @@
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stdint.h>
+#ifdef __cplusplus
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* Data interface handlers */
+/* ------------------------------------------------------------------------ */
+/* DATA interface */
+/* */
+/* Messages on the data interface carry one radio burst per UDP message. */
+/* */
+/* Received Data Burst: */
+/* 1 byte timeslot index */
+/* 4 bytes GSM frame number, BE */
+/* 1 byte RSSI in -dBm */
+/* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */
+/* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */
+/* 2 bytes are not used, but being sent by OsmoTRX */
+/* */
+/* Transmit Data Burst: */
+/* 1 byte timeslot index */
+/* 4 bytes GSM frame number, BE */
+/* 1 byte transmit level wrt ARFCN max, -dB (attenuation) */
+/* 148 bytes output symbol values, 0 & 1 */
+/* ------------------------------------------------------------------------ */
+
+struct trxd_to_trx {
+ uint8_t ts;
+ uint32_t fn;
+ uint8_t txlev;
+ uint8_t symbols[148];
+};
+
+struct trxd_from_trx {
+ uint8_t ts;
+ uint32_t fn;
+ uint8_t rssi;
+ uint16_t toa;
+ uint8_t symbols[148];
+ uint8_t pad[2];
+};
+
+#define TRXC_BUF_SIZE 1024
+
+struct TRX_C {
+ char cmd[TRXC_BUF_SIZE];
+};
+
+#ifdef __cplusplus
+void push_c(TRX_C* i);
+TRX_C* pop_c();
+
+void push_d(trxd_from_trx* i);
+trxd_to_trx* pop_d();
+
+#else
+
+
+char* trxif_from_trx_c();
+void trxif_to_trx_c(char* msg);
+
+struct trxd_from_trx* trxif_from_trx_d();
+void trxif_to_trx_d(struct trxd_to_trx* msg);
+
+struct osmo_fd* get_c_fd();
+struct osmo_fd* get_d_fd();
+
+#endif
+
diff --git a/Transceiver52M/osmo-trx-org.cpp b/Transceiver52M/osmo-trx-org.cpp
new file mode 100644
index 0000000..bd93ab7
--- /dev/null
+++ b/Transceiver52M/osmo-trx-org.cpp
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Transceiver.h"
+#include "radioDevice.h"
+#include "Utils.h"
+
+#include <time.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sched.h>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <sys/signalfd.h>
+
+#include <GSMCommon.h>
+#include <Logger.h>
+
+extern "C" {
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/ctrl/ports.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/cpu_sched_vty.h>
+
+#include "convolve.h"
+#include "convert.h"
+#include "trx_vty.h"
+#include "debug.h"
+#include "osmo_signal.h"
+#include "trx_rate_ctr.h"
+}
+
+#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
+
+#define charp2str(a) ((a) ? std::string(a) : std::string(""))
+
+static char* config_file = (char*)DEFAULT_CONFIG_FILE;
+
+struct osmo_fd signal_ofd;
+volatile bool gshutdown = false;
+
+static void *tall_trx_ctx;
+static struct trx_ctx *g_trx_ctx;
+static struct ctrl_handle *g_ctrlh;
+
+static RadioDevice *usrp;
+static RadioInterface *radio;
+
+/* Create radio interface
+ * The interface consists of sample rate changes, frequency shifts,
+ * channel multiplexing, and other conversions. The transceiver core
+ * accepts input vectors sampled at multiples of the GSM symbol rate.
+ * The radio interface connects the main transceiver with the device
+ * object, which may be operating some other rate.
+ */
+RadioInterface *makeRadioInterface(struct trx_ctx *trx,
+ RadioDevice *usrp, int type)
+{
+ RadioInterface *radio = NULL;
+ size_t div = 1;
+ int offset = 3;
+
+ if (trx->cfg.ms) {
+ if (type != RadioDevice::NORMAL) {
+ LOG(ALERT) << "Unsupported configuration";
+ return NULL;
+ }
+
+ offset *= -1;
+ }
+
+ switch (type) {
+ case RadioDevice::NORMAL:
+ radio = new RadioInterface(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps, trx->cfg.num_chans, offset, offset);
+ break;
+ case RadioDevice::RESAMP_64M:
+ case RadioDevice::RESAMP_100M:
+ radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps);
+ break;
+ case RadioDevice::MULTI_ARFCN:
+ radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps, trx->cfg.num_chans);
+ break;
+ default:
+ LOG(ALERT) << "Unsupported radio interface configuration";
+ return NULL;
+ }
+
+ if (!radio->init(type)) {
+ LOG(ALERT) << "Failed to initialize radio interface";
+ return NULL;
+ }
+
+ return radio;
+}
+
+/* Callback function to be called every time we receive a signal from TRANSC */
+static int transc_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ switch (signal) {
+ case S_MAIN_STOP_REQUIRED:
+ gshutdown = true;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Create transceiver core
+ * The multi-threaded modem core operates at multiples of the GSM rate of
+ * 270.8333 ksps and consists of GSM specific modulation, demodulation,
+ * and decoding schemes. Also included are the socket interfaces for
+ * connecting to the upper layer stack.
+ */
+int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
+{
+ VectorFIFO *fifo;
+
+ transceiver = new Transceiver2(&trx->cfg, GSM::Time(3,0), radio);
+ if (!transceiver->init()) {
+ LOG(ALERT) << "Failed to initialize transceiver";
+ return -1;
+ }
+
+ for (size_t i = 0; i < trx->cfg.num_chans; i++) {
+ fifo = radio->receiveFIFO(i);
+ if (fifo && transceiver->receiveFIFO(fifo, i))
+ continue;
+
+ LOG(ALERT) << "Could not attach FIFO to channel " << i;
+ return -1;
+ }
+ return 0;
+}
+
+static void sig_handler(int signo)
+{
+
+ if (gshutdown)
+ /* We are in the middle of shutdown process, avoid any kind of extra
+ action like printing */
+ return;
+
+ fprintf(stderr, "signal %d received\n", signo);
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ fprintf(stderr, "shutting down\n");
+ gshutdown = true;
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report(tall_trx_ctx, stderr);
+ talloc_report_full(tall_trx_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ talloc_report(tall_trx_ctx, stderr);
+ talloc_report_full(tall_trx_ctx, stderr);
+ break;
+ case SIGUSR2:
+ talloc_report_full(tall_trx_ctx, stderr);
+ break;
+ case SIGHUP:
+ log_targets_reopen();
+ default:
+ break;
+ }
+
+}
+
+static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
+{
+ struct signalfd_siginfo fdsi;
+ ssize_t s;
+
+ s = read(ofd->fd, &fdsi, sizeof(struct signalfd_siginfo));
+ if (s < 0) {
+ LOG(FATAL) << "Failed to read from signalfd ("<< ofd->fd << "): " << errno;
+ gshutdown = true;
+ return 0;
+ }
+ sig_handler(fdsi.ssi_signo);
+ return 0;
+}
+
+static void setup_signal_handlers()
+{
+ sigset_t set;
+ int sfd;
+
+ signal(SIGABRT, &sig_handler);
+ osmo_init_ignore_signals();
+
+ /* Other threads created by this thread (main) will inherit a copy of the
+ signal mask. */
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGUSR1);
+ sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGHUP);
+ if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
+ fprintf(stderr, "pthread_sigmask() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((sfd = signalfd(-1, &set, 0)) == -1) {
+ fprintf(stderr, "signalfd() failed (%d).\n", errno);
+ exit(EXIT_FAILURE);
+ }
+
+ osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
+ if (osmo_fd_register(&signal_ofd) < 0) {
+ fprintf(stderr, "osmo_fd_register() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void print_help()
+{
+ printf( "Some useful options:\n"
+ " -h, --help This text\n"
+ " -C, --config Filename The config file to use\n"
+ " -V, --version Print the version of OsmoTRX\n"
+ "\nVTY reference generation:\n"
+ " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
+ " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ );
+}
+
+static void print_deprecated(char opt)
+{
+ LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
+ << " Please use VTY cfg option instead."
+ << " All cmd line options are already being overridden by VTY options if set.";
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation "
+ "mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
+}
+
+static void handle_options(int argc, char **argv, struct trx_ctx* trx)
+{
+ int option;
+ unsigned int i;
+ std::vector<std::string> rx_paths, tx_paths;
+ bool rx_paths_set = false, tx_paths_set = false;
+ static int long_option = 0;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"config", 1, 0, 'C'},
+ {"version", 0, 0, 'V'},
+ {"vty-ref-mode", 1, &long_option, 1},
+ {"vty-ref-xml", 0, &long_option, 2},
+ {NULL, 0, 0, 0}
+ };
+
+ while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
+ NULL)) != -1) {
+ switch (option) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 0:
+ handle_long_options(argv[0], long_option);
+ break;
+ case 'a':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
+ break;
+ case 'l':
+ print_deprecated(option);
+ log_set_log_level(osmo_stderr_target, atoi(optarg));
+ break;
+ case 'i':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
+ break;
+ case 'j':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
+ break;
+ case 'p':
+ print_deprecated(option);
+ trx->cfg.base_port = atoi(optarg);
+ break;
+ case 'c':
+ print_deprecated(option);
+ trx->cfg.num_chans = atoi(optarg);
+ break;
+ case 'm':
+ print_deprecated(option);
+ trx->cfg.multi_arfcn = true;
+ break;
+ case 'x':
+ print_deprecated(option);
+ trx->cfg.clock_ref = REF_EXTERNAL;
+ break;
+ case 'g':
+ print_deprecated(option);
+ trx->cfg.clock_ref = REF_GPS;
+ break;
+ case 'f':
+ print_deprecated(option);
+ trx->cfg.filler = FILLER_DUMMY;
+ break;
+ case 'o':
+ print_deprecated(option);
+ trx->cfg.offset = atof(optarg);
+ break;
+ case 's':
+ print_deprecated(option);
+ trx->cfg.tx_sps = atoi(optarg);
+ break;
+ case 'b':
+ print_deprecated(option);
+ trx->cfg.rx_sps = atoi(optarg);
+ break;
+ case 'r':
+ print_deprecated(option);
+ trx->cfg.rtsc = atoi(optarg);
+ if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
+ trx->cfg.filler = FILLER_NORM_RAND;
+ break;
+ case 'A':
+ print_deprecated(option);
+ trx->cfg.rach_delay = atoi(optarg);
+ trx->cfg.filler = FILLER_ACCESS_RAND;
+ break;
+ case 'R':
+ print_deprecated(option);
+ trx->cfg.rssi_offset = atof(optarg);
+ trx->cfg.force_rssi_offset = true;
+ break;
+ case 'S':
+ print_deprecated(option);
+ trx->cfg.swap_channels = true;
+ break;
+ case 'e':
+ print_deprecated(option);
+ trx->cfg.egprs = true;
+ break;
+ case 't':
+ print_deprecated(option);
+ trx->cfg.sched_rr = atoi(optarg);
+ break;
+ case 'y':
+ print_deprecated(option);
+ tx_paths = comma_delimited_to_vector(optarg);
+ tx_paths_set = true;
+ break;
+ case 'z':
+ print_deprecated(option);
+ rx_paths = comma_delimited_to_vector(optarg);
+ rx_paths_set = true;
+ break;
+ case 'C':
+ config_file = optarg;
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ default:
+ goto bad_config;
+ }
+ }
+
+ trx->cfg.ms = true;
+
+ if (argc > optind) {
+ LOG(ERROR) << "Unsupported positional arguments on command line";
+ goto bad_config;
+ }
+
+ /* Cmd line option specific validation & setup */
+
+ if (trx->cfg.num_chans > TRX_CHAN_MAX) {
+ LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
+ goto bad_config;
+ }
+ if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
+ (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
+ LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
+ goto bad_config;
+ }
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ trx->cfg.chans[i].trx = trx;
+ trx->cfg.chans[i].idx = i;
+ if (tx_paths_set)
+ osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
+ if (rx_paths_set)
+ osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
+ }
+
+ return;
+
+bad_config:
+ print_help();
+ exit(0);
+}
+
+int trx_validate_config(struct trx_ctx *trx)
+{
+ if (trx->cfg.multi_arfcn && trx->cfg.num_chans > TRX_MCHAN_MAX) {
+ LOG(ERROR) << "Unsupported number of channels";
+ return -1;
+ }
+
+ /* Force 4 SPS for EDGE or multi-ARFCN configurations */
+ if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
+ (trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
+ LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_sched_rr(unsigned int prio)
+{
+ struct sched_param param;
+ int rc;
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = prio;
+ LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority
+ << ". This setting is DEPRECATED, please use 'policy rr " << param.sched_priority
+ << "' under the 'sched' VTY node instead.";
+ rc = sched_setscheduler(getpid(), SCHED_RR, &param);
+ if (rc != 0) {
+ LOG(ERROR) << "Config: Setting SCHED_RR failed";
+ return -1;
+ }
+ return 0;
+}
+
+static void print_simd_info(void)
+{
+#ifdef HAVE_SSE3
+ LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse3"))
+ LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
+ else
+ LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
+#else
+ LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+#ifdef HAVE_SSE4_1
+ LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse4.1"))
+ LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
+ else
+ LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
+#else
+ LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+#ifndef HAVE_ATOMIC_OPS
+#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
+ LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
+#endif
+}
+
+static void print_config(struct trx_ctx *trx)
+{
+ unsigned int i;
+ std::ostringstream ost("");
+
+ ost << "Config Settings" << std::endl;
+ ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
+ ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
+ ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
+ ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
+ ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
+ ost << " Channels................ " << trx->cfg.num_chans << std::endl;
+ ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
+ ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
+ ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
+ ost << " Extended RACH support... " << trx->cfg.ext_rach << std::endl;
+ ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
+ ost << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
+ ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
+ ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
+ ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
+ ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
+ if (trx->cfg.freq_offset_khz != 0)
+ ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
+ ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
+ ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
+ ost << " Tx Antennas.............";
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ std::string p = charp2str(trx->cfg.chans[i].tx_path);
+ ost << " '" << ((p != "") ? p : "<default>") << "'";
+ }
+ ost << std::endl;
+ ost << " Rx Antennas.............";
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ std::string p = charp2str(trx->cfg.chans[i].rx_path);
+ ost << " '" << ((p != "") ? p : "<default>") << "'";
+ }
+ ost << std::endl;
+
+ LOG(INFO) << ost << std::endl;
+}
+
+static void trx_stop()
+{
+ LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
+
+ delete transceiver;
+ delete radio;
+ delete usrp;
+}
+
+static int trx_start(struct trx_ctx *trx)
+{
+ int type, chans;
+ unsigned int i;
+ std::vector<std::string> rx_paths, tx_paths;
+ RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
+
+ /* Create the low level device object */
+ if (trx->cfg.multi_arfcn)
+ iface = RadioDevice::MULTI_ARFCN;
+
+ /* Generate vector of rx/tx_path: */
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
+ tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
+ }
+
+ usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
+ trx->cfg.num_chans, trx->cfg.offset,
+ tx_paths, rx_paths);
+ type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
+ if (type < 0) {
+ LOG(ALERT) << "Failed to create radio device" << std::endl;
+ goto shutdown;
+ }
+
+ /* Setup the appropriate device interface */
+ radio = makeRadioInterface(trx, usrp, type);
+ if (!radio)
+ goto shutdown;
+
+ /* Create the transceiver core */
+ if (makeTransceiver(trx, radio) < 0)
+ goto shutdown;
+
+ chans = transceiver->numChans();
+ LOG(NOTICE) << "-- Transceiver active with "
+ << chans << " channel(s)" << std::endl;
+
+ return 0;
+
+shutdown:
+ trx_stop();
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
+ msgb_talloc_ctx_init(tall_trx_ctx, 0);
+ g_vty_info.tall_ctx = tall_trx_ctx;
+
+ setup_signal_handlers();
+
+ g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
+
+ convolve_init();
+ convert_init();
+
+ osmo_init_logging2(tall_trx_ctx, &log_info);
+ log_enable_multithread();
+ osmo_stats_init(tall_trx_ctx);
+ vty_init(&g_vty_info);
+ logging_vty_add_cmds();
+ ctrl_vty_init(tall_trx_ctx);
+ osmo_cpu_sched_vty_init(tall_trx_ctx);
+ trx_vty_init(g_trx_ctx);
+
+ osmo_talloc_vty_add_cmds();
+ osmo_stats_vty_add_cmds();
+
+ handle_options(argc, argv, g_trx_ctx);
+
+ rate_ctr_init(tall_trx_ctx);
+
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
+ exit(2);
+ }
+
+ rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
+ if (rc < 0)
+ exit(1);
+
+ g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
+ if (!g_ctrlh) {
+ LOG(ERROR) << "Failed to create CTRL interface.\n";
+ exit(1);
+ }
+
+ /* Backward compatibility: Hack to have 1 channel allocated by default.
+ * Can be Dropped once we * get rid of "-c" cmdline param */
+ if (g_trx_ctx->cfg.num_chans == 0) {
+ g_trx_ctx->cfg.num_chans = 1;
+ g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
+ g_trx_ctx->cfg.chans[0].idx = 0;
+ LOG(ERROR) << "No explicit channel config found. Make sure you" \
+ " configure channels in VTY config. Using 1 channel as default," \
+ " but expect your config to break in the future.";
+ }
+
+ print_simd_info();
+ print_config(g_trx_ctx);
+
+ if (trx_validate_config(g_trx_ctx) < 0) {
+ LOG(ERROR) << "Config failure - exiting";
+ return EXIT_FAILURE;
+ }
+
+ if (g_trx_ctx->cfg.sched_rr) {
+ if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
+ return EXIT_FAILURE;
+ }
+
+ osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
+ trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
+
+ srandom(time(NULL));
+
+ if(trx_start(g_trx_ctx) < 0)
+ return EXIT_FAILURE;
+
+ while (!gshutdown)
+ osmo_select_main(0);
+
+ trx_stop();
+
+ osmo_fd_unregister(&signal_ofd);
+ osmo_fd_close(&signal_ofd);
+ osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
+ return 0;
+}
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
index b6f378b..4872f62 100644
--- a/Transceiver52M/osmo-trx.cpp
+++ b/Transceiver52M/osmo-trx.cpp
@@ -1,8 +1,6 @@
/*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
- * SPDX-License-Identifier: LGPL-2.1+
- *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -12,27 +10,23 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include "Transceiver.h"
+#include "Transceiver2.h"
#include "radioDevice.h"
-#include "Utils.h"
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
-#include <getopt.h>
-#include <sched.h>
-#include <vector>
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <sys/signalfd.h>
#include <GSMCommon.h>
#include <Logger.h>
@@ -61,21 +55,101 @@ extern "C" {
#include "trx_rate_ctr.h"
}
-#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
-#define charp2str(a) ((a) ? std::string(a) : std::string(""))
+//#include <mempool.cpp>
+//atomicstackpool<NUM_ALLOCS,ALLOC_SZ> ALLOCPRIO a0;
+
+/* Samples-per-symbol for downlink path
+ * 4 - Uses precision modulator (more computation, less distortion)
+ * 1 - Uses minimized modulator (less computation, more distortion)
+ *
+ * Other values are invalid. Receive path (uplink) is always
+ * downsampled to 1 sps. Default to 4 sps for all cases except for
+ * ARM and non-SIMD enabled architectures.
+ */
+#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
+#define DEFAULT_SPS 1
+#else
+#define DEFAULT_SPS 4
+#endif
+
+/* Default configuration parameters
+ * Note that these values are only used if the particular key does not
+ * exist in the configuration database. IP port and address values will
+ * typically be overwritten by the OpenBTS.db values. Other values will
+ * not be in the database by default.
+ */
+#define DEFAULT_TRX_PORT 5700
+#define DEFAULT_TRX_IP "127.0.0.1"
+#define DEFAULT_EXTREF false
+#define DEFAULT_DIVERSITY false
+#define DEFAULT_CHANS 1
+
+struct trx_config {
+ std::string log_level;
+ std::string addr;
+ std::string dev_args;
+ unsigned port;
+ unsigned sps;
+ unsigned chans;
+ bool extref;
+ bool filler;
+ bool diversity;
+ bool ms;
+ double offset;
+};
+
+
+
+extern "C" volatile bool gshutdown = false;
+
+
+
+/* Setup configuration values
+ * Don't query the existence of the Log.Level because it's a
+ * mandatory value. That is, if it doesn't exist, the configuration
+ * table will crash or will have already crashed. Everything else we
+ * can survive without and use default values if the database entries
+ * are empty.
+ */
+bool trx_setup_config(struct trx_config *config)
+{
+ std::string refstr, fillstr, divstr, msstr;
-static char* config_file = (char*)DEFAULT_CONFIG_FILE;
+ config->log_level = "foo";
+ config->port = DEFAULT_TRX_PORT;
+ config->addr = DEFAULT_TRX_IP;
+ //config->extref = DEFAULT_EXTREF;
+ config->diversity = DEFAULT_DIVERSITY;
+ config->sps = 4;//DEFAULT_SPS;
+ config->chans = DEFAULT_CHANS;
-struct osmo_fd signal_ofd;
-volatile bool gshutdown = false;
+ /* Diversity only supported on 2 channels */
+ if (config->diversity)
+ config->chans = 2;
-static void *tall_trx_ctx;
-static struct trx_ctx *g_trx_ctx;
-static struct ctrl_handle *g_ctrlh;
+ refstr = config->extref ? "Enabled" : "Disabled";
+ fillstr = config->filler ? "Enabled" : "Disabled";
+ divstr = config->diversity ? "Enabled" : "Disabled";
+ msstr = config->ms ? "Enabled" : "Disabled";
-static RadioDevice *usrp;
-static RadioInterface *radio;
+ std::ostringstream ost("");
+ ost << "Config Settings" << std::endl;
+ ost << " Log Level............... " << config->log_level << std::endl;
+ ost << " Device args............. " << config->dev_args << std::endl;
+ ost << " TRX Base Port........... " << config->port << std::endl;
+ ost << " TRX Address............. " << config->addr << std::endl;
+ ost << " Channels................ " << config->chans << std::endl;
+ ost << " Samples-per-Symbol...... " << config->sps << std::endl;
+ 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.str() << std::endl;
+
+ return true;
+}
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
@@ -84,24 +158,26 @@ static RadioInterface *radio;
* The radio interface connects the main transceiver with the device
* object, which may be operating some other rate.
*/
-RadioInterface *makeRadioInterface(struct trx_ctx *trx,
+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, trx->cfg.tx_sps,
- trx->cfg.rx_sps, trx->cfg.num_chans);
- break;
- case RadioDevice::RESAMP_64M:
- case RadioDevice::RESAMP_100M:
- radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
- trx->cfg.rx_sps);
- break;
- case RadioDevice::MULTI_ARFCN:
- radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
- trx->cfg.rx_sps, trx->cfg.num_chans);
+ radio = new RadioInterface(usrp, config->sps, config->sps,
+ config->chans, div, offset);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -116,590 +192,257 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
return radio;
}
-/* Callback function to be called every time we receive a signal from TRANSC */
-static int transc_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- switch (signal) {
- case S_MAIN_STOP_REQUIRED:
- gshutdown = true;
- break;
- default:
- break;
- }
- return 0;
-}
-
/* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
* and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack.
*/
-int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
+Transceiver2 *makeTransceiver(struct trx_config *config, RadioInterface *radio)
{
+ Transceiver2 *trx;
VectorFIFO *fifo;
- transceiver = new Transceiver(&trx->cfg, GSM::Time(3,0), radio);
- if (!transceiver->init()) {
+ trx = new Transceiver2(config->port, config->addr.c_str(), config->sps,
+ config->chans, GSM::Time(3,0), radio);
+ if (!trx->init(config->filler)) {
LOG(ALERT) << "Failed to initialize transceiver";
- return -1;
+ delete trx;
+ return NULL;
}
- for (size_t i = 0; i < trx->cfg.num_chans; i++) {
+ for (size_t i = 0; i < config->chans; i++) {
fifo = radio->receiveFIFO(i);
- if (fifo && transceiver->receiveFIFO(fifo, i))
+ if (fifo && trx->receiveFIFO(fifo, i))
continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i;
- return -1;
- }
- return 0;
-}
-
-static void sig_handler(int signo)
-{
-
- if (gshutdown)
- /* We are in the middle of shutdown process, avoid any kind of extra
- action like printing */
- return;
-
- fprintf(stderr, "signal %d received\n", signo);
- switch (signo) {
- case SIGINT:
- case SIGTERM:
- fprintf(stderr, "shutting down\n");
- gshutdown = true;
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report and
- * then run default SIGABRT handler, who will generate coredump
- * and abort the process. abort() should do this for us after we
- * return, but program wouldn't exit if an external SIGABRT is
- * received.
- */
- talloc_report(tall_trx_ctx, stderr);
- talloc_report_full(tall_trx_ctx, stderr);
- signal(SIGABRT, SIG_DFL);
- raise(SIGABRT);
- break;
- case SIGUSR1:
- talloc_report(tall_trx_ctx, stderr);
- talloc_report_full(tall_trx_ctx, stderr);
- break;
- case SIGUSR2:
- talloc_report_full(tall_trx_ctx, stderr);
- break;
- case SIGHUP:
- log_targets_reopen();
- default:
- break;
+ delete trx;
+ return NULL;
}
+ return trx;
}
-static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
+static void sig_handler(int signo)
{
- struct signalfd_siginfo fdsi;
- ssize_t s;
-
- s = read(ofd->fd, &fdsi, sizeof(struct signalfd_siginfo));
- if (s < 0) {
- LOG(FATAL) << "Failed to read from signalfd ("<< ofd->fd << "): " << errno;
- gshutdown = true;
- return 0;
- }
- sig_handler(fdsi.ssi_signo);
- return 0;
+ fprintf(stdout, "Received shutdown signal");
+ gshutdown = true;
}
static void setup_signal_handlers()
{
- sigset_t set;
- int sfd;
-
- signal(SIGABRT, &sig_handler);
- osmo_init_ignore_signals();
-
- /* Other threads created by this thread (main) will inherit a copy of the
- signal mask. */
- sigemptyset(&set);
- sigaddset(&set, SIGINT);
- sigaddset(&set, SIGTERM);
- sigaddset(&set, SIGUSR1);
- sigaddset(&set, SIGUSR2);
- sigaddset(&set, SIGHUP);
- if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
- fprintf(stderr, "pthread_sigmask() failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if ((sfd = signalfd(-1, &set, 0)) == -1) {
- fprintf(stderr, "signalfd() failed (%d).\n", errno);
+ if (signal(SIGINT, sig_handler) == SIG_ERR) {
+ fprintf(stderr, "Failed to install SIGINT signal handler\n");
exit(EXIT_FAILURE);
}
-
- osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
- if (osmo_fd_register(&signal_ofd) < 0) {
- fprintf(stderr, "osmo_fd_register() failed.\n");
- exit(EXIT_FAILURE);
+ if (signal(SIGTERM, sig_handler) == SIG_ERR) {
+ fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
+ exit( EXIT_FAILURE);
}
}
static void print_help()
{
- printf( "Some useful options:\n"
- " -h, --help This text\n"
- " -C, --config Filename The config file to use\n"
- " -V, --version Print the version of OsmoTRX\n"
- "\nVTY reference generation:\n"
- " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
- " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
- );
+ fprintf(stdout, "Options:\n"
+ " -h This text\n"
+ " -a UHD device args\n"
+ " -l Logging level (%s)\n"
+ " -i IP address of GSM core\n"
+ " -p Base port number\n"
+ " -d Enable dual channel diversity receiver\n"
+ " -x Enable external 10 MHz reference\n"
+ " -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");
}
-static void print_deprecated(char opt)
+static void handle_options(int argc, char **argv, struct trx_config *config)
{
- LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
- << " Please use VTY cfg option instead."
- << " All cmd line options are already being overridden by VTY options if set.";
-}
+ int option;
-static void handle_long_options(const char *prog_name, const int long_option)
-{
- static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
-
- switch (long_option) {
- case 1:
- vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
- if (vty_ref_mode < 0) {
- fprintf(stderr, "%s: Unknown VTY reference generation "
- "mode '%s'\n", prog_name, optarg);
- exit(2);
- }
- break;
- case 2:
- fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
- get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
- get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
- vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
- exit(0);
- default:
- fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
- exit(2);
- }
-}
+ optind=1;
-static void handle_options(int argc, char **argv, struct trx_ctx* trx)
-{
- int option;
- unsigned int i;
- std::vector<std::string> rx_paths, tx_paths;
- bool rx_paths_set = false, tx_paths_set = false;
- static int long_option = 0;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"config", 1, 0, 'C'},
- {"version", 0, 0, 'V'},
- {"vty-ref-mode", 1, &long_option, 1},
- {"vty-ref-xml", 0, &long_option, 2},
- {NULL, 0, 0, 0}
- };
-
- while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
- NULL)) != -1) {
+ config->port = 0;
+ config->sps = 0;
+ config->chans = 0;
+ config->extref = true;
+ config->filler = false;
+ config->diversity = false;
+ config->ms = true;
+ config->offset = 0.0;
+
+ while ((option = getopt(argc, argv, "xo")) != -1) {
switch (option) {
- case 'h':
- print_help();
- exit(0);
- break;
- case 0:
- handle_long_options(argv[0], long_option);
- break;
- case 'a':
- print_deprecated(option);
- osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
- break;
- case 'l':
- print_deprecated(option);
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'i':
- print_deprecated(option);
- osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
- break;
- case 'j':
- print_deprecated(option);
- osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
- break;
- case 'p':
- print_deprecated(option);
- trx->cfg.base_port = atoi(optarg);
- break;
- case 'c':
- print_deprecated(option);
- trx->cfg.num_chans = atoi(optarg);
- break;
- case 'm':
- print_deprecated(option);
- trx->cfg.multi_arfcn = true;
- break;
case 'x':
- print_deprecated(option);
- trx->cfg.clock_ref = REF_EXTERNAL;
- break;
- case 'g':
- print_deprecated(option);
- trx->cfg.clock_ref = REF_GPS;
- break;
- case 'f':
- print_deprecated(option);
- trx->cfg.filler = FILLER_DUMMY;
+ config->extref = true;
break;
case 'o':
- print_deprecated(option);
- trx->cfg.offset = atof(optarg);
- break;
- case 's':
- print_deprecated(option);
- trx->cfg.tx_sps = atoi(optarg);
- break;
- case 'b':
- print_deprecated(option);
- trx->cfg.rx_sps = atoi(optarg);
- break;
- case 'r':
- print_deprecated(option);
- trx->cfg.rtsc = atoi(optarg);
- if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
- trx->cfg.filler = FILLER_NORM_RAND;
- break;
- case 'A':
- print_deprecated(option);
- trx->cfg.rach_delay = atoi(optarg);
- trx->cfg.filler = FILLER_ACCESS_RAND;
- break;
- case 'R':
- print_deprecated(option);
- trx->cfg.rssi_offset = atof(optarg);
- trx->cfg.force_rssi_offset = true;
- break;
- case 'S':
- print_deprecated(option);
- trx->cfg.swap_channels = true;
- break;
- case 'e':
- print_deprecated(option);
- trx->cfg.egprs = true;
- break;
- case 't':
- print_deprecated(option);
- trx->cfg.sched_rr = atoi(optarg);
- break;
- case 'y':
- print_deprecated(option);
- tx_paths = comma_delimited_to_vector(optarg);
- tx_paths_set = true;
- break;
- case 'z':
- print_deprecated(option);
- rx_paths = comma_delimited_to_vector(optarg);
- rx_paths_set = true;
- break;
- case 'C':
- config_file = optarg;
- break;
- case 'V':
- print_version(1);
- exit(0);
+ config->offset = atof(optarg);
break;
default:
- goto bad_config;
+ print_help();
+ exit(0);
}
}
-
- if (argc > optind) {
- LOG(ERROR) << "Unsupported positional arguments on command line";
- goto bad_config;
- }
-
- /* Cmd line option specific validation & setup */
-
- if (trx->cfg.num_chans > TRX_CHAN_MAX) {
- LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
- goto bad_config;
- }
- if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
- (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
- LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
- goto bad_config;
- }
- for (i = 0; i < trx->cfg.num_chans; i++) {
- trx->cfg.chans[i].trx = trx;
- trx->cfg.chans[i].idx = i;
- if (tx_paths_set)
- osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
- if (rx_paths_set)
- osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
- }
-
- return;
-
-bad_config:
- print_help();
- exit(0);
}
-int trx_validate_config(struct trx_ctx *trx)
+const char* commands[] {
+"CMD POWEROFF",
+//"CMD RXTUNE 1782000",
+"CMD RXTUNE 931400",
+//"CMD TXTUNE 1877000",
+"CMD TXTUNE 931900",
+"CMD SETTSC 7",
+"CMD POWERON",
+"CMD SETRXGAIN 30",
+"CMD SETPOWER 0",
+//"CMD SETSLOT 6 7",
+//"CMD SETSLOT 7 13",
+//"CMD NOHANDOVER 1 0",
+"CMD SYNC",
+"CMD GETBSIC",
+};
+
+const char* commands2[] {
+"CMD GETBSIC",
+};
+
+RadioDevice *usrp;
+RadioInterface *radio = NULL;
+Transceiver2 *trx = NULL;
+
+ void* tall_trx_ctx;
+ struct trx_ctx* g_trx_ctx;
+
+int trx_main(int argc, char *argv[])
{
- if (trx->cfg.multi_arfcn && trx->cfg.num_chans > TRX_MCHAN_MAX) {
- LOG(ERROR) << "Unsupported number of channels";
- return -1;
- }
+ int type, chans;
- /* Force 4 SPS for EDGE or multi-ARFCN configurations */
- if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
- (trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
- LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
- return -1;
- }
+ struct trx_config config;
- return 0;
-}
+ std::cerr << "starting.." << std::endl;
-static int set_sched_rr(unsigned int prio)
-{
- struct sched_param param;
- int rc;
- memset(&param, 0, sizeof(param));
- param.sched_priority = prio;
- LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority
- << ". This setting is DEPRECATED, please use 'policy rr " << param.sched_priority
- << "' under the 'sched' VTY node instead.";
- rc = sched_setscheduler(getpid(), SCHED_RR, &param);
- if (rc != 0) {
- LOG(ERROR) << "Config: Setting SCHED_RR failed";
- return -1;
- }
- return 0;
-}
+ convolve_init();
+ convert_init();
-static void print_simd_info(void)
-{
-#ifdef HAVE_SSE3
- LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
-#ifdef HAVE___BUILTIN_CPU_SUPPORTS
- if (__builtin_cpu_supports("sse3"))
- LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
- else
- LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
-#else
- LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
-#endif
-#endif
-#ifdef HAVE_SSE4_1
- LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
-#ifdef HAVE___BUILTIN_CPU_SUPPORTS
- if (__builtin_cpu_supports("sse4.1"))
- LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
- else
- LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
-#else
- LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
-#endif
-#endif
+ tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
+ msgb_talloc_ctx_init(tall_trx_ctx, 0);
+ g_vty_info.tall_ctx = tall_trx_ctx;
-#ifndef HAVE_ATOMIC_OPS
-#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
- LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
-#endif
-}
+ //setup_signal_handlers();
-static void print_config(struct trx_ctx *trx)
-{
- unsigned int i;
- std::ostringstream ost("");
+ g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
- ost << "Config Settings" << std::endl;
- ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
- ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
- ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
- ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
- ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
- ost << " Channels................ " << trx->cfg.num_chans << std::endl;
- ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
- ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
- ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
- ost << " Extended RACH support... " << trx->cfg.ext_rach << std::endl;
- ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
- ost << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
- ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
- ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
- ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
- ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
- if (trx->cfg.freq_offset_khz != 0)
- ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
- ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
- ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
- ost << " Tx Antennas.............";
- for (i = 0; i < trx->cfg.num_chans; i++) {
- std::string p = charp2str(trx->cfg.chans[i].tx_path);
- ost << " '" << ((p != "") ? p : "<default>") << "'";
- }
- ost << std::endl;
- ost << " Rx Antennas.............";
- for (i = 0; i < trx->cfg.num_chans; i++) {
- std::string p = charp2str(trx->cfg.chans[i].rx_path);
- ost << " '" << ((p != "") ? p : "<default>") << "'";
- }
- ost << std::endl;
+ osmo_init_logging2(tall_trx_ctx, &log_info);
+ log_enable_multithread();
+ //osmo_stats_init(tall_trx_ctx);
+ vty_init(&g_vty_info);
+ logging_vty_add_cmds();
+ //ctrl_vty_init(tall_trx_ctx);
+ osmo_cpu_sched_vty_init(tall_trx_ctx);
+ trx_vty_init(g_trx_ctx);
- LOG(INFO) << ost << std::endl;
-}
+ osmo_talloc_vty_add_cmds();
+ //osmo_stats_vty_add_cmds();
-static void trx_stop()
-{
- LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
- delete transceiver;
- delete radio;
- delete usrp;
-}
+ sched_param sch_params;
+ sch_params.sched_priority = 19;
+ pthread_setschedparam(pthread_self(), SCHED_RR, &sch_params);
-static int trx_start(struct trx_ctx *trx)
-{
- int type, chans;
- unsigned int i;
- std::vector<std::string> rx_paths, tx_paths;
- RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
+ handle_options(argc, argv, &config);
- /* Create the low level device object */
- if (trx->cfg.multi_arfcn)
- iface = RadioDevice::MULTI_ARFCN;
+ //setup_signal_handlers();
- /* Generate vector of rx/tx_path: */
- for (i = 0; i < trx->cfg.num_chans; i++) {
- rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
- tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
+ /* Check database sanity */
+ if (!trx_setup_config(&config)) {
+ std::cerr << "Config: Database failure - exiting" << std::endl;
+ return EXIT_FAILURE;
}
- usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
- trx->cfg.num_chans, trx->cfg.offset,
- tx_paths, rx_paths);
- type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
+ //gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
+
+ srandom(time(NULL));
+
+ /* Create the low level device object */
+ usrp = RadioDevice::make(config.sps, config.sps, RadioDevice::NORMAL, config.chans,
+ config.offset);
+ type = usrp->open(config.dev_args, config.extref, false);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
}
/* Setup the appropriate device interface */
- radio = makeRadioInterface(trx, usrp, type);
+ radio = makeRadioInterface(&config, usrp, type);
if (!radio)
goto shutdown;
/* Create the transceiver core */
- if (makeTransceiver(trx, radio) < 0)
+ trx = makeTransceiver(&config, radio);
+ if (!trx)
goto shutdown;
- chans = transceiver->numChans();
- LOG(NOTICE) << "-- Transceiver active with "
- << chans << " channel(s)" << std::endl;
-
- return 0;
-
-shutdown:
- trx_stop();
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- int rc;
-
- tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
- msgb_talloc_ctx_init(tall_trx_ctx, 0);
- g_vty_info.tall_ctx = tall_trx_ctx;
-
- setup_signal_handlers();
-
- g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
-
- convolve_init();
- convert_init();
+ trx->start();
- osmo_init_logging2(tall_trx_ctx, &log_info);
- log_enable_multithread();
- osmo_stats_init(tall_trx_ctx);
- vty_init(&g_vty_info);
- logging_vty_add_cmds();
- ctrl_vty_init(tall_trx_ctx);
- osmo_cpu_sched_vty_init(tall_trx_ctx);
- trx_vty_init(g_trx_ctx);
-
- osmo_talloc_vty_add_cmds();
- osmo_stats_vty_add_cmds();
-
- handle_options(argc, argv, g_trx_ctx);
-
- rate_ctr_init(tall_trx_ctx);
-
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
- exit(2);
- }
+ chans = trx->numChans();
+ std::cout << "-- Transceiver active with "
+ << chans << " channel(s)" << std::endl;
- rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
- if (rc < 0)
- exit(1);
- g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
- if (!g_ctrlh) {
- LOG(ERROR) << "Failed to create CTRL interface.\n";
- exit(1);
+#if 0
+ for(auto i: commands) {
+ int MAX_PACKET_LENGTH = 100;
+ char response[MAX_PACKET_LENGTH];
+ char buffer[MAX_PACKET_LENGTH];
+ memset(response, 0, sizeof(response));
+ memset(buffer, 0, sizeof(buffer));
+ sprintf(buffer, "%s", i);
+ trx->commandhandler(buffer, response, 0);
+ std::cerr << i << "\tr: " << response << " ##" << std::endl;
}
- /* Backward compatibility: Hack to have 1 channel allocated by default.
- * Can be Dropped once we * get rid of "-c" cmdline param */
- if (g_trx_ctx->cfg.num_chans == 0) {
- g_trx_ctx->cfg.num_chans = 1;
- g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
- g_trx_ctx->cfg.chans[0].idx = 0;
- LOG(ERROR) << "No explicit channel config found. Make sure you" \
- " configure channels in VTY config. Using 1 channel as default," \
- " but expect your config to break in the future.";
+ sleep(30);
+
+ for(auto i: commands2) {
+ int MAX_PACKET_LENGTH = 100;
+ char response[MAX_PACKET_LENGTH];
+ char buffer[MAX_PACKET_LENGTH];
+ memset(response, 0, sizeof(response));
+ memset(buffer, 0, sizeof(buffer));
+ sprintf(buffer, "%s", i);
+ trx->commandhandler(buffer, response, 0);
+ std::cerr << i << "\tr: " << response << " ##" << std::endl;
}
+#endif
- print_simd_info();
- print_config(g_trx_ctx);
+// while (!gshutdown)
+// sleep(1);
- if (trx_validate_config(g_trx_ctx) < 0) {
- LOG(ERROR) << "Config failure - exiting";
- return EXIT_FAILURE;
- }
-
- if (g_trx_ctx->cfg.sched_rr) {
- if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
- return EXIT_FAILURE;
- }
+shutdown:
+// std::cout << "Shutting down transceiver..." << std::endl;
- osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
- trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
+// delete trx;
+// delete radio;
+// delete usrp;
- srandom(time(NULL));
+ return 0;
+}
- if(trx_start(g_trx_ctx) < 0)
- return EXIT_FAILURE;
+extern "C" void init_external_transceiver(int argc, char **argv) {
+ trx_main(argc, argv);
+}
- while (!gshutdown)
- osmo_select_main(0);
+extern "C" void stop_trx() {
+ std::cout << "Shutting down transceiver..." << std::endl;
- trx_stop();
+ delete trx;
+ delete radio;
+ delete usrp;
- osmo_fd_unregister(&signal_ofd);
- osmo_fd_close(&signal_ofd);
- osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
- return 0;
}
diff --git a/Transceiver52M/radioClock.cpp b/Transceiver52M/radioClock.cpp
index 2befd0d..61255c0 100644
--- a/Transceiver52M/radioClock.cpp
+++ b/Transceiver52M/radioClock.cpp
@@ -30,6 +30,41 @@ void RadioClock::set(const GSM::Time& wTime)
updateSignal.signal();
}
+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()
{
ScopedLock lock(mLock);
diff --git a/Transceiver52M/radioClock.h b/Transceiver52M/radioClock.h
index 472f43d..9dd2e43 100644
--- a/Transceiver52M/radioClock.h
+++ b/Transceiver52M/radioClock.h
@@ -28,7 +28,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();
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index 875245d..70f098e 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -41,7 +41,8 @@ RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
int wReceiveOffset, GSM::Time wStartTime)
: mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
- receiveOffset(wReceiveOffset), mOn(false)
+ receiveOffset(wReceiveOffset), shiftOffset(0), shiftUpdate(false),
+ mOn(false)
{
mClock.set(wStartTime);
}
@@ -165,11 +166,21 @@ bool RadioInterface::tuneTx(double freq, size_t chan)
return mDevice->setTxFreq(freq, chan);
}
+void RadioInterface::adjustClock(GSM::Time &offset)
+{
+ mClock.adjust(offset);
+}
+
bool RadioInterface::tuneRx(double freq, size_t chan)
{
return mDevice->setRxFreq(freq, chan);
}
+bool RadioInterface::tuneRxOffset(double offset, size_t chan)
+{
+ return mDevice->setRxOffset(offset, chan);
+}
+
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
@@ -256,7 +267,10 @@ int RadioInterface::driveReceiveRadio()
return -1;
GSM::Time rcvClock = mClock.get();
- rcvClock.decTN(receiveOffset);
+ if (receiveOffset < 0)
+ rcvClock.incTN(-receiveOffset);
+ else
+ rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int recvSz = recvBuffer[0]->getAvailSamples();
const int symbolsPerSlot = gSlotLen + 8;
@@ -309,6 +323,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())
@@ -341,7 +361,7 @@ int RadioInterface::pullBuffer()
numRecv = mDevice->readSamples(convertRecvBuffer,
segmentLen,
&overrun,
- readTimestamp,
+ readTimestamp + mSPSRx * shiftOffset,
&local_underrun);
if ((size_t) numRecv != segmentLen) {
@@ -376,11 +396,16 @@ bool RadioInterface::pushBuffer()
segmentLen * 2);
}
+ if (shiftUpdate) {
+ mDevice->updateAlignment(0);
+ shiftUpdate = false;
+ }
+
/* Send the all samples in the send buffer */
numSent = mDevice->writeSamples(convertSendBuffer,
segmentLen,
&local_underrun,
- writeTimestamp);
+ writeTimestamp + mSPSTx * shiftOffset);
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += numSent;
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index efe5606..f05a376 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -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:
@@ -93,6 +94,7 @@ public:
/** check for underrun, resets underrun value */
bool isUnderrun();
+ void applyOffset(int offset);
/** return the receive FIFO */
VectorFIFO* receiveFIFO(size_t chan = 0);
@@ -100,12 +102,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 */
virtual 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 */
virtual double setRxGain(double dB, size_t chan = 0);
diff --git a/Transceiver52M/radioInterfaceMulti.cpp b/Transceiver52M/radioInterfaceMulti.cpp
index eaf0886..a419086 100644
--- a/Transceiver52M/radioInterfaceMulti.cpp
+++ b/Transceiver52M/radioInterfaceMulti.cpp
@@ -258,7 +258,7 @@ int RadioInterfaceMulti::pullBuffer()
num = mDevice->readSamples(convertRecvBuffer,
outerRecvBuffer->size(),
&overrun,
- readTimestamp,
+ readTimestamp + shiftOffset,
&local_underrun);
if (num != channelizer->inputLen()) {
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index b92432f..d377022 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -174,7 +174,7 @@ int RadioInterfaceResamp::pullBuffer()
num_recv = mDevice->readSamples(convertRecvBuffer,
resamp_outchunk,
&overrun,
- readTimestamp,
+ readTimestamp + shiftOffset,
&local_underrun);
if (num_recv != (int) resamp_outchunk) {
LOG(ALERT) << "Receive error " << num_recv;
diff --git a/Transceiver52M/radioVector.cpp b/Transceiver52M/radioVector.cpp
index 68e42c5..2e85d7a 100644
--- a/Transceiver52M/radioVector.cpp
+++ b/Transceiver52M/radioVector.cpp
@@ -85,6 +85,9 @@ float noiseVector::avg() const
{
float val = 0.0;
+ if (!size())
+ return 0.0f;
+
for (size_t i = 0; i < size(); i++)
val += (*this)[i];
@@ -93,8 +96,10 @@ float noiseVector::avg() const
bool noiseVector::insert(float val)
{
- if (!size())
- return false;
+ if (size() < max) {
+ push_back(val);
+ return true;
+ }
if (itr >= this->size())
itr = 0;
@@ -104,6 +109,16 @@ bool noiseVector::insert(float val)
return true;
}
+bool noiseVector::full() const
+{
+ return size() >= max;
+}
+
+void noiseVector::reset()
+{
+ resize(0);
+}
+
GSM::Time VectorQueue::nextTime() const
{
GSM::Time retVal;
diff --git a/Transceiver52M/radioVector.h b/Transceiver52M/radioVector.h
index 84e3987..2cb2806 100644
--- a/Transceiver52M/radioVector.h
+++ b/Transceiver52M/radioVector.h
@@ -52,10 +52,13 @@ class noiseVector : std::vector<float> {
public:
noiseVector(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> { };
diff --git a/Transceiver52M/sch.c b/Transceiver52M/sch.c
new file mode 100644
index 0000000..5d2b5c6
--- /dev/null
+++ b/Transceiver52M/sch.c
@@ -0,0 +1,299 @@
+#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 * 4
+#define GSM_SYM_RATE (1625e3 / 6) * 4
+
+/* 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 FCCH burst */
+int gsm_fcch_check_fn(int fn)
+{
+ int fn51 = fn % 51;
+
+ switch (fn51) {
+ case 0:
+ case 10:
+ case 20:
+ case 30:
+ case 40:
+ return 1;
+ }
+
+ 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) | (p->bsic[5] << 5);
+
+ 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*4
+#define FCCH_DATA_LEN 100*4// 142
+#if 1
+/* Compute FCCH frequency offset */
+double org_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;
+}
+
+
+static const int L1 = 3;
+static const int L2 = 32;
+static const int N1 = 92;
+static const int N2 = 92;
+
+static struct { int8_t r; int8_t s; } P_inv_table[3+32];
+
+void pinv(int P, int8_t* r, int8_t* s, int L1, int L2) {
+ for (int i = 0; i < L1; i++)
+ for (int j = 0; j < L2; j++)
+ if (P == L2 * i - L1 * j) {
+ *r = i;
+ *s = j;
+ return;
+ }
+}
+
+
+float ac_sum_with_lag( complex float* in, int lag, int offset, int N) {
+ complex float v = 0 + 0*I;
+ int total_offset = offset + lag;
+ for (int s = 0; s < N; s++)
+ v += in[s + total_offset] * conjf(in[s + total_offset - lag]);
+ return cargf(v);
+}
+
+
+double gsm_fcch_offset(float *burst, int len)
+{
+ int start;
+
+ const float fs = 13. / 48. * 1e6 * 4;
+ const float expected_fcch_val = ((2 * M_PI) / (fs)) * 67700;
+
+ if (len > GSM_MAX_BURST_LEN)
+ len = GSM_MAX_BURST_LEN;
+
+ start = FCCH_TAIL_BITS_LEN+10 * 4;
+ float alpha_one = ac_sum_with_lag((complex float*)burst, L1, start, N1);
+ float alpha_two = ac_sum_with_lag((complex float*)burst, L2, start, N2);
+
+ float P_unrounded = (L1 * alpha_two - L2 * alpha_one) / (2 * M_PI);
+ int P = roundf(P_unrounded);
+
+ int8_t r = 0, s = 0;
+ pinv(P, &r, &s, L1, L2);
+
+ float omegal1 = (alpha_one + 2 * M_PI * r) / L1;
+ float omegal2 = (alpha_two + 2 * M_PI * s) / L2;
+
+ float rv = org_gsm_fcch_offset(burst, len);
+ //return rv;
+
+ float reval = GSM_SYM_RATE / (2 * M_PI) * (expected_fcch_val - (omegal1+omegal2)/2);
+ //fprintf(stderr, "XX rv %f %f %f %f\n", rv, reval, omegal1 / (2 * M_PI) * fs, omegal2 / (2 * M_PI) * fs);
+
+ //fprintf(stderr, "XX rv %f %f\n", rv, reval);
+
+ return -reval;
+}
+#endif
+/* 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
new file mode 100644
index 0000000..445792b
--- /dev/null
+++ b/Transceiver52M/sch.h
@@ -0,0 +1,27 @@
+#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 gsm_fcch_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_ */
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
index df87f94..792c781 100644
--- a/Transceiver52M/sigProcLib.cpp
+++ b/Transceiver52M/sigProcLib.cpp
@@ -98,6 +98,7 @@ struct CorrelationSequence {
signalVector *sequence;
void *buffer;
+ void *history;
float toa;
complex gain;
};
@@ -129,6 +130,7 @@ struct PulseSequence {
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
+static CorrelationSequence *gSCHSequence = NULL;
static PulseSequence *GSMPulse1 = NULL;
static PulseSequence *GSMPulse4 = NULL;
@@ -151,6 +153,9 @@ void sigProcLibDestroy()
gRACHSequences[i] = NULL;
}
+ delete gSCHSequence;
+ gSCHSequence = NULL;
+
delete GMSKRotation1;
delete GMSKReverseRotation1;
delete GMSKRotation4;
@@ -315,6 +320,8 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
append = true;
break;
case CUSTOM:
+
+ // FIXME: x->getstart?
if (start < h->size() - 1) {
head = h->size() - start;
append = true;
@@ -1384,6 +1391,70 @@ 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;
+}
+
+
/*
* Peak-to-average computation +/- range from peak in symbols
*/
@@ -1469,6 +1540,9 @@ static float computeCI(const signalVector *burst, const CorrelationSequence *syn
/* Integer position where the sequence starts */
const int ps = start + 1 - N + (int)roundf(toa);
+ if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
+ return 0;
+
/* Estimate Signal power */
S = 0.0f;
for (int i=0, j=ps; i<(int)N; i++,j++)
@@ -1652,6 +1726,66 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
return rc;
}
+int detectSCHBurst(signalVector &burst,
+ float thresh,
+ int sps,
+ sch_detect_type state, struct estim_burst_params *ebp)
+{
+ 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_type::SCH_DETECT_NARROW:
+ head = 4;
+ tail = 4;
+ break;
+ case sch_detect_type::SCH_DETECT_FULL:
+ default:
+ head = target - 1;
+ tail = 39 + 3 + 9;
+ break;
+ }
+
+ start = (target - head) * 1 - 1;
+ len = (head + tail) * 1;
+ 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, start, len, ebp);
+ delete corr;
+
+ if (rc < 0) {
+ return -1;
+ } else if (!rc) {
+ ebp->amp = 0.0f;
+ ebp->toa = 0.0f;
+ return 0;
+ }
+
+ /* Subtract forward search bits from delay */
+ ebp->toa = ebp->toa - head;
+
+ return rc;
+}
+
+
/*
* Normal burst detection
*
@@ -1670,7 +1804,7 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
return -SIGERR_UNSUPPORTED;
target = 3 + 58 + 16 + 5;
- head = 6;
+ head = 10;
tail = 6 + max_toa;
sync = gMidambles[tsc];
@@ -1921,6 +2055,8 @@ bool sigProcLibSetup()
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
+ generateSCHSequence(1);
+
for (int tsc = 0; tsc < 8; tsc++) {
generateMidamble(1, tsc);
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
index 0c3c7c6..73d4704 100644
--- a/Transceiver52M/sigProcLib.h
+++ b/Transceiver52M/sigProcLib.h
@@ -31,6 +31,7 @@ enum CorrType{
TSC, ///< timeslot should contain a normal burst
EXT_RACH, ///< timeslot should contain an extended access burst
RACH, ///< timeslot should contain an access burst
+ SCH,
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
};
@@ -133,6 +134,16 @@ int detectAnyBurst(const signalVector &burst,
unsigned max_toa,
struct estim_burst_params *ebp);
+enum class sch_detect_type {
+ SCH_DETECT_FULL,
+ SCH_DETECT_NARROW,
+};
+
+int detectSCHBurst(signalVector &rxBurst,
+ float detectThreshold,
+ int sps,
+ sch_detect_type state, struct estim_burst_params *ebp);
+
/** Demodulate burst basde on type and output soft bits */
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
int sps, struct estim_burst_params *ebp);