blob: 67ccb256e797b9560550b091db8f0ba099c80e4c [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;
233
234 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200235 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700236
237 /* Outer buffer access size is fixed */
238 num = mRadio->readSamples(convertRecvBuffer,
239 outerRecvBuffer->size(),
240 &overrun,
241 readTimestamp,
242 &local_underrun);
243 if (num != channelizer->inputLen()) {
244 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200245 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700246 }
247
248 convert_short_float((float *) outerRecvBuffer->begin(),
249 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
250
251 underrun |= local_underrun;
252 readTimestamp += num;
253
254 channelizer->rotate((float *) outerRecvBuffer->begin(),
255 outerRecvBuffer->size());
256
257 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
258 if (!active[pchan])
259 continue;
260
261 int lchan = getLogicalChan(pchan, mChans);
262 if (lchan < 0) {
263 LOG(ALERT) << "Invalid logical channel " << pchan;
264 continue;
265 }
266
267 /*
268 * Update history by writing into the head portion of the
269 * channelizer output buffer. For this to work, filter length of
270 * the polyphase channelizer partition filter should be equal to
271 * or larger than the resampling filter.
272 */
273 buf = channelizer->outputBuffer(pchan);
274 size_t cLen = channelizer->outputLen();
275 size_t hLen = dnsampler->len();
276 size_t hSize = 2 * hLen * sizeof(float);
277
278 memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
279 memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
280
281 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
282
283 /* Write to the end of the inner receive buffer */
284 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
285 channelizer->outputLen(),
286 wr_segment,
287 recvBuffer[lchan]->getSegmentLen())) {
288 LOG(ALERT) << "Sample rate upsampling error";
289 }
290 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200291 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700292}
293
294/* Send a timestamped chunk to the device */
295bool RadioInterfaceMulti::pushBuffer()
296{
297 if (sendBuffer[0]->getAvailSegments() <= 0)
298 return false;
299
300 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
301 if (!active[pchan]) {
302 synthesis->resetBuffer(pchan);
303 continue;
304 }
305
306 int lchan = getLogicalChan(pchan, mChans);
307 if (lchan < 0) {
308 LOG(ALERT) << "Invalid logical channel " << pchan;
309 continue;
310 }
311
312 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
313 sendBuffer[lchan]->getSegmentLen(),
314 synthesis->inputBuffer(pchan),
315 synthesis->inputLen())) {
316 LOG(ALERT) << "Sample rate downsampling error";
317 }
318 }
319
320 synthesis->rotate((float *) outerSendBuffer->begin(),
321 outerSendBuffer->size());
322
323 convert_float_short(convertSendBuffer[0],
324 (float *) outerSendBuffer->begin(),
325 1.0 / (float) mChans, 2 * outerSendBuffer->size());
326
327 size_t num = mRadio->writeSamples(convertSendBuffer,
328 outerSendBuffer->size(),
329 &underrun,
330 writeTimestamp);
331 if (num != outerSendBuffer->size()) {
332 LOG(ALERT) << "Transmit error " << num;
333 }
334
335 writeTimestamp += num;
336
337 return true;
338}
339
340/* Frequency comparison limit */
341#define FREQ_DELTA_LIMIT 10.0
342
343static bool fltcmp(double a, double b)
344{
345 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
346}
347
348bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
349{
350 if (chan >= mChans)
351 return false;
352
353 double shift = (double) getFreqShift(mChans);
354
355 if (!chan)
356 return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
357
358 double center = mRadio->getTxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700359 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
360 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
361 << freq / 1e6 << " MHz";
362 }
Tom Tsou76764272016-06-24 14:25:39 -0700363
364 return true;
365}
366
367bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
368{
369 if (chan >= mChans)
370 return false;
371
372 double shift = (double) getFreqShift(mChans);
373
374 if (!chan)
375 return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
376
377 double center = mRadio->getRxFreq();
Tom Tsouc37594f2016-07-08 14:39:42 -0700378 if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
379 LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
380 << freq / 1e6 << " MHz";
381 }
Tom Tsou76764272016-06-24 14:25:39 -0700382
383 return true;
384}
385
386double RadioInterfaceMulti::setRxGain(double db, size_t chan)
387{
388 if (!chan)
389 return mRadio->setRxGain(db);
390 else
391 return mRadio->getRxGain();
392}