blob: 7a844309ee10ef930a63e09b0f39522531ebefff [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,
34 size_t rx_sps, size_t chans, size_t diversity,
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 Tsou28670fb2015-08-21 19:32:58 -070037 mMIMO(diversity), underrun(false), overrun(false),
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{
Thomas Tsoue90a42b2013-11-13 23:38:09 -050050 if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
51 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{
Thomas Tsou204a9f12013-10-29 18:34:16 -040079 sendBuffer.resize(0);
80 recvBuffer.resize(0);
81 convertSendBuffer.resize(0);
82 convertRecvBuffer.resize(0);
dburgessb3a0ca42011-10-12 07:44:40 +000083}
84
85double RadioInterface::fullScaleInputValue(void) {
86 return mRadio->fullScaleInputValue();
87}
88
89double RadioInterface::fullScaleOutputValue(void) {
90 return mRadio->fullScaleOutputValue();
91}
92
Tom Tsoua4d1a412014-11-25 15:46:56 -080093int RadioInterface::setPowerAttenuation(int atten, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +000094{
kurtis.heimerl16c65402011-11-26 03:18:11 +000095 double rfGain, digAtten;
kurtis.heimerlee5347a2011-11-26 03:18:05 +000096
Thomas Tsou204a9f12013-10-29 18:34:16 -040097 if (chan >= mChans) {
98 LOG(ALERT) << "Invalid channel requested";
Tom Tsoua4d1a412014-11-25 15:46:56 -080099 return -1;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400100 }
101
Tom Tsoua4d1a412014-11-25 15:46:56 -0800102 if (atten < 0.0)
103 atten = 0.0;
104
105 rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
106 digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000107
108 if (digAtten < 1.0)
Thomas Tsoucb269a32013-11-15 14:15:47 -0500109 powerScaling[chan] = 1.0;
kurtis.heimerlee5347a2011-11-26 03:18:05 +0000110 else
Thomas Tsoucb269a32013-11-15 14:15:47 -0500111 powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
Tom Tsoua4d1a412014-11-25 15:46:56 -0800112
113 return atten;
dburgessb3a0ca42011-10-12 07:44:40 +0000114}
115
kurtis.heimerl9b557832011-11-26 03:18:34 +0000116int RadioInterface::radioifyVector(signalVector &wVector,
Tom Tsou28670fb2015-08-21 19:32:58 -0700117 size_t chan, bool zero)
dburgessb3a0ca42011-10-12 07:44:40 +0000118{
Tom Tsou28670fb2015-08-21 19:32:58 -0700119 if (zero)
120 sendBuffer[chan]->zero(wVector.size());
121 else
122 sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
kurtis.heimerl9b557832011-11-26 03:18:34 +0000123
124 return wVector.size();
dburgessb3a0ca42011-10-12 07:44:40 +0000125}
126
Tom Tsou28670fb2015-08-21 19:32:58 -0700127int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000128{
Tom Tsou28670fb2015-08-21 19:32:58 -0700129 if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
Thomas Tsou9471d762013-08-20 21:24:24 -0400130 LOG(ALERT) << "Insufficient number of samples in receive buffer";
131 return -1;
132 }
133
Tom Tsou28670fb2015-08-21 19:32:58 -0700134 recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
dburgessb3a0ca42011-10-12 07:44:40 +0000135
Tom Tsou28670fb2015-08-21 19:32:58 -0700136 return newVector->size();
dburgessb3a0ca42011-10-12 07:44:40 +0000137}
138
Thomas Tsou204a9f12013-10-29 18:34:16 -0400139bool RadioInterface::tuneTx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000140{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400141 return mRadio->setTxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000142}
143
Thomas Tsou204a9f12013-10-29 18:34:16 -0400144bool RadioInterface::tuneRx(double freq, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000145{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400146 return mRadio->setRxFreq(freq, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000147}
148
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800149bool RadioInterface::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000150{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800151 if (mOn)
152 return true;
153
154 LOG(INFO) << "Starting radio device";
Thomas Tsouf2293b82013-04-08 13:35:36 -0400155#ifdef USRP1
dburgessb3a0ca42011-10-12 07:44:40 +0000156 mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
157 (void*)this);
Thomas Tsouf2293b82013-04-08 13:35:36 -0400158#endif
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800159
160 if (!mRadio->start())
161 return false;
162
Tom Tsou28670fb2015-08-21 19:32:58 -0700163 for (size_t i = 0; i < mChans; i++) {
164 sendBuffer[i]->reset();
165 recvBuffer[i]->reset();
166 }
Tom Tsoud7610cf2015-05-07 17:39:49 -0700167
dburgessb3a0ca42011-10-12 07:44:40 +0000168 writeTimestamp = mRadio->initialWriteTimestamp();
169 readTimestamp = mRadio->initialReadTimestamp();
Thomas Tsou18d3b832014-02-13 14:55:23 -0500170
171 mRadio->updateAlignment(writeTimestamp-10000);
dburgessb3a0ca42011-10-12 07:44:40 +0000172 mRadio->updateAlignment(writeTimestamp-10000);
173
dburgessb3a0ca42011-10-12 07:44:40 +0000174 mOn = true;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500175 LOG(INFO) << "Radio started";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800176 return true;
177}
178
179/*
180 * Stop the radio device
181 *
182 * This is a pass-through call to the device interface. Because the underlying
183 * stop command issuance generally doesn't return confirmation on device status,
184 * this call will only return false if the device is already stopped.
185 */
186bool RadioInterface::stop()
187{
188 if (!mOn || !mRadio->stop())
189 return false;
190
191 mOn = false;
192 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000193}
194
Thomas Tsouf2293b82013-04-08 13:35:36 -0400195#ifdef USRP1
dburgessb3a0ca42011-10-12 07:44:40 +0000196void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
197{
198 while (1) {
199 radioInterface->alignRadio();
200 pthread_testcancel();
201 }
202 return NULL;
203}
204
205void RadioInterface::alignRadio() {
206 sleep(60);
207 mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
208}
Thomas Tsouf2293b82013-04-08 13:35:36 -0400209#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000210
Thomas Tsou204a9f12013-10-29 18:34:16 -0400211void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
212 std::vector<bool> &zeros)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400213{
214 if (!mOn)
215 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000216
Tom Tsou28670fb2015-08-21 19:32:58 -0700217 for (size_t i = 0; i < mChans; i++)
218 radioifyVector(*bursts[i], i, zeros[i]);
dburgessb3a0ca42011-10-12 07:44:40 +0000219
Tom Tsou28670fb2015-08-21 19:32:58 -0700220 while (pushBuffer());
dburgessb3a0ca42011-10-12 07:44:40 +0000221}
222
Thomas Tsou204a9f12013-10-29 18:34:16 -0400223bool RadioInterface::driveReceiveRadio()
224{
225 radioVector *burst = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000226
Thomas Tsou204a9f12013-10-29 18:34:16 -0400227 if (!mOn)
228 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000229
230 pullBuffer();
231
232 GSM::Time rcvClock = mClock.get();
233 rcvClock.decTN(receiveOffset);
234 unsigned tN = rcvClock.TN();
Tom Tsou28670fb2015-08-21 19:32:58 -0700235 int recvSz = recvBuffer[0]->getAvailSamples();
dburgessb3a0ca42011-10-12 07:44:40 +0000236 const int symbolsPerSlot = gSlotLen + 8;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800237 int burstSize;
238
239 if (mSPSRx == 4)
240 burstSize = 625;
241 else
242 burstSize = symbolsPerSlot + (tN % 4 == 0);
dburgessb3a0ca42011-10-12 07:44:40 +0000243
Thomas Tsoud0f3ca32013-11-09 22:05:23 -0500244 /*
245 * Pre-allocate head room for the largest correlation size
246 * so we can later avoid a re-allocation and copy
247 * */
248 size_t head = GSM::gRACHSynchSequence.size();
249
250 /*
251 * Form receive bursts and pass up to transceiver. Use repeating
252 * pattern of 157-156-156-156 symbols per timeslot
253 */
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500254 while (recvSz > burstSize) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400255 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500256 burst = new radioVector(rcvClock, burstSize, head, mMIMO);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400257
Tom Tsou28670fb2015-08-21 19:32:58 -0700258 for (size_t n = 0; n < mMIMO; n++)
259 unRadioifyVector(burst->getVector(n), i);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500260
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500261 if (mReceiveFIFO[i].size() < 32)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400262 mReceiveFIFO[i].write(burst);
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500263 else
Thomas Tsou204a9f12013-10-29 18:34:16 -0400264 delete burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000265 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400266
267 mClock.incTN();
dburgessb3a0ca42011-10-12 07:44:40 +0000268 rcvClock.incTN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500269 recvSz -= burstSize;
dburgessb3a0ca42011-10-12 07:44:40 +0000270
271 tN = rcvClock.TN();
Thomas Tsoue0fa2bf2013-11-09 02:46:29 -0500272
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800273 if (mSPSRx != 4)
274 burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
dburgessb3a0ca42011-10-12 07:44:40 +0000275 }
276
Thomas Tsou204a9f12013-10-29 18:34:16 -0400277 return true;
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000278}
279
280bool RadioInterface::isUnderrun()
281{
282 bool retVal = underrun;
283 underrun = false;
284
285 return retVal;
286}
287
Thomas Tsou204a9f12013-10-29 18:34:16 -0400288VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
289{
290 if (chan >= mReceiveFIFO.size())
291 return NULL;
292
293 return &mReceiveFIFO[chan];
294}
295
296double RadioInterface::setRxGain(double dB, size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000297{
Tom Tsou28670fb2015-08-21 19:32:58 -0700298 return mRadio->setRxGain(dB, chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000299}
300
Thomas Tsou204a9f12013-10-29 18:34:16 -0400301double RadioInterface::getRxGain(size_t chan)
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000302{
Tom Tsou28670fb2015-08-21 19:32:58 -0700303 return mRadio->getRxGain(chan);
kurtis.heimerle724d6d2011-11-26 03:18:46 +0000304}
Thomas Tsoucb69f082013-04-08 14:18:26 -0400305
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400306/* Receive a timestamped chunk from the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400307void RadioInterface::pullBuffer()
308{
309 bool local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700310 size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400311
Tom Tsou28670fb2015-08-21 19:32:58 -0700312 if (recvBuffer[0]->getFreeSegments() <= 0)
Thomas Tsou3952d802013-10-15 15:12:24 -0400313 return;
314
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400315 /* Outer buffer access size is fixed */
Tom Tsou28670fb2015-08-21 19:32:58 -0700316 numRecv = mRadio->readSamples(convertRecvBuffer,
317 segmentLen,
318 &overrun,
319 readTimestamp,
320 &local_underrun);
321
322 if (numRecv != segmentLen) {
323 LOG(ALERT) << "Receive error " << numRecv;
Thomas Tsou9471d762013-08-20 21:24:24 -0400324 return;
325 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400326
Thomas Tsou204a9f12013-10-29 18:34:16 -0400327 for (size_t i = 0; i < mChans; i++) {
Tom Tsou28670fb2015-08-21 19:32:58 -0700328 convert_short_float(recvBuffer[i]->getWriteSegment(),
329 convertRecvBuffer[i],
330 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400331 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400332
333 underrun |= local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700334 readTimestamp += numRecv;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400335}
336
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400337/* Send timestamped chunk to the device with arbitrary size */
Tom Tsou28670fb2015-08-21 19:32:58 -0700338bool RadioInterface::pushBuffer()
Thomas Tsoucb69f082013-04-08 14:18:26 -0400339{
Tom Tsou28670fb2015-08-21 19:32:58 -0700340 size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
Thomas Tsou9471d762013-08-20 21:24:24 -0400341
Tom Tsou28670fb2015-08-21 19:32:58 -0700342 if (sendBuffer[0]->getAvailSegments() < 1)
343 return false;
Thomas Tsou3952d802013-10-15 15:12:24 -0400344
Thomas Tsou204a9f12013-10-29 18:34:16 -0400345 for (size_t i = 0; i < mChans; i++) {
346 convert_float_short(convertSendBuffer[i],
Tom Tsoub577ef02016-07-12 16:07:48 -0700347 (float *) sendBuffer[i]->getReadSegment(),
Tom Tsou28670fb2015-08-21 19:32:58 -0700348 powerScaling[i],
349 segmentLen * 2);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400350 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400351
Tom Tsou28670fb2015-08-21 19:32:58 -0700352 /* Send the all samples in the send buffer */
353 numSent = mRadio->writeSamples(convertSendBuffer,
354 segmentLen,
355 &underrun,
356 writeTimestamp);
357 writeTimestamp += numSent;
358
359 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400360}