blob: 0208e82c487fc2c5108aa7d76271b065c386aec4 [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 *
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02008 * SPDX-License-Identifier: AGPL-3.0+
9 *
Tom Tsou76764272016-06-24 14:25:39 -070010 * 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 * See the COPYING file in the main directory for details.
23 */
24
25#include <radioInterface.h>
26#include <Logger.h>
27
28#include "Resampler.h"
29
30extern "C" {
31#include "convert.h"
32}
33
34/* Resampling parameters for 64 MHz clocking */
35#define RESAMP_INRATE 65
36#define RESAMP_OUTRATE (96 / 2)
37
38/* Universal resampling parameters */
39#define NUMCHUNKS 24
40
41#define MCHANS 4
42
43RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
44 size_t rx_sps, size_t chans)
45 : RadioInterface(radio, tx_sps, rx_sps, chans),
46 outerSendBuffer(NULL), outerRecvBuffer(NULL),
47 dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
48{
49}
50
51RadioInterfaceMulti::~RadioInterfaceMulti()
52{
53 close();
54}
55
56void RadioInterfaceMulti::close()
57{
58 delete outerSendBuffer;
59 delete outerRecvBuffer;
60 delete dnsampler;
61 delete upsampler;
62 delete channelizer;
63 delete synthesis;
64
65 outerSendBuffer = NULL;
66 outerRecvBuffer = NULL;
67 dnsampler = NULL;
68 upsampler = NULL;
69 channelizer = NULL;
70 synthesis = NULL;
71
72 mReceiveFIFO.resize(0);
73 powerScaling.resize(0);
74 history.resize(0);
75 active.resize(0);
76
77 RadioInterface::close();
78}
79
80static int getLogicalChan(size_t pchan, size_t chans)
81{
82 switch (chans) {
83 case 1:
84 if (pchan == 0)
85 return 0;
86 else
87 return -1;
88 break;
89 case 2:
90 if (pchan == 0)
91 return 0;
92 if (pchan == 3)
93 return 1;
94 else
95 return -1;
96 break;
97 case 3:
98 if (pchan == 1)
99 return 0;
100 if (pchan == 0)
101 return 1;
102 if (pchan == 3)
103 return 2;
104 else
105 return -1;
106 break;
107 default:
108 break;
109 };
110
111 return -1;
112}
113
114static int getFreqShift(size_t chans)
115{
116 switch (chans) {
117 case 1:
118 return 0;
119 case 2:
120 return 0;
121 case 3:
122 return 1;
123 default:
124 break;
125 };
126
127 return -1;
128}
129
130/* Initialize I/O specific objects */
131bool RadioInterfaceMulti::init(int type)
132{
133 float cutoff = 1.0f;
134 size_t inchunk = 0, outchunk = 0;
135
136 if (mChans > MCHANS - 1) {
137 LOG(ALERT) << "Invalid channel configuration " << mChans;
138 return false;
139 }
140
141 close();
142
143 sendBuffer.resize(mChans);
144 recvBuffer.resize(mChans);
145 convertSendBuffer.resize(1);
146 convertRecvBuffer.resize(1);
147
148 mReceiveFIFO.resize(mChans);
149 powerScaling.resize(mChans);
150 history.resize(mChans);
151 active.resize(MCHANS, false);
152
153 inchunk = RESAMP_INRATE * 4;
154 outchunk = RESAMP_OUTRATE * 4;
155
156 if (inchunk * NUMCHUNKS < 625 * 2) {
157 LOG(ALERT) << "Invalid inner chunk size " << inchunk;
158 return false;
159 }
160
161 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
162 if (!dnsampler->init(1.0)) {
163 LOG(ALERT) << "Rx resampler failed to initialize";
164 return false;
165 }
166
167 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
168 if (!upsampler->init(cutoff)) {
169 LOG(ALERT) << "Tx resampler failed to initialize";
170 return false;
171 }
172
173 channelizer = new Channelizer(MCHANS, outchunk);
174 if (!channelizer->init()) {
175 LOG(ALERT) << "Rx channelizer failed to initialize";
176 return false;
177 }
178
179 synthesis = new Synthesis(MCHANS, outchunk);
180 if (!synthesis->init()) {
181 LOG(ALERT) << "Tx synthesis filter failed to initialize";
182 return false;
183 }
184
185 /*
186 * Allocate high and low rate buffers. The high rate receive
187 * buffer and low rate transmit vectors feed into the resampler
188 * and requires headroom equivalent to the filter length. Low
189 * rate buffers are allocated in the main radio interface code.
190 */
191 for (size_t i = 0; i < mChans; i++) {
192 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
193 upsampler->len(), true);
194 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
195 0, false);
196 history[i] = new signalVector(dnsampler->len());
197
198 synthesis->resetBuffer(i);
199 }
200
201 outerSendBuffer = new signalVector(synthesis->outputLen());
202 outerRecvBuffer = new signalVector(channelizer->inputLen());
203
204 convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
205 convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
206
207 /* Configure channels */
208 switch (mChans) {
209 case 1:
210 active[0] = true;
211 break;
212 case 2:
213 active[0] = true;
214 active[3] = true;
215 break;
216 case 3:
217 active[0] = true;
218 active[1] = true;
219 active[3] = true;
220 break;
221 default:
222 LOG(ALERT) << "Unsupported channel combination";
223 return false;
224 }
225
226 return true;
227}
228
229/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200230int RadioInterfaceMulti::pullBuffer()
Tom Tsou76764272016-06-24 14:25:39 -0700231{
232 bool local_underrun;
233 size_t num;
234 float *buf;
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200235 unsigned int i;
Tom Tsou76764272016-06-24 14:25:39 -0700236
237 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200238 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700239
240 /* Outer buffer access size is fixed */
241 num = mRadio->readSamples(convertRecvBuffer,
242 outerRecvBuffer->size(),
243 &overrun,
244 readTimestamp,
245 &local_underrun);
246 if (num != channelizer->inputLen()) {
247 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200248 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700249 }
250
251 convert_short_float((float *) outerRecvBuffer->begin(),
252 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
253
254 underrun |= local_underrun;
255 readTimestamp += num;
256
257 channelizer->rotate((float *) outerRecvBuffer->begin(),
258 outerRecvBuffer->size());
259
260 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
261 if (!active[pchan])
262 continue;
263
264 int lchan = getLogicalChan(pchan, mChans);
265 if (lchan < 0) {
266 LOG(ALERT) << "Invalid logical channel " << pchan;
267 continue;
268 }
269
270 /*
271 * Update history by writing into the head portion of the
272 * channelizer output buffer. For this to work, filter length of
273 * the polyphase channelizer partition filter should be equal to
274 * or larger than the resampling filter.
275 */
276 buf = channelizer->outputBuffer(pchan);
277 size_t cLen = channelizer->outputLen();
278 size_t hLen = dnsampler->len();
Tom Tsou76764272016-06-24 14:25:39 -0700279
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200280 float *fdst = &buf[2 * -hLen];
281 complex *src = history[lchan]->begin();
282 for (i = 0; i < hLen; i++) {
283 fdst[0] = src->real();
284 fdst[1] = src->imag();
285 src++;
286 fdst += 2;
287 }
288 complex *dst = history[lchan]->begin();
289 float *fsrc = &buf[2 * (cLen - hLen)];
290 for (i = 0; i < hLen; i++) {
291 *dst = complex(fdst[0], fdst[1]);
292 fsrc += 2;
293 dst++;
294 }
Tom Tsou76764272016-06-24 14:25:39 -0700295
296 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
297
298 /* Write to the end of the inner receive buffer */
299 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
300 channelizer->outputLen(),
301 wr_segment,
302 recvBuffer[lchan]->getSegmentLen())) {
303 LOG(ALERT) << "Sample rate upsampling error";
304 }
305 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200306 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700307}
308
309/* Send a timestamped chunk to the device */
310bool RadioInterfaceMulti::pushBuffer()
311{
312 if (sendBuffer[0]->getAvailSegments() <= 0)
313 return false;
314
315 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
316 if (!active[pchan]) {
317 synthesis->resetBuffer(pchan);
318 continue;
319 }
320
321 int lchan = getLogicalChan(pchan, mChans);
322 if (lchan < 0) {
323 LOG(ALERT) << "Invalid logical channel " << pchan;
324 continue;
325 }
326
327 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
328 sendBuffer[lchan]->getSegmentLen(),
329 synthesis->inputBuffer(pchan),
330 synthesis->inputLen())) {
331 LOG(ALERT) << "Sample rate downsampling error";
332 }
333 }
334
335 synthesis->rotate((float *) outerSendBuffer->begin(),
336 outerSendBuffer->size());
337
338 convert_float_short(convertSendBuffer[0],
339 (float *) outerSendBuffer->begin(),
340 1.0 / (float) mChans, 2 * outerSendBuffer->size());
341
342 size_t num = mRadio->writeSamples(convertSendBuffer,
343 outerSendBuffer->size(),
344 &underrun,
345 writeTimestamp);
346 if (num != outerSendBuffer->size()) {
347 LOG(ALERT) << "Transmit error " << num;
348 }
349
350 writeTimestamp += num;
351
352 return true;
353}
354
355/* Frequency comparison limit */
356#define FREQ_DELTA_LIMIT 10.0
357
358static bool fltcmp(double a, double b)
359{
360 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
361}
362
363bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
364{
365 if (chan >= mChans)
366 return false;
367
368 double shift = (double) getFreqShift(mChans);
369
370 if (!chan)
371 return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
372
373 double center = mRadio->getTxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700374 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
375 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
376 << freq / 1e6 << " MHz";
377 }
Tom Tsou76764272016-06-24 14:25:39 -0700378
379 return true;
380}
381
382bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
383{
384 if (chan >= mChans)
385 return false;
386
387 double shift = (double) getFreqShift(mChans);
388
389 if (!chan)
390 return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
391
392 double center = mRadio->getRxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700393 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
394 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
395 << freq / 1e6 << " MHz";
396 }
Tom Tsou76764272016-06-24 14:25:39 -0700397
398 return true;
399}
400
401double RadioInterfaceMulti::setRxGain(double db, size_t chan)
402{
403 if (!chan)
404 return mRadio->setRxGain(db);
405 else
406 return mRadio->getRxGain();
407}