blob: 99f623101aac55ffb6440aa585ae4bf8edf4058d [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 */
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200241 num = mDevice->readSamples(convertRecvBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700242 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{
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200312 bool local_underrun;
Tom Tsou76764272016-06-24 14:25:39 -0700313 if (sendBuffer[0]->getAvailSegments() <= 0)
314 return false;
315
316 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
317 if (!active[pchan]) {
318 synthesis->resetBuffer(pchan);
319 continue;
320 }
321
322 int lchan = getLogicalChan(pchan, mChans);
323 if (lchan < 0) {
324 LOG(ALERT) << "Invalid logical channel " << pchan;
325 continue;
326 }
327
328 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
329 sendBuffer[lchan]->getSegmentLen(),
330 synthesis->inputBuffer(pchan),
331 synthesis->inputLen())) {
332 LOG(ALERT) << "Sample rate downsampling error";
333 }
334 }
335
336 synthesis->rotate((float *) outerSendBuffer->begin(),
337 outerSendBuffer->size());
338
339 convert_float_short(convertSendBuffer[0],
340 (float *) outerSendBuffer->begin(),
341 1.0 / (float) mChans, 2 * outerSendBuffer->size());
342
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200343 size_t num = mDevice->writeSamples(convertSendBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700344 outerSendBuffer->size(),
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200345 &local_underrun,
Tom Tsou76764272016-06-24 14:25:39 -0700346 writeTimestamp);
347 if (num != outerSendBuffer->size()) {
348 LOG(ALERT) << "Transmit error " << num;
349 }
350
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200351 underrun |= local_underrun;
Tom Tsou76764272016-06-24 14:25:39 -0700352 writeTimestamp += num;
353
354 return true;
355}
356
357/* Frequency comparison limit */
358#define FREQ_DELTA_LIMIT 10.0
359
360static bool fltcmp(double a, double b)
361{
362 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
363}
364
365bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
366{
367 if (chan >= mChans)
368 return false;
369
370 double shift = (double) getFreqShift(mChans);
371
372 if (!chan)
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200373 return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
Tom Tsou76764272016-06-24 14:25:39 -0700374
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200375 double center = mDevice->getTxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700376 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
377 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
378 << freq / 1e6 << " MHz";
379 }
Tom Tsou76764272016-06-24 14:25:39 -0700380
381 return true;
382}
383
384bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
385{
386 if (chan >= mChans)
387 return false;
388
389 double shift = (double) getFreqShift(mChans);
390
391 if (!chan)
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200392 return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
Tom Tsou76764272016-06-24 14:25:39 -0700393
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200394 double center = mDevice->getRxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700395 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
396 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
397 << freq / 1e6 << " MHz";
398 }
Tom Tsou76764272016-06-24 14:25:39 -0700399
400 return true;
401}
402
403double RadioInterfaceMulti::setRxGain(double db, size_t chan)
404{
Pau Espin Pedrol17e6cd02019-09-13 16:19:58 +0200405 if (chan == 0)
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200406 return mDevice->setRxGain(db);
Tom Tsou76764272016-06-24 14:25:39 -0700407 else
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200408 return mDevice->getRxGain();
Tom Tsou76764272016-06-24 14:25:39 -0700409}
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200410
411double RadioInterfaceMulti::setTxGain(double dB, size_t chan)
412{
413 if (chan == 0)
414 return mDevice->setTxGain(dB);
415 else
416 return mDevice->getTxGain();
417
418}