blob: b12a4984d02e05b6e6ae4ccee400a4bffcf833ab [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>
Alexander Chemerise8905a02015-06-03 23:47:56 -040027#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040028#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000029#include "Transceiver.h"
30#include <Logger.h>
31
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020032extern "C" {
33#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020034#include "proto_trxd.h"
35
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020036#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020037#include <osmocom/core/socket.h>
Vadim Yanitskiyb3123252019-07-15 23:53:08 +070038#include <osmocom/core/bits.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020039}
40
ttsou2173abf2012-08-08 00:51:31 +000041#ifdef HAVE_CONFIG_H
42#include "config.h"
43#endif
dburgessb3a0ca42011-10-12 07:44:40 +000044
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040045using namespace GSM;
46
kurtis.heimerlec842de2012-11-23 08:37:32 +000047#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000048
Thomas Tsoufa3a7872013-10-17 21:23:34 -040049/* Number of running values use in noise average */
50#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000051
Thomas Tsouf0782732013-10-29 15:55:47 -040052TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080053 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040054{
55 for (int i = 0; i < 8; i++) {
56 chanType[i] = Transceiver::NONE;
57 fillerModulus[i] = 26;
58 chanResponse[i] = NULL;
59 DFEForward[i] = NULL;
60 DFEFeedback[i] = NULL;
61
62 for (int n = 0; n < 102; n++)
63 fillerTable[n][i] = NULL;
64 }
65}
66
67TransceiverState::~TransceiverState()
68{
69 for (int i = 0; i < 8; i++) {
70 delete chanResponse[i];
71 delete DFEForward[i];
72 delete DFEFeedback[i];
73
74 for (int n = 0; n < 102; n++)
75 delete fillerTable[n][i];
76 }
77}
78
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010079bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070080{
Tom Tsou64ad7122015-05-19 18:26:31 -070081 signalVector *burst;
82
83 if ((sps != 1) && (sps != 4))
84 return false;
85
86 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070087 for (size_t i = 0; i < 102; i++) {
88 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010089 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080090 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070091 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010092 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080093 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070094 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010095 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080096 burst = generateEdgeBurst(rtsc);
97 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010098 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +030099 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300100 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100101 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700102 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800103 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700104 }
105
106 scaleVector(*burst, scale);
107 fillerTable[i][n] = burst;
108 }
109
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100110 if ((filler == FILLER_NORM_RAND) ||
111 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700112 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800113 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500114 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700115
116 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400117}
118
dburgessb3a0ca42011-10-12 07:44:40 +0000119Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200120 const char *TRXAddress,
121 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800122 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400123 GSM::Time wTransmitLatency,
124 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200125 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200126 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200127 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200128 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol758381b2019-07-16 21:55:03 +0200129 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
130 mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300131 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200132 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000133{
dburgessb3a0ca42011-10-12 07:44:40 +0000134 txFullScale = mRadioInterface->fullScaleInputValue();
135 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300136
137 for (int i = 0; i < 8; i++) {
138 for (int j = 0; j < 8; j++)
139 mHandover[i][j] = false;
140 }
dburgessb3a0ca42011-10-12 07:44:40 +0000141}
142
143Transceiver::~Transceiver()
144{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800145 stop();
146
dburgessb3a0ca42011-10-12 07:44:40 +0000147 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400148
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200149 if (mClockSocket >= 0)
150 close(mClockSocket);
151
Thomas Tsou204a9f12013-10-29 18:34:16 -0400152 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800153 mControlServiceLoopThreads[i]->cancel();
154 mControlServiceLoopThreads[i]->join();
155 delete mControlServiceLoopThreads[i];
156
Thomas Tsou204a9f12013-10-29 18:34:16 -0400157 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200158 if (mCtrlSockets[i] >= 0)
159 close(mCtrlSockets[i]);
160 if (mDataSockets[i] >= 0)
161 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400162 }
dburgessb3a0ca42011-10-12 07:44:40 +0000163}
Thomas Tsou83e06892013-08-20 16:10:01 -0400164
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800165/*
166 * Initialize transceiver
167 *
168 * Start or restart the control loop. Any further control is handled through the
169 * socket API. Randomize the central radio clock set the downlink burst
170 * counters. Note that the clock will not update until the radio starts, but we
171 * are still expected to report clock indications through control channel
172 * activity.
173 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200174bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
175 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400176{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500177 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400178
Thomas Tsou204a9f12013-10-29 18:34:16 -0400179 if (!mChans) {
180 LOG(ALERT) << "No channels assigned";
181 return false;
182 }
183
Tom Tsou2079a3c2016-03-06 00:58:56 -0800184 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400185 LOG(ALERT) << "Failed to initialize signal processing library";
186 return false;
187 }
188
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200189 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700190 mEdge = edge;
191
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200192 mDataSockets.resize(mChans, -1);
193 mCtrlSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400194 mControlServiceLoopThreads.resize(mChans);
195 mTxPriorityQueueServiceLoopThreads.resize(mChans);
196 mRxServiceLoopThreads.resize(mChans);
197
198 mTxPriorityQueues.resize(mChans);
199 mReceiveFIFO.resize(mChans);
200 mStates.resize(mChans);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200201 mVersionTRXD.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400202
Thomas Tsouccb73e12014-04-15 17:41:28 -0400203 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700204 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400205 mStates[0].mRetrans = true;
206
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800207 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200208 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
209 mLocalAddr.c_str(), mBasePort,
210 mRemoteAddr.c_str(), mBasePort + 100,
211 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
212
Thomas Tsou204a9f12013-10-29 18:34:16 -0400213 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500214 c_srcport = mBasePort + 2 * i + 1;
215 c_dstport = mBasePort + 2 * i + 101;
216 d_srcport = mBasePort + 2 * i + 2;
217 d_dstport = mBasePort + 2 * i + 102;
218
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200219 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
220 mLocalAddr.c_str(), c_srcport,
221 mRemoteAddr.c_str(), c_dstport,
222 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
223 if (mCtrlSockets[i] < 0)
224 return false;
225
226 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
227 mLocalAddr.c_str(), d_srcport,
228 mRemoteAddr.c_str(), d_dstport,
229 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
230 if (mCtrlSockets[i] < 0)
231 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400232 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400233
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800234 /* Randomize the central clock */
235 GSM::Time startTime(random() % gHyperframe, 0);
236 mRadioInterface->getClock()->set(startTime);
237 mTransmitDeadlineClock = startTime;
238 mLastClockUpdateTime = startTime;
239 mLatencyUpdateTime = startTime;
240
241 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400242 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200243 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
244 params->trx = this;
245 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200246 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800247 mControlServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200248 ControlServiceLoopAdapter, (void*) params);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400249
Tom Tsou64ad7122015-05-19 18:26:31 -0700250 if (i && filler == FILLER_DUMMY)
251 filler = FILLER_ZERO;
252
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300253 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400254 }
255
256 return true;
257}
dburgessb3a0ca42011-10-12 07:44:40 +0000258
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800259/*
260 * Start the transceiver
261 *
262 * Submit command(s) to the radio device to commence streaming samples and
263 * launch threads to handle sample I/O. Re-synchronize the transmit burst
264 * counters to the central radio clock here as well.
265 */
266bool Transceiver::start()
267{
268 ScopedLock lock(mLock);
269
270 if (mOn) {
271 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300272 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800273 }
274
275 LOG(NOTICE) << "Starting the transceiver";
276
277 GSM::Time time = mRadioInterface->getClock()->get();
278 mTransmitDeadlineClock = time;
279 mLastClockUpdateTime = time;
280 mLatencyUpdateTime = time;
281
282 if (!mRadioInterface->start()) {
283 LOG(ALERT) << "Device failed to start";
284 return false;
285 }
286
287 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200288 mRxLowerLoopThread = new Thread(stackSize);
289 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800290 mTxLowerLoopThread->start((void * (*)(void*))
291 TxLowerLoopAdapter,(void*) this);
292 mRxLowerLoopThread->start((void * (*)(void*))
293 RxLowerLoopAdapter,(void*) this);
294
295 /* Launch uplink and downlink burst processing threads */
296 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200297 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
298 params->trx = this;
299 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200300 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800301 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200302 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800303
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200304 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
305 params->trx = this;
306 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200307 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800308 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200309 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800310 }
311
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200312 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800313 mOn = true;
314 return true;
315}
316
317/*
318 * Stop the transceiver
319 *
320 * Perform stopping by disabling receive streaming and issuing cancellation
321 * requests to running threads. Most threads will timeout and terminate once
322 * device is disabled, but the transmit loop may block waiting on the central
323 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
324 * makes it to the thread cancellation point.
325 */
326void Transceiver::stop()
327{
328 ScopedLock lock(mLock);
329
330 if (!mOn)
331 return;
332
333 LOG(NOTICE) << "Stopping the transceiver";
334 mTxLowerLoopThread->cancel();
335 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700336 mTxLowerLoopThread->join();
337 mRxLowerLoopThread->join();
338 delete mTxLowerLoopThread;
339 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800340
341 for (size_t i = 0; i < mChans; i++) {
342 mRxServiceLoopThreads[i]->cancel();
343 mTxPriorityQueueServiceLoopThreads[i]->cancel();
344 }
345
346 LOG(INFO) << "Stopping the device";
347 mRadioInterface->stop();
348
349 for (size_t i = 0; i < mChans; i++) {
350 mRxServiceLoopThreads[i]->join();
351 mTxPriorityQueueServiceLoopThreads[i]->join();
352 delete mRxServiceLoopThreads[i];
353 delete mTxPriorityQueueServiceLoopThreads[i];
354
355 mTxPriorityQueues[i].clear();
356 }
357
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800358 mOn = false;
359 LOG(NOTICE) << "Transceiver stopped";
360}
361
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500362void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400363 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000364{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500365 signalVector *burst;
366 radioVector *radio_burst;
367
Thomas Tsou204a9f12013-10-29 18:34:16 -0400368 if (chan >= mTxPriorityQueues.size()) {
369 LOG(ALERT) << "Invalid channel " << chan;
370 return;
371 }
372
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500373 if (wTime.TN() > 7) {
374 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
375 return;
376 }
377
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800378 /* Use the number of bits as the EDGE burst indicator */
379 if (bits.size() == EDGE_BURST_NBITS)
380 burst = modulateEdgeBurst(bits, mSPSTx);
381 else
382 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
383
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500384 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000385
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500386 radio_burst = new radioVector(wTime, burst);
387
388 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000389}
390
Thomas Tsou15d743e2014-01-25 02:34:03 -0500391void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
392{
393 int TN, modFN;
394 TransceiverState *state = &mStates[chan];
395
396 TN = burst->getTime().TN();
397 modFN = burst->getTime().FN() % state->fillerModulus[TN];
398
399 delete state->fillerTable[modFN][TN];
400 state->fillerTable[modFN][TN] = burst->getVector();
401 burst->setVector(NULL);
402}
403
dburgessb3a0ca42011-10-12 07:44:40 +0000404void Transceiver::pushRadioVector(GSM::Time &nowTime)
405{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400406 int TN, modFN;
407 radioVector *burst;
408 TransceiverState *state;
409 std::vector<signalVector *> bursts(mChans);
410 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500411 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000412
Thomas Tsou204a9f12013-10-29 18:34:16 -0400413 for (size_t i = 0; i < mChans; i ++) {
414 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000415
Thomas Tsou204a9f12013-10-29 18:34:16 -0400416 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200417 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200418 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500419 if (state->mRetrans)
420 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500421 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400422 }
423
424 TN = nowTime.TN();
425 modFN = nowTime.FN() % state->fillerModulus[TN];
426
427 bursts[i] = state->fillerTable[modFN][TN];
428 zeros[i] = state->chanType[TN] == NONE;
429
430 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500431 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500432
433 if (state->mRetrans) {
434 updateFillerTable(i, burst);
435 } else {
436 burst->setVector(NULL);
437 filler[i] = false;
438 }
439
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500440 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400441 }
dburgessb3a0ca42011-10-12 07:44:40 +0000442 }
443
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444 mRadioInterface->driveTransmitRadio(bursts, zeros);
445
Thomas Tsou15d743e2014-01-25 02:34:03 -0500446 for (size_t i = 0; i < mChans; i++) {
447 if (!filler[i])
448 delete bursts[i];
449 }
dburgessb3a0ca42011-10-12 07:44:40 +0000450}
451
Thomas Tsou204a9f12013-10-29 18:34:16 -0400452void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000453{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400454 TransceiverState *state = &mStates[chan];
455
456 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000457 case NONE:
458 case I:
459 case II:
460 case III:
461 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400462 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000463 break;
464 case IV:
465 case VI:
466 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400467 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000468 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200469 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000470 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400471 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000472 break;
ttsoufc40a842013-06-09 22:38:18 +0000473 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400474 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000475 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000476 default:
477 break;
478 }
479}
480
481
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700482CorrType Transceiver::expectedCorrType(GSM::Time currTime,
483 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000484{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300485 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 };
486 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,
487 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 };
488 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,
489 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 -0400490 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000491 unsigned burstTN = currTime.TN();
492 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300493 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000494
Thomas Tsou204a9f12013-10-29 18:34:16 -0400495 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000496 case NONE:
497 return OFF;
498 break;
499 case FILL:
500 return IDLE;
501 break;
502 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300503 // TODO: Are we expecting RACH on an IDLE frame?
504/* if (burstFN % 26 == 25)
505 return IDLE;*/
506 if (mHandover[burstTN][0])
507 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000508 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000509 break;
510 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300511 subch = tchh_subslot[burstFN % 26];
512 if (subch == 1)
513 return IDLE;
514 if (mHandover[burstTN][0])
515 return RACH;
ttsou20642972013-03-27 22:00:25 +0000516 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000517 break;
518 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300519 subch = tchh_subslot[burstFN % 26];
520 if (mHandover[burstTN][subch])
521 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000522 return TSC;
523 break;
524 case IV:
525 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200526 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000527 break;
528 case V: {
529 int mod51 = burstFN % 51;
530 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200531 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000532 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200533 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000534 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200535 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300536 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
537 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000538 else
539 return TSC;
540 break;
541 }
542 case VII:
543 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
544 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300545 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
546 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000547 else
548 return TSC;
549 break;
ttsoufc40a842013-06-09 22:38:18 +0000550 case XIII: {
551 int mod52 = burstFN % 52;
552 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200553 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000554 else if ((mod52 == 25) || (mod52 == 51))
555 return IDLE;
556 else
557 return TSC;
558 break;
559 }
dburgessb3a0ca42011-10-12 07:44:40 +0000560 case LOOPBACK:
561 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
562 return IDLE;
563 else
564 return TSC;
565 break;
566 default:
567 return OFF;
568 break;
569 }
dburgessb3a0ca42011-10-12 07:44:40 +0000570}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400571
Alexander Chemerise692ce92015-06-12 00:15:31 -0400572void writeToFile(radioVector *radio_burst, size_t chan)
573{
574 GSM::Time time = radio_burst->getTime();
575 std::ostringstream fname;
576 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
577 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
578 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
579 outfile.close();
580}
581
Thomas Tsou30421a72013-11-13 23:14:48 -0500582/*
583 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200584 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500585 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200586bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000587{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800588 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200589 struct estim_burst_params ebp;
590 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200591 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500592 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500593 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200594 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200595 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500596 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000597
Thomas Tsou30421a72013-11-13 23:14:48 -0500598 /* Blocking FIFO read */
599 radioVector *radio_burst = mReceiveFIFO[chan]->read();
600 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200601 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000602
Thomas Tsou30421a72013-11-13 23:14:48 -0500603 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200604 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200605 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000606
Tom Tsou64464e62016-07-01 03:46:46 -0700607 /* Enable 8-PSK burst detection if EDGE is enabled */
608 if (mEdge && (type == TSC))
609 type = EDGE;
610
Alexander Chemerise692ce92015-06-12 00:15:31 -0400611 /* Debug: dump bursts to disk */
612 /* bits 0-7 - chan 0 timeslots
613 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200614 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400615 writeToFile(radio_burst, chan);
616
Alexander Chemeris2b542102015-06-08 22:46:38 -0400617 /* No processing if the timeslot is off.
618 * Not even power level or noise calculation. */
619 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500620 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200621 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000622 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000623
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200624 /* Initialize struct bi */
625 bi->nbits = 0;
626 bi->fn = burstTime.FN();
627 bi->tn = burstTime.TN();
628 bi->rssi = 0.0;
629 bi->toa = 0.0;
630 bi->noise = 0.0;
631 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200632 bi->modulation = MODULATION_GMSK;
633 bi->tss = 0; /* TODO: we only support tss 0 right now */
634 bi->tsc = 0;
635 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200636
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500637 /* Select the diversity channel with highest energy */
638 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300639 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500640 if (pow > max) {
641 max = pow;
642 max_i = i;
643 }
644 avg += pow;
645 }
646
647 if (max_i < 0) {
648 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200649 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500650 }
651
Thomas Tsou30421a72013-11-13 23:14:48 -0500652 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500653 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500654 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400655
Alexander Chemeris2b542102015-06-08 22:46:38 -0400656 if (type == IDLE) {
657 /* Update noise levels */
658 state->mNoises.insert(avg);
659 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200660 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400661
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200662 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
663 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
664
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200665 if (type == IDLE)
666 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400667
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200668 max_toa = (type == RACH || type == EXT_RACH) ?
669 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200670
Thomas Tsou30421a72013-11-13 23:14:48 -0500671 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200672 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200673 if (rc <= 0) {
674 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400675 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200676 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400677 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200678 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000679 }
dburgessb3a0ca42011-10-12 07:44:40 +0000680
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200681 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200682 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200683 bi->tsc = ebp.tsc;
684 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200685 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500686
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200687 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200688 if (rxBurst->size() == EDGE_BURST_NBITS) {
689 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200690 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200691 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
692 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200693 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200694 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200695
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200696 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200697 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200698
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200699 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500700 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200701 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200702
703ret_idle:
704 bi->idle = true;
705 delete radio_burst;
706 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000707}
708
dburgessb3a0ca42011-10-12 07:44:40 +0000709void Transceiver::reset()
710{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400711 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
712 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000713}
714
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200715
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700716#define MAX_PACKET_LENGTH 100
717
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700718/**
719 * Matches a buffer with a command.
720 * @param buf a buffer to look command in
721 * @param cmd a command to look in buffer
722 * @param params pointer to arguments, or NULL
723 * @return true if command matches, otherwise false
724 */
725static bool match_cmd(char *buf,
726 const char *cmd, char **params)
727{
728 size_t cmd_len = strlen(cmd);
729
730 /* Check a command itself */
731 if (strncmp(buf, cmd, cmd_len))
732 return false;
733
734 /* A command has arguments */
735 if (params != NULL) {
736 /* Make sure there is a space */
737 if (buf[cmd_len] != ' ')
738 return false;
739
740 /* Update external pointer */
741 *params = buf + cmd_len + 1;
742 }
743
744 return true;
745}
746
Thomas Tsou204a9f12013-10-29 18:34:16 -0400747void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000748{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700749 char buffer[MAX_PACKET_LENGTH + 1];
750 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700751 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700752 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400753
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700754 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200755 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
756 if (msgLen <= 0) {
757 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000758 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200759 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700760
761 /* Zero-terminate received string */
762 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000763
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700764 /* Verify a command signature */
765 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100766 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000767 return;
768 }
dburgessb3a0ca42011-10-12 07:44:40 +0000769
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700770 /* Set command pointer */
771 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200772 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700773
774 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800775 stop();
776 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700778 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000779 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700780 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000781 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300782 for (int i = 0; i < 8; i++) {
783 for (int j = 0; j < 8; j++)
784 mHandover[i][j] = false;
785 }
Tom Tsou365bc382016-10-19 15:26:04 -0700786 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700787 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700788 unsigned ts = 0, ss = 0;
789 sscanf(params, "%u %u", &ts, &ss);
790 if (ts > 7 || ss > 7) {
791 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
792 } else {
793 mHandover[ts][ss] = true;
794 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
795 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700796 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700797 unsigned ts = 0, ss = 0;
798 sscanf(params, "%u %u", &ts, &ss);
799 if (ts > 7 || ss > 7) {
800 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
801 } else {
802 mHandover[ts][ss] = false;
803 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
804 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700805 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000806 //set expected maximum time-of-arrival
807 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700808 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300809 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000810 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700811 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300812 //set expected maximum time-of-arrival
813 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700814 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300815 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
816 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700817 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000818 //set expected maximum time-of-arrival
819 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700820 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400821 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000822 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000824 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500825 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000826 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500827 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000828 }
829 else {
830 sprintf(response,"RSP NOISELEV 1 0");
831 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700832 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800833 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700834 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800835 power = mRadioInterface->setPowerAttenuation(power, chan);
836 mStates[chan].mPower = power;
837 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800839 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700840 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800841 power = mStates[chan].mPower + step;
842 power = mRadioInterface->setPowerAttenuation(power, chan);
843 mStates[chan].mPower = power;
844 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700845 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000846 // tune receiver
847 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700848 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500849 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400850 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100851 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000852 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
853 }
854 else
855 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700856 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000857 // tune txmtr
858 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700859 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500860 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400861 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100862 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000863 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
864 }
865 else
866 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700867 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000868 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500869 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700870 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700871 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500872 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700873 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100874 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000875 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400876 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000877 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700878 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700879 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000880 int corrCode;
881 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700882 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000883 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100884 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000885 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
886 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200887 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400888 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
889 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000890 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200891 } else if (match_cmd(command, "SETFORMAT", &params)) {
892 // set TRXD protocol version
893 unsigned version_recv;
894 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200895 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200896 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200897 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
898 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200899 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
900 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200901 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
902 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200903 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
904 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700905 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400906 // debug command! may change or disapear without notice
907 // set a mask which bursts to dump to disk
908 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700909 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400910 mWriteBurstToDiskMask = mask;
911 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700912 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100913 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200914 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000915 }
916
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200917 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200918 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
919 if (msgLen <= 0)
920 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000921}
922
Thomas Tsou204a9f12013-10-29 18:34:16 -0400923bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000924{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200925 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700926 int burstLen;
927 char buffer[EDGE_BURST_NBITS + 50];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700928 struct trxd_hdr_common *chdr;
929 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000930
931 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200932 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
933 if (msgLen <= 0) {
934 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
935 return false;
936 }
dburgessb3a0ca42011-10-12 07:44:40 +0000937
Tom Tsoue8871082016-07-01 02:46:04 -0700938 if (msgLen == gSlotLen + 1 + 4 + 1) {
939 burstLen = gSlotLen;
940 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
941 if (mSPSTx != 4)
942 return false;
943
944 burstLen = EDGE_BURST_NBITS;
945 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000946 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
947 return false;
948 }
949
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700950 /* Common header part: HDR version, TDMA TN & FN */
951 chdr = (struct trxd_hdr_common *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +0000952
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700953 /* Convert TDMA FN to the host endianness */
954 fn = osmo_load32be(&chdr->fn);
955
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700956 /* Make sure we support the received header format */
957 switch (chdr->version) {
958 case 0:
959 /* Version 1 has the same format */
960 case 1:
961 break;
962
963 default:
964 LOG(ERR) << "Rx TRXD message with unknown header version " << chdr->version;
965 return false;
966 }
967
Vadim Yanitskiy56c5f292019-07-16 00:02:56 +0700968 LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << chdr->version << "): "
969 << "fn=" << fn << ", tn=" << chdr->tn << ", "
970 << "burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200971
dburgessb3a0ca42011-10-12 07:44:40 +0000972 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700973 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000974 BitVector::iterator itr = newBurst.begin();
975 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200976 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000977 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200978
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700979 GSM::Time currTime = GSM::Time(fn, chdr->tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400980
981 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000982
983 return true;
984
985
986}
dburgessb3a0ca42011-10-12 07:44:40 +0000987
Thomas Tsou204a9f12013-10-29 18:34:16 -0400988void Transceiver::driveReceiveRadio()
989{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200990 int rc = mRadioInterface->driveReceiveRadio();
991 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400992 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200993 } else if (rc < 0) {
994 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200995 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200996 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
997 mForceClockInterface = false;
998 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400999 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001000}
1001
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001002void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001003{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001004 std::ostringstream os;
1005 for (size_t i=0; i < bi->nbits; i++) {
1006 if (bi->rx_burst[i] > 0.5) os << "1";
1007 else if (bi->rx_burst[i] > 0.25) os << "|";
1008 else if (bi->rx_burst[i] > 0.0) os << "'";
1009 else os << "-";
1010 }
1011
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001012 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +03001013 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +02001014 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001015 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1016 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1017 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1018 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001019 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001020 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001021 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001022}
1023
Thomas Tsou204a9f12013-10-29 18:34:16 -04001024void Transceiver::driveReceiveFIFO(size_t chan)
1025{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001026 struct trx_ul_burst_ind bi;
1027
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001028 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001029 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001030 if (!bi.idle)
1031 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001032
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001033 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001034 case 0:
1035 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
1036 break;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001037 case 1:
1038 trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
1039 break;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001040 default:
1041 OSMO_ASSERT(false);
1042 }
dburgessb3a0ca42011-10-12 07:44:40 +00001043}
1044
Thomas Tsou204a9f12013-10-29 18:34:16 -04001045void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001046{
1047
1048 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001049 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001050 assure that transmit packets arrive at the radio/USRP
1051 before they need to be transmitted.
1052
1053 Deadline clock indicates the burst that needs to be
1054 pushed into the FIFO right NOW. If transmit queue does
1055 not have a burst, stick in filler data.
1056 */
1057
1058
1059 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001060
dburgessb3a0ca42011-10-12 07:44:40 +00001061 if (mOn) {
1062 //radioClock->wait(); // wait until clock updates
1063 LOG(DEBUG) << "radio clock " << radioClock->get();
1064 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1065 // if underrun, then we're not providing bursts to radio/USRP fast
1066 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001067 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001068 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001069 // only update latency at the defined frame interval
1070 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001071 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001072 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1073 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001074 mLatencyUpdateTime = radioClock->get();
1075 }
1076 }
1077 else {
1078 // if underrun hasn't occurred in the last sec (216 frames) drop
1079 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001080 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001081 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1082 mTransmitLatency.decTN();
1083 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1084 mLatencyUpdateTime = radioClock->get();
1085 }
1086 }
1087 }
dburgessb3a0ca42011-10-12 07:44:40 +00001088 }
dburgessb3a0ca42011-10-12 07:44:40 +00001089 // time to push burst to transmit FIFO
1090 pushRadioVector(mTransmitDeadlineClock);
1091 mTransmitDeadlineClock.incTN();
1092 }
dburgessb3a0ca42011-10-12 07:44:40 +00001093 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001094
1095 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001096}
1097
1098
1099
1100void Transceiver::writeClockInterface()
1101{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001102 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001103 char command[50];
1104 // FIXME -- This should be adaptive.
1105 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1106
1107 LOG(INFO) << "ClockInterface: sending " << command;
1108
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001109 msgLen = write(mClockSocket, command, strlen(command) + 1);
1110 if (msgLen <= 0)
1111 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001112
1113 mLastClockUpdateTime = mTransmitDeadlineClock;
1114
Thomas Tsou92c16df2013-09-28 18:04:19 -04001115}
dburgessb3a0ca42011-10-12 07:44:40 +00001116
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001117void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001118{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001119 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001120 Transceiver *trx = params->trx;
1121 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001122
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001123 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001124
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001125 snprintf(thread_name, 16, "RxUpper%zu", num);
1126 set_selfthread_name(thread_name);
1127
Thomas Tsou7553aa92013-11-08 12:50:03 -05001128 trx->setPriority(0.42);
1129
Thomas Tsou204a9f12013-10-29 18:34:16 -04001130 while (1) {
1131 trx->driveReceiveFIFO(num);
1132 pthread_testcancel();
1133 }
1134 return NULL;
1135}
1136
1137void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001138{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001139 set_selfthread_name("RxLower");
1140
Thomas Tsou7553aa92013-11-08 12:50:03 -05001141 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001142
dburgessb3a0ca42011-10-12 07:44:40 +00001143 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001144 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001145 pthread_testcancel();
1146 }
1147 return NULL;
1148}
1149
Thomas Tsou204a9f12013-10-29 18:34:16 -04001150void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001151{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001152 set_selfthread_name("TxLower");
1153
Thomas Tsou7553aa92013-11-08 12:50:03 -05001154 transceiver->setPriority(0.44);
1155
Thomas Tsou92c16df2013-09-28 18:04:19 -04001156 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001157 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001158 pthread_testcancel();
1159 }
1160 return NULL;
1161}
1162
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001163void *ControlServiceLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001164{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001165 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001166 Transceiver *trx = params->trx;
1167 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001168
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001169 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001170
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001171 snprintf(thread_name, 16, "CtrlService%zu", num);
1172 set_selfthread_name(thread_name);
1173
dburgessb3a0ca42011-10-12 07:44:40 +00001174 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001175 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001176 pthread_testcancel();
1177 }
1178 return NULL;
1179}
1180
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001181void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001182{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001183 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001184 Transceiver *trx = params->trx;
1185 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001186
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001187 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001188
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001189 snprintf(thread_name, 16, "TxUpper%zu", num);
1190 set_selfthread_name(thread_name);
1191
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001192 trx->setPriority(0.40);
1193
dburgessb3a0ca42011-10-12 07:44:40 +00001194 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001195 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001196 pthread_testcancel();
1197 }
1198 return NULL;
1199}