blob: 076db3e6f10ff4451518fd9ef84a7b0af23f0b34 [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 */
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100162bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
Thomas Tsou83e06892013-08-20 16:10:01 -0400163{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500164 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400165
Thomas Tsou204a9f12013-10-29 18:34:16 -0400166 if (!mChans) {
167 LOG(ALERT) << "No channels assigned";
168 return false;
169 }
170
Tom Tsou2079a3c2016-03-06 00:58:56 -0800171 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400172 LOG(ALERT) << "Failed to initialize signal processing library";
173 return false;
174 }
175
Tom Tsou64464e62016-07-01 03:46:46 -0700176 mEdge = edge;
177
Thomas Tsou204a9f12013-10-29 18:34:16 -0400178 mDataSockets.resize(mChans);
179 mCtrlSockets.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400180 mControlServiceLoopThreads.resize(mChans);
181 mTxPriorityQueueServiceLoopThreads.resize(mChans);
182 mRxServiceLoopThreads.resize(mChans);
183
184 mTxPriorityQueues.resize(mChans);
185 mReceiveFIFO.resize(mChans);
186 mStates.resize(mChans);
187
Thomas Tsouccb73e12014-04-15 17:41:28 -0400188 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700189 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400190 mStates[0].mRetrans = true;
191
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800192 /* Setup sockets */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400193 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500194 c_srcport = mBasePort + 2 * i + 1;
195 c_dstport = mBasePort + 2 * i + 101;
196 d_srcport = mBasePort + 2 * i + 2;
197 d_dstport = mBasePort + 2 * i + 102;
198
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200199 mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
200 mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400201 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400202
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800203 /* Randomize the central clock */
204 GSM::Time startTime(random() % gHyperframe, 0);
205 mRadioInterface->getClock()->set(startTime);
206 mTransmitDeadlineClock = startTime;
207 mLastClockUpdateTime = startTime;
208 mLatencyUpdateTime = startTime;
209
210 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400211 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800212 TransceiverChannel *chan = new TransceiverChannel(this, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400213 mControlServiceLoopThreads[i] = new Thread(32768);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800214 mControlServiceLoopThreads[i]->start((void * (*)(void*))
215 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216
Tom Tsou64ad7122015-05-19 18:26:31 -0700217 if (i && filler == FILLER_DUMMY)
218 filler = FILLER_ZERO;
219
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300220 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400221 }
222
223 return true;
224}
dburgessb3a0ca42011-10-12 07:44:40 +0000225
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200226void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
227{
228 if (this->sig_cbfn)
229 osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
230
231 if (cbfn) {
232 this->sig_cbfn = cbfn;
233 osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
234 }
235}
236
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800237/*
238 * Start the transceiver
239 *
240 * Submit command(s) to the radio device to commence streaming samples and
241 * launch threads to handle sample I/O. Re-synchronize the transmit burst
242 * counters to the central radio clock here as well.
243 */
244bool Transceiver::start()
245{
246 ScopedLock lock(mLock);
247
248 if (mOn) {
249 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300250 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800251 }
252
253 LOG(NOTICE) << "Starting the transceiver";
254
255 GSM::Time time = mRadioInterface->getClock()->get();
256 mTransmitDeadlineClock = time;
257 mLastClockUpdateTime = time;
258 mLatencyUpdateTime = time;
259
260 if (!mRadioInterface->start()) {
261 LOG(ALERT) << "Device failed to start";
262 return false;
263 }
264
265 /* Device is running - launch I/O threads */
266 mRxLowerLoopThread = new Thread(32768);
267 mTxLowerLoopThread = new Thread(32768);
268 mTxLowerLoopThread->start((void * (*)(void*))
269 TxLowerLoopAdapter,(void*) this);
270 mRxLowerLoopThread->start((void * (*)(void*))
271 RxLowerLoopAdapter,(void*) this);
272
273 /* Launch uplink and downlink burst processing threads */
274 for (size_t i = 0; i < mChans; i++) {
275 TransceiverChannel *chan = new TransceiverChannel(this, i);
276 mRxServiceLoopThreads[i] = new Thread(32768);
277 mRxServiceLoopThreads[i]->start((void * (*)(void*))
278 RxUpperLoopAdapter, (void*) chan);
279
280 chan = new TransceiverChannel(this, i);
281 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
282 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
283 TxUpperLoopAdapter, (void*) chan);
284 }
285
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200286 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800287 mOn = true;
288 return true;
289}
290
291/*
292 * Stop the transceiver
293 *
294 * Perform stopping by disabling receive streaming and issuing cancellation
295 * requests to running threads. Most threads will timeout and terminate once
296 * device is disabled, but the transmit loop may block waiting on the central
297 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
298 * makes it to the thread cancellation point.
299 */
300void Transceiver::stop()
301{
302 ScopedLock lock(mLock);
303
304 if (!mOn)
305 return;
306
307 LOG(NOTICE) << "Stopping the transceiver";
308 mTxLowerLoopThread->cancel();
309 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700310 mTxLowerLoopThread->join();
311 mRxLowerLoopThread->join();
312 delete mTxLowerLoopThread;
313 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800314
315 for (size_t i = 0; i < mChans; i++) {
316 mRxServiceLoopThreads[i]->cancel();
317 mTxPriorityQueueServiceLoopThreads[i]->cancel();
318 }
319
320 LOG(INFO) << "Stopping the device";
321 mRadioInterface->stop();
322
323 for (size_t i = 0; i < mChans; i++) {
324 mRxServiceLoopThreads[i]->join();
325 mTxPriorityQueueServiceLoopThreads[i]->join();
326 delete mRxServiceLoopThreads[i];
327 delete mTxPriorityQueueServiceLoopThreads[i];
328
329 mTxPriorityQueues[i].clear();
330 }
331
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800332 mOn = false;
333 LOG(NOTICE) << "Transceiver stopped";
334}
335
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500336void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400337 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000338{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500339 signalVector *burst;
340 radioVector *radio_burst;
341
Thomas Tsou204a9f12013-10-29 18:34:16 -0400342 if (chan >= mTxPriorityQueues.size()) {
343 LOG(ALERT) << "Invalid channel " << chan;
344 return;
345 }
346
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500347 if (wTime.TN() > 7) {
348 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
349 return;
350 }
351
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800352 /* Use the number of bits as the EDGE burst indicator */
353 if (bits.size() == EDGE_BURST_NBITS)
354 burst = modulateEdgeBurst(bits, mSPSTx);
355 else
356 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
357
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500358 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000359
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500360 radio_burst = new radioVector(wTime, burst);
361
362 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000363}
364
Thomas Tsou15d743e2014-01-25 02:34:03 -0500365void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
366{
367 int TN, modFN;
368 TransceiverState *state = &mStates[chan];
369
370 TN = burst->getTime().TN();
371 modFN = burst->getTime().FN() % state->fillerModulus[TN];
372
373 delete state->fillerTable[modFN][TN];
374 state->fillerTable[modFN][TN] = burst->getVector();
375 burst->setVector(NULL);
376}
377
dburgessb3a0ca42011-10-12 07:44:40 +0000378void Transceiver::pushRadioVector(GSM::Time &nowTime)
379{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400380 int TN, modFN;
381 radioVector *burst;
382 TransceiverState *state;
383 std::vector<signalVector *> bursts(mChans);
384 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500385 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000386
Thomas Tsou204a9f12013-10-29 18:34:16 -0400387 for (size_t i = 0; i < mChans; i ++) {
388 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000389
Thomas Tsou204a9f12013-10-29 18:34:16 -0400390 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200391 LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface ("
392 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500393 if (state->mRetrans)
394 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500395 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400396 }
397
398 TN = nowTime.TN();
399 modFN = nowTime.FN() % state->fillerModulus[TN];
400
401 bursts[i] = state->fillerTable[modFN][TN];
402 zeros[i] = state->chanType[TN] == NONE;
403
404 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500405 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500406
407 if (state->mRetrans) {
408 updateFillerTable(i, burst);
409 } else {
410 burst->setVector(NULL);
411 filler[i] = false;
412 }
413
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500414 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400415 }
dburgessb3a0ca42011-10-12 07:44:40 +0000416 }
417
Thomas Tsou204a9f12013-10-29 18:34:16 -0400418 mRadioInterface->driveTransmitRadio(bursts, zeros);
419
Thomas Tsou15d743e2014-01-25 02:34:03 -0500420 for (size_t i = 0; i < mChans; i++) {
421 if (!filler[i])
422 delete bursts[i];
423 }
dburgessb3a0ca42011-10-12 07:44:40 +0000424}
425
Thomas Tsou204a9f12013-10-29 18:34:16 -0400426void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000427{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428 TransceiverState *state = &mStates[chan];
429
430 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000431 case NONE:
432 case I:
433 case II:
434 case III:
435 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400436 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000437 break;
438 case IV:
439 case VI:
440 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400441 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000442 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200443 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000444 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400445 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000446 break;
ttsoufc40a842013-06-09 22:38:18 +0000447 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400448 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000449 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000450 default:
451 break;
452 }
453}
454
455
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700456CorrType Transceiver::expectedCorrType(GSM::Time currTime,
457 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000458{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300459 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 };
460 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,
461 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 };
462 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,
463 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 -0400464 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000465 unsigned burstTN = currTime.TN();
466 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300467 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000468
Thomas Tsou204a9f12013-10-29 18:34:16 -0400469 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000470 case NONE:
471 return OFF;
472 break;
473 case FILL:
474 return IDLE;
475 break;
476 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300477 // TODO: Are we expecting RACH on an IDLE frame?
478/* if (burstFN % 26 == 25)
479 return IDLE;*/
480 if (mHandover[burstTN][0])
481 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000482 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000483 break;
484 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300485 subch = tchh_subslot[burstFN % 26];
486 if (subch == 1)
487 return IDLE;
488 if (mHandover[burstTN][0])
489 return RACH;
ttsou20642972013-03-27 22:00:25 +0000490 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000491 break;
492 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300493 subch = tchh_subslot[burstFN % 26];
494 if (mHandover[burstTN][subch])
495 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000496 return TSC;
497 break;
498 case IV:
499 case VI:
500 return RACH;
501 break;
502 case V: {
503 int mod51 = burstFN % 51;
504 if ((mod51 <= 36) && (mod51 >= 14))
505 return RACH;
506 else if ((mod51 == 4) || (mod51 == 5))
507 return RACH;
508 else if ((mod51 == 45) || (mod51 == 46))
509 return RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300510 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
511 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000512 else
513 return TSC;
514 break;
515 }
516 case VII:
517 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
518 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300519 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
520 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000521 else
522 return TSC;
523 break;
ttsoufc40a842013-06-09 22:38:18 +0000524 case XIII: {
525 int mod52 = burstFN % 52;
526 if ((mod52 == 12) || (mod52 == 38))
527 return RACH;
528 else if ((mod52 == 25) || (mod52 == 51))
529 return IDLE;
530 else
531 return TSC;
532 break;
533 }
dburgessb3a0ca42011-10-12 07:44:40 +0000534 case LOOPBACK:
535 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
536 return IDLE;
537 else
538 return TSC;
539 break;
540 default:
541 return OFF;
542 break;
543 }
dburgessb3a0ca42011-10-12 07:44:40 +0000544}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400545
Alexander Chemerise692ce92015-06-12 00:15:31 -0400546void writeToFile(radioVector *radio_burst, size_t chan)
547{
548 GSM::Time time = radio_burst->getTime();
549 std::ostringstream fname;
550 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
551 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
552 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
553 outfile.close();
554}
555
Thomas Tsou30421a72013-11-13 23:14:48 -0500556/*
557 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200558 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500559 */
Alexander Chemeris2b542102015-06-08 22:46:38 -0400560SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400561 double &timingOffset, double &noise,
562 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000563{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800564 int rc;
Thomas Tsou30421a72013-11-13 23:14:48 -0500565 complex amp;
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300566 float toa, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500567 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500568 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500569 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500570 TransceiverState *state = &mStates[chan];
Alexander Chemeris2b542102015-06-08 22:46:38 -0400571 isRssiValid = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000572
Thomas Tsou30421a72013-11-13 23:14:48 -0500573 /* Blocking FIFO read */
574 radioVector *radio_burst = mReceiveFIFO[chan]->read();
575 if (!radio_burst)
576 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000577
Thomas Tsou30421a72013-11-13 23:14:48 -0500578 /* Set time and determine correlation type */
579 GSM::Time time = radio_burst->getTime();
580 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000581
Tom Tsou64464e62016-07-01 03:46:46 -0700582 /* Enable 8-PSK burst detection if EDGE is enabled */
583 if (mEdge && (type == TSC))
584 type = EDGE;
585
Alexander Chemerise692ce92015-06-12 00:15:31 -0400586 /* Debug: dump bursts to disk */
587 /* bits 0-7 - chan 0 timeslots
588 * bits 8-15 - chan 1 timeslots */
589 if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
590 writeToFile(radio_burst, chan);
591
Alexander Chemeris2b542102015-06-08 22:46:38 -0400592 /* No processing if the timeslot is off.
593 * Not even power level or noise calculation. */
594 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500595 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000596 return NULL;
597 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000598
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500599 /* Select the diversity channel with highest energy */
600 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300601 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500602 if (pow > max) {
603 max = pow;
604 max_i = i;
605 }
606 avg += pow;
607 }
608
609 if (max_i < 0) {
610 LOG(ALERT) << "Received empty burst";
611 delete radio_burst;
612 return NULL;
613 }
614
Thomas Tsou30421a72013-11-13 23:14:48 -0500615 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500616 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500617 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400618
619 wTime = time;
620 RSSI = 20.0 * log10(rxFullScale / avg);
621
622 /* RSSI estimation are valid */
623 isRssiValid = true;
624
625 if (type == IDLE) {
626 /* Update noise levels */
627 state->mNoises.insert(avg);
628 state->mNoiseLev = state->mNoises.avg();
629 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
630
631 delete radio_burst;
632 return NULL;
633 } else {
634 /* Do not update noise levels */
635 noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
636 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400637
Thomas Tsou30421a72013-11-13 23:14:48 -0500638 /* Detect normal or RACH bursts */
Alexander Chemeris4e6c9382017-03-17 15:24:18 -0700639 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa,
640 (type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB);
Thomas Tsouf0782732013-10-29 15:55:47 -0400641
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800642 if (rc > 0) {
643 type = (CorrType) rc;
644 } else if (rc <= 0) {
645 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400646 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800647 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400648 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700649 }
650
Thomas Tsou30421a72013-11-13 23:14:48 -0500651 delete radio_burst;
652 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000653 }
dburgessb3a0ca42011-10-12 07:44:40 +0000654
Tom Tsou4609f322016-07-19 11:28:51 -0700655 timingOffset = toa;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400656
Alexander Chemeris6e1dffd2017-03-17 16:13:51 -0700657 bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500658
Thomas Tsou30421a72013-11-13 23:14:48 -0500659 delete radio_burst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500660 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000661}
662
dburgessb3a0ca42011-10-12 07:44:40 +0000663void Transceiver::reset()
664{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400665 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
666 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000667}
668
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200669
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700670#define MAX_PACKET_LENGTH 100
671
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700672/**
673 * Matches a buffer with a command.
674 * @param buf a buffer to look command in
675 * @param cmd a command to look in buffer
676 * @param params pointer to arguments, or NULL
677 * @return true if command matches, otherwise false
678 */
679static bool match_cmd(char *buf,
680 const char *cmd, char **params)
681{
682 size_t cmd_len = strlen(cmd);
683
684 /* Check a command itself */
685 if (strncmp(buf, cmd, cmd_len))
686 return false;
687
688 /* A command has arguments */
689 if (params != NULL) {
690 /* Make sure there is a space */
691 if (buf[cmd_len] != ' ')
692 return false;
693
694 /* Update external pointer */
695 *params = buf + cmd_len + 1;
696 }
697
698 return true;
699}
700
Thomas Tsou204a9f12013-10-29 18:34:16 -0400701void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000702{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700703 char buffer[MAX_PACKET_LENGTH + 1];
704 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700705 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700706 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400707
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700708 /* Attempt to read from control socket */
709 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
710 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000711 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700712
713 /* Zero-terminate received string */
714 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000715
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700716 /* Verify a command signature */
717 if (strncmp(buffer, "CMD ", 4)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000718 LOG(WARNING) << "bogus message on control interface";
719 return;
720 }
dburgessb3a0ca42011-10-12 07:44:40 +0000721
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700722 /* Set command pointer */
723 command = buffer + 4;
724 LOG(INFO) << "command is " << command;
725
726 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800727 stop();
728 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700729 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700730 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000731 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700732 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000733 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300734 for (int i = 0; i < 8; i++) {
735 for (int j = 0; j < 8; j++)
736 mHandover[i][j] = false;
737 }
Tom Tsou365bc382016-10-19 15:26:04 -0700738 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700739 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700740 unsigned ts = 0, ss = 0;
741 sscanf(params, "%u %u", &ts, &ss);
742 if (ts > 7 || ss > 7) {
743 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
744 } else {
745 mHandover[ts][ss] = true;
746 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
747 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700748 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700749 unsigned ts = 0, ss = 0;
750 sscanf(params, "%u %u", &ts, &ss);
751 if (ts > 7 || ss > 7) {
752 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
753 } else {
754 mHandover[ts][ss] = false;
755 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
756 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700757 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000758 //set expected maximum time-of-arrival
759 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700760 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300761 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000762 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700763 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300764 //set expected maximum time-of-arrival
765 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700766 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300767 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
768 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700769 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000770 //set expected maximum time-of-arrival
771 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700772 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400773 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000774 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700775 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000776 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500777 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000778 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500779 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000780 }
781 else {
782 sprintf(response,"RSP NOISELEV 1 0");
783 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700784 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800785 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700786 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800787 power = mRadioInterface->setPowerAttenuation(power, chan);
788 mStates[chan].mPower = power;
789 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800791 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700792 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800793 power = mStates[chan].mPower + step;
794 power = mRadioInterface->setPowerAttenuation(power, chan);
795 mStates[chan].mPower = power;
796 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700797 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000798 // tune receiver
799 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700800 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500801 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400802 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000803 LOG(ALERT) << "RX failed to tune";
804 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
805 }
806 else
807 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700808 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000809 // tune txmtr
810 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700811 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500812 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400813 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000814 LOG(ALERT) << "TX failed to tune";
815 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
816 }
817 else
818 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700819 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000820 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500821 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700822 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700823 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500824 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700825 } else {
Tom Tsouc37594f2016-07-08 14:39:42 -0700826 LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000827 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400828 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000829 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700830 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700831 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000832 int corrCode;
833 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700834 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000835 if ((timeslot < 0) || (timeslot > 7)) {
836 LOG(WARNING) << "bogus message on control interface";
837 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
838 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200839 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400840 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
841 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000842 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700843 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400844 // debug command! may change or disapear without notice
845 // set a mask which bursts to dump to disk
846 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700847 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400848 mWriteBurstToDiskMask = mask;
849 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700850 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000851 LOG(WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200852 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000853 }
854
Thomas Tsou204a9f12013-10-29 18:34:16 -0400855 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000856}
857
Thomas Tsou204a9f12013-10-29 18:34:16 -0400858bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000859{
Tom Tsoue8871082016-07-01 02:46:04 -0700860 int burstLen;
861 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000862
863 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700864 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000865
Tom Tsoue8871082016-07-01 02:46:04 -0700866 if (msgLen == gSlotLen + 1 + 4 + 1) {
867 burstLen = gSlotLen;
868 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
869 if (mSPSTx != 4)
870 return false;
871
872 burstLen = EDGE_BURST_NBITS;
873 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000874 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
875 return false;
876 }
877
878 int timeSlot = (int) buffer[0];
879 uint64_t frameNum = 0;
880 for (int i = 0; i < 4; i++)
881 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000882
dburgessb3a0ca42011-10-12 07:44:40 +0000883 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200884
dburgessb3a0ca42011-10-12 07:44:40 +0000885 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700886 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000887 BitVector::iterator itr = newBurst.begin();
888 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200889 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000890 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200891
dburgessb3a0ca42011-10-12 07:44:40 +0000892 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400893
894 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000895
896 return true;
897
898
899}
dburgessb3a0ca42011-10-12 07:44:40 +0000900
Thomas Tsou204a9f12013-10-29 18:34:16 -0400901void Transceiver::driveReceiveRadio()
902{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200903 int rc = mRadioInterface->driveReceiveRadio();
904 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400905 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200906 } else if (rc < 0) {
907 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
908 osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200909 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
910 mForceClockInterface = false;
911 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400912 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913}
914
Alexander Chemeris58e95912016-03-25 18:20:28 +0300915void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800916 double rssi, double noise, double toa)
917{
918 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300919 << " chan: " << chan
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800920 << " time: " << time
921 << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
922 << "dBFS/" << std::setw(6) << -dbm << "dBm"
923 << " noise: " << std::setw(5) << std::setprecision(1) << noise
924 << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
925 << " TOA: " << std::setw(5) << std::setprecision(2) << toa
926 << " bits: " << *burst;
927}
928
Thomas Tsou204a9f12013-10-29 18:34:16 -0400929void Transceiver::driveReceiveFIFO(size_t chan)
930{
dburgessb3a0ca42011-10-12 07:44:40 +0000931 SoftVector *rxBurst = NULL;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400932 double RSSI; // in dBFS
933 double dBm; // in dBm
934 double TOA; // in symbols
935 int TOAint; // in 1/256 symbols
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400936 double noise; // noise level in dBFS
dburgessb3a0ca42011-10-12 07:44:40 +0000937 GSM::Time burstTime;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400938 bool isRssiValid; // are RSSI, noise and burstTime valid
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800939 unsigned nbits = gSlotLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000940
Alexander Chemeris2b542102015-06-08 22:46:38 -0400941 rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800942 if (!rxBurst)
943 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000944
Alexander Chemerisb61c6102017-03-17 18:22:19 -0700945 // Convert -1..+1 soft bits to 0..1 soft bits
946 vectorSlicer(rxBurst);
947
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800948 /*
949 * EDGE demodulator returns 444 (148 * 3) bits
950 */
951 if (rxBurst->size() == gSlotLen * 3)
952 nbits = gSlotLen * 3;
dburgessb3a0ca42011-10-12 07:44:40 +0000953
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800954 dBm = RSSI + rssiOffset;
Alexander Chemeris58e95912016-03-25 18:20:28 +0300955 logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400956
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800957 TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000958
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800959 char burstString[nbits + 10];
960 burstString[0] = burstTime.TN();
961 for (int i = 0; i < 4; i++)
962 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
963 burstString[5] = (int)dBm;
964 burstString[6] = (TOAint >> 8) & 0x0ff;
965 burstString[7] = TOAint & 0x0ff;
966 SoftVector::iterator burstItr = rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000967
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800968 for (unsigned i = 0; i < nbits; i++)
969 burstString[8 + i] = (char) round((*burstItr++) * 255.0);
970
971 burstString[nbits + 9] = '\0';
972 delete rxBurst;
973
974 mDataSockets[chan]->write(burstString, nbits + 10);
dburgessb3a0ca42011-10-12 07:44:40 +0000975}
976
Thomas Tsou204a9f12013-10-29 18:34:16 -0400977void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000978{
979
980 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200981 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000982 assure that transmit packets arrive at the radio/USRP
983 before they need to be transmitted.
984
985 Deadline clock indicates the burst that needs to be
986 pushed into the FIFO right NOW. If transmit queue does
987 not have a burst, stick in filler data.
988 */
989
990
991 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200992
dburgessb3a0ca42011-10-12 07:44:40 +0000993 if (mOn) {
994 //radioClock->wait(); // wait until clock updates
995 LOG(DEBUG) << "radio clock " << radioClock->get();
996 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
997 // if underrun, then we're not providing bursts to radio/USRP fast
998 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400999 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001000 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001001 // only update latency at the defined frame interval
1002 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001003 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
1004 LOG(INFO) << "new latency: " << mTransmitLatency;
1005 mLatencyUpdateTime = radioClock->get();
1006 }
1007 }
1008 else {
1009 // if underrun hasn't occurred in the last sec (216 frames) drop
1010 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001011 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001012 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1013 mTransmitLatency.decTN();
1014 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1015 mLatencyUpdateTime = radioClock->get();
1016 }
1017 }
1018 }
dburgessb3a0ca42011-10-12 07:44:40 +00001019 }
dburgessb3a0ca42011-10-12 07:44:40 +00001020 // time to push burst to transmit FIFO
1021 pushRadioVector(mTransmitDeadlineClock);
1022 mTransmitDeadlineClock.incTN();
1023 }
dburgessb3a0ca42011-10-12 07:44:40 +00001024 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001025
1026 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001027}
1028
1029
1030
1031void Transceiver::writeClockInterface()
1032{
1033 char command[50];
1034 // FIXME -- This should be adaptive.
1035 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1036
1037 LOG(INFO) << "ClockInterface: sending " << command;
1038
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001039 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001040
1041 mLastClockUpdateTime = mTransmitDeadlineClock;
1042
Thomas Tsou92c16df2013-09-28 18:04:19 -04001043}
dburgessb3a0ca42011-10-12 07:44:40 +00001044
Thomas Tsou204a9f12013-10-29 18:34:16 -04001045void *RxUpperLoopAdapter(TransceiverChannel *chan)
1046{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001047 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001048 Transceiver *trx = chan->trx;
1049 size_t num = chan->num;
1050
1051 delete chan;
1052
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001053 snprintf(thread_name, 16, "RxUpper%zu", num);
1054 set_selfthread_name(thread_name);
1055
Thomas Tsou7553aa92013-11-08 12:50:03 -05001056 trx->setPriority(0.42);
1057
Thomas Tsou204a9f12013-10-29 18:34:16 -04001058 while (1) {
1059 trx->driveReceiveFIFO(num);
1060 pthread_testcancel();
1061 }
1062 return NULL;
1063}
1064
1065void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001066{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001067 set_selfthread_name("RxLower");
1068
Thomas Tsou7553aa92013-11-08 12:50:03 -05001069 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001070
dburgessb3a0ca42011-10-12 07:44:40 +00001071 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001072 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001073 pthread_testcancel();
1074 }
1075 return NULL;
1076}
1077
Thomas Tsou204a9f12013-10-29 18:34:16 -04001078void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001079{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001080 set_selfthread_name("TxLower");
1081
Thomas Tsou7553aa92013-11-08 12:50:03 -05001082 transceiver->setPriority(0.44);
1083
Thomas Tsou92c16df2013-09-28 18:04:19 -04001084 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001085 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001086 pthread_testcancel();
1087 }
1088 return NULL;
1089}
1090
Thomas Tsou204a9f12013-10-29 18:34:16 -04001091void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001092{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001093 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001094 Transceiver *trx = chan->trx;
1095 size_t num = chan->num;
1096
1097 delete chan;
1098
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001099 snprintf(thread_name, 16, "CtrlService%zu", num);
1100 set_selfthread_name(thread_name);
1101
dburgessb3a0ca42011-10-12 07:44:40 +00001102 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001103 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001104 pthread_testcancel();
1105 }
1106 return NULL;
1107}
1108
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001110{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001111 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001112 Transceiver *trx = chan->trx;
1113 size_t num = chan->num;
1114
1115 delete chan;
1116
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001117 snprintf(thread_name, 16, "TxUpper%zu", num);
1118 set_selfthread_name(thread_name);
1119
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001120 trx->setPriority(0.40);
1121
dburgessb3a0ca42011-10-12 07:44:40 +00001122 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001123 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001124 pthread_testcancel();
1125 }
1126 return NULL;
1127}