blob: a0c24b5b13513f4c05443d9083681f9f675214b2 [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);
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +010076 rx_freq_state.resize(0);
77 tx_freq_state.resize(0);
Tom Tsou76764272016-06-24 14:25:39 -070078
79 RadioInterface::close();
80}
81
82static int getLogicalChan(size_t pchan, size_t chans)
83{
84 switch (chans) {
85 case 1:
86 if (pchan == 0)
87 return 0;
88 else
89 return -1;
90 break;
91 case 2:
92 if (pchan == 0)
93 return 0;
94 if (pchan == 3)
95 return 1;
96 else
97 return -1;
98 break;
99 case 3:
100 if (pchan == 1)
101 return 0;
102 if (pchan == 0)
103 return 1;
104 if (pchan == 3)
105 return 2;
106 else
107 return -1;
108 break;
109 default:
110 break;
111 };
112
113 return -1;
114}
115
116static int getFreqShift(size_t chans)
117{
118 switch (chans) {
119 case 1:
120 return 0;
121 case 2:
122 return 0;
123 case 3:
124 return 1;
125 default:
126 break;
127 };
128
129 return -1;
130}
131
132/* Initialize I/O specific objects */
133bool RadioInterfaceMulti::init(int type)
134{
135 float cutoff = 1.0f;
136 size_t inchunk = 0, outchunk = 0;
137
138 if (mChans > MCHANS - 1) {
139 LOG(ALERT) << "Invalid channel configuration " << mChans;
140 return false;
141 }
142
143 close();
144
145 sendBuffer.resize(mChans);
146 recvBuffer.resize(mChans);
147 convertSendBuffer.resize(1);
148 convertRecvBuffer.resize(1);
149
150 mReceiveFIFO.resize(mChans);
151 powerScaling.resize(mChans);
152 history.resize(mChans);
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100153 rx_freq_state.resize(mChans);
154 tx_freq_state.resize(mChans);
Tom Tsou76764272016-06-24 14:25:39 -0700155 active.resize(MCHANS, false);
156
157 inchunk = RESAMP_INRATE * 4;
158 outchunk = RESAMP_OUTRATE * 4;
159
160 if (inchunk * NUMCHUNKS < 625 * 2) {
161 LOG(ALERT) << "Invalid inner chunk size " << inchunk;
162 return false;
163 }
164
165 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
166 if (!dnsampler->init(1.0)) {
167 LOG(ALERT) << "Rx resampler failed to initialize";
168 return false;
169 }
170
171 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
172 if (!upsampler->init(cutoff)) {
173 LOG(ALERT) << "Tx resampler failed to initialize";
174 return false;
175 }
176
177 channelizer = new Channelizer(MCHANS, outchunk);
178 if (!channelizer->init()) {
179 LOG(ALERT) << "Rx channelizer failed to initialize";
180 return false;
181 }
182
183 synthesis = new Synthesis(MCHANS, outchunk);
184 if (!synthesis->init()) {
185 LOG(ALERT) << "Tx synthesis filter failed to initialize";
186 return false;
187 }
188
189 /*
190 * Allocate high and low rate buffers. The high rate receive
191 * buffer and low rate transmit vectors feed into the resampler
192 * and requires headroom equivalent to the filter length. Low
193 * rate buffers are allocated in the main radio interface code.
194 */
195 for (size_t i = 0; i < mChans; i++) {
196 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
197 upsampler->len(), true);
198 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
199 0, false);
200 history[i] = new signalVector(dnsampler->len());
201
202 synthesis->resetBuffer(i);
203 }
204
205 outerSendBuffer = new signalVector(synthesis->outputLen());
206 outerRecvBuffer = new signalVector(channelizer->inputLen());
207
208 convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
209 convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
210
211 /* Configure channels */
212 switch (mChans) {
213 case 1:
214 active[0] = true;
215 break;
216 case 2:
217 active[0] = true;
218 active[3] = true;
219 break;
220 case 3:
221 active[0] = true;
222 active[1] = true;
223 active[3] = true;
224 break;
225 default:
226 LOG(ALERT) << "Unsupported channel combination";
227 return false;
228 }
229
230 return true;
231}
232
233/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200234int RadioInterfaceMulti::pullBuffer()
Tom Tsou76764272016-06-24 14:25:39 -0700235{
236 bool local_underrun;
237 size_t num;
238 float *buf;
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200239 unsigned int i;
Tom Tsou76764272016-06-24 14:25:39 -0700240
241 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200242 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700243
244 /* Outer buffer access size is fixed */
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200245 num = mDevice->readSamples(convertRecvBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700246 outerRecvBuffer->size(),
247 &overrun,
248 readTimestamp,
249 &local_underrun);
250 if (num != channelizer->inputLen()) {
251 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200252 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700253 }
254
255 convert_short_float((float *) outerRecvBuffer->begin(),
256 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
257
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200258 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700259 readTimestamp += num;
260
261 channelizer->rotate((float *) outerRecvBuffer->begin(),
262 outerRecvBuffer->size());
263
264 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
265 if (!active[pchan])
266 continue;
267
268 int lchan = getLogicalChan(pchan, mChans);
269 if (lchan < 0) {
270 LOG(ALERT) << "Invalid logical channel " << pchan;
271 continue;
272 }
273
274 /*
275 * Update history by writing into the head portion of the
276 * channelizer output buffer. For this to work, filter length of
277 * the polyphase channelizer partition filter should be equal to
278 * or larger than the resampling filter.
279 */
280 buf = channelizer->outputBuffer(pchan);
281 size_t cLen = channelizer->outputLen();
282 size_t hLen = dnsampler->len();
Tom Tsou76764272016-06-24 14:25:39 -0700283
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200284 float *fdst = &buf[2 * -hLen];
285 complex *src = history[lchan]->begin();
286 for (i = 0; i < hLen; i++) {
287 fdst[0] = src->real();
288 fdst[1] = src->imag();
289 src++;
290 fdst += 2;
291 }
292 complex *dst = history[lchan]->begin();
293 float *fsrc = &buf[2 * (cLen - hLen)];
294 for (i = 0; i < hLen; i++) {
Timo Jacobus18a61512019-09-05 12:12:16 +0200295 *dst = complex(fsrc[0], fsrc[1]);
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200296 fsrc += 2;
297 dst++;
298 }
Tom Tsou76764272016-06-24 14:25:39 -0700299
300 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
301
302 /* Write to the end of the inner receive buffer */
303 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
304 channelizer->outputLen(),
305 wr_segment,
306 recvBuffer[lchan]->getSegmentLen())) {
307 LOG(ALERT) << "Sample rate upsampling error";
308 }
309 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200310 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700311}
312
313/* Send a timestamped chunk to the device */
314bool RadioInterfaceMulti::pushBuffer()
315{
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200316 bool local_underrun;
Tom Tsou76764272016-06-24 14:25:39 -0700317 if (sendBuffer[0]->getAvailSegments() <= 0)
318 return false;
319
320 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
321 if (!active[pchan]) {
322 synthesis->resetBuffer(pchan);
323 continue;
324 }
325
326 int lchan = getLogicalChan(pchan, mChans);
327 if (lchan < 0) {
328 LOG(ALERT) << "Invalid logical channel " << pchan;
329 continue;
330 }
331
332 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
333 sendBuffer[lchan]->getSegmentLen(),
334 synthesis->inputBuffer(pchan),
335 synthesis->inputLen())) {
336 LOG(ALERT) << "Sample rate downsampling error";
337 }
338 }
339
340 synthesis->rotate((float *) outerSendBuffer->begin(),
341 outerSendBuffer->size());
342
343 convert_float_short(convertSendBuffer[0],
344 (float *) outerSendBuffer->begin(),
345 1.0 / (float) mChans, 2 * outerSendBuffer->size());
346
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200347 size_t num = mDevice->writeSamples(convertSendBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700348 outerSendBuffer->size(),
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200349 &local_underrun,
Tom Tsou76764272016-06-24 14:25:39 -0700350 writeTimestamp);
351 if (num != outerSendBuffer->size()) {
352 LOG(ALERT) << "Transmit error " << num;
353 }
354
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200355 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700356 writeTimestamp += num;
357
358 return true;
359}
360
361/* Frequency comparison limit */
362#define FREQ_DELTA_LIMIT 10.0
363
364static bool fltcmp(double a, double b)
365{
366 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
367}
368
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100369bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
370{
371 double freq_i;
372 std::string str_dir = tx ? "Tx" : "Rx";
373 std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
374
375 for (size_t i = 0; i < mChans; i++) {
376 if (i == chan)
377 continue;
378 if (!v[i].set)
379 continue;
380
381 freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
382 if (!fltcmp(freq, freq_i)) {
383 LOGCHAN(chan, DMAIN, ERROR)
384 << "Setting " << str_dir << " frequency " << freq
385 << " is incompatible: already configured channel "
386 << i << " uses frequency " << v[i].freq_hz
387 << " (expected " << freq_i << ")";
388 return false;
389 }
390 }
391 v[chan].set = true;
392 v[chan].freq_hz = freq;
393 return true;
394}
395
Tom Tsou76764272016-06-24 14:25:39 -0700396bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
397{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100398 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700399
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100400 if (chan >= mChans)
401 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700402
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100403 if (!verify_arfcn_consistency(freq, chan, true))
404 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700405
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100406 if (chan == 0) {
407 shift = (double) getFreqShift(mChans);
408 return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
409 }
Tom Tsou76764272016-06-24 14:25:39 -0700410
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100411 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700412}
413
414bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
415{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100416 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700417
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100418 if (chan >= mChans)
419 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700420
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100421 if (!verify_arfcn_consistency(freq, chan, false))
422 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700423
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100424 if (chan == 0) {
425 shift = (double) getFreqShift(mChans);
426 return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
427 }
Tom Tsou76764272016-06-24 14:25:39 -0700428
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100429 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700430}
431
432double RadioInterfaceMulti::setRxGain(double db, size_t chan)
433{
Pau Espin Pedrol17e6cd02019-09-13 16:19:58 +0200434 if (chan == 0)
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200435 return mDevice->setRxGain(db);
Tom Tsou76764272016-06-24 14:25:39 -0700436 else
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200437 return mDevice->getRxGain();
Tom Tsou76764272016-06-24 14:25:39 -0700438}
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200439
440double RadioInterfaceMulti::setTxGain(double dB, size_t chan)
441{
442 if (chan == 0)
443 return mDevice->setTxGain(dB);
444 else
445 return mDevice->getTxGain();
446
447}