blob: 4da187628cc9daaa0eb768bbc0362701e8d7eae5 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02004* SPDX-License-Identifier: GPL-3.0+
5*
dburgessb3a0ca42011-10-12 07:44:40 +00006* This software is distributed under the terms of the GNU Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24*/
25
dburgessb3a0ca42011-10-12 07:44:40 +000026#include <stdio.h>
Pau Espin Pedrol27424a32019-08-07 12:45:53 +020027#include <netinet/in.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040028#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040029#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000030#include "Transceiver.h"
31#include <Logger.h>
32
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020033extern "C" {
34#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020035#include "proto_trxd.h"
36
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020037#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020038#include <osmocom/core/socket.h>
Vadim Yanitskiyb3123252019-07-15 23:53:08 +070039#include <osmocom/core/bits.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020040}
41
ttsou2173abf2012-08-08 00:51:31 +000042#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif
dburgessb3a0ca42011-10-12 07:44:40 +000045
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040046using namespace GSM;
47
kurtis.heimerlec842de2012-11-23 08:37:32 +000048#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000049
Thomas Tsoufa3a7872013-10-17 21:23:34 -040050/* Number of running values use in noise average */
51#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000052
Thomas Tsouf0782732013-10-29 15:55:47 -040053TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080054 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040055{
56 for (int i = 0; i < 8; i++) {
57 chanType[i] = Transceiver::NONE;
58 fillerModulus[i] = 26;
59 chanResponse[i] = NULL;
60 DFEForward[i] = NULL;
61 DFEFeedback[i] = NULL;
62
63 for (int n = 0; n < 102; n++)
64 fillerTable[n][i] = NULL;
65 }
66}
67
68TransceiverState::~TransceiverState()
69{
70 for (int i = 0; i < 8; i++) {
71 delete chanResponse[i];
72 delete DFEForward[i];
73 delete DFEFeedback[i];
74
75 for (int n = 0; n < 102; n++)
76 delete fillerTable[n][i];
77 }
78}
79
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010080bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070081{
Tom Tsou64ad7122015-05-19 18:26:31 -070082 signalVector *burst;
83
84 if ((sps != 1) && (sps != 4))
85 return false;
86
87 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070088 for (size_t i = 0; i < 102; i++) {
89 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010090 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080091 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070092 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010093 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080094 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070095 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010096 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080097 burst = generateEdgeBurst(rtsc);
98 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010099 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300100 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300101 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100102 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700103 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800104 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700105 }
106
107 scaleVector(*burst, scale);
108 fillerTable[i][n] = burst;
109 }
110
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100111 if ((filler == FILLER_NORM_RAND) ||
112 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700113 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800114 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500115 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700116
117 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400118}
119
dburgessb3a0ca42011-10-12 07:44:40 +0000120Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200121 const char *TRXAddress,
122 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800123 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400124 GSM::Time wTransmitLatency,
125 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200126 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200127 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200128 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200129 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol758381b2019-07-16 21:55:03 +0200130 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
131 mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300132 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200133 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000134{
dburgessb3a0ca42011-10-12 07:44:40 +0000135 txFullScale = mRadioInterface->fullScaleInputValue();
136 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300137
138 for (int i = 0; i < 8; i++) {
139 for (int j = 0; j < 8; j++)
140 mHandover[i][j] = false;
141 }
dburgessb3a0ca42011-10-12 07:44:40 +0000142}
143
144Transceiver::~Transceiver()
145{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800146 stop();
147
dburgessb3a0ca42011-10-12 07:44:40 +0000148 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400149
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200150 if (mClockSocket >= 0)
151 close(mClockSocket);
152
Thomas Tsou204a9f12013-10-29 18:34:16 -0400153 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800154 mControlServiceLoopThreads[i]->cancel();
155 mControlServiceLoopThreads[i]->join();
156 delete mControlServiceLoopThreads[i];
157
Thomas Tsou204a9f12013-10-29 18:34:16 -0400158 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200159 if (mCtrlSockets[i] >= 0)
160 close(mCtrlSockets[i]);
161 if (mDataSockets[i] >= 0)
162 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400163 }
dburgessb3a0ca42011-10-12 07:44:40 +0000164}
Thomas Tsou83e06892013-08-20 16:10:01 -0400165
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800166/*
167 * Initialize transceiver
168 *
169 * Start or restart the control loop. Any further control is handled through the
170 * socket API. Randomize the central radio clock set the downlink burst
171 * counters. Note that the clock will not update until the radio starts, but we
172 * are still expected to report clock indications through control channel
173 * activity.
174 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200175bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
176 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400177{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500178 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400179
Thomas Tsou204a9f12013-10-29 18:34:16 -0400180 if (!mChans) {
181 LOG(ALERT) << "No channels assigned";
182 return false;
183 }
184
Tom Tsou2079a3c2016-03-06 00:58:56 -0800185 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400186 LOG(ALERT) << "Failed to initialize signal processing library";
187 return false;
188 }
189
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200190 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700191 mEdge = edge;
192
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200193 mDataSockets.resize(mChans, -1);
194 mCtrlSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400195 mControlServiceLoopThreads.resize(mChans);
196 mTxPriorityQueueServiceLoopThreads.resize(mChans);
197 mRxServiceLoopThreads.resize(mChans);
198
199 mTxPriorityQueues.resize(mChans);
200 mReceiveFIFO.resize(mChans);
201 mStates.resize(mChans);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200202 mVersionTRXD.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400203
Thomas Tsouccb73e12014-04-15 17:41:28 -0400204 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700205 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400206 mStates[0].mRetrans = true;
207
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800208 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200209 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
210 mLocalAddr.c_str(), mBasePort,
211 mRemoteAddr.c_str(), mBasePort + 100,
212 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
213
Thomas Tsou204a9f12013-10-29 18:34:16 -0400214 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500215 c_srcport = mBasePort + 2 * i + 1;
216 c_dstport = mBasePort + 2 * i + 101;
217 d_srcport = mBasePort + 2 * i + 2;
218 d_dstport = mBasePort + 2 * i + 102;
219
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200220 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
221 mLocalAddr.c_str(), c_srcport,
222 mRemoteAddr.c_str(), c_dstport,
223 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
224 if (mCtrlSockets[i] < 0)
225 return false;
226
227 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
228 mLocalAddr.c_str(), d_srcport,
229 mRemoteAddr.c_str(), d_dstport,
230 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
231 if (mCtrlSockets[i] < 0)
232 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400233 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400234
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800235 /* Randomize the central clock */
236 GSM::Time startTime(random() % gHyperframe, 0);
237 mRadioInterface->getClock()->set(startTime);
238 mTransmitDeadlineClock = startTime;
239 mLastClockUpdateTime = startTime;
240 mLatencyUpdateTime = startTime;
241
242 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400243 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200244 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
245 params->trx = this;
246 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200247 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800248 mControlServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200249 ControlServiceLoopAdapter, (void*) params);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400250
Tom Tsou64ad7122015-05-19 18:26:31 -0700251 if (i && filler == FILLER_DUMMY)
252 filler = FILLER_ZERO;
253
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300254 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400255 }
256
257 return true;
258}
dburgessb3a0ca42011-10-12 07:44:40 +0000259
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800260/*
261 * Start the transceiver
262 *
263 * Submit command(s) to the radio device to commence streaming samples and
264 * launch threads to handle sample I/O. Re-synchronize the transmit burst
265 * counters to the central radio clock here as well.
266 */
267bool Transceiver::start()
268{
269 ScopedLock lock(mLock);
270
271 if (mOn) {
272 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300273 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800274 }
275
276 LOG(NOTICE) << "Starting the transceiver";
277
278 GSM::Time time = mRadioInterface->getClock()->get();
279 mTransmitDeadlineClock = time;
280 mLastClockUpdateTime = time;
281 mLatencyUpdateTime = time;
282
283 if (!mRadioInterface->start()) {
284 LOG(ALERT) << "Device failed to start";
285 return false;
286 }
287
288 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200289 mRxLowerLoopThread = new Thread(stackSize);
290 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800291 mTxLowerLoopThread->start((void * (*)(void*))
292 TxLowerLoopAdapter,(void*) this);
293 mRxLowerLoopThread->start((void * (*)(void*))
294 RxLowerLoopAdapter,(void*) this);
295
296 /* Launch uplink and downlink burst processing threads */
297 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200298 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
299 params->trx = this;
300 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200301 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800302 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200303 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800304
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200305 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
306 params->trx = this;
307 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200308 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800309 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200310 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800311 }
312
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200313 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800314 mOn = true;
315 return true;
316}
317
318/*
319 * Stop the transceiver
320 *
321 * Perform stopping by disabling receive streaming and issuing cancellation
322 * requests to running threads. Most threads will timeout and terminate once
323 * device is disabled, but the transmit loop may block waiting on the central
324 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
325 * makes it to the thread cancellation point.
326 */
327void Transceiver::stop()
328{
329 ScopedLock lock(mLock);
330
331 if (!mOn)
332 return;
333
334 LOG(NOTICE) << "Stopping the transceiver";
335 mTxLowerLoopThread->cancel();
336 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700337 mTxLowerLoopThread->join();
338 mRxLowerLoopThread->join();
339 delete mTxLowerLoopThread;
340 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800341
342 for (size_t i = 0; i < mChans; i++) {
343 mRxServiceLoopThreads[i]->cancel();
344 mTxPriorityQueueServiceLoopThreads[i]->cancel();
345 }
346
347 LOG(INFO) << "Stopping the device";
348 mRadioInterface->stop();
349
350 for (size_t i = 0; i < mChans; i++) {
351 mRxServiceLoopThreads[i]->join();
352 mTxPriorityQueueServiceLoopThreads[i]->join();
353 delete mRxServiceLoopThreads[i];
354 delete mTxPriorityQueueServiceLoopThreads[i];
355
356 mTxPriorityQueues[i].clear();
357 }
358
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800359 mOn = false;
360 LOG(NOTICE) << "Transceiver stopped";
361}
362
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500363void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400364 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000365{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500366 signalVector *burst;
367 radioVector *radio_burst;
368
Thomas Tsou204a9f12013-10-29 18:34:16 -0400369 if (chan >= mTxPriorityQueues.size()) {
370 LOG(ALERT) << "Invalid channel " << chan;
371 return;
372 }
373
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500374 if (wTime.TN() > 7) {
375 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
376 return;
377 }
378
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800379 /* Use the number of bits as the EDGE burst indicator */
380 if (bits.size() == EDGE_BURST_NBITS)
381 burst = modulateEdgeBurst(bits, mSPSTx);
382 else
383 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
384
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500385 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000386
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500387 radio_burst = new radioVector(wTime, burst);
388
389 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000390}
391
Thomas Tsou15d743e2014-01-25 02:34:03 -0500392void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
393{
394 int TN, modFN;
395 TransceiverState *state = &mStates[chan];
396
397 TN = burst->getTime().TN();
398 modFN = burst->getTime().FN() % state->fillerModulus[TN];
399
400 delete state->fillerTable[modFN][TN];
401 state->fillerTable[modFN][TN] = burst->getVector();
402 burst->setVector(NULL);
403}
404
dburgessb3a0ca42011-10-12 07:44:40 +0000405void Transceiver::pushRadioVector(GSM::Time &nowTime)
406{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400407 int TN, modFN;
408 radioVector *burst;
409 TransceiverState *state;
410 std::vector<signalVector *> bursts(mChans);
411 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500412 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000413
Thomas Tsou204a9f12013-10-29 18:34:16 -0400414 for (size_t i = 0; i < mChans; i ++) {
415 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000416
Thomas Tsou204a9f12013-10-29 18:34:16 -0400417 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200418 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200419 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500420 if (state->mRetrans)
421 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500422 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400423 }
424
425 TN = nowTime.TN();
426 modFN = nowTime.FN() % state->fillerModulus[TN];
427
428 bursts[i] = state->fillerTable[modFN][TN];
429 zeros[i] = state->chanType[TN] == NONE;
430
431 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500432 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500433
434 if (state->mRetrans) {
435 updateFillerTable(i, burst);
436 } else {
437 burst->setVector(NULL);
438 filler[i] = false;
439 }
440
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500441 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400442 }
dburgessb3a0ca42011-10-12 07:44:40 +0000443 }
444
Thomas Tsou204a9f12013-10-29 18:34:16 -0400445 mRadioInterface->driveTransmitRadio(bursts, zeros);
446
Thomas Tsou15d743e2014-01-25 02:34:03 -0500447 for (size_t i = 0; i < mChans; i++) {
448 if (!filler[i])
449 delete bursts[i];
450 }
dburgessb3a0ca42011-10-12 07:44:40 +0000451}
452
Thomas Tsou204a9f12013-10-29 18:34:16 -0400453void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000454{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455 TransceiverState *state = &mStates[chan];
456
457 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000458 case NONE:
459 case I:
460 case II:
461 case III:
462 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400463 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000464 break;
465 case IV:
466 case VI:
467 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400468 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000469 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200470 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000471 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400472 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000473 break;
ttsoufc40a842013-06-09 22:38:18 +0000474 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400475 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000476 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000477 default:
478 break;
479 }
480}
481
482
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700483CorrType Transceiver::expectedCorrType(GSM::Time currTime,
484 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000485{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300486 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 };
487 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,
488 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 };
489 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,
490 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 -0400491 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000492 unsigned burstTN = currTime.TN();
493 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300494 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000495
Thomas Tsou204a9f12013-10-29 18:34:16 -0400496 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000497 case NONE:
498 return OFF;
499 break;
500 case FILL:
501 return IDLE;
502 break;
503 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300504 // TODO: Are we expecting RACH on an IDLE frame?
505/* if (burstFN % 26 == 25)
506 return IDLE;*/
507 if (mHandover[burstTN][0])
508 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000509 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000510 break;
511 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300512 subch = tchh_subslot[burstFN % 26];
513 if (subch == 1)
514 return IDLE;
515 if (mHandover[burstTN][0])
516 return RACH;
ttsou20642972013-03-27 22:00:25 +0000517 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000518 break;
519 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300520 subch = tchh_subslot[burstFN % 26];
521 if (mHandover[burstTN][subch])
522 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000523 return TSC;
524 break;
525 case IV:
526 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200527 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000528 break;
529 case V: {
530 int mod51 = burstFN % 51;
531 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200532 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000533 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200534 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000535 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200536 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300537 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
538 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000539 else
540 return TSC;
541 break;
542 }
543 case VII:
544 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
545 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300546 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
547 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000548 else
549 return TSC;
550 break;
ttsoufc40a842013-06-09 22:38:18 +0000551 case XIII: {
552 int mod52 = burstFN % 52;
553 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200554 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000555 else if ((mod52 == 25) || (mod52 == 51))
556 return IDLE;
557 else
558 return TSC;
559 break;
560 }
dburgessb3a0ca42011-10-12 07:44:40 +0000561 case LOOPBACK:
562 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
563 return IDLE;
564 else
565 return TSC;
566 break;
567 default:
568 return OFF;
569 break;
570 }
dburgessb3a0ca42011-10-12 07:44:40 +0000571}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400572
Alexander Chemerise692ce92015-06-12 00:15:31 -0400573void writeToFile(radioVector *radio_burst, size_t chan)
574{
575 GSM::Time time = radio_burst->getTime();
576 std::ostringstream fname;
577 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
578 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
579 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
580 outfile.close();
581}
582
Thomas Tsou30421a72013-11-13 23:14:48 -0500583/*
584 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200585 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500586 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200587bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000588{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800589 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200590 struct estim_burst_params ebp;
591 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200592 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500593 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500594 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200595 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200596 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500597 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000598
Thomas Tsou30421a72013-11-13 23:14:48 -0500599 /* Blocking FIFO read */
600 radioVector *radio_burst = mReceiveFIFO[chan]->read();
601 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200602 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000603
Thomas Tsou30421a72013-11-13 23:14:48 -0500604 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200605 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200606 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000607
Tom Tsou64464e62016-07-01 03:46:46 -0700608 /* Enable 8-PSK burst detection if EDGE is enabled */
609 if (mEdge && (type == TSC))
610 type = EDGE;
611
Alexander Chemerise692ce92015-06-12 00:15:31 -0400612 /* Debug: dump bursts to disk */
613 /* bits 0-7 - chan 0 timeslots
614 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200615 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400616 writeToFile(radio_burst, chan);
617
Alexander Chemeris2b542102015-06-08 22:46:38 -0400618 /* No processing if the timeslot is off.
619 * Not even power level or noise calculation. */
620 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500621 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200622 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000623 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000624
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200625 /* Initialize struct bi */
626 bi->nbits = 0;
627 bi->fn = burstTime.FN();
628 bi->tn = burstTime.TN();
629 bi->rssi = 0.0;
630 bi->toa = 0.0;
631 bi->noise = 0.0;
632 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200633 bi->modulation = MODULATION_GMSK;
634 bi->tss = 0; /* TODO: we only support tss 0 right now */
635 bi->tsc = 0;
636 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200637
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500638 /* Select the diversity channel with highest energy */
639 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300640 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500641 if (pow > max) {
642 max = pow;
643 max_i = i;
644 }
645 avg += pow;
646 }
647
648 if (max_i < 0) {
649 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200650 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500651 }
652
Thomas Tsou30421a72013-11-13 23:14:48 -0500653 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500654 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500655 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400656
Alexander Chemeris2b542102015-06-08 22:46:38 -0400657 if (type == IDLE) {
658 /* Update noise levels */
659 state->mNoises.insert(avg);
660 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200661 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400662
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200663 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
664 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
665
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200666 if (type == IDLE)
667 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400668
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200669 max_toa = (type == RACH || type == EXT_RACH) ?
670 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200671
Thomas Tsou30421a72013-11-13 23:14:48 -0500672 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200673 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200674 if (rc <= 0) {
675 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400676 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200677 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400678 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200679 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000680 }
dburgessb3a0ca42011-10-12 07:44:40 +0000681
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200682 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200683 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200684 bi->tsc = ebp.tsc;
685 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200686 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500687
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200688 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200689 if (rxBurst->size() == EDGE_BURST_NBITS) {
690 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200691 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200692 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
693 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200694 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200695 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200696
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200697 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200698 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200699
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200700 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500701 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200702 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200703
704ret_idle:
705 bi->idle = true;
706 delete radio_burst;
707 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000708}
709
dburgessb3a0ca42011-10-12 07:44:40 +0000710void Transceiver::reset()
711{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400712 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
713 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000714}
715
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200716
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700717#define MAX_PACKET_LENGTH 100
718
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700719/**
720 * Matches a buffer with a command.
721 * @param buf a buffer to look command in
722 * @param cmd a command to look in buffer
723 * @param params pointer to arguments, or NULL
724 * @return true if command matches, otherwise false
725 */
726static bool match_cmd(char *buf,
727 const char *cmd, char **params)
728{
729 size_t cmd_len = strlen(cmd);
730
731 /* Check a command itself */
732 if (strncmp(buf, cmd, cmd_len))
733 return false;
734
735 /* A command has arguments */
736 if (params != NULL) {
737 /* Make sure there is a space */
738 if (buf[cmd_len] != ' ')
739 return false;
740
741 /* Update external pointer */
742 *params = buf + cmd_len + 1;
743 }
744
745 return true;
746}
747
Thomas Tsou204a9f12013-10-29 18:34:16 -0400748void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000749{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700750 char buffer[MAX_PACKET_LENGTH + 1];
751 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700752 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700753 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400754
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700755 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200756 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
757 if (msgLen <= 0) {
758 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000759 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200760 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700761
762 /* Zero-terminate received string */
763 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000764
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700765 /* Verify a command signature */
766 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100767 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000768 return;
769 }
dburgessb3a0ca42011-10-12 07:44:40 +0000770
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700771 /* Set command pointer */
772 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200773 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700774
775 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800776 stop();
777 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700778 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700779 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000780 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700781 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000782 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300783 for (int i = 0; i < 8; i++) {
784 for (int j = 0; j < 8; j++)
785 mHandover[i][j] = false;
786 }
Tom Tsou365bc382016-10-19 15:26:04 -0700787 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700788 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700789 unsigned ts = 0, ss = 0;
790 sscanf(params, "%u %u", &ts, &ss);
791 if (ts > 7 || ss > 7) {
792 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
793 } else {
794 mHandover[ts][ss] = true;
795 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
796 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700797 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700798 unsigned ts = 0, ss = 0;
799 sscanf(params, "%u %u", &ts, &ss);
800 if (ts > 7 || ss > 7) {
801 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
802 } else {
803 mHandover[ts][ss] = false;
804 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
805 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700806 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000807 //set expected maximum time-of-arrival
808 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700809 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300810 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000811 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300813 //set expected maximum time-of-arrival
814 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700815 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300816 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
817 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700818 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000819 //set expected maximum time-of-arrival
820 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700821 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400822 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000823 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700824 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000825 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500826 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000827 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500828 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000829 }
830 else {
831 sprintf(response,"RSP NOISELEV 1 0");
832 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700833 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800834 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700835 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800836 power = mRadioInterface->setPowerAttenuation(power, chan);
837 mStates[chan].mPower = power;
838 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700839 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800840 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700841 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800842 power = mStates[chan].mPower + step;
843 power = mRadioInterface->setPowerAttenuation(power, chan);
844 mStates[chan].mPower = power;
845 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700846 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000847 // tune receiver
848 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700849 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500850 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400851 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100852 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000853 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
854 }
855 else
856 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700857 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000858 // tune txmtr
859 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700860 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500861 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400862 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100863 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000864 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
865 }
866 else
867 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700868 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000869 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500870 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700871 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700872 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500873 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700874 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100875 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000876 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400877 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000878 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700879 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700880 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000881 int corrCode;
882 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700883 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000884 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100885 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000886 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
887 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200888 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400889 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
890 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000891 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200892 } else if (match_cmd(command, "SETFORMAT", &params)) {
893 // set TRXD protocol version
894 unsigned version_recv;
895 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200896 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200897 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200898 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
899 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200900 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
901 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200902 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
903 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200904 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
905 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700906 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400907 // debug command! may change or disapear without notice
908 // set a mask which bursts to dump to disk
909 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700910 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400911 mWriteBurstToDiskMask = mask;
912 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700913 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100914 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200915 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000916 }
917
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200918 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200919 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
920 if (msgLen <= 0)
921 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000922}
923
Thomas Tsou204a9f12013-10-29 18:34:16 -0400924bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000925{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200926 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700927 int burstLen;
928 char buffer[EDGE_BURST_NBITS + 50];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700929 struct trxd_hdr_common *chdr;
930 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000931
932 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200933 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
934 if (msgLen <= 0) {
935 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
936 return false;
937 }
dburgessb3a0ca42011-10-12 07:44:40 +0000938
Tom Tsoue8871082016-07-01 02:46:04 -0700939 if (msgLen == gSlotLen + 1 + 4 + 1) {
940 burstLen = gSlotLen;
941 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
942 if (mSPSTx != 4)
943 return false;
944
945 burstLen = EDGE_BURST_NBITS;
946 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000947 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
948 return false;
949 }
950
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700951 /* Common header part: HDR version, TDMA TN & FN */
952 chdr = (struct trxd_hdr_common *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +0000953
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700954 /* Convert TDMA FN to the host endianness */
955 fn = osmo_load32be(&chdr->fn);
956
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700957 /* Make sure we support the received header format */
958 switch (chdr->version) {
959 case 0:
960 /* Version 1 has the same format */
961 case 1:
962 break;
963
964 default:
965 LOG(ERR) << "Rx TRXD message with unknown header version " << chdr->version;
966 return false;
967 }
968
Vadim Yanitskiy56c5f292019-07-16 00:02:56 +0700969 LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << chdr->version << "): "
970 << "fn=" << fn << ", tn=" << chdr->tn << ", "
971 << "burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200972
dburgessb3a0ca42011-10-12 07:44:40 +0000973 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700974 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000975 BitVector::iterator itr = newBurst.begin();
976 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200977 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000978 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200979
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700980 GSM::Time currTime = GSM::Time(fn, chdr->tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400981
982 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000983
984 return true;
985
986
987}
dburgessb3a0ca42011-10-12 07:44:40 +0000988
Thomas Tsou204a9f12013-10-29 18:34:16 -0400989void Transceiver::driveReceiveRadio()
990{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200991 int rc = mRadioInterface->driveReceiveRadio();
992 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200994 } else if (rc < 0) {
995 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200996 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200997 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
998 mForceClockInterface = false;
999 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001000 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001001}
1002
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001003void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001004{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001005 std::ostringstream os;
1006 for (size_t i=0; i < bi->nbits; i++) {
1007 if (bi->rx_burst[i] > 0.5) os << "1";
1008 else if (bi->rx_burst[i] > 0.25) os << "|";
1009 else if (bi->rx_burst[i] > 0.0) os << "'";
1010 else os << "-";
1011 }
1012
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001013 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +03001014 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +02001015 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001016 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1017 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1018 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1019 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001020 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001021 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001022 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001023}
1024
Thomas Tsou204a9f12013-10-29 18:34:16 -04001025void Transceiver::driveReceiveFIFO(size_t chan)
1026{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001027 struct trx_ul_burst_ind bi;
1028
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001029 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001030 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001031 if (!bi.idle)
1032 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001033
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001034 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001035 case 0:
1036 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
1037 break;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001038 case 1:
1039 trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
1040 break;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001041 default:
1042 OSMO_ASSERT(false);
1043 }
dburgessb3a0ca42011-10-12 07:44:40 +00001044}
1045
Thomas Tsou204a9f12013-10-29 18:34:16 -04001046void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001047{
1048
1049 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001050 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001051 assure that transmit packets arrive at the radio/USRP
1052 before they need to be transmitted.
1053
1054 Deadline clock indicates the burst that needs to be
1055 pushed into the FIFO right NOW. If transmit queue does
1056 not have a burst, stick in filler data.
1057 */
1058
1059
1060 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001061
dburgessb3a0ca42011-10-12 07:44:40 +00001062 if (mOn) {
1063 //radioClock->wait(); // wait until clock updates
1064 LOG(DEBUG) << "radio clock " << radioClock->get();
1065 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1066 // if underrun, then we're not providing bursts to radio/USRP fast
1067 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001068 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001069 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001070 // only update latency at the defined frame interval
1071 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001072 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001073 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1074 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001075 mLatencyUpdateTime = radioClock->get();
1076 }
1077 }
1078 else {
1079 // if underrun hasn't occurred in the last sec (216 frames) drop
1080 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001081 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001082 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1083 mTransmitLatency.decTN();
1084 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1085 mLatencyUpdateTime = radioClock->get();
1086 }
1087 }
1088 }
dburgessb3a0ca42011-10-12 07:44:40 +00001089 }
dburgessb3a0ca42011-10-12 07:44:40 +00001090 // time to push burst to transmit FIFO
1091 pushRadioVector(mTransmitDeadlineClock);
1092 mTransmitDeadlineClock.incTN();
1093 }
dburgessb3a0ca42011-10-12 07:44:40 +00001094 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001095
1096 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001097}
1098
1099
1100
1101void Transceiver::writeClockInterface()
1102{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001103 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001104 char command[50];
1105 // FIXME -- This should be adaptive.
1106 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1107
1108 LOG(INFO) << "ClockInterface: sending " << command;
1109
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001110 msgLen = write(mClockSocket, command, strlen(command) + 1);
1111 if (msgLen <= 0)
1112 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001113
1114 mLastClockUpdateTime = mTransmitDeadlineClock;
1115
Thomas Tsou92c16df2013-09-28 18:04:19 -04001116}
dburgessb3a0ca42011-10-12 07:44:40 +00001117
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001118void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001119{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001120 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001121 Transceiver *trx = params->trx;
1122 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001123
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001124 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001125
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001126 snprintf(thread_name, 16, "RxUpper%zu", num);
1127 set_selfthread_name(thread_name);
1128
Thomas Tsou7553aa92013-11-08 12:50:03 -05001129 trx->setPriority(0.42);
1130
Thomas Tsou204a9f12013-10-29 18:34:16 -04001131 while (1) {
1132 trx->driveReceiveFIFO(num);
1133 pthread_testcancel();
1134 }
1135 return NULL;
1136}
1137
1138void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001139{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001140 set_selfthread_name("RxLower");
1141
Thomas Tsou7553aa92013-11-08 12:50:03 -05001142 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001143
dburgessb3a0ca42011-10-12 07:44:40 +00001144 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001146 pthread_testcancel();
1147 }
1148 return NULL;
1149}
1150
Thomas Tsou204a9f12013-10-29 18:34:16 -04001151void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001152{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001153 set_selfthread_name("TxLower");
1154
Thomas Tsou7553aa92013-11-08 12:50:03 -05001155 transceiver->setPriority(0.44);
1156
Thomas Tsou92c16df2013-09-28 18:04:19 -04001157 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001158 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001159 pthread_testcancel();
1160 }
1161 return NULL;
1162}
1163
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001164void *ControlServiceLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001165{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001166 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001167 Transceiver *trx = params->trx;
1168 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001169
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001170 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001171
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001172 snprintf(thread_name, 16, "CtrlService%zu", num);
1173 set_selfthread_name(thread_name);
1174
dburgessb3a0ca42011-10-12 07:44:40 +00001175 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001176 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001177 pthread_testcancel();
1178 }
1179 return NULL;
1180}
1181
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001182void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001183{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001184 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001185 Transceiver *trx = params->trx;
1186 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001187
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001188 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001189
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001190 snprintf(thread_name, 16, "TxUpper%zu", num);
1191 set_selfthread_name(thread_name);
1192
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001193 trx->setPriority(0.40);
1194
dburgessb3a0ca42011-10-12 07:44:40 +00001195 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001196 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001197 pthread_testcancel();
1198 }
1199 return NULL;
1200}