blob: 04b98dac4ba403d3e557543131cfe4da6a80cca7 [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 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
dburgessb3a0ca42011-10-12 07:44:40 +000021
dburgessb3a0ca42011-10-12 07:44:40 +000022#include "radioInterface.h"
Thomas Tsouc1f7c422013-10-11 13:49:55 -040023#include "Resampler.h"
dburgessb3a0ca42011-10-12 07:44:40 +000024#include <Logger.h>
25
Thomas Tsou9471d762013-08-20 21:24:24 -040026extern "C" {
27#include "convert.h"
28}
29
Thomas Tsou3952d802013-10-15 15:12:24 -040030#define CHUNK 625
31#define NUMCHUNKS 4
kurtis.heimerl9b557832011-11-26 03:18:34 +000032
Tom Tsou5cd70dc2016-03-06 01:28:40 -080033RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
Tom Tsoud6ae8642017-03-30 17:22:58 -070034 size_t rx_sps, size_t chans,
Thomas Tsoue90a42b2013-11-13 23:38:09 -050035 int wReceiveOffset, GSM::Time wStartTime)
Tom Tsou5cd70dc2016-03-06 01:28:40 -080036 : mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
Harald Welte2896cec2019-07-21 11:55:22 +020037 underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
38 receiveOffset(wReceiveOffset), mOn(false)
dburgessb3a0ca42011-10-12 07:44:40 +000039{
dburgessb3a0ca42011-10-12 07:44:40 +000040 mClock.set(wStartTime);
dburgessb3a0ca42011-10-12 07:44:40 +000041}
42
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040043RadioInterface::~RadioInterface(void)
44{
45 close();
46}
kurtis.heimerl54354042011-11-26 03:18:49 +000047
Thomas Tsoufe269fe2013-10-14 23:56:51 -040048bool RadioInterface::init(int type)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040049{
Tom Tsoud6ae8642017-03-30 17:22:58 -070050 if ((type != RadioDevice::NORMAL) || !mChans) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -050051 LOG(ALERT) << "Invalid configuration";
Thomas Tsou204a9f12013-10-29 18:34:16 -040052 return false;
53 }
54
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040055 close();
56
Thomas Tsou204a9f12013-10-29 18:34:16 -040057 sendBuffer.resize(mChans);
58 recvBuffer.resize(mChans);
59 convertSendBuffer.resize(mChans);
60 convertRecvBuffer.resize(mChans);
61 mReceiveFIFO.resize(mChans);
Thomas Tsoucb269a32013-11-15 14:15:47 -050062 powerScaling.resize(mChans);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040063
Thomas Tsou204a9f12013-10-29 18:34:16 -040064 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -070065 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
66 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
Thomas Tsou204a9f12013-10-29 18:34:16 -040067
Tom Tsou28670fb2015-08-21 19:32:58 -070068 convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
69 convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
Alexander Chemeris19174f52016-06-18 11:16:54 +030070
71 powerScaling[i] = 1.0;
Thomas Tsou204a9f12013-10-29 18:34:16 -040072 }
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040073
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040074 return true;
75}
76
77void RadioInterface::close()
78{
Pau Espin Pedrol39e0bba2018-12-03 12:19:51 +010079 for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it)
80 delete *it;
81 for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it)
82 delete *it;
83 for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it)
84 delete[] *it;
85 for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it)
86 delete[] *it;
Thomas Tsou204a9f12013-10-29 18:34:16 -040087 sendBuffer.resize(0);
88 recvBuffer.resize(0);
89 convertSendBuffer.resize(0);
90 convertRecvBuffer.resize(0);
dburgessb3a0ca42011-10-12 07:44:40 +000091}
92
93double RadioInterface::fullScaleInputValue(void) {
94 return mRadio->fullScaleInputValue();
95}
96
97double RadioInterface::fullScaleOutputValue(void) {
98 return mRadio->fullScaleOutputValue();
99}
100
Tom Tsoua4d1a412014-11-25 15:46:56 -0800101int RadioInterface::setPowerAttenuation(int atten, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000102{
kurtis.heimerl16c65402011-11-26 03:18:11 +0000103 double rfGain, digAtten;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000104
Thomas Tsou204a9f12013-10-29 18:34:16 -0400105 if (chan >= mChans) {
106 LOG(ALERT) << "Invalid channel requested";
Tom Tsoua4d1a412014-11-25 15:46:56 -0800107 return -1;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400108 }
109
Tom Tsoua4d1a412014-11-25 15:46:56 -0800110 if (atten < 0.0)
111 atten = 0.0;
112
113 rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
114 digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000115
116 if (digAtten < 1.0)
Thomas Tsoucb269a32013-11-15 14:15:47 -0500117 powerScaling[chan] = 1.0;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000118 else
Thomas Tsoucb269a32013-11-15 14:15:47 -0500119 powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
Tom Tsoua4d1a412014-11-25 15:46:56 -0800120
121 return atten;
dburgessb3a0ca42011-10-12 07:44:40 +0000122}
123
kurtis.heimerl9b557832011-11-26 03:18:34 +0000124int RadioInterface::radioifyVector(signalVector &wVector,
Tom Tsou28670fb2015-08-21 19:32:58 -0700125 size_t chan, bool zero)
dburgessb3a0ca42011-10-12 07:44:40 +0000126{
Tom Tsou28670fb2015-08-21 19:32:58 -0700127 if (zero)
128 sendBuffer[chan]->zero(wVector.size());
129 else
130 sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
kurtis.heimerl9b557832011-11-26 03:18:34 +0000131
132 return wVector.size();
dburgessb3a0ca42011-10-12 07:44:40 +0000133}
134
Tom Tsou28670fb2015-08-21 19:32:58 -0700135int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000136{
Tom Tsou28670fb2015-08-21 19:32:58 -0700137 if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
Thomas Tsou9471d762013-08-20 21:24:24 -0400138 LOG(ALERT) << "Insufficient number of samples in receive buffer";
139 return -1;
140 }
141
Tom Tsou28670fb2015-08-21 19:32:58 -0700142 recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
dburgessb3a0ca42011-10-12 07:44:40 +0000143
Tom Tsou28670fb2015-08-21 19:32:58 -0700144 return newVector->size();
dburgessb3a0ca42011-10-12 07:44:40 +0000145}
146
Thomas Tsou204a9f12013-10-29 18:34:16 -0400147bool RadioInterface::tuneTx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000148{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400149 return mRadio->setTxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000150}
151
Thomas Tsou204a9f12013-10-29 18:34:16 -0400152bool RadioInterface::tuneRx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000153{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400154 return mRadio->setRxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000155}
156
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200157/** synchronization thread loop */
158void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
159{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200160 set_selfthread_name("AlignRadio");
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200161 while (1) {
162 sleep(60);
163 radioInterface->alignRadio();
164 pthread_testcancel();
165 }
166 return NULL;
167}
168
169void RadioInterface::alignRadio() {
170 mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
171}
172
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800173bool RadioInterface::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000174{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800175 if (mOn)
176 return true;
177
178 LOG(INFO) << "Starting radio device";
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200179 if (mRadio->requiresRadioAlign())
180 mAlignRadioServiceLoopThread.start(
181 (void * (*)(void*))AlignRadioServiceLoopAdapter,
182 (void*)this);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800183
184 if (!mRadio->start())
185 return false;
186
Tom Tsou28670fb2015-08-21 19:32:58 -0700187 for (size_t i = 0; i < mChans; i++) {
188 sendBuffer[i]->reset();
189 recvBuffer[i]->reset();
190 }
Tom Tsoud7610cf2015-05-07 17:39:49 -0700191
dburgessb3a0ca42011-10-12 07:44:40 +0000192 writeTimestamp = mRadio->initialWriteTimestamp();
193 readTimestamp = mRadio->initialReadTimestamp();
Thomas Tsou18d3b832014-02-13 14:55:23 -0500194
195 mRadio->updateAlignment(writeTimestamp-10000);
dburgessb3a0ca42011-10-12 07:44:40 +0000196 mRadio->updateAlignment(writeTimestamp-10000);
197
dburgessb3a0ca42011-10-12 07:44:40 +0000198 mOn = true;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500199 LOG(INFO) << "Radio started";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800200 return true;
201}
202
203/*
204 * Stop the radio device
205 *
206 * This is a pass-through call to the device interface. Because the underlying
207 * stop command issuance generally doesn't return confirmation on device status,
208 * this call will only return false if the device is already stopped.
209 */
210bool RadioInterface::stop()
211{
212 if (!mOn || !mRadio->stop())
213 return false;
214
215 mOn = false;
216 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000217}
218
Thomas Tsou204a9f12013-10-29 18:34:16 -0400219void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
220 std::vector<bool> &zeros)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400221{
222 if (!mOn)
223 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000224
Tom Tsou28670fb2015-08-21 19:32:58 -0700225 for (size_t i = 0; i < mChans; i++)
226 radioifyVector(*bursts[i], i, zeros[i]);
dburgessb3a0ca42011-10-12 07:44:40 +0000227
Tom Tsou28670fb2015-08-21 19:32:58 -0700228 while (pushBuffer());
dburgessb3a0ca42011-10-12 07:44:40 +0000229}
230
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200231int RadioInterface::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -0400232{
233 radioVector *burst = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000234
Thomas Tsou204a9f12013-10-29 18:34:16 -0400235 if (!mOn)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200236 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000237
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200238 if (pullBuffer() < 0)
239 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000240
241 GSM::Time rcvClock = mClock.get();
242 rcvClock.decTN(receiveOffset);
243 unsigned tN = rcvClock.TN();
Tom Tsou28670fb2015-08-21 19:32:58 -0700244 int recvSz = recvBuffer[0]->getAvailSamples();
dburgessb3a0ca42011-10-12 07:44:40 +0000245 const int symbolsPerSlot = gSlotLen + 8;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800246 int burstSize;
247
248 if (mSPSRx == 4)
249 burstSize = 625;
250 else
251 burstSize = symbolsPerSlot + (tN % 4 == 0);
dburgessb3a0ca42011-10-12 07:44:40 +0000252
Pau Espin Pedrol46444632018-09-03 16:42:04 +0200253 /*
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500254 * Pre-allocate head room for the largest correlation size
255 * so we can later avoid a re-allocation and copy
256 * */
Vadim Yanitskiya79bc702018-10-17 11:01:58 +0200257 size_t head = GSM::gRACHSynchSequenceTS0.size();
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500258
259 /*
260 * Form receive bursts and pass up to transceiver. Use repeating
261 * pattern of 157-156-156-156 symbols per timeslot
262 */
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500263 while (recvSz > burstSize) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400264 for (size_t i = 0; i < mChans; i++) {
Tom Tsoud6ae8642017-03-30 17:22:58 -0700265 burst = new radioVector(rcvClock, burstSize, head);
266 unRadioifyVector(burst->getVector(), i);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500267
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500268 if (mReceiveFIFO[i].size() < 32)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400269 mReceiveFIFO[i].write(burst);
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500270 else
Thomas Tsou204a9f12013-10-29 18:34:16 -0400271 delete burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000272 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400273
274 mClock.incTN();
dburgessb3a0ca42011-10-12 07:44:40 +0000275 rcvClock.incTN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500276 recvSz -= burstSize;
dburgessb3a0ca42011-10-12 07:44:40 +0000277
278 tN = rcvClock.TN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500279
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800280 if (mSPSRx != 4)
281 burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
dburgessb3a0ca42011-10-12 07:44:40 +0000282 }
283
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200284 return 1;
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000285}
286
287bool RadioInterface::isUnderrun()
288{
289 bool retVal = underrun;
290 underrun = false;
291
292 return retVal;
293}
294
Thomas Tsou204a9f12013-10-29 18:34:16 -0400295VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
296{
297 if (chan >= mReceiveFIFO.size())
298 return NULL;
299
300 return &mReceiveFIFO[chan];
301}
302
303double RadioInterface::setRxGain(double dB, size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000304{
Tom Tsou28670fb2015-08-21 19:32:58 -0700305 return mRadio->setRxGain(dB, chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000306}
307
Thomas Tsou204a9f12013-10-29 18:34:16 -0400308double RadioInterface::getRxGain(size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000309{
Tom Tsou28670fb2015-08-21 19:32:58 -0700310 return mRadio->getRxGain(chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000311}
Thomas Tsoucb69f082013-04-08 14:18:26 -0400312
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400313/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200314int RadioInterface::pullBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400315{
316 bool local_underrun;
Pau Espin Pedrol97009692018-09-03 17:01:59 +0200317 int numRecv;
318 size_t segmentLen = recvBuffer[0]->getSegmentLen();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400319
Tom Tsou28670fb2015-08-21 19:32:58 -0700320 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200321 return -1;
Thomas Tsou3952d802013-10-15 15:12:24 -0400322
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400323 /* Outer buffer access size is fixed */
Tom Tsou28670fb2015-08-21 19:32:58 -0700324 numRecv = mRadio->readSamples(convertRecvBuffer,
325 segmentLen,
326 &overrun,
327 readTimestamp,
328 &local_underrun);
329
Pau Espin Pedrol97009692018-09-03 17:01:59 +0200330 if ((size_t) numRecv != segmentLen) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700331 LOG(ALERT) << "Receive error " << numRecv;
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200332 return -1;
Thomas Tsou9471d762013-08-20 21:24:24 -0400333 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400334
Thomas Tsou204a9f12013-10-29 18:34:16 -0400335 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700336 convert_short_float(recvBuffer[i]->getWriteSegment(),
337 convertRecvBuffer[i],
338 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400340
341 underrun |= local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700342 readTimestamp += numRecv;
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200343 return 0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400344}
345
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400346/* Send timestamped chunk to the device with arbitrary size */
Tom Tsou28670fb2015-08-21 19:32:58 -0700347bool RadioInterface::pushBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400348{
Tom Tsou28670fb2015-08-21 19:32:58 -0700349 size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
Thomas Tsou9471d762013-08-20 21:24:24 -0400350
Tom Tsou28670fb2015-08-21 19:32:58 -0700351 if (sendBuffer[0]->getAvailSegments() < 1)
352 return false;
Thomas Tsou3952d802013-10-15 15:12:24 -0400353
Thomas Tsou204a9f12013-10-29 18:34:16 -0400354 for (size_t i = 0; i < mChans; i++) {
355 convert_float_short(convertSendBuffer[i],
Tom Tsoub577ef02016-07-12 16:07:48 -0700356 (float *) sendBuffer[i]->getReadSegment(),
Tom Tsou28670fb2015-08-21 19:32:58 -0700357 powerScaling[i],
358 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400359 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400360
Tom Tsou28670fb2015-08-21 19:32:58 -0700361 /* Send the all samples in the send buffer */
362 numSent = mRadio->writeSamples(convertSendBuffer,
363 segmentLen,
364 &underrun,
365 writeTimestamp);
366 writeTimestamp += numSent;
367
368 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400369}