blob: e039d5c5481c16b7a0244d16ae1eb1b30e69cdc7 [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),
Tom Tsoud6ae8642017-03-30 17:22:58 -070037 underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
dburgessb3a0ca42011-10-12 07:44:40 +000038{
dburgessb3a0ca42011-10-12 07:44:40 +000039 mClock.set(wStartTime);
dburgessb3a0ca42011-10-12 07:44:40 +000040}
41
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040042RadioInterface::~RadioInterface(void)
43{
44 close();
45}
kurtis.heimerl54354042011-11-26 03:18:49 +000046
Thomas Tsoufe269fe2013-10-14 23:56:51 -040047bool RadioInterface::init(int type)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040048{
Tom Tsoud6ae8642017-03-30 17:22:58 -070049 if ((type != RadioDevice::NORMAL) || !mChans) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -050050 LOG(ALERT) << "Invalid configuration";
Thomas Tsou204a9f12013-10-29 18:34:16 -040051 return false;
52 }
53
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040054 close();
55
Thomas Tsou204a9f12013-10-29 18:34:16 -040056 sendBuffer.resize(mChans);
57 recvBuffer.resize(mChans);
58 convertSendBuffer.resize(mChans);
59 convertRecvBuffer.resize(mChans);
60 mReceiveFIFO.resize(mChans);
Thomas Tsoucb269a32013-11-15 14:15:47 -050061 powerScaling.resize(mChans);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040062
Thomas Tsou204a9f12013-10-29 18:34:16 -040063 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -070064 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
65 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
Thomas Tsou204a9f12013-10-29 18:34:16 -040066
Tom Tsou28670fb2015-08-21 19:32:58 -070067 convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
68 convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
Alexander Chemeris19174f52016-06-18 11:16:54 +030069
70 powerScaling[i] = 1.0;
Thomas Tsou204a9f12013-10-29 18:34:16 -040071 }
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040072
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040073 return true;
74}
75
76void RadioInterface::close()
77{
Thomas Tsou204a9f12013-10-29 18:34:16 -040078 sendBuffer.resize(0);
79 recvBuffer.resize(0);
80 convertSendBuffer.resize(0);
81 convertRecvBuffer.resize(0);
dburgessb3a0ca42011-10-12 07:44:40 +000082}
83
84double RadioInterface::fullScaleInputValue(void) {
85 return mRadio->fullScaleInputValue();
86}
87
88double RadioInterface::fullScaleOutputValue(void) {
89 return mRadio->fullScaleOutputValue();
90}
91
Tom Tsoua4d1a412014-11-25 15:46:56 -080092int RadioInterface::setPowerAttenuation(int atten, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +000093{
kurtis.heimerl16c65402011-11-26 03:18:11 +000094 double rfGain, digAtten;
kurtis.heimerlee5347a2011-11-26 03:18:05 +000095
Thomas Tsou204a9f12013-10-29 18:34:16 -040096 if (chan >= mChans) {
97 LOG(ALERT) << "Invalid channel requested";
Tom Tsoua4d1a412014-11-25 15:46:56 -080098 return -1;
Thomas Tsou204a9f12013-10-29 18:34:16 -040099 }
100
Tom Tsoua4d1a412014-11-25 15:46:56 -0800101 if (atten < 0.0)
102 atten = 0.0;
103
104 rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
105 digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000106
107 if (digAtten < 1.0)
Thomas Tsoucb269a32013-11-15 14:15:47 -0500108 powerScaling[chan] = 1.0;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000109 else
Thomas Tsoucb269a32013-11-15 14:15:47 -0500110 powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
Tom Tsoua4d1a412014-11-25 15:46:56 -0800111
112 return atten;
dburgessb3a0ca42011-10-12 07:44:40 +0000113}
114
kurtis.heimerl9b557832011-11-26 03:18:34 +0000115int RadioInterface::radioifyVector(signalVector &wVector,
Tom Tsou28670fb2015-08-21 19:32:58 -0700116 size_t chan, bool zero)
dburgessb3a0ca42011-10-12 07:44:40 +0000117{
Tom Tsou28670fb2015-08-21 19:32:58 -0700118 if (zero)
119 sendBuffer[chan]->zero(wVector.size());
120 else
121 sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
kurtis.heimerl9b557832011-11-26 03:18:34 +0000122
123 return wVector.size();
dburgessb3a0ca42011-10-12 07:44:40 +0000124}
125
Tom Tsou28670fb2015-08-21 19:32:58 -0700126int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000127{
Tom Tsou28670fb2015-08-21 19:32:58 -0700128 if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
Thomas Tsou9471d762013-08-20 21:24:24 -0400129 LOG(ALERT) << "Insufficient number of samples in receive buffer";
130 return -1;
131 }
132
Tom Tsou28670fb2015-08-21 19:32:58 -0700133 recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
dburgessb3a0ca42011-10-12 07:44:40 +0000134
Tom Tsou28670fb2015-08-21 19:32:58 -0700135 return newVector->size();
dburgessb3a0ca42011-10-12 07:44:40 +0000136}
137
Thomas Tsou204a9f12013-10-29 18:34:16 -0400138bool RadioInterface::tuneTx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000139{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400140 return mRadio->setTxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000141}
142
Thomas Tsou204a9f12013-10-29 18:34:16 -0400143bool RadioInterface::tuneRx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000144{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400145 return mRadio->setRxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000146}
147
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800148bool RadioInterface::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000149{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800150 if (mOn)
151 return true;
152
153 LOG(INFO) << "Starting radio device";
Thomas Tsouf2293b82013-04-08 13:35:36 -0400154#ifdef USRP1
dburgessb3a0ca42011-10-12 07:44:40 +0000155 mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
156 (void*)this);
Thomas Tsouf2293b82013-04-08 13:35:36 -0400157#endif
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800158
159 if (!mRadio->start())
160 return false;
161
Tom Tsou28670fb2015-08-21 19:32:58 -0700162 for (size_t i = 0; i < mChans; i++) {
163 sendBuffer[i]->reset();
164 recvBuffer[i]->reset();
165 }
Tom Tsoud7610cf2015-05-07 17:39:49 -0700166
dburgessb3a0ca42011-10-12 07:44:40 +0000167 writeTimestamp = mRadio->initialWriteTimestamp();
168 readTimestamp = mRadio->initialReadTimestamp();
Thomas Tsou18d3b832014-02-13 14:55:23 -0500169
170 mRadio->updateAlignment(writeTimestamp-10000);
dburgessb3a0ca42011-10-12 07:44:40 +0000171 mRadio->updateAlignment(writeTimestamp-10000);
172
dburgessb3a0ca42011-10-12 07:44:40 +0000173 mOn = true;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500174 LOG(INFO) << "Radio started";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800175 return true;
176}
177
178/*
179 * Stop the radio device
180 *
181 * This is a pass-through call to the device interface. Because the underlying
182 * stop command issuance generally doesn't return confirmation on device status,
183 * this call will only return false if the device is already stopped.
184 */
185bool RadioInterface::stop()
186{
187 if (!mOn || !mRadio->stop())
188 return false;
189
190 mOn = false;
191 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000192}
193
Thomas Tsouf2293b82013-04-08 13:35:36 -0400194#ifdef USRP1
dburgessb3a0ca42011-10-12 07:44:40 +0000195void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
196{
197 while (1) {
198 radioInterface->alignRadio();
199 pthread_testcancel();
200 }
201 return NULL;
202}
203
204void RadioInterface::alignRadio() {
205 sleep(60);
206 mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
207}
Thomas Tsouf2293b82013-04-08 13:35:36 -0400208#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000209
Thomas Tsou204a9f12013-10-29 18:34:16 -0400210void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
211 std::vector<bool> &zeros)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400212{
213 if (!mOn)
214 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000215
Tom Tsou28670fb2015-08-21 19:32:58 -0700216 for (size_t i = 0; i < mChans; i++)
217 radioifyVector(*bursts[i], i, zeros[i]);
dburgessb3a0ca42011-10-12 07:44:40 +0000218
Tom Tsou28670fb2015-08-21 19:32:58 -0700219 while (pushBuffer());
dburgessb3a0ca42011-10-12 07:44:40 +0000220}
221
Thomas Tsou204a9f12013-10-29 18:34:16 -0400222bool RadioInterface::driveReceiveRadio()
223{
224 radioVector *burst = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000225
Thomas Tsou204a9f12013-10-29 18:34:16 -0400226 if (!mOn)
227 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000228
229 pullBuffer();
230
231 GSM::Time rcvClock = mClock.get();
232 rcvClock.decTN(receiveOffset);
233 unsigned tN = rcvClock.TN();
Tom Tsou28670fb2015-08-21 19:32:58 -0700234 int recvSz = recvBuffer[0]->getAvailSamples();
dburgessb3a0ca42011-10-12 07:44:40 +0000235 const int symbolsPerSlot = gSlotLen + 8;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800236 int burstSize;
237
238 if (mSPSRx == 4)
239 burstSize = 625;
240 else
241 burstSize = symbolsPerSlot + (tN % 4 == 0);
dburgessb3a0ca42011-10-12 07:44:40 +0000242
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500243 /*
244 * Pre-allocate head room for the largest correlation size
245 * so we can later avoid a re-allocation and copy
246 * */
247 size_t head = GSM::gRACHSynchSequence.size();
248
249 /*
250 * Form receive bursts and pass up to transceiver. Use repeating
251 * pattern of 157-156-156-156 symbols per timeslot
252 */
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500253 while (recvSz > burstSize) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400254 for (size_t i = 0; i < mChans; i++) {
Tom Tsoud6ae8642017-03-30 17:22:58 -0700255 burst = new radioVector(rcvClock, burstSize, head);
256 unRadioifyVector(burst->getVector(), i);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500257
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500258 if (mReceiveFIFO[i].size() < 32)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400259 mReceiveFIFO[i].write(burst);
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500260 else
Thomas Tsou204a9f12013-10-29 18:34:16 -0400261 delete burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000262 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400263
264 mClock.incTN();
dburgessb3a0ca42011-10-12 07:44:40 +0000265 rcvClock.incTN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500266 recvSz -= burstSize;
dburgessb3a0ca42011-10-12 07:44:40 +0000267
268 tN = rcvClock.TN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500269
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800270 if (mSPSRx != 4)
271 burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
dburgessb3a0ca42011-10-12 07:44:40 +0000272 }
273
Thomas Tsou204a9f12013-10-29 18:34:16 -0400274 return true;
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000275}
276
277bool RadioInterface::isUnderrun()
278{
279 bool retVal = underrun;
280 underrun = false;
281
282 return retVal;
283}
284
Thomas Tsou204a9f12013-10-29 18:34:16 -0400285VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
286{
287 if (chan >= mReceiveFIFO.size())
288 return NULL;
289
290 return &mReceiveFIFO[chan];
291}
292
293double RadioInterface::setRxGain(double dB, size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000294{
Tom Tsou28670fb2015-08-21 19:32:58 -0700295 return mRadio->setRxGain(dB, chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000296}
297
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298double RadioInterface::getRxGain(size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000299{
Tom Tsou28670fb2015-08-21 19:32:58 -0700300 return mRadio->getRxGain(chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000301}
Thomas Tsoucb69f082013-04-08 14:18:26 -0400302
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400303/* Receive a timestamped chunk from the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400304void RadioInterface::pullBuffer()
305{
306 bool local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700307 size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400308
Tom Tsou28670fb2015-08-21 19:32:58 -0700309 if (recvBuffer[0]->getFreeSegments() <= 0)
Thomas Tsou3952d802013-10-15 15:12:24 -0400310 return;
311
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400312 /* Outer buffer access size is fixed */
Tom Tsou28670fb2015-08-21 19:32:58 -0700313 numRecv = mRadio->readSamples(convertRecvBuffer,
314 segmentLen,
315 &overrun,
316 readTimestamp,
317 &local_underrun);
318
319 if (numRecv != segmentLen) {
320 LOG(ALERT) << "Receive error " << numRecv;
Thomas Tsou9471d762013-08-20 21:24:24 -0400321 return;
322 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400323
Thomas Tsou204a9f12013-10-29 18:34:16 -0400324 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700325 convert_short_float(recvBuffer[i]->getWriteSegment(),
326 convertRecvBuffer[i],
327 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400328 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400329
330 underrun |= local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700331 readTimestamp += numRecv;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400332}
333
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400334/* Send timestamped chunk to the device with arbitrary size */
Tom Tsou28670fb2015-08-21 19:32:58 -0700335bool RadioInterface::pushBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400336{
Tom Tsou28670fb2015-08-21 19:32:58 -0700337 size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
Thomas Tsou9471d762013-08-20 21:24:24 -0400338
Tom Tsou28670fb2015-08-21 19:32:58 -0700339 if (sendBuffer[0]->getAvailSegments() < 1)
340 return false;
Thomas Tsou3952d802013-10-15 15:12:24 -0400341
Thomas Tsou204a9f12013-10-29 18:34:16 -0400342 for (size_t i = 0; i < mChans; i++) {
343 convert_float_short(convertSendBuffer[i],
Tom Tsoub577ef02016-07-12 16:07:48 -0700344 (float *) sendBuffer[i]->getReadSegment(),
Tom Tsou28670fb2015-08-21 19:32:58 -0700345 powerScaling[i],
346 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400347 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400348
Tom Tsou28670fb2015-08-21 19:32:58 -0700349 /* Send the all samples in the send buffer */
350 numSent = mRadio->writeSamples(convertSendBuffer,
351 segmentLen,
352 &underrun,
353 writeTimestamp);
354 writeTimestamp += numSent;
355
356 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400357}