1
|
/*
|
2
|
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
3
|
*
|
4
|
* This software is distributed under the terms of the GNU Affero Public License.
|
5
|
* See the COPYING file in the main directory for details.
|
6
|
*
|
7
|
* This use of this software may be subject to additional restrictions.
|
8
|
* See the LEGAL file in the main directory for details.
|
9
|
|
10
|
This program is free software: you can redistribute it and/or modify
|
11
|
it under the terms of the GNU Affero General Public License as published by
|
12
|
the Free Software Foundation, either version 3 of the License, or
|
13
|
(at your option) any later version.
|
14
|
|
15
|
This program is distributed in the hope that it will be useful,
|
16
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
GNU Affero General Public License for more details.
|
19
|
|
20
|
You should have received a copy of the GNU Affero General Public License
|
21
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
22
|
|
23
|
*/
|
24
|
|
25
|
#include "radioInterface.h"
|
26
|
#include "Resampler.h"
|
27
|
#include <Logger.h>
|
28
|
|
29
|
extern "C" {
|
30
|
#include "convert.h"
|
31
|
}
|
32
|
|
33
|
#define CHUNK 625
|
34
|
#define NUMCHUNKS 4
|
35
|
|
36
|
RadioInterface::RadioInterface(RadioDevice *wRadio,
|
37
|
size_t sps_rx, size_t sps_tx,
|
38
|
size_t chans, size_t diversity,
|
39
|
int wReceiveOffset, GSM::Time wStartTime)
|
40
|
: mRadio(wRadio), mSPSTx(sps_tx), mSPSRx(sps_rx), mChans(chans),
|
41
|
mMIMO(diversity), sendCursor(0), recvCursor(0), underrun(false),
|
42
|
overrun(false), receiveOffset(wReceiveOffset), mOn(false)
|
43
|
{
|
44
|
mClock.set(wStartTime);
|
45
|
}
|
46
|
|
47
|
RadioInterface::~RadioInterface(void)
|
48
|
{
|
49
|
close();
|
50
|
}
|
51
|
|
52
|
bool RadioInterface::init(int type)
|
53
|
{
|
54
|
if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
|
55
|
LOG(ALERT) << "Invalid configuration";
|
56
|
return false;
|
57
|
}
|
58
|
|
59
|
close();
|
60
|
|
61
|
sendBuffer.resize(mChans);
|
62
|
recvBuffer.resize(mChans);
|
63
|
convertSendBuffer.resize(mChans);
|
64
|
convertRecvBuffer.resize(mChans);
|
65
|
mReceiveFIFO.resize(mChans);
|
66
|
powerScaling.resize(mChans);
|
67
|
|
68
|
for (size_t i = 0; i < mChans; i++) {
|
69
|
sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
|
70
|
recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
|
71
|
|
72
|
convertSendBuffer[i] = new short[sendBuffer[i]->size() * 2];
|
73
|
convertRecvBuffer[i] = new short[recvBuffer[i]->size() * 2];
|
74
|
}
|
75
|
|
76
|
sendCursor = 0;
|
77
|
recvCursor = 0;
|
78
|
|
79
|
return true;
|
80
|
}
|
81
|
|
82
|
void RadioInterface::close()
|
83
|
{
|
84
|
for (size_t i = 0; i < sendBuffer.size(); i++)
|
85
|
delete sendBuffer[i];
|
86
|
|
87
|
for (size_t i = 0; i < recvBuffer.size(); i++)
|
88
|
delete recvBuffer[i];
|
89
|
|
90
|
for (size_t i = 0; i < convertSendBuffer.size(); i++)
|
91
|
delete convertSendBuffer[i];
|
92
|
|
93
|
for (size_t i = 0; i < convertRecvBuffer.size(); i++)
|
94
|
delete convertRecvBuffer[i];
|
95
|
|
96
|
sendBuffer.resize(0);
|
97
|
recvBuffer.resize(0);
|
98
|
convertSendBuffer.resize(0);
|
99
|
convertRecvBuffer.resize(0);
|
100
|
}
|
101
|
|
102
|
double RadioInterface::fullScaleInputValue(void) {
|
103
|
return mRadio->fullScaleInputValue();
|
104
|
}
|
105
|
|
106
|
double RadioInterface::fullScaleOutputValue(void) {
|
107
|
return mRadio->fullScaleOutputValue();
|
108
|
}
|
109
|
|
110
|
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
111
|
{
|
112
|
double rfGain, digAtten;
|
113
|
|
114
|
if (chan >= mChans) {
|
115
|
LOG(ALERT) << "Invalid channel requested";
|
116
|
return -1;
|
117
|
}
|
118
|
|
119
|
if (atten < 0.0)
|
120
|
atten = 0.0;
|
121
|
|
122
|
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
|
123
|
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
|
124
|
|
125
|
if (digAtten < 1.0)
|
126
|
powerScaling[chan] = 1.0;
|
127
|
else
|
128
|
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
|
129
|
|
130
|
return atten;
|
131
|
}
|
132
|
|
133
|
int RadioInterface::radioifyVector(signalVector &wVector,
|
134
|
float *retVector,
|
135
|
bool zero)
|
136
|
{
|
137
|
if (zero) {
|
138
|
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
|
139
|
return wVector.size();
|
140
|
}
|
141
|
|
142
|
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
|
143
|
|
144
|
return wVector.size();
|
145
|
}
|
146
|
|
147
|
int RadioInterface::unRadioifyVector(float *floatVector,
|
148
|
signalVector& newVector)
|
149
|
{
|
150
|
signalVector::iterator itr = newVector.begin();
|
151
|
|
152
|
if (newVector.size() > recvCursor) {
|
153
|
LOG(ALERT) << "Insufficient number of samples in receive buffer";
|
154
|
return -1;
|
155
|
}
|
156
|
|
157
|
for (size_t i = 0; i < newVector.size(); i++) {
|
158
|
*itr++ = Complex<float>(floatVector[2 * i + 0],
|
159
|
floatVector[2 * i + 1]);
|
160
|
}
|
161
|
|
162
|
return newVector.size();
|
163
|
}
|
164
|
|
165
|
bool RadioInterface::tuneTx(double freq, size_t chan)
|
166
|
{
|
167
|
return mRadio->setTxFreq(freq, chan);
|
168
|
}
|
169
|
|
170
|
bool RadioInterface::tuneRx(double freq, size_t chan)
|
171
|
{
|
172
|
return mRadio->setRxFreq(freq, chan);
|
173
|
}
|
174
|
|
175
|
bool RadioInterface::start()
|
176
|
{
|
177
|
if (mOn)
|
178
|
return true;
|
179
|
|
180
|
LOG(INFO) << "Starting radio device";
|
181
|
#if defined(USRP1) || defined(USE_BLADERF)
|
182
|
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
183
|
(void*)this);
|
184
|
#endif
|
185
|
|
186
|
if (!mRadio->start())
|
187
|
return false;
|
188
|
|
189
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
190
|
readTimestamp = mRadio->initialReadTimestamp();
|
191
|
|
192
|
mRadio->updateAlignment(writeTimestamp-10000);
|
193
|
mRadio->updateAlignment(writeTimestamp-10000);
|
194
|
|
195
|
mOn = true;
|
196
|
LOG(INFO) << "Radio started";
|
197
|
return true;
|
198
|
}
|
199
|
|
200
|
/*
|
201
|
* Stop the radio device
|
202
|
*
|
203
|
* This is a pass-through call to the device interface. Because the underlying
|
204
|
* stop command issuance generally doesn't return confirmation on device status,
|
205
|
* this call will only return false if the device is already stopped.
|
206
|
*/
|
207
|
bool RadioInterface::stop()
|
208
|
{
|
209
|
if (!mOn || !mRadio->stop())
|
210
|
return false;
|
211
|
|
212
|
mOn = false;
|
213
|
return true;
|
214
|
}
|
215
|
|
216
|
#if defined(USRP1) || defined(USE_BLADERF)
|
217
|
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
|
218
|
{
|
219
|
while (1) {
|
220
|
radioInterface->alignRadio();
|
221
|
pthread_testcancel();
|
222
|
}
|
223
|
return NULL;
|
224
|
}
|
225
|
|
226
|
void RadioInterface::alignRadio() {
|
227
|
sleep(60);
|
228
|
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
|
229
|
}
|
230
|
#endif
|
231
|
|
232
|
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
|
233
|
std::vector<bool> &zeros)
|
234
|
{
|
235
|
if (!mOn)
|
236
|
return;
|
237
|
|
238
|
for (size_t i = 0; i < mChans; i++) {
|
239
|
radioifyVector(*bursts[i],
|
240
|
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
|
241
|
}
|
242
|
|
243
|
sendCursor += bursts[0]->size();
|
244
|
|
245
|
pushBuffer();
|
246
|
}
|
247
|
|
248
|
bool RadioInterface::driveReceiveRadio()
|
249
|
{
|
250
|
radioVector *burst = NULL;
|
251
|
|
252
|
if (!mOn)
|
253
|
return false;
|
254
|
|
255
|
pullBuffer();
|
256
|
|
257
|
GSM::Time rcvClock = mClock.get();
|
258
|
rcvClock.decTN(receiveOffset);
|
259
|
unsigned tN = rcvClock.TN();
|
260
|
int recvSz = recvCursor;
|
261
|
int readSz = 0;
|
262
|
const int symbolsPerSlot = gSlotLen + 8;
|
263
|
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
264
|
|
265
|
/*
|
266
|
* Pre-allocate head room for the largest correlation size
|
267
|
* so we can later avoid a re-allocation and copy
|
268
|
* */
|
269
|
size_t head = GSM::gRACHSynchSequence.size();
|
270
|
|
271
|
/*
|
272
|
* Form receive bursts and pass up to transceiver. Use repeating
|
273
|
* pattern of 157-156-156-156 symbols per timeslot
|
274
|
*/
|
275
|
while (recvSz > burstSize) {
|
276
|
for (size_t i = 0; i < mChans; i++) {
|
277
|
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
|
278
|
|
279
|
for (size_t n = 0; n < mMIMO; n++) {
|
280
|
unRadioifyVector((float *)
|
281
|
(recvBuffer[mMIMO * i + n]->begin() + readSz),
|
282
|
*burst->getVector(n));
|
283
|
}
|
284
|
|
285
|
if (mReceiveFIFO[i].size() < 32)
|
286
|
mReceiveFIFO[i].write(burst);
|
287
|
else
|
288
|
delete burst;
|
289
|
}
|
290
|
|
291
|
mClock.incTN();
|
292
|
rcvClock.incTN();
|
293
|
readSz += burstSize;
|
294
|
recvSz -= burstSize;
|
295
|
|
296
|
tN = rcvClock.TN();
|
297
|
|
298
|
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
299
|
}
|
300
|
|
301
|
if (readSz > 0) {
|
302
|
for (size_t i = 0; i < recvBuffer.size(); i++) {
|
303
|
memmove(recvBuffer[i]->begin(),
|
304
|
recvBuffer[i]->begin() + readSz,
|
305
|
(recvCursor - readSz) * 2 * sizeof(float));
|
306
|
}
|
307
|
|
308
|
recvCursor -= readSz;
|
309
|
}
|
310
|
|
311
|
return true;
|
312
|
}
|
313
|
|
314
|
bool RadioInterface::isUnderrun()
|
315
|
{
|
316
|
bool retVal = underrun;
|
317
|
underrun = false;
|
318
|
|
319
|
return retVal;
|
320
|
}
|
321
|
|
322
|
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
323
|
{
|
324
|
if (chan >= mReceiveFIFO.size())
|
325
|
return NULL;
|
326
|
|
327
|
return &mReceiveFIFO[chan];
|
328
|
}
|
329
|
|
330
|
double RadioInterface::setRxGain(double dB, size_t chan)
|
331
|
{
|
332
|
if (mRadio)
|
333
|
return mRadio->setRxGain(dB, chan);
|
334
|
else
|
335
|
return -1;
|
336
|
}
|
337
|
|
338
|
double RadioInterface::getRxGain(size_t chan)
|
339
|
{
|
340
|
if (mRadio)
|
341
|
return mRadio->getRxGain(chan);
|
342
|
else
|
343
|
return -1;
|
344
|
}
|
345
|
|
346
|
/* Receive a timestamped chunk from the device */
|
347
|
void RadioInterface::pullBuffer()
|
348
|
{
|
349
|
bool local_underrun;
|
350
|
int num_recv;
|
351
|
float *output;
|
352
|
|
353
|
if (recvCursor > recvBuffer[0]->size() - CHUNK)
|
354
|
return;
|
355
|
|
356
|
/* Outer buffer access size is fixed */
|
357
|
num_recv = mRadio->readSamples(convertRecvBuffer,
|
358
|
CHUNK,
|
359
|
&overrun,
|
360
|
readTimestamp,
|
361
|
&local_underrun);
|
362
|
if (num_recv != CHUNK) {
|
363
|
LOG(ALERT) << "Receive error " << num_recv;
|
364
|
return;
|
365
|
}
|
366
|
|
367
|
for (size_t i = 0; i < mChans; i++) {
|
368
|
output = (float *) (recvBuffer[i]->begin() + recvCursor);
|
369
|
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
|
370
|
}
|
371
|
|
372
|
underrun |= local_underrun;
|
373
|
|
374
|
readTimestamp += num_recv;
|
375
|
recvCursor += num_recv;
|
376
|
}
|
377
|
|
378
|
/* Send timestamped chunk to the device with arbitrary size */
|
379
|
void RadioInterface::pushBuffer()
|
380
|
{
|
381
|
int num_sent;
|
382
|
|
383
|
if (sendCursor < CHUNK)
|
384
|
return;
|
385
|
|
386
|
if (sendCursor > sendBuffer[0]->size())
|
387
|
LOG(ALERT) << "Send buffer overflow";
|
388
|
|
389
|
for (size_t i = 0; i < mChans; i++) {
|
390
|
convert_float_short(convertSendBuffer[i],
|
391
|
(float *) sendBuffer[i]->begin(),
|
392
|
powerScaling[i], 2 * sendCursor);
|
393
|
}
|
394
|
|
395
|
/* Send the all samples in the send buffer */
|
396
|
num_sent = mRadio->writeSamples(convertSendBuffer,
|
397
|
sendCursor,
|
398
|
&underrun,
|
399
|
writeTimestamp);
|
400
|
writeTimestamp += num_sent;
|
401
|
sendCursor = 0;
|
402
|
}
|