blob: 9c55d020fce37f596ab32f7b7cf898fa1c53e06a [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
dburgessb3a0ca42011-10-12 07:44:40 +000024#include <stdio.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040025#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040026#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000027#include "Transceiver.h"
28#include <Logger.h>
29
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020030extern "C" {
31#include "osmo_signal.h"
32}
33
ttsou2173abf2012-08-08 00:51:31 +000034#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
dburgessb3a0ca42011-10-12 07:44:40 +000037
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040038using namespace GSM;
39
kurtis.heimerlec842de2012-11-23 08:37:32 +000040#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000041
Thomas Tsoufa3a7872013-10-17 21:23:34 -040042/* Number of running values use in noise average */
43#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000044
Thomas Tsouf0782732013-10-29 15:55:47 -040045TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080046 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040047{
48 for (int i = 0; i < 8; i++) {
49 chanType[i] = Transceiver::NONE;
50 fillerModulus[i] = 26;
51 chanResponse[i] = NULL;
52 DFEForward[i] = NULL;
53 DFEFeedback[i] = NULL;
54
55 for (int n = 0; n < 102; n++)
56 fillerTable[n][i] = NULL;
57 }
58}
59
60TransceiverState::~TransceiverState()
61{
62 for (int i = 0; i < 8; i++) {
63 delete chanResponse[i];
64 delete DFEForward[i];
65 delete DFEFeedback[i];
66
67 for (int n = 0; n < 102; n++)
68 delete fillerTable[n][i];
69 }
70}
71
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010072bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070073{
Tom Tsou64ad7122015-05-19 18:26:31 -070074 signalVector *burst;
75
76 if ((sps != 1) && (sps != 4))
77 return false;
78
79 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070080 for (size_t i = 0; i < 102; i++) {
81 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010082 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080083 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070084 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010085 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080086 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070087 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010088 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080089 burst = generateEdgeBurst(rtsc);
90 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010091 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +030092 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +030093 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010094 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -070095 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -080096 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070097 }
98
99 scaleVector(*burst, scale);
100 fillerTable[i][n] = burst;
101 }
102
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100103 if ((filler == FILLER_NORM_RAND) ||
104 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700105 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800106 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500107 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700108
109 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400110}
111
dburgessb3a0ca42011-10-12 07:44:40 +0000112Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200113 const char *TRXAddress,
114 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800115 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400116 GSM::Time wTransmitLatency,
117 RadioInterface *wRadioInterface,
118 double wRssiOffset)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200119 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
120 mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800121 mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200122 rssiOffset(wRssiOffset),
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200123 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300124 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
125 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000126{
dburgessb3a0ca42011-10-12 07:44:40 +0000127 txFullScale = mRadioInterface->fullScaleInputValue();
128 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300129
130 for (int i = 0; i < 8; i++) {
131 for (int j = 0; j < 8; j++)
132 mHandover[i][j] = false;
133 }
dburgessb3a0ca42011-10-12 07:44:40 +0000134}
135
136Transceiver::~Transceiver()
137{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800138 stop();
139
dburgessb3a0ca42011-10-12 07:44:40 +0000140 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400141
Thomas Tsou204a9f12013-10-29 18:34:16 -0400142 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800143 mControlServiceLoopThreads[i]->cancel();
144 mControlServiceLoopThreads[i]->join();
145 delete mControlServiceLoopThreads[i];
146
Thomas Tsou204a9f12013-10-29 18:34:16 -0400147 mTxPriorityQueues[i].clear();
148 delete mCtrlSockets[i];
149 delete mDataSockets[i];
150 }
dburgessb3a0ca42011-10-12 07:44:40 +0000151}
Thomas Tsou83e06892013-08-20 16:10:01 -0400152
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800153/*
154 * Initialize transceiver
155 *
156 * Start or restart the control loop. Any further control is handled through the
157 * socket API. Randomize the central radio clock set the downlink burst
158 * counters. Note that the clock will not update until the radio starts, but we
159 * are still expected to report clock indications through control channel
160 * activity.
161 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200162bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
163 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400164{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500165 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400166
Thomas Tsou204a9f12013-10-29 18:34:16 -0400167 if (!mChans) {
168 LOG(ALERT) << "No channels assigned";
169 return false;
170 }
171
Tom Tsou2079a3c2016-03-06 00:58:56 -0800172 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400173 LOG(ALERT) << "Failed to initialize signal processing library";
174 return false;
175 }
176
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200177 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700178 mEdge = edge;
179
Thomas Tsou204a9f12013-10-29 18:34:16 -0400180 mDataSockets.resize(mChans);
181 mCtrlSockets.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400182 mControlServiceLoopThreads.resize(mChans);
183 mTxPriorityQueueServiceLoopThreads.resize(mChans);
184 mRxServiceLoopThreads.resize(mChans);
185
186 mTxPriorityQueues.resize(mChans);
187 mReceiveFIFO.resize(mChans);
188 mStates.resize(mChans);
189
Thomas Tsouccb73e12014-04-15 17:41:28 -0400190 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700191 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400192 mStates[0].mRetrans = true;
193
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800194 /* Setup sockets */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400195 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500196 c_srcport = mBasePort + 2 * i + 1;
197 c_dstport = mBasePort + 2 * i + 101;
198 d_srcport = mBasePort + 2 * i + 2;
199 d_dstport = mBasePort + 2 * i + 102;
200
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200201 mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
202 mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400203 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400204
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800205 /* Randomize the central clock */
206 GSM::Time startTime(random() % gHyperframe, 0);
207 mRadioInterface->getClock()->set(startTime);
208 mTransmitDeadlineClock = startTime;
209 mLastClockUpdateTime = startTime;
210 mLatencyUpdateTime = startTime;
211
212 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400213 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800214 TransceiverChannel *chan = new TransceiverChannel(this, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400215 mControlServiceLoopThreads[i] = new Thread(32768);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800216 mControlServiceLoopThreads[i]->start((void * (*)(void*))
217 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400218
Tom Tsou64ad7122015-05-19 18:26:31 -0700219 if (i && filler == FILLER_DUMMY)
220 filler = FILLER_ZERO;
221
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300222 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400223 }
224
225 return true;
226}
dburgessb3a0ca42011-10-12 07:44:40 +0000227
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800228/*
229 * Start the transceiver
230 *
231 * Submit command(s) to the radio device to commence streaming samples and
232 * launch threads to handle sample I/O. Re-synchronize the transmit burst
233 * counters to the central radio clock here as well.
234 */
235bool Transceiver::start()
236{
237 ScopedLock lock(mLock);
238
239 if (mOn) {
240 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300241 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800242 }
243
244 LOG(NOTICE) << "Starting the transceiver";
245
246 GSM::Time time = mRadioInterface->getClock()->get();
247 mTransmitDeadlineClock = time;
248 mLastClockUpdateTime = time;
249 mLatencyUpdateTime = time;
250
251 if (!mRadioInterface->start()) {
252 LOG(ALERT) << "Device failed to start";
253 return false;
254 }
255
256 /* Device is running - launch I/O threads */
257 mRxLowerLoopThread = new Thread(32768);
258 mTxLowerLoopThread = new Thread(32768);
259 mTxLowerLoopThread->start((void * (*)(void*))
260 TxLowerLoopAdapter,(void*) this);
261 mRxLowerLoopThread->start((void * (*)(void*))
262 RxLowerLoopAdapter,(void*) this);
263
264 /* Launch uplink and downlink burst processing threads */
265 for (size_t i = 0; i < mChans; i++) {
266 TransceiverChannel *chan = new TransceiverChannel(this, i);
267 mRxServiceLoopThreads[i] = new Thread(32768);
268 mRxServiceLoopThreads[i]->start((void * (*)(void*))
269 RxUpperLoopAdapter, (void*) chan);
270
271 chan = new TransceiverChannel(this, i);
272 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
273 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
274 TxUpperLoopAdapter, (void*) chan);
275 }
276
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200277 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800278 mOn = true;
279 return true;
280}
281
282/*
283 * Stop the transceiver
284 *
285 * Perform stopping by disabling receive streaming and issuing cancellation
286 * requests to running threads. Most threads will timeout and terminate once
287 * device is disabled, but the transmit loop may block waiting on the central
288 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
289 * makes it to the thread cancellation point.
290 */
291void Transceiver::stop()
292{
293 ScopedLock lock(mLock);
294
295 if (!mOn)
296 return;
297
298 LOG(NOTICE) << "Stopping the transceiver";
299 mTxLowerLoopThread->cancel();
300 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700301 mTxLowerLoopThread->join();
302 mRxLowerLoopThread->join();
303 delete mTxLowerLoopThread;
304 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800305
306 for (size_t i = 0; i < mChans; i++) {
307 mRxServiceLoopThreads[i]->cancel();
308 mTxPriorityQueueServiceLoopThreads[i]->cancel();
309 }
310
311 LOG(INFO) << "Stopping the device";
312 mRadioInterface->stop();
313
314 for (size_t i = 0; i < mChans; i++) {
315 mRxServiceLoopThreads[i]->join();
316 mTxPriorityQueueServiceLoopThreads[i]->join();
317 delete mRxServiceLoopThreads[i];
318 delete mTxPriorityQueueServiceLoopThreads[i];
319
320 mTxPriorityQueues[i].clear();
321 }
322
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800323 mOn = false;
324 LOG(NOTICE) << "Transceiver stopped";
325}
326
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500327void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400328 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000329{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500330 signalVector *burst;
331 radioVector *radio_burst;
332
Thomas Tsou204a9f12013-10-29 18:34:16 -0400333 if (chan >= mTxPriorityQueues.size()) {
334 LOG(ALERT) << "Invalid channel " << chan;
335 return;
336 }
337
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500338 if (wTime.TN() > 7) {
339 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
340 return;
341 }
342
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800343 /* Use the number of bits as the EDGE burst indicator */
344 if (bits.size() == EDGE_BURST_NBITS)
345 burst = modulateEdgeBurst(bits, mSPSTx);
346 else
347 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
348
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500349 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000350
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500351 radio_burst = new radioVector(wTime, burst);
352
353 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000354}
355
Thomas Tsou15d743e2014-01-25 02:34:03 -0500356void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
357{
358 int TN, modFN;
359 TransceiverState *state = &mStates[chan];
360
361 TN = burst->getTime().TN();
362 modFN = burst->getTime().FN() % state->fillerModulus[TN];
363
364 delete state->fillerTable[modFN][TN];
365 state->fillerTable[modFN][TN] = burst->getVector();
366 burst->setVector(NULL);
367}
368
dburgessb3a0ca42011-10-12 07:44:40 +0000369void Transceiver::pushRadioVector(GSM::Time &nowTime)
370{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400371 int TN, modFN;
372 radioVector *burst;
373 TransceiverState *state;
374 std::vector<signalVector *> bursts(mChans);
375 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500376 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000377
Thomas Tsou204a9f12013-10-29 18:34:16 -0400378 for (size_t i = 0; i < mChans; i ++) {
379 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000380
Thomas Tsou204a9f12013-10-29 18:34:16 -0400381 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200382 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200383 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500384 if (state->mRetrans)
385 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500386 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400387 }
388
389 TN = nowTime.TN();
390 modFN = nowTime.FN() % state->fillerModulus[TN];
391
392 bursts[i] = state->fillerTable[modFN][TN];
393 zeros[i] = state->chanType[TN] == NONE;
394
395 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500396 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500397
398 if (state->mRetrans) {
399 updateFillerTable(i, burst);
400 } else {
401 burst->setVector(NULL);
402 filler[i] = false;
403 }
404
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500405 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400406 }
dburgessb3a0ca42011-10-12 07:44:40 +0000407 }
408
Thomas Tsou204a9f12013-10-29 18:34:16 -0400409 mRadioInterface->driveTransmitRadio(bursts, zeros);
410
Thomas Tsou15d743e2014-01-25 02:34:03 -0500411 for (size_t i = 0; i < mChans; i++) {
412 if (!filler[i])
413 delete bursts[i];
414 }
dburgessb3a0ca42011-10-12 07:44:40 +0000415}
416
Thomas Tsou204a9f12013-10-29 18:34:16 -0400417void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000418{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400419 TransceiverState *state = &mStates[chan];
420
421 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000422 case NONE:
423 case I:
424 case II:
425 case III:
426 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400427 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000428 break;
429 case IV:
430 case VI:
431 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000433 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200434 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000435 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400436 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000437 break;
ttsoufc40a842013-06-09 22:38:18 +0000438 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400439 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000440 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000441 default:
442 break;
443 }
444}
445
446
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700447CorrType Transceiver::expectedCorrType(GSM::Time currTime,
448 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000449{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300450 static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
451 static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
452 3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
453 static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
454 1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000456 unsigned burstTN = currTime.TN();
457 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300458 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000459
Thomas Tsou204a9f12013-10-29 18:34:16 -0400460 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000461 case NONE:
462 return OFF;
463 break;
464 case FILL:
465 return IDLE;
466 break;
467 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300468 // TODO: Are we expecting RACH on an IDLE frame?
469/* if (burstFN % 26 == 25)
470 return IDLE;*/
471 if (mHandover[burstTN][0])
472 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000473 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000474 break;
475 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300476 subch = tchh_subslot[burstFN % 26];
477 if (subch == 1)
478 return IDLE;
479 if (mHandover[burstTN][0])
480 return RACH;
ttsou20642972013-03-27 22:00:25 +0000481 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000482 break;
483 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300484 subch = tchh_subslot[burstFN % 26];
485 if (mHandover[burstTN][subch])
486 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000487 return TSC;
488 break;
489 case IV:
490 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200491 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000492 break;
493 case V: {
494 int mod51 = burstFN % 51;
495 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200496 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000497 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200498 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000499 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200500 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300501 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
502 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000503 else
504 return TSC;
505 break;
506 }
507 case VII:
508 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
509 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300510 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
511 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000512 else
513 return TSC;
514 break;
ttsoufc40a842013-06-09 22:38:18 +0000515 case XIII: {
516 int mod52 = burstFN % 52;
517 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200518 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000519 else if ((mod52 == 25) || (mod52 == 51))
520 return IDLE;
521 else
522 return TSC;
523 break;
524 }
dburgessb3a0ca42011-10-12 07:44:40 +0000525 case LOOPBACK:
526 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
527 return IDLE;
528 else
529 return TSC;
530 break;
531 default:
532 return OFF;
533 break;
534 }
dburgessb3a0ca42011-10-12 07:44:40 +0000535}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400536
Alexander Chemerise692ce92015-06-12 00:15:31 -0400537void writeToFile(radioVector *radio_burst, size_t chan)
538{
539 GSM::Time time = radio_burst->getTime();
540 std::ostringstream fname;
541 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
542 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
543 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
544 outfile.close();
545}
546
Thomas Tsou30421a72013-11-13 23:14:48 -0500547/*
548 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200549 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500550 */
Alexander Chemeris2b542102015-06-08 22:46:38 -0400551SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400552 double &timingOffset, double &noise,
553 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000554{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800555 int rc;
Thomas Tsou30421a72013-11-13 23:14:48 -0500556 complex amp;
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300557 float toa, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500558 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500559 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500560 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500561 TransceiverState *state = &mStates[chan];
Alexander Chemeris2b542102015-06-08 22:46:38 -0400562 isRssiValid = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000563
Thomas Tsou30421a72013-11-13 23:14:48 -0500564 /* Blocking FIFO read */
565 radioVector *radio_burst = mReceiveFIFO[chan]->read();
566 if (!radio_burst)
567 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000568
Thomas Tsou30421a72013-11-13 23:14:48 -0500569 /* Set time and determine correlation type */
570 GSM::Time time = radio_burst->getTime();
571 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000572
Tom Tsou64464e62016-07-01 03:46:46 -0700573 /* Enable 8-PSK burst detection if EDGE is enabled */
574 if (mEdge && (type == TSC))
575 type = EDGE;
576
Alexander Chemerise692ce92015-06-12 00:15:31 -0400577 /* Debug: dump bursts to disk */
578 /* bits 0-7 - chan 0 timeslots
579 * bits 8-15 - chan 1 timeslots */
580 if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
581 writeToFile(radio_burst, chan);
582
Alexander Chemeris2b542102015-06-08 22:46:38 -0400583 /* No processing if the timeslot is off.
584 * Not even power level or noise calculation. */
585 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500586 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000587 return NULL;
588 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000589
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500590 /* Select the diversity channel with highest energy */
591 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300592 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500593 if (pow > max) {
594 max = pow;
595 max_i = i;
596 }
597 avg += pow;
598 }
599
600 if (max_i < 0) {
601 LOG(ALERT) << "Received empty burst";
602 delete radio_burst;
603 return NULL;
604 }
605
Thomas Tsou30421a72013-11-13 23:14:48 -0500606 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500607 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500608 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400609
610 wTime = time;
611 RSSI = 20.0 * log10(rxFullScale / avg);
612
613 /* RSSI estimation are valid */
614 isRssiValid = true;
615
616 if (type == IDLE) {
617 /* Update noise levels */
618 state->mNoises.insert(avg);
619 state->mNoiseLev = state->mNoises.avg();
620 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
621
622 delete radio_burst;
623 return NULL;
624 } else {
625 /* Do not update noise levels */
626 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
627 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400628
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200629 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
630 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
631
Thomas Tsou30421a72013-11-13 23:14:48 -0500632 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200633 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400634
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800635 if (rc > 0) {
636 type = (CorrType) rc;
637 } else if (rc <= 0) {
638 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400639 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800640 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400641 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700642 }
643
Thomas Tsou30421a72013-11-13 23:14:48 -0500644 delete radio_burst;
645 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000646 }
dburgessb3a0ca42011-10-12 07:44:40 +0000647
Tom Tsou4609f322016-07-19 11:28:51 -0700648 timingOffset = toa;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400649
Alexander Chemeris6e1dffd2017-03-17 16:13:51 -0700650 bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500651
Thomas Tsou30421a72013-11-13 23:14:48 -0500652 delete radio_burst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500653 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000654}
655
dburgessb3a0ca42011-10-12 07:44:40 +0000656void Transceiver::reset()
657{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400658 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
659 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000660}
661
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200662
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700663#define MAX_PACKET_LENGTH 100
664
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700665/**
666 * Matches a buffer with a command.
667 * @param buf a buffer to look command in
668 * @param cmd a command to look in buffer
669 * @param params pointer to arguments, or NULL
670 * @return true if command matches, otherwise false
671 */
672static bool match_cmd(char *buf,
673 const char *cmd, char **params)
674{
675 size_t cmd_len = strlen(cmd);
676
677 /* Check a command itself */
678 if (strncmp(buf, cmd, cmd_len))
679 return false;
680
681 /* A command has arguments */
682 if (params != NULL) {
683 /* Make sure there is a space */
684 if (buf[cmd_len] != ' ')
685 return false;
686
687 /* Update external pointer */
688 *params = buf + cmd_len + 1;
689 }
690
691 return true;
692}
693
Thomas Tsou204a9f12013-10-29 18:34:16 -0400694void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000695{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700696 char buffer[MAX_PACKET_LENGTH + 1];
697 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700698 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700699 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400700
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700701 /* Attempt to read from control socket */
702 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
703 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000704 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700705
706 /* Zero-terminate received string */
707 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000708
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700709 /* Verify a command signature */
710 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100711 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000712 return;
713 }
dburgessb3a0ca42011-10-12 07:44:40 +0000714
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700715 /* Set command pointer */
716 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200717 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700718
719 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800720 stop();
721 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700722 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700723 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000724 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700725 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000726 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300727 for (int i = 0; i < 8; i++) {
728 for (int j = 0; j < 8; j++)
729 mHandover[i][j] = false;
730 }
Tom Tsou365bc382016-10-19 15:26:04 -0700731 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700732 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700733 unsigned ts = 0, ss = 0;
734 sscanf(params, "%u %u", &ts, &ss);
735 if (ts > 7 || ss > 7) {
736 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
737 } else {
738 mHandover[ts][ss] = true;
739 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
740 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700741 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700742 unsigned ts = 0, ss = 0;
743 sscanf(params, "%u %u", &ts, &ss);
744 if (ts > 7 || ss > 7) {
745 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
746 } else {
747 mHandover[ts][ss] = false;
748 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
749 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700750 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000751 //set expected maximum time-of-arrival
752 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700753 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300754 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000755 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700756 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300757 //set expected maximum time-of-arrival
758 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700759 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300760 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
761 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700762 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000763 //set expected maximum time-of-arrival
764 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700765 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400766 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000767 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700768 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000769 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500770 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000771 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500772 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000773 }
774 else {
775 sprintf(response,"RSP NOISELEV 1 0");
776 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800778 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700779 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800780 power = mRadioInterface->setPowerAttenuation(power, chan);
781 mStates[chan].mPower = power;
782 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800784 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800786 power = mStates[chan].mPower + step;
787 power = mRadioInterface->setPowerAttenuation(power, chan);
788 mStates[chan].mPower = power;
789 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000791 // tune receiver
792 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700793 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500794 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400795 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100796 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000797 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
798 }
799 else
800 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700801 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000802 // tune txmtr
803 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700804 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500805 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400806 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100807 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000808 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
809 }
810 else
811 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000813 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500814 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700815 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700816 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500817 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700818 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100819 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000820 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400821 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000822 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700824 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000825 int corrCode;
826 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700827 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000828 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100829 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000830 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
831 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200832 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400833 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
834 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000835 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700836 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400837 // debug command! may change or disapear without notice
838 // set a mask which bursts to dump to disk
839 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700840 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400841 mWriteBurstToDiskMask = mask;
842 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700843 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100844 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200845 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000846 }
847
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200848 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400849 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000850}
851
Thomas Tsou204a9f12013-10-29 18:34:16 -0400852bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000853{
Tom Tsoue8871082016-07-01 02:46:04 -0700854 int burstLen;
855 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000856
857 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700858 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000859
Tom Tsoue8871082016-07-01 02:46:04 -0700860 if (msgLen == gSlotLen + 1 + 4 + 1) {
861 burstLen = gSlotLen;
862 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
863 if (mSPSTx != 4)
864 return false;
865
866 burstLen = EDGE_BURST_NBITS;
867 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000868 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
869 return false;
870 }
871
872 int timeSlot = (int) buffer[0];
873 uint64_t frameNum = 0;
874 for (int i = 0; i < 4; i++)
875 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000876
dburgessb3a0ca42011-10-12 07:44:40 +0000877 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200878
dburgessb3a0ca42011-10-12 07:44:40 +0000879 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700880 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000881 BitVector::iterator itr = newBurst.begin();
882 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200883 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000884 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200885
dburgessb3a0ca42011-10-12 07:44:40 +0000886 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400887
888 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000889
890 return true;
891
892
893}
dburgessb3a0ca42011-10-12 07:44:40 +0000894
Thomas Tsou204a9f12013-10-29 18:34:16 -0400895void Transceiver::driveReceiveRadio()
896{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200897 int rc = mRadioInterface->driveReceiveRadio();
898 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400899 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200900 } else if (rc < 0) {
901 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200902 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200903 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
904 mForceClockInterface = false;
905 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400906 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400907}
908
Alexander Chemeris58e95912016-03-25 18:20:28 +0300909void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800910 double rssi, double noise, double toa)
911{
912 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300913 << " chan: " << chan
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800914 << " time: " << time
915 << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
916 << "dBFS/" << std::setw(6) << -dbm << "dBm"
917 << " noise: " << std::setw(5) << std::setprecision(1) << noise
918 << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
919 << " TOA: " << std::setw(5) << std::setprecision(2) << toa
920 << " bits: " << *burst;
921}
922
Thomas Tsou204a9f12013-10-29 18:34:16 -0400923void Transceiver::driveReceiveFIFO(size_t chan)
924{
dburgessb3a0ca42011-10-12 07:44:40 +0000925 SoftVector *rxBurst = NULL;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400926 double RSSI; // in dBFS
927 double dBm; // in dBm
928 double TOA; // in symbols
929 int TOAint; // in 1/256 symbols
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400930 double noise; // noise level in dBFS
dburgessb3a0ca42011-10-12 07:44:40 +0000931 GSM::Time burstTime;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400932 bool isRssiValid; // are RSSI, noise and burstTime valid
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800933 unsigned nbits = gSlotLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000934
Alexander Chemeris2b542102015-06-08 22:46:38 -0400935 rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800936 if (!rxBurst)
937 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000938
Alexander Chemerisb61c6102017-03-17 18:22:19 -0700939 // Convert -1..+1 soft bits to 0..1 soft bits
940 vectorSlicer(rxBurst);
941
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800942 /*
943 * EDGE demodulator returns 444 (148 * 3) bits
944 */
945 if (rxBurst->size() == gSlotLen * 3)
946 nbits = gSlotLen * 3;
dburgessb3a0ca42011-10-12 07:44:40 +0000947
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800948 dBm = RSSI + rssiOffset;
Alexander Chemeris58e95912016-03-25 18:20:28 +0300949 logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400950
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800951 TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000952
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800953 char burstString[nbits + 10];
954 burstString[0] = burstTime.TN();
955 for (int i = 0; i < 4; i++)
956 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
957 burstString[5] = (int)dBm;
958 burstString[6] = (TOAint >> 8) & 0x0ff;
959 burstString[7] = TOAint & 0x0ff;
960 SoftVector::iterator burstItr = rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000961
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800962 for (unsigned i = 0; i < nbits; i++)
963 burstString[8 + i] = (char) round((*burstItr++) * 255.0);
964
965 burstString[nbits + 9] = '\0';
966 delete rxBurst;
967
968 mDataSockets[chan]->write(burstString, nbits + 10);
dburgessb3a0ca42011-10-12 07:44:40 +0000969}
970
Thomas Tsou204a9f12013-10-29 18:34:16 -0400971void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000972{
973
974 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200975 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000976 assure that transmit packets arrive at the radio/USRP
977 before they need to be transmitted.
978
979 Deadline clock indicates the burst that needs to be
980 pushed into the FIFO right NOW. If transmit queue does
981 not have a burst, stick in filler data.
982 */
983
984
985 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200986
dburgessb3a0ca42011-10-12 07:44:40 +0000987 if (mOn) {
988 //radioClock->wait(); // wait until clock updates
989 LOG(DEBUG) << "radio clock " << radioClock->get();
990 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
991 // if underrun, then we're not providing bursts to radio/USRP fast
992 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400993 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000994 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000995 // only update latency at the defined frame interval
996 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000997 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +0200998 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
999 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001000 mLatencyUpdateTime = radioClock->get();
1001 }
1002 }
1003 else {
1004 // if underrun hasn't occurred in the last sec (216 frames) drop
1005 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001006 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001007 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1008 mTransmitLatency.decTN();
1009 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1010 mLatencyUpdateTime = radioClock->get();
1011 }
1012 }
1013 }
dburgessb3a0ca42011-10-12 07:44:40 +00001014 }
dburgessb3a0ca42011-10-12 07:44:40 +00001015 // time to push burst to transmit FIFO
1016 pushRadioVector(mTransmitDeadlineClock);
1017 mTransmitDeadlineClock.incTN();
1018 }
dburgessb3a0ca42011-10-12 07:44:40 +00001019 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001020
1021 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001022}
1023
1024
1025
1026void Transceiver::writeClockInterface()
1027{
1028 char command[50];
1029 // FIXME -- This should be adaptive.
1030 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1031
1032 LOG(INFO) << "ClockInterface: sending " << command;
1033
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001034 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001035
1036 mLastClockUpdateTime = mTransmitDeadlineClock;
1037
Thomas Tsou92c16df2013-09-28 18:04:19 -04001038}
dburgessb3a0ca42011-10-12 07:44:40 +00001039
Thomas Tsou204a9f12013-10-29 18:34:16 -04001040void *RxUpperLoopAdapter(TransceiverChannel *chan)
1041{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001042 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001043 Transceiver *trx = chan->trx;
1044 size_t num = chan->num;
1045
1046 delete chan;
1047
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001048 snprintf(thread_name, 16, "RxUpper%zu", num);
1049 set_selfthread_name(thread_name);
1050
Thomas Tsou7553aa92013-11-08 12:50:03 -05001051 trx->setPriority(0.42);
1052
Thomas Tsou204a9f12013-10-29 18:34:16 -04001053 while (1) {
1054 trx->driveReceiveFIFO(num);
1055 pthread_testcancel();
1056 }
1057 return NULL;
1058}
1059
1060void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001061{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001062 set_selfthread_name("RxLower");
1063
Thomas Tsou7553aa92013-11-08 12:50:03 -05001064 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001065
dburgessb3a0ca42011-10-12 07:44:40 +00001066 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001068 pthread_testcancel();
1069 }
1070 return NULL;
1071}
1072
Thomas Tsou204a9f12013-10-29 18:34:16 -04001073void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001074{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001075 set_selfthread_name("TxLower");
1076
Thomas Tsou7553aa92013-11-08 12:50:03 -05001077 transceiver->setPriority(0.44);
1078
Thomas Tsou92c16df2013-09-28 18:04:19 -04001079 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001080 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001081 pthread_testcancel();
1082 }
1083 return NULL;
1084}
1085
Thomas Tsou204a9f12013-10-29 18:34:16 -04001086void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001087{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001088 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001089 Transceiver *trx = chan->trx;
1090 size_t num = chan->num;
1091
1092 delete chan;
1093
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001094 snprintf(thread_name, 16, "CtrlService%zu", num);
1095 set_selfthread_name(thread_name);
1096
dburgessb3a0ca42011-10-12 07:44:40 +00001097 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001098 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001099 pthread_testcancel();
1100 }
1101 return NULL;
1102}
1103
Thomas Tsou204a9f12013-10-29 18:34:16 -04001104void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001105{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001106 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001107 Transceiver *trx = chan->trx;
1108 size_t num = chan->num;
1109
1110 delete chan;
1111
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001112 snprintf(thread_name, 16, "TxUpper%zu", num);
1113 set_selfthread_name(thread_name);
1114
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001115 trx->setPriority(0.40);
1116
dburgessb3a0ca42011-10-12 07:44:40 +00001117 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001118 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001119 pthread_testcancel();
1120 }
1121 return NULL;
1122}