blob: a7195b4069bbc8be0134f36862f8d4d20f3317a6 [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
Harald Welte82c72212020-09-11 09:59:33 +020041/* number of narrow-band virtual ARFCNs in this wide-band multi-ARFCN device */
Tom Tsou76764272016-06-24 14:25:39 -070042#define MCHANS 4
43
44RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
45 size_t rx_sps, size_t chans)
46 : RadioInterface(radio, tx_sps, rx_sps, chans),
Eric5561f112022-07-19 21:18:21 +020047 outerSendBuffer(NULL), outerRecvBuffer(NULL), history(mChans), active(MCHANS, false),
48 rx_freq_state(mChans), tx_freq_state(mChans), dnsampler(NULL), upsampler(NULL), channelizer(NULL),
49 synthesis(NULL)
Tom Tsou76764272016-06-24 14:25:39 -070050{
51}
52
53RadioInterfaceMulti::~RadioInterfaceMulti()
54{
55 close();
56}
57
58void RadioInterfaceMulti::close()
59{
60 delete outerSendBuffer;
61 delete outerRecvBuffer;
62 delete dnsampler;
63 delete upsampler;
64 delete channelizer;
65 delete synthesis;
66
67 outerSendBuffer = NULL;
68 outerRecvBuffer = NULL;
69 dnsampler = NULL;
70 upsampler = NULL;
71 channelizer = NULL;
72 synthesis = NULL;
73
Pau Espin Pedrolfca503d2021-01-15 16:28:50 +010074
75 for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
76 delete *it;
77
Eric5561f112022-07-19 21:18:21 +020078 mReceiveFIFO.clear();
79 powerScaling.clear();
80 history.clear();
81 active.clear();
82 rx_freq_state.clear();
83 tx_freq_state.clear();
Tom Tsou76764272016-06-24 14:25:39 -070084
85 RadioInterface::close();
86}
87
Harald Welte82c72212020-09-11 09:59:33 +020088/*! we re-map the physical channels from the filter bank to logical per-TRX channels
89 * \param[in] pchan physical channel number within the channelizer
90 * \param[in] chans total number of narrow-band ARFCN channels
91 * \returns logical (TRX) channel number, or -1 in case there is none */
Tom Tsou76764272016-06-24 14:25:39 -070092static int getLogicalChan(size_t pchan, size_t chans)
93{
94 switch (chans) {
95 case 1:
96 if (pchan == 0)
97 return 0;
98 else
99 return -1;
100 break;
101 case 2:
102 if (pchan == 0)
103 return 0;
104 if (pchan == 3)
105 return 1;
106 else
107 return -1;
108 break;
109 case 3:
110 if (pchan == 1)
111 return 0;
112 if (pchan == 0)
113 return 1;
114 if (pchan == 3)
115 return 2;
116 else
117 return -1;
118 break;
119 default:
120 break;
121 };
122
123 return -1;
124}
125
Harald Welte82c72212020-09-11 09:59:33 +0200126/*! do we need to frequency shift our spectrum or not?
127 * \param chans total number of channels
128 * \returns 1 if we need to shift; 0 if not; -1 on error */
Tom Tsou76764272016-06-24 14:25:39 -0700129static int getFreqShift(size_t chans)
130{
131 switch (chans) {
132 case 1:
133 return 0;
134 case 2:
135 return 0;
136 case 3:
137 return 1;
138 default:
139 break;
140 };
141
142 return -1;
143}
144
145/* Initialize I/O specific objects */
146bool RadioInterfaceMulti::init(int type)
147{
148 float cutoff = 1.0f;
149 size_t inchunk = 0, outchunk = 0;
150
151 if (mChans > MCHANS - 1) {
152 LOG(ALERT) << "Invalid channel configuration " << mChans;
153 return false;
154 }
155
Tom Tsou76764272016-06-24 14:25:39 -0700156 convertSendBuffer.resize(1);
157 convertRecvBuffer.resize(1);
158
Harald Welte82c72212020-09-11 09:59:33 +0200159 /* 4 == sps */
Tom Tsou76764272016-06-24 14:25:39 -0700160 inchunk = RESAMP_INRATE * 4;
161 outchunk = RESAMP_OUTRATE * 4;
162
163 if (inchunk * NUMCHUNKS < 625 * 2) {
164 LOG(ALERT) << "Invalid inner chunk size " << inchunk;
165 return false;
166 }
167
168 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
169 if (!dnsampler->init(1.0)) {
170 LOG(ALERT) << "Rx resampler failed to initialize";
171 return false;
172 }
173
174 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
175 if (!upsampler->init(cutoff)) {
176 LOG(ALERT) << "Tx resampler failed to initialize";
177 return false;
178 }
179
180 channelizer = new Channelizer(MCHANS, outchunk);
181 if (!channelizer->init()) {
182 LOG(ALERT) << "Rx channelizer failed to initialize";
183 return false;
184 }
185
186 synthesis = new Synthesis(MCHANS, outchunk);
187 if (!synthesis->init()) {
188 LOG(ALERT) << "Tx synthesis filter failed to initialize";
189 return false;
190 }
191
192 /*
193 * Allocate high and low rate buffers. The high rate receive
194 * buffer and low rate transmit vectors feed into the resampler
195 * and requires headroom equivalent to the filter length. Low
196 * rate buffers are allocated in the main radio interface code.
197 */
198 for (size_t i = 0; i < mChans; i++) {
199 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
200 upsampler->len(), true);
201 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
202 0, false);
203 history[i] = new signalVector(dnsampler->len());
204
205 synthesis->resetBuffer(i);
206 }
207
208 outerSendBuffer = new signalVector(synthesis->outputLen());
209 outerRecvBuffer = new signalVector(channelizer->inputLen());
210
211 convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
212 convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
213
214 /* Configure channels */
215 switch (mChans) {
216 case 1:
217 active[0] = true;
218 break;
219 case 2:
220 active[0] = true;
221 active[3] = true;
222 break;
223 case 3:
224 active[0] = true;
225 active[1] = true;
226 active[3] = true;
227 break;
228 default:
229 LOG(ALERT) << "Unsupported channel combination";
230 return false;
231 }
232
233 return true;
234}
235
236/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200237int RadioInterfaceMulti::pullBuffer()
Tom Tsou76764272016-06-24 14:25:39 -0700238{
239 bool local_underrun;
240 size_t num;
241 float *buf;
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200242 unsigned int i;
Tom Tsou76764272016-06-24 14:25:39 -0700243
244 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200245 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700246
247 /* Outer buffer access size is fixed */
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200248 num = mDevice->readSamples(convertRecvBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700249 outerRecvBuffer->size(),
250 &overrun,
251 readTimestamp,
252 &local_underrun);
253 if (num != channelizer->inputLen()) {
254 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200255 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700256 }
257
258 convert_short_float((float *) outerRecvBuffer->begin(),
259 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
260
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200261 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700262 readTimestamp += num;
263
264 channelizer->rotate((float *) outerRecvBuffer->begin(),
265 outerRecvBuffer->size());
266
267 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
268 if (!active[pchan])
269 continue;
270
271 int lchan = getLogicalChan(pchan, mChans);
272 if (lchan < 0) {
273 LOG(ALERT) << "Invalid logical channel " << pchan;
274 continue;
275 }
276
277 /*
278 * Update history by writing into the head portion of the
279 * channelizer output buffer. For this to work, filter length of
280 * the polyphase channelizer partition filter should be equal to
281 * or larger than the resampling filter.
282 */
283 buf = channelizer->outputBuffer(pchan);
284 size_t cLen = channelizer->outputLen();
285 size_t hLen = dnsampler->len();
Tom Tsou76764272016-06-24 14:25:39 -0700286
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200287 float *fdst = &buf[2 * -hLen];
288 complex *src = history[lchan]->begin();
289 for (i = 0; i < hLen; i++) {
290 fdst[0] = src->real();
291 fdst[1] = src->imag();
292 src++;
293 fdst += 2;
294 }
295 complex *dst = history[lchan]->begin();
296 float *fsrc = &buf[2 * (cLen - hLen)];
297 for (i = 0; i < hLen; i++) {
Timo Jacobus18a61512019-09-05 12:12:16 +0200298 *dst = complex(fsrc[0], fsrc[1]);
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200299 fsrc += 2;
300 dst++;
301 }
Tom Tsou76764272016-06-24 14:25:39 -0700302
303 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
304
305 /* Write to the end of the inner receive buffer */
306 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
307 channelizer->outputLen(),
308 wr_segment,
309 recvBuffer[lchan]->getSegmentLen())) {
310 LOG(ALERT) << "Sample rate upsampling error";
311 }
312 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200313 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700314}
315
316/* Send a timestamped chunk to the device */
317bool RadioInterfaceMulti::pushBuffer()
318{
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200319 bool local_underrun;
Tom Tsou76764272016-06-24 14:25:39 -0700320 if (sendBuffer[0]->getAvailSegments() <= 0)
321 return false;
322
323 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
324 if (!active[pchan]) {
325 synthesis->resetBuffer(pchan);
326 continue;
327 }
328
329 int lchan = getLogicalChan(pchan, mChans);
330 if (lchan < 0) {
331 LOG(ALERT) << "Invalid logical channel " << pchan;
332 continue;
333 }
334
335 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
336 sendBuffer[lchan]->getSegmentLen(),
337 synthesis->inputBuffer(pchan),
338 synthesis->inputLen())) {
339 LOG(ALERT) << "Sample rate downsampling error";
340 }
341 }
342
343 synthesis->rotate((float *) outerSendBuffer->begin(),
344 outerSendBuffer->size());
345
346 convert_float_short(convertSendBuffer[0],
347 (float *) outerSendBuffer->begin(),
348 1.0 / (float) mChans, 2 * outerSendBuffer->size());
349
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200350 size_t num = mDevice->writeSamples(convertSendBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700351 outerSendBuffer->size(),
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200352 &local_underrun,
Tom Tsou76764272016-06-24 14:25:39 -0700353 writeTimestamp);
354 if (num != outerSendBuffer->size()) {
355 LOG(ALERT) << "Transmit error " << num;
356 }
357
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200358 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700359 writeTimestamp += num;
360
361 return true;
362}
363
364/* Frequency comparison limit */
365#define FREQ_DELTA_LIMIT 10.0
366
367static bool fltcmp(double a, double b)
368{
369 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
370}
371
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100372bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
373{
374 double freq_i;
375 std::string str_dir = tx ? "Tx" : "Rx";
376 std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
377
378 for (size_t i = 0; i < mChans; i++) {
379 if (i == chan)
380 continue;
381 if (!v[i].set)
382 continue;
383
384 freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
385 if (!fltcmp(freq, freq_i)) {
386 LOGCHAN(chan, DMAIN, ERROR)
387 << "Setting " << str_dir << " frequency " << freq
388 << " is incompatible: already configured channel "
389 << i << " uses frequency " << v[i].freq_hz
390 << " (expected " << freq_i << ")";
391 return false;
392 }
393 }
394 v[chan].set = true;
395 v[chan].freq_hz = freq;
396 return true;
397}
398
Tom Tsou76764272016-06-24 14:25:39 -0700399bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
400{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100401 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700402
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100403 if (chan >= mChans)
404 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700405
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100406 if (!verify_arfcn_consistency(freq, chan, true))
407 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700408
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100409 if (chan == 0) {
410 shift = (double) getFreqShift(mChans);
411 return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
412 }
Tom Tsou76764272016-06-24 14:25:39 -0700413
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100414 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700415}
416
417bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
418{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100419 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700420
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100421 if (chan >= mChans)
422 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700423
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100424 if (!verify_arfcn_consistency(freq, chan, false))
425 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700426
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100427 if (chan == 0) {
428 shift = (double) getFreqShift(mChans);
429 return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
430 }
Tom Tsou76764272016-06-24 14:25:39 -0700431
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100432 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700433}
434
435double RadioInterfaceMulti::setRxGain(double db, size_t chan)
436{
Harald Weltefd885642020-09-11 09:59:44 +0200437 if (chan == 0)
438 return mDevice->setRxGain(db);
439 else
440 return mDevice->getRxGain();
Tom Tsou76764272016-06-24 14:25:39 -0700441}
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200442
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200443double RadioInterfaceMulti::rssiOffset(size_t chan)
444{
445 return mDevice->rssiOffset(0);
446}
447
Pau Espin Pedrol992c9bd2020-06-08 13:44:24 +0200448int RadioInterfaceMulti::setPowerAttenuation(int atten, size_t chan)
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200449{
Harald Weltefd885642020-09-11 09:59:44 +0200450 return RadioInterface::setPowerAttenuation(atten, 0);
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200451}