blob: 17a015bc3b679f470c5bde31bd9c3ed103b480c0 [file] [log] [blame]
Tom Tsou76764272016-06-24 14:25:39 -07001/*
2 * Multi-carrier radio interface
3 *
Pau Espin Pedrol46444632018-09-03 16:42:04 +02004 * Copyright (C) 2016 Ettus Research LLC
Tom Tsou76764272016-06-24 14:25:39 -07005 *
6 * Author: Tom Tsou <tom.tsou@ettus.com>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * See the COPYING file in the main directory for details.
21 */
22
23#include <radioInterface.h>
24#include <Logger.h>
25
26#include "Resampler.h"
27
28extern "C" {
29#include "convert.h"
30}
31
32/* Resampling parameters for 64 MHz clocking */
33#define RESAMP_INRATE 65
34#define RESAMP_OUTRATE (96 / 2)
35
36/* Universal resampling parameters */
37#define NUMCHUNKS 24
38
39#define MCHANS 4
40
41RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
42 size_t rx_sps, size_t chans)
43 : RadioInterface(radio, tx_sps, rx_sps, chans),
44 outerSendBuffer(NULL), outerRecvBuffer(NULL),
45 dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
46{
47}
48
49RadioInterfaceMulti::~RadioInterfaceMulti()
50{
51 close();
52}
53
54void RadioInterfaceMulti::close()
55{
56 delete outerSendBuffer;
57 delete outerRecvBuffer;
58 delete dnsampler;
59 delete upsampler;
60 delete channelizer;
61 delete synthesis;
62
63 outerSendBuffer = NULL;
64 outerRecvBuffer = NULL;
65 dnsampler = NULL;
66 upsampler = NULL;
67 channelizer = NULL;
68 synthesis = NULL;
69
70 mReceiveFIFO.resize(0);
71 powerScaling.resize(0);
72 history.resize(0);
73 active.resize(0);
74
75 RadioInterface::close();
76}
77
78static int getLogicalChan(size_t pchan, size_t chans)
79{
80 switch (chans) {
81 case 1:
82 if (pchan == 0)
83 return 0;
84 else
85 return -1;
86 break;
87 case 2:
88 if (pchan == 0)
89 return 0;
90 if (pchan == 3)
91 return 1;
92 else
93 return -1;
94 break;
95 case 3:
96 if (pchan == 1)
97 return 0;
98 if (pchan == 0)
99 return 1;
100 if (pchan == 3)
101 return 2;
102 else
103 return -1;
104 break;
105 default:
106 break;
107 };
108
109 return -1;
110}
111
112static int getFreqShift(size_t chans)
113{
114 switch (chans) {
115 case 1:
116 return 0;
117 case 2:
118 return 0;
119 case 3:
120 return 1;
121 default:
122 break;
123 };
124
125 return -1;
126}
127
128/* Initialize I/O specific objects */
129bool RadioInterfaceMulti::init(int type)
130{
131 float cutoff = 1.0f;
132 size_t inchunk = 0, outchunk = 0;
133
134 if (mChans > MCHANS - 1) {
135 LOG(ALERT) << "Invalid channel configuration " << mChans;
136 return false;
137 }
138
139 close();
140
141 sendBuffer.resize(mChans);
142 recvBuffer.resize(mChans);
143 convertSendBuffer.resize(1);
144 convertRecvBuffer.resize(1);
145
146 mReceiveFIFO.resize(mChans);
147 powerScaling.resize(mChans);
148 history.resize(mChans);
149 active.resize(MCHANS, false);
150
151 inchunk = RESAMP_INRATE * 4;
152 outchunk = RESAMP_OUTRATE * 4;
153
154 if (inchunk * NUMCHUNKS < 625 * 2) {
155 LOG(ALERT) << "Invalid inner chunk size " << inchunk;
156 return false;
157 }
158
159 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
160 if (!dnsampler->init(1.0)) {
161 LOG(ALERT) << "Rx resampler failed to initialize";
162 return false;
163 }
164
165 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
166 if (!upsampler->init(cutoff)) {
167 LOG(ALERT) << "Tx resampler failed to initialize";
168 return false;
169 }
170
171 channelizer = new Channelizer(MCHANS, outchunk);
172 if (!channelizer->init()) {
173 LOG(ALERT) << "Rx channelizer failed to initialize";
174 return false;
175 }
176
177 synthesis = new Synthesis(MCHANS, outchunk);
178 if (!synthesis->init()) {
179 LOG(ALERT) << "Tx synthesis filter failed to initialize";
180 return false;
181 }
182
183 /*
184 * Allocate high and low rate buffers. The high rate receive
185 * buffer and low rate transmit vectors feed into the resampler
186 * and requires headroom equivalent to the filter length. Low
187 * rate buffers are allocated in the main radio interface code.
188 */
189 for (size_t i = 0; i < mChans; i++) {
190 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
191 upsampler->len(), true);
192 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
193 0, false);
194 history[i] = new signalVector(dnsampler->len());
195
196 synthesis->resetBuffer(i);
197 }
198
199 outerSendBuffer = new signalVector(synthesis->outputLen());
200 outerRecvBuffer = new signalVector(channelizer->inputLen());
201
202 convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
203 convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
204
205 /* Configure channels */
206 switch (mChans) {
207 case 1:
208 active[0] = true;
209 break;
210 case 2:
211 active[0] = true;
212 active[3] = true;
213 break;
214 case 3:
215 active[0] = true;
216 active[1] = true;
217 active[3] = true;
218 break;
219 default:
220 LOG(ALERT) << "Unsupported channel combination";
221 return false;
222 }
223
224 return true;
225}
226
227/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200228int RadioInterfaceMulti::pullBuffer()
Tom Tsou76764272016-06-24 14:25:39 -0700229{
230 bool local_underrun;
231 size_t num;
232 float *buf;
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200233 unsigned int i;
Tom Tsou76764272016-06-24 14:25:39 -0700234
235 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200236 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700237
238 /* Outer buffer access size is fixed */
239 num = mRadio->readSamples(convertRecvBuffer,
240 outerRecvBuffer->size(),
241 &overrun,
242 readTimestamp,
243 &local_underrun);
244 if (num != channelizer->inputLen()) {
245 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200246 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700247 }
248
249 convert_short_float((float *) outerRecvBuffer->begin(),
250 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
251
252 underrun |= local_underrun;
253 readTimestamp += num;
254
255 channelizer->rotate((float *) outerRecvBuffer->begin(),
256 outerRecvBuffer->size());
257
258 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
259 if (!active[pchan])
260 continue;
261
262 int lchan = getLogicalChan(pchan, mChans);
263 if (lchan < 0) {
264 LOG(ALERT) << "Invalid logical channel " << pchan;
265 continue;
266 }
267
268 /*
269 * Update history by writing into the head portion of the
270 * channelizer output buffer. For this to work, filter length of
271 * the polyphase channelizer partition filter should be equal to
272 * or larger than the resampling filter.
273 */
274 buf = channelizer->outputBuffer(pchan);
275 size_t cLen = channelizer->outputLen();
276 size_t hLen = dnsampler->len();
Tom Tsou76764272016-06-24 14:25:39 -0700277
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200278 float *fdst = &buf[2 * -hLen];
279 complex *src = history[lchan]->begin();
280 for (i = 0; i < hLen; i++) {
281 fdst[0] = src->real();
282 fdst[1] = src->imag();
283 src++;
284 fdst += 2;
285 }
286 complex *dst = history[lchan]->begin();
287 float *fsrc = &buf[2 * (cLen - hLen)];
288 for (i = 0; i < hLen; i++) {
289 *dst = complex(fdst[0], fdst[1]);
290 fsrc += 2;
291 dst++;
292 }
Tom Tsou76764272016-06-24 14:25:39 -0700293
294 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
295
296 /* Write to the end of the inner receive buffer */
297 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
298 channelizer->outputLen(),
299 wr_segment,
300 recvBuffer[lchan]->getSegmentLen())) {
301 LOG(ALERT) << "Sample rate upsampling error";
302 }
303 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200304 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700305}
306
307/* Send a timestamped chunk to the device */
308bool RadioInterfaceMulti::pushBuffer()
309{
310 if (sendBuffer[0]->getAvailSegments() <= 0)
311 return false;
312
313 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
314 if (!active[pchan]) {
315 synthesis->resetBuffer(pchan);
316 continue;
317 }
318
319 int lchan = getLogicalChan(pchan, mChans);
320 if (lchan < 0) {
321 LOG(ALERT) << "Invalid logical channel " << pchan;
322 continue;
323 }
324
325 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
326 sendBuffer[lchan]->getSegmentLen(),
327 synthesis->inputBuffer(pchan),
328 synthesis->inputLen())) {
329 LOG(ALERT) << "Sample rate downsampling error";
330 }
331 }
332
333 synthesis->rotate((float *) outerSendBuffer->begin(),
334 outerSendBuffer->size());
335
336 convert_float_short(convertSendBuffer[0],
337 (float *) outerSendBuffer->begin(),
338 1.0 / (float) mChans, 2 * outerSendBuffer->size());
339
340 size_t num = mRadio->writeSamples(convertSendBuffer,
341 outerSendBuffer->size(),
342 &underrun,
343 writeTimestamp);
344 if (num != outerSendBuffer->size()) {
345 LOG(ALERT) << "Transmit error " << num;
346 }
347
348 writeTimestamp += num;
349
350 return true;
351}
352
353/* Frequency comparison limit */
354#define FREQ_DELTA_LIMIT 10.0
355
356static bool fltcmp(double a, double b)
357{
358 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
359}
360
361bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
362{
363 if (chan >= mChans)
364 return false;
365
366 double shift = (double) getFreqShift(mChans);
367
368 if (!chan)
369 return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
370
371 double center = mRadio->getTxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700372 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
373 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
374 << freq / 1e6 << " MHz";
375 }
Tom Tsou76764272016-06-24 14:25:39 -0700376
377 return true;
378}
379
380bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
381{
382 if (chan >= mChans)
383 return false;
384
385 double shift = (double) getFreqShift(mChans);
386
387 if (!chan)
388 return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
389
390 double center = mRadio->getRxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700391 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
392 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
393 << freq / 1e6 << " MHz";
394 }
Tom Tsou76764272016-06-24 14:25:39 -0700395
396 return true;
397}
398
399double RadioInterfaceMulti::setRxGain(double db, size_t chan)
400{
401 if (!chan)
402 return mRadio->setRxGain(db);
403 else
404 return mRadio->getRxGain();
405}