blob: 0d33f225bae7d37e817fdf273823c1c0592af9da [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
Tom Tsou28670fb2015-08-21 19:32:58 -07002 * Radio device interface
3 *
4 * Copyright (C) 2008-2014 Free Software Foundation, Inc.
5 * Copyright (C) 2015 Ettus Research LLC
6 *
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02007 * SPDX-License-Identifier: AGPL-3.0+
8 *
Tom Tsou28670fb2015-08-21 19:32:58 -07009 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * See the COPYING file in the main directory for details.
22 */
dburgessb3a0ca42011-10-12 07:44:40 +000023
dburgessb3a0ca42011-10-12 07:44:40 +000024#include "radioInterface.h"
Thomas Tsouc1f7c422013-10-11 13:49:55 -040025#include "Resampler.h"
dburgessb3a0ca42011-10-12 07:44:40 +000026#include <Logger.h>
27
Thomas Tsou9471d762013-08-20 21:24:24 -040028extern "C" {
29#include "convert.h"
30}
31
Thomas Tsou3952d802013-10-15 15:12:24 -040032#define CHUNK 625
33#define NUMCHUNKS 4
kurtis.heimerl9b557832011-11-26 03:18:34 +000034
Tom Tsou5cd70dc2016-03-06 01:28:40 -080035RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
Tom Tsoud6ae8642017-03-30 17:22:58 -070036 size_t rx_sps, size_t chans,
Thomas Tsoue90a42b2013-11-13 23:38:09 -050037 int wReceiveOffset, GSM::Time wStartTime)
Tom Tsou5cd70dc2016-03-06 01:28:40 -080038 : mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
Harald Welte2896cec2019-07-21 11:55:22 +020039 underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
40 receiveOffset(wReceiveOffset), mOn(false)
dburgessb3a0ca42011-10-12 07:44:40 +000041{
dburgessb3a0ca42011-10-12 07:44:40 +000042 mClock.set(wStartTime);
dburgessb3a0ca42011-10-12 07:44:40 +000043}
44
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040045RadioInterface::~RadioInterface(void)
46{
47 close();
48}
kurtis.heimerl54354042011-11-26 03:18:49 +000049
Thomas Tsoufe269fe2013-10-14 23:56:51 -040050bool RadioInterface::init(int type)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040051{
Tom Tsoud6ae8642017-03-30 17:22:58 -070052 if ((type != RadioDevice::NORMAL) || !mChans) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -050053 LOG(ALERT) << "Invalid configuration";
Thomas Tsou204a9f12013-10-29 18:34:16 -040054 return false;
55 }
56
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040057 close();
58
Thomas Tsou204a9f12013-10-29 18:34:16 -040059 sendBuffer.resize(mChans);
60 recvBuffer.resize(mChans);
61 convertSendBuffer.resize(mChans);
62 convertRecvBuffer.resize(mChans);
63 mReceiveFIFO.resize(mChans);
Thomas Tsoucb269a32013-11-15 14:15:47 -050064 powerScaling.resize(mChans);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040065
Thomas Tsou204a9f12013-10-29 18:34:16 -040066 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -070067 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
68 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
Thomas Tsou204a9f12013-10-29 18:34:16 -040069
Tom Tsou28670fb2015-08-21 19:32:58 -070070 convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
71 convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
Alexander Chemeris19174f52016-06-18 11:16:54 +030072
73 powerScaling[i] = 1.0;
Thomas Tsou204a9f12013-10-29 18:34:16 -040074 }
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040075
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040076 return true;
77}
78
79void RadioInterface::close()
80{
Pau Espin Pedrol39e0bba2018-12-03 12:19:51 +010081 for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it)
82 delete *it;
83 for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it)
84 delete *it;
85 for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it)
86 delete[] *it;
87 for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it)
88 delete[] *it;
Thomas Tsou204a9f12013-10-29 18:34:16 -040089 sendBuffer.resize(0);
90 recvBuffer.resize(0);
91 convertSendBuffer.resize(0);
92 convertRecvBuffer.resize(0);
dburgessb3a0ca42011-10-12 07:44:40 +000093}
94
95double RadioInterface::fullScaleInputValue(void) {
96 return mRadio->fullScaleInputValue();
97}
98
99double RadioInterface::fullScaleOutputValue(void) {
100 return mRadio->fullScaleOutputValue();
101}
102
Tom Tsoua4d1a412014-11-25 15:46:56 -0800103int RadioInterface::setPowerAttenuation(int atten, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000104{
kurtis.heimerl16c65402011-11-26 03:18:11 +0000105 double rfGain, digAtten;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000106
Thomas Tsou204a9f12013-10-29 18:34:16 -0400107 if (chan >= mChans) {
108 LOG(ALERT) << "Invalid channel requested";
Tom Tsoua4d1a412014-11-25 15:46:56 -0800109 return -1;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400110 }
111
Tom Tsoua4d1a412014-11-25 15:46:56 -0800112 if (atten < 0.0)
113 atten = 0.0;
114
115 rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
116 digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000117
118 if (digAtten < 1.0)
Thomas Tsoucb269a32013-11-15 14:15:47 -0500119 powerScaling[chan] = 1.0;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000120 else
Thomas Tsoucb269a32013-11-15 14:15:47 -0500121 powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
Tom Tsoua4d1a412014-11-25 15:46:56 -0800122
123 return atten;
dburgessb3a0ca42011-10-12 07:44:40 +0000124}
125
kurtis.heimerl9b557832011-11-26 03:18:34 +0000126int RadioInterface::radioifyVector(signalVector &wVector,
Tom Tsou28670fb2015-08-21 19:32:58 -0700127 size_t chan, bool zero)
dburgessb3a0ca42011-10-12 07:44:40 +0000128{
Tom Tsou28670fb2015-08-21 19:32:58 -0700129 if (zero)
130 sendBuffer[chan]->zero(wVector.size());
131 else
132 sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
kurtis.heimerl9b557832011-11-26 03:18:34 +0000133
134 return wVector.size();
dburgessb3a0ca42011-10-12 07:44:40 +0000135}
136
Tom Tsou28670fb2015-08-21 19:32:58 -0700137int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000138{
Tom Tsou28670fb2015-08-21 19:32:58 -0700139 if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
Thomas Tsou9471d762013-08-20 21:24:24 -0400140 LOG(ALERT) << "Insufficient number of samples in receive buffer";
141 return -1;
142 }
143
Tom Tsou28670fb2015-08-21 19:32:58 -0700144 recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
dburgessb3a0ca42011-10-12 07:44:40 +0000145
Tom Tsou28670fb2015-08-21 19:32:58 -0700146 return newVector->size();
dburgessb3a0ca42011-10-12 07:44:40 +0000147}
148
Thomas Tsou204a9f12013-10-29 18:34:16 -0400149bool RadioInterface::tuneTx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000150{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400151 return mRadio->setTxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000152}
153
Thomas Tsou204a9f12013-10-29 18:34:16 -0400154bool RadioInterface::tuneRx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000155{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400156 return mRadio->setRxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000157}
158
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200159/** synchronization thread loop */
160void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
161{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200162 set_selfthread_name("AlignRadio");
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200163 while (1) {
164 sleep(60);
165 radioInterface->alignRadio();
166 pthread_testcancel();
167 }
168 return NULL;
169}
170
171void RadioInterface::alignRadio() {
172 mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
173}
174
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800175bool RadioInterface::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000176{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800177 if (mOn)
178 return true;
179
180 LOG(INFO) << "Starting radio device";
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200181 if (mRadio->requiresRadioAlign())
182 mAlignRadioServiceLoopThread.start(
183 (void * (*)(void*))AlignRadioServiceLoopAdapter,
184 (void*)this);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800185
186 if (!mRadio->start())
187 return false;
188
Tom Tsou28670fb2015-08-21 19:32:58 -0700189 for (size_t i = 0; i < mChans; i++) {
190 sendBuffer[i]->reset();
191 recvBuffer[i]->reset();
192 }
Tom Tsoud7610cf2015-05-07 17:39:49 -0700193
dburgessb3a0ca42011-10-12 07:44:40 +0000194 writeTimestamp = mRadio->initialWriteTimestamp();
195 readTimestamp = mRadio->initialReadTimestamp();
Thomas Tsou18d3b832014-02-13 14:55:23 -0500196
197 mRadio->updateAlignment(writeTimestamp-10000);
dburgessb3a0ca42011-10-12 07:44:40 +0000198 mRadio->updateAlignment(writeTimestamp-10000);
199
dburgessb3a0ca42011-10-12 07:44:40 +0000200 mOn = true;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500201 LOG(INFO) << "Radio started";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800202 return true;
203}
204
205/*
206 * Stop the radio device
207 *
208 * This is a pass-through call to the device interface. Because the underlying
209 * stop command issuance generally doesn't return confirmation on device status,
210 * this call will only return false if the device is already stopped.
211 */
212bool RadioInterface::stop()
213{
214 if (!mOn || !mRadio->stop())
215 return false;
216
217 mOn = false;
218 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000219}
220
Thomas Tsou204a9f12013-10-29 18:34:16 -0400221void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
222 std::vector<bool> &zeros)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400223{
224 if (!mOn)
225 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000226
Tom Tsou28670fb2015-08-21 19:32:58 -0700227 for (size_t i = 0; i < mChans; i++)
228 radioifyVector(*bursts[i], i, zeros[i]);
dburgessb3a0ca42011-10-12 07:44:40 +0000229
Tom Tsou28670fb2015-08-21 19:32:58 -0700230 while (pushBuffer());
dburgessb3a0ca42011-10-12 07:44:40 +0000231}
232
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200233int RadioInterface::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -0400234{
235 radioVector *burst = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000236
Thomas Tsou204a9f12013-10-29 18:34:16 -0400237 if (!mOn)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200238 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000239
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200240 if (pullBuffer() < 0)
241 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000242
243 GSM::Time rcvClock = mClock.get();
244 rcvClock.decTN(receiveOffset);
245 unsigned tN = rcvClock.TN();
Tom Tsou28670fb2015-08-21 19:32:58 -0700246 int recvSz = recvBuffer[0]->getAvailSamples();
dburgessb3a0ca42011-10-12 07:44:40 +0000247 const int symbolsPerSlot = gSlotLen + 8;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800248 int burstSize;
249
250 if (mSPSRx == 4)
251 burstSize = 625;
252 else
253 burstSize = symbolsPerSlot + (tN % 4 == 0);
dburgessb3a0ca42011-10-12 07:44:40 +0000254
Pau Espin Pedrol46444632018-09-03 16:42:04 +0200255 /*
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500256 * Pre-allocate head room for the largest correlation size
257 * so we can later avoid a re-allocation and copy
258 * */
Vadim Yanitskiya79bc702018-10-17 11:01:58 +0200259 size_t head = GSM::gRACHSynchSequenceTS0.size();
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500260
261 /*
262 * Form receive bursts and pass up to transceiver. Use repeating
263 * pattern of 157-156-156-156 symbols per timeslot
264 */
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500265 while (recvSz > burstSize) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400266 for (size_t i = 0; i < mChans; i++) {
Tom Tsoud6ae8642017-03-30 17:22:58 -0700267 burst = new radioVector(rcvClock, burstSize, head);
268 unRadioifyVector(burst->getVector(), i);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500269
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500270 if (mReceiveFIFO[i].size() < 32)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400271 mReceiveFIFO[i].write(burst);
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500272 else
Thomas Tsou204a9f12013-10-29 18:34:16 -0400273 delete burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000274 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400275
276 mClock.incTN();
dburgessb3a0ca42011-10-12 07:44:40 +0000277 rcvClock.incTN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500278 recvSz -= burstSize;
dburgessb3a0ca42011-10-12 07:44:40 +0000279
280 tN = rcvClock.TN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500281
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800282 if (mSPSRx != 4)
283 burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
dburgessb3a0ca42011-10-12 07:44:40 +0000284 }
285
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200286 return 1;
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000287}
288
289bool RadioInterface::isUnderrun()
290{
291 bool retVal = underrun;
292 underrun = false;
293
294 return retVal;
295}
296
Thomas Tsou204a9f12013-10-29 18:34:16 -0400297VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
298{
299 if (chan >= mReceiveFIFO.size())
300 return NULL;
301
302 return &mReceiveFIFO[chan];
303}
304
305double RadioInterface::setRxGain(double dB, size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000306{
Tom Tsou28670fb2015-08-21 19:32:58 -0700307 return mRadio->setRxGain(dB, chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000308}
309
Thomas Tsou204a9f12013-10-29 18:34:16 -0400310double RadioInterface::getRxGain(size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000311{
Tom Tsou28670fb2015-08-21 19:32:58 -0700312 return mRadio->getRxGain(chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000313}
Thomas Tsoucb69f082013-04-08 14:18:26 -0400314
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400315/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200316int RadioInterface::pullBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400317{
318 bool local_underrun;
Pau Espin Pedrol97009692018-09-03 17:01:59 +0200319 int numRecv;
320 size_t segmentLen = recvBuffer[0]->getSegmentLen();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400321
Tom Tsou28670fb2015-08-21 19:32:58 -0700322 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200323 return -1;
Thomas Tsou3952d802013-10-15 15:12:24 -0400324
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400325 /* Outer buffer access size is fixed */
Tom Tsou28670fb2015-08-21 19:32:58 -0700326 numRecv = mRadio->readSamples(convertRecvBuffer,
327 segmentLen,
328 &overrun,
329 readTimestamp,
330 &local_underrun);
331
Pau Espin Pedrol97009692018-09-03 17:01:59 +0200332 if ((size_t) numRecv != segmentLen) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700333 LOG(ALERT) << "Receive error " << numRecv;
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200334 return -1;
Thomas Tsou9471d762013-08-20 21:24:24 -0400335 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400336
Thomas Tsou204a9f12013-10-29 18:34:16 -0400337 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700338 convert_short_float(recvBuffer[i]->getWriteSegment(),
339 convertRecvBuffer[i],
340 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400341 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400342
343 underrun |= local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700344 readTimestamp += numRecv;
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200345 return 0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400346}
347
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400348/* Send timestamped chunk to the device with arbitrary size */
Tom Tsou28670fb2015-08-21 19:32:58 -0700349bool RadioInterface::pushBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400350{
Pau Espin Pedrol2c673e02019-07-29 20:14:47 +0200351 bool local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700352 size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
Thomas Tsou9471d762013-08-20 21:24:24 -0400353
Tom Tsou28670fb2015-08-21 19:32:58 -0700354 if (sendBuffer[0]->getAvailSegments() < 1)
355 return false;
Thomas Tsou3952d802013-10-15 15:12:24 -0400356
Thomas Tsou204a9f12013-10-29 18:34:16 -0400357 for (size_t i = 0; i < mChans; i++) {
358 convert_float_short(convertSendBuffer[i],
Tom Tsoub577ef02016-07-12 16:07:48 -0700359 (float *) sendBuffer[i]->getReadSegment(),
Tom Tsou28670fb2015-08-21 19:32:58 -0700360 powerScaling[i],
361 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400362 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400363
Tom Tsou28670fb2015-08-21 19:32:58 -0700364 /* Send the all samples in the send buffer */
365 numSent = mRadio->writeSamples(convertSendBuffer,
366 segmentLen,
Pau Espin Pedrol2c673e02019-07-29 20:14:47 +0200367 &local_underrun,
Tom Tsou28670fb2015-08-21 19:32:58 -0700368 writeTimestamp);
Pau Espin Pedrol2c673e02019-07-29 20:14:47 +0200369 underrun |= local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700370 writeTimestamp += numSent;
371
372 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400373}