blob: 54b9ddd7f5bc820ed38035f3bf3b8aa5f697b306 [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 Pedroldb936b92018-09-03 16:50:49 +0200122 rssiOffset(wRssiOffset), sig_cbfn(NULL),
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
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200228void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
229{
230 if (this->sig_cbfn)
231 osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
232
233 if (cbfn) {
234 this->sig_cbfn = cbfn;
235 osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
236 }
237}
238
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800239/*
240 * Start the transceiver
241 *
242 * Submit command(s) to the radio device to commence streaming samples and
243 * launch threads to handle sample I/O. Re-synchronize the transmit burst
244 * counters to the central radio clock here as well.
245 */
246bool Transceiver::start()
247{
248 ScopedLock lock(mLock);
249
250 if (mOn) {
251 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300252 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800253 }
254
255 LOG(NOTICE) << "Starting the transceiver";
256
257 GSM::Time time = mRadioInterface->getClock()->get();
258 mTransmitDeadlineClock = time;
259 mLastClockUpdateTime = time;
260 mLatencyUpdateTime = time;
261
262 if (!mRadioInterface->start()) {
263 LOG(ALERT) << "Device failed to start";
264 return false;
265 }
266
267 /* Device is running - launch I/O threads */
268 mRxLowerLoopThread = new Thread(32768);
269 mTxLowerLoopThread = new Thread(32768);
270 mTxLowerLoopThread->start((void * (*)(void*))
271 TxLowerLoopAdapter,(void*) this);
272 mRxLowerLoopThread->start((void * (*)(void*))
273 RxLowerLoopAdapter,(void*) this);
274
275 /* Launch uplink and downlink burst processing threads */
276 for (size_t i = 0; i < mChans; i++) {
277 TransceiverChannel *chan = new TransceiverChannel(this, i);
278 mRxServiceLoopThreads[i] = new Thread(32768);
279 mRxServiceLoopThreads[i]->start((void * (*)(void*))
280 RxUpperLoopAdapter, (void*) chan);
281
282 chan = new TransceiverChannel(this, i);
283 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
284 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
285 TxUpperLoopAdapter, (void*) chan);
286 }
287
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200288 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800289 mOn = true;
290 return true;
291}
292
293/*
294 * Stop the transceiver
295 *
296 * Perform stopping by disabling receive streaming and issuing cancellation
297 * requests to running threads. Most threads will timeout and terminate once
298 * device is disabled, but the transmit loop may block waiting on the central
299 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
300 * makes it to the thread cancellation point.
301 */
302void Transceiver::stop()
303{
304 ScopedLock lock(mLock);
305
306 if (!mOn)
307 return;
308
309 LOG(NOTICE) << "Stopping the transceiver";
310 mTxLowerLoopThread->cancel();
311 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700312 mTxLowerLoopThread->join();
313 mRxLowerLoopThread->join();
314 delete mTxLowerLoopThread;
315 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800316
317 for (size_t i = 0; i < mChans; i++) {
318 mRxServiceLoopThreads[i]->cancel();
319 mTxPriorityQueueServiceLoopThreads[i]->cancel();
320 }
321
322 LOG(INFO) << "Stopping the device";
323 mRadioInterface->stop();
324
325 for (size_t i = 0; i < mChans; i++) {
326 mRxServiceLoopThreads[i]->join();
327 mTxPriorityQueueServiceLoopThreads[i]->join();
328 delete mRxServiceLoopThreads[i];
329 delete mTxPriorityQueueServiceLoopThreads[i];
330
331 mTxPriorityQueues[i].clear();
332 }
333
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800334 mOn = false;
335 LOG(NOTICE) << "Transceiver stopped";
336}
337
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500338void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000340{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500341 signalVector *burst;
342 radioVector *radio_burst;
343
Thomas Tsou204a9f12013-10-29 18:34:16 -0400344 if (chan >= mTxPriorityQueues.size()) {
345 LOG(ALERT) << "Invalid channel " << chan;
346 return;
347 }
348
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500349 if (wTime.TN() > 7) {
350 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
351 return;
352 }
353
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800354 /* Use the number of bits as the EDGE burst indicator */
355 if (bits.size() == EDGE_BURST_NBITS)
356 burst = modulateEdgeBurst(bits, mSPSTx);
357 else
358 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
359
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500360 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000361
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500362 radio_burst = new radioVector(wTime, burst);
363
364 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000365}
366
Thomas Tsou15d743e2014-01-25 02:34:03 -0500367void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
368{
369 int TN, modFN;
370 TransceiverState *state = &mStates[chan];
371
372 TN = burst->getTime().TN();
373 modFN = burst->getTime().FN() % state->fillerModulus[TN];
374
375 delete state->fillerTable[modFN][TN];
376 state->fillerTable[modFN][TN] = burst->getVector();
377 burst->setVector(NULL);
378}
379
dburgessb3a0ca42011-10-12 07:44:40 +0000380void Transceiver::pushRadioVector(GSM::Time &nowTime)
381{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400382 int TN, modFN;
383 radioVector *burst;
384 TransceiverState *state;
385 std::vector<signalVector *> bursts(mChans);
386 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500387 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000388
Thomas Tsou204a9f12013-10-29 18:34:16 -0400389 for (size_t i = 0; i < mChans; i ++) {
390 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000391
Thomas Tsou204a9f12013-10-29 18:34:16 -0400392 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200393 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200394 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500395 if (state->mRetrans)
396 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500397 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400398 }
399
400 TN = nowTime.TN();
401 modFN = nowTime.FN() % state->fillerModulus[TN];
402
403 bursts[i] = state->fillerTable[modFN][TN];
404 zeros[i] = state->chanType[TN] == NONE;
405
406 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500407 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500408
409 if (state->mRetrans) {
410 updateFillerTable(i, burst);
411 } else {
412 burst->setVector(NULL);
413 filler[i] = false;
414 }
415
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500416 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400417 }
dburgessb3a0ca42011-10-12 07:44:40 +0000418 }
419
Thomas Tsou204a9f12013-10-29 18:34:16 -0400420 mRadioInterface->driveTransmitRadio(bursts, zeros);
421
Thomas Tsou15d743e2014-01-25 02:34:03 -0500422 for (size_t i = 0; i < mChans; i++) {
423 if (!filler[i])
424 delete bursts[i];
425 }
dburgessb3a0ca42011-10-12 07:44:40 +0000426}
427
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000429{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400430 TransceiverState *state = &mStates[chan];
431
432 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000433 case NONE:
434 case I:
435 case II:
436 case III:
437 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400438 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000439 break;
440 case IV:
441 case VI:
442 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400443 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000444 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200445 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000446 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400447 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000448 break;
ttsoufc40a842013-06-09 22:38:18 +0000449 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400450 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000451 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000452 default:
453 break;
454 }
455}
456
457
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700458CorrType Transceiver::expectedCorrType(GSM::Time currTime,
459 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000460{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300461 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 };
462 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,
463 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 };
464 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,
465 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 -0400466 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000467 unsigned burstTN = currTime.TN();
468 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300469 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000470
Thomas Tsou204a9f12013-10-29 18:34:16 -0400471 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000472 case NONE:
473 return OFF;
474 break;
475 case FILL:
476 return IDLE;
477 break;
478 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300479 // TODO: Are we expecting RACH on an IDLE frame?
480/* if (burstFN % 26 == 25)
481 return IDLE;*/
482 if (mHandover[burstTN][0])
483 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000484 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000485 break;
486 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300487 subch = tchh_subslot[burstFN % 26];
488 if (subch == 1)
489 return IDLE;
490 if (mHandover[burstTN][0])
491 return RACH;
ttsou20642972013-03-27 22:00:25 +0000492 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000493 break;
494 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300495 subch = tchh_subslot[burstFN % 26];
496 if (mHandover[burstTN][subch])
497 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000498 return TSC;
499 break;
500 case IV:
501 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200502 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000503 break;
504 case V: {
505 int mod51 = burstFN % 51;
506 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200507 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000508 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200509 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000510 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200511 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300512 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
513 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000514 else
515 return TSC;
516 break;
517 }
518 case VII:
519 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
520 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300521 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
522 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000523 else
524 return TSC;
525 break;
ttsoufc40a842013-06-09 22:38:18 +0000526 case XIII: {
527 int mod52 = burstFN % 52;
528 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200529 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000530 else if ((mod52 == 25) || (mod52 == 51))
531 return IDLE;
532 else
533 return TSC;
534 break;
535 }
dburgessb3a0ca42011-10-12 07:44:40 +0000536 case LOOPBACK:
537 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
538 return IDLE;
539 else
540 return TSC;
541 break;
542 default:
543 return OFF;
544 break;
545 }
dburgessb3a0ca42011-10-12 07:44:40 +0000546}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400547
Alexander Chemerise692ce92015-06-12 00:15:31 -0400548void writeToFile(radioVector *radio_burst, size_t chan)
549{
550 GSM::Time time = radio_burst->getTime();
551 std::ostringstream fname;
552 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
553 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
554 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
555 outfile.close();
556}
557
Thomas Tsou30421a72013-11-13 23:14:48 -0500558/*
559 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200560 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500561 */
Alexander Chemeris2b542102015-06-08 22:46:38 -0400562SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400563 double &timingOffset, double &noise,
564 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000565{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800566 int rc;
Thomas Tsou30421a72013-11-13 23:14:48 -0500567 complex amp;
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300568 float toa, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500569 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500570 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500571 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500572 TransceiverState *state = &mStates[chan];
Alexander Chemeris2b542102015-06-08 22:46:38 -0400573 isRssiValid = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000574
Thomas Tsou30421a72013-11-13 23:14:48 -0500575 /* Blocking FIFO read */
576 radioVector *radio_burst = mReceiveFIFO[chan]->read();
577 if (!radio_burst)
578 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000579
Thomas Tsou30421a72013-11-13 23:14:48 -0500580 /* Set time and determine correlation type */
581 GSM::Time time = radio_burst->getTime();
582 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000583
Tom Tsou64464e62016-07-01 03:46:46 -0700584 /* Enable 8-PSK burst detection if EDGE is enabled */
585 if (mEdge && (type == TSC))
586 type = EDGE;
587
Alexander Chemerise692ce92015-06-12 00:15:31 -0400588 /* Debug: dump bursts to disk */
589 /* bits 0-7 - chan 0 timeslots
590 * bits 8-15 - chan 1 timeslots */
591 if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
592 writeToFile(radio_burst, chan);
593
Alexander Chemeris2b542102015-06-08 22:46:38 -0400594 /* No processing if the timeslot is off.
595 * Not even power level or noise calculation. */
596 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500597 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000598 return NULL;
599 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000600
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500601 /* Select the diversity channel with highest energy */
602 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300603 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500604 if (pow > max) {
605 max = pow;
606 max_i = i;
607 }
608 avg += pow;
609 }
610
611 if (max_i < 0) {
612 LOG(ALERT) << "Received empty burst";
613 delete radio_burst;
614 return NULL;
615 }
616
Thomas Tsou30421a72013-11-13 23:14:48 -0500617 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500618 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500619 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400620
621 wTime = time;
622 RSSI = 20.0 * log10(rxFullScale / avg);
623
624 /* RSSI estimation are valid */
625 isRssiValid = true;
626
627 if (type == IDLE) {
628 /* Update noise levels */
629 state->mNoises.insert(avg);
630 state->mNoiseLev = state->mNoises.avg();
631 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
632
633 delete radio_burst;
634 return NULL;
635 } else {
636 /* Do not update noise levels */
637 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
638 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400639
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200640 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
641 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
642
Thomas Tsou30421a72013-11-13 23:14:48 -0500643 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200644 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400645
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800646 if (rc > 0) {
647 type = (CorrType) rc;
648 } else if (rc <= 0) {
649 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400650 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800651 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400652 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700653 }
654
Thomas Tsou30421a72013-11-13 23:14:48 -0500655 delete radio_burst;
656 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000657 }
dburgessb3a0ca42011-10-12 07:44:40 +0000658
Tom Tsou4609f322016-07-19 11:28:51 -0700659 timingOffset = toa;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400660
Alexander Chemeris6e1dffd2017-03-17 16:13:51 -0700661 bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500662
Thomas Tsou30421a72013-11-13 23:14:48 -0500663 delete radio_burst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500664 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000665}
666
dburgessb3a0ca42011-10-12 07:44:40 +0000667void Transceiver::reset()
668{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400669 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
670 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000671}
672
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200673
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700674#define MAX_PACKET_LENGTH 100
675
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700676/**
677 * Matches a buffer with a command.
678 * @param buf a buffer to look command in
679 * @param cmd a command to look in buffer
680 * @param params pointer to arguments, or NULL
681 * @return true if command matches, otherwise false
682 */
683static bool match_cmd(char *buf,
684 const char *cmd, char **params)
685{
686 size_t cmd_len = strlen(cmd);
687
688 /* Check a command itself */
689 if (strncmp(buf, cmd, cmd_len))
690 return false;
691
692 /* A command has arguments */
693 if (params != NULL) {
694 /* Make sure there is a space */
695 if (buf[cmd_len] != ' ')
696 return false;
697
698 /* Update external pointer */
699 *params = buf + cmd_len + 1;
700 }
701
702 return true;
703}
704
Thomas Tsou204a9f12013-10-29 18:34:16 -0400705void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000706{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700707 char buffer[MAX_PACKET_LENGTH + 1];
708 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700709 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700710 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400711
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700712 /* Attempt to read from control socket */
713 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
714 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000715 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700716
717 /* Zero-terminate received string */
718 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000719
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700720 /* Verify a command signature */
721 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100722 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000723 return;
724 }
dburgessb3a0ca42011-10-12 07:44:40 +0000725
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700726 /* Set command pointer */
727 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200728 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700729
730 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800731 stop();
732 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700733 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700734 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000735 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700736 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000737 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300738 for (int i = 0; i < 8; i++) {
739 for (int j = 0; j < 8; j++)
740 mHandover[i][j] = false;
741 }
Tom Tsou365bc382016-10-19 15:26:04 -0700742 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700743 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700744 unsigned ts = 0, ss = 0;
745 sscanf(params, "%u %u", &ts, &ss);
746 if (ts > 7 || ss > 7) {
747 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
748 } else {
749 mHandover[ts][ss] = true;
750 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
751 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700752 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700753 unsigned ts = 0, ss = 0;
754 sscanf(params, "%u %u", &ts, &ss);
755 if (ts > 7 || ss > 7) {
756 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
757 } else {
758 mHandover[ts][ss] = false;
759 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
760 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700761 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000762 //set expected maximum time-of-arrival
763 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700764 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300765 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000766 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300768 //set expected maximum time-of-arrival
769 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700770 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300771 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
772 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700773 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000774 //set expected maximum time-of-arrival
775 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400777 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000778 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700779 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000780 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500781 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000782 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500783 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000784 }
785 else {
786 sprintf(response,"RSP NOISELEV 1 0");
787 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700788 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800789 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800791 power = mRadioInterface->setPowerAttenuation(power, chan);
792 mStates[chan].mPower = power;
793 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700794 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800795 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700796 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800797 power = mStates[chan].mPower + step;
798 power = mRadioInterface->setPowerAttenuation(power, chan);
799 mStates[chan].mPower = power;
800 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700801 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000802 // tune receiver
803 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700804 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500805 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400806 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100807 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000808 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
809 }
810 else
811 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000813 // tune txmtr
814 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700815 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500816 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400817 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100818 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000819 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
820 }
821 else
822 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000824 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500825 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700826 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700827 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500828 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700829 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100830 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000831 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400832 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000833 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700834 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700835 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000836 int corrCode;
837 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000839 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100840 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000841 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
842 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200843 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400844 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
845 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000846 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700847 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400848 // debug command! may change or disapear without notice
849 // set a mask which bursts to dump to disk
850 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700851 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400852 mWriteBurstToDiskMask = mask;
853 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700854 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100855 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200856 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000857 }
858
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200859 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400860 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000861}
862
Thomas Tsou204a9f12013-10-29 18:34:16 -0400863bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000864{
Tom Tsoue8871082016-07-01 02:46:04 -0700865 int burstLen;
866 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000867
868 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700869 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000870
Tom Tsoue8871082016-07-01 02:46:04 -0700871 if (msgLen == gSlotLen + 1 + 4 + 1) {
872 burstLen = gSlotLen;
873 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
874 if (mSPSTx != 4)
875 return false;
876
877 burstLen = EDGE_BURST_NBITS;
878 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000879 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
880 return false;
881 }
882
883 int timeSlot = (int) buffer[0];
884 uint64_t frameNum = 0;
885 for (int i = 0; i < 4; i++)
886 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000887
dburgessb3a0ca42011-10-12 07:44:40 +0000888 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200889
dburgessb3a0ca42011-10-12 07:44:40 +0000890 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700891 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000892 BitVector::iterator itr = newBurst.begin();
893 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200894 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000895 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200896
dburgessb3a0ca42011-10-12 07:44:40 +0000897 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400898
899 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000900
901 return true;
902
903
904}
dburgessb3a0ca42011-10-12 07:44:40 +0000905
Thomas Tsou204a9f12013-10-29 18:34:16 -0400906void Transceiver::driveReceiveRadio()
907{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200908 int rc = mRadioInterface->driveReceiveRadio();
909 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400910 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200911 } else if (rc < 0) {
912 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
913 osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200914 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
915 mForceClockInterface = false;
916 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400917 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400918}
919
Alexander Chemeris58e95912016-03-25 18:20:28 +0300920void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800921 double rssi, double noise, double toa)
922{
923 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300924 << " chan: " << chan
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800925 << " time: " << time
926 << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
927 << "dBFS/" << std::setw(6) << -dbm << "dBm"
928 << " noise: " << std::setw(5) << std::setprecision(1) << noise
929 << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
930 << " TOA: " << std::setw(5) << std::setprecision(2) << toa
931 << " bits: " << *burst;
932}
933
Thomas Tsou204a9f12013-10-29 18:34:16 -0400934void Transceiver::driveReceiveFIFO(size_t chan)
935{
dburgessb3a0ca42011-10-12 07:44:40 +0000936 SoftVector *rxBurst = NULL;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400937 double RSSI; // in dBFS
938 double dBm; // in dBm
939 double TOA; // in symbols
940 int TOAint; // in 1/256 symbols
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400941 double noise; // noise level in dBFS
dburgessb3a0ca42011-10-12 07:44:40 +0000942 GSM::Time burstTime;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400943 bool isRssiValid; // are RSSI, noise and burstTime valid
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800944 unsigned nbits = gSlotLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000945
Alexander Chemeris2b542102015-06-08 22:46:38 -0400946 rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800947 if (!rxBurst)
948 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000949
Alexander Chemerisb61c6102017-03-17 18:22:19 -0700950 // Convert -1..+1 soft bits to 0..1 soft bits
951 vectorSlicer(rxBurst);
952
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800953 /*
954 * EDGE demodulator returns 444 (148 * 3) bits
955 */
956 if (rxBurst->size() == gSlotLen * 3)
957 nbits = gSlotLen * 3;
dburgessb3a0ca42011-10-12 07:44:40 +0000958
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800959 dBm = RSSI + rssiOffset;
Alexander Chemeris58e95912016-03-25 18:20:28 +0300960 logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400961
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800962 TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000963
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800964 char burstString[nbits + 10];
965 burstString[0] = burstTime.TN();
966 for (int i = 0; i < 4; i++)
967 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
968 burstString[5] = (int)dBm;
969 burstString[6] = (TOAint >> 8) & 0x0ff;
970 burstString[7] = TOAint & 0x0ff;
971 SoftVector::iterator burstItr = rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000972
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800973 for (unsigned i = 0; i < nbits; i++)
974 burstString[8 + i] = (char) round((*burstItr++) * 255.0);
975
976 burstString[nbits + 9] = '\0';
977 delete rxBurst;
978
979 mDataSockets[chan]->write(burstString, nbits + 10);
dburgessb3a0ca42011-10-12 07:44:40 +0000980}
981
Thomas Tsou204a9f12013-10-29 18:34:16 -0400982void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000983{
984
985 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200986 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000987 assure that transmit packets arrive at the radio/USRP
988 before they need to be transmitted.
989
990 Deadline clock indicates the burst that needs to be
991 pushed into the FIFO right NOW. If transmit queue does
992 not have a burst, stick in filler data.
993 */
994
995
996 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200997
dburgessb3a0ca42011-10-12 07:44:40 +0000998 if (mOn) {
999 //radioClock->wait(); // wait until clock updates
1000 LOG(DEBUG) << "radio clock " << radioClock->get();
1001 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1002 // if underrun, then we're not providing bursts to radio/USRP fast
1003 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001004 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001005 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001006 // only update latency at the defined frame interval
1007 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001008 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001009 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1010 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001011 mLatencyUpdateTime = radioClock->get();
1012 }
1013 }
1014 else {
1015 // if underrun hasn't occurred in the last sec (216 frames) drop
1016 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001017 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001018 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1019 mTransmitLatency.decTN();
1020 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1021 mLatencyUpdateTime = radioClock->get();
1022 }
1023 }
1024 }
dburgessb3a0ca42011-10-12 07:44:40 +00001025 }
dburgessb3a0ca42011-10-12 07:44:40 +00001026 // time to push burst to transmit FIFO
1027 pushRadioVector(mTransmitDeadlineClock);
1028 mTransmitDeadlineClock.incTN();
1029 }
dburgessb3a0ca42011-10-12 07:44:40 +00001030 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001031
1032 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001033}
1034
1035
1036
1037void Transceiver::writeClockInterface()
1038{
1039 char command[50];
1040 // FIXME -- This should be adaptive.
1041 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1042
1043 LOG(INFO) << "ClockInterface: sending " << command;
1044
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001045 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001046
1047 mLastClockUpdateTime = mTransmitDeadlineClock;
1048
Thomas Tsou92c16df2013-09-28 18:04:19 -04001049}
dburgessb3a0ca42011-10-12 07:44:40 +00001050
Thomas Tsou204a9f12013-10-29 18:34:16 -04001051void *RxUpperLoopAdapter(TransceiverChannel *chan)
1052{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001053 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054 Transceiver *trx = chan->trx;
1055 size_t num = chan->num;
1056
1057 delete chan;
1058
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001059 snprintf(thread_name, 16, "RxUpper%zu", num);
1060 set_selfthread_name(thread_name);
1061
Thomas Tsou7553aa92013-11-08 12:50:03 -05001062 trx->setPriority(0.42);
1063
Thomas Tsou204a9f12013-10-29 18:34:16 -04001064 while (1) {
1065 trx->driveReceiveFIFO(num);
1066 pthread_testcancel();
1067 }
1068 return NULL;
1069}
1070
1071void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001072{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001073 set_selfthread_name("RxLower");
1074
Thomas Tsou7553aa92013-11-08 12:50:03 -05001075 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001076
dburgessb3a0ca42011-10-12 07:44:40 +00001077 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001078 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001079 pthread_testcancel();
1080 }
1081 return NULL;
1082}
1083
Thomas Tsou204a9f12013-10-29 18:34:16 -04001084void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001085{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001086 set_selfthread_name("TxLower");
1087
Thomas Tsou7553aa92013-11-08 12:50:03 -05001088 transceiver->setPriority(0.44);
1089
Thomas Tsou92c16df2013-09-28 18:04:19 -04001090 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001091 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001092 pthread_testcancel();
1093 }
1094 return NULL;
1095}
1096
Thomas Tsou204a9f12013-10-29 18:34:16 -04001097void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001098{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001099 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001100 Transceiver *trx = chan->trx;
1101 size_t num = chan->num;
1102
1103 delete chan;
1104
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001105 snprintf(thread_name, 16, "CtrlService%zu", num);
1106 set_selfthread_name(thread_name);
1107
dburgessb3a0ca42011-10-12 07:44:40 +00001108 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001110 pthread_testcancel();
1111 }
1112 return NULL;
1113}
1114
Thomas Tsou204a9f12013-10-29 18:34:16 -04001115void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001116{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001117 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001118 Transceiver *trx = chan->trx;
1119 size_t num = chan->num;
1120
1121 delete chan;
1122
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001123 snprintf(thread_name, 16, "TxUpper%zu", num);
1124 set_selfthread_name(thread_name);
1125
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001126 trx->setPriority(0.40);
1127
dburgessb3a0ca42011-10-12 07:44:40 +00001128 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001129 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001130 pthread_testcancel();
1131 }
1132 return NULL;
1133}