blob: eaf08868774f150a46b918fd18bba96877ccbcfb [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),
47 outerSendBuffer(NULL), outerRecvBuffer(NULL),
48 dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
49{
50}
51
52RadioInterfaceMulti::~RadioInterfaceMulti()
53{
54 close();
55}
56
57void RadioInterfaceMulti::close()
58{
59 delete outerSendBuffer;
60 delete outerRecvBuffer;
61 delete dnsampler;
62 delete upsampler;
63 delete channelizer;
64 delete synthesis;
65
66 outerSendBuffer = NULL;
67 outerRecvBuffer = NULL;
68 dnsampler = NULL;
69 upsampler = NULL;
70 channelizer = NULL;
71 synthesis = NULL;
72
Pau Espin Pedrolfca503d2021-01-15 16:28:50 +010073
74 for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
75 delete *it;
76
Tom Tsou76764272016-06-24 14:25:39 -070077 mReceiveFIFO.resize(0);
78 powerScaling.resize(0);
79 history.resize(0);
80 active.resize(0);
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +010081 rx_freq_state.resize(0);
82 tx_freq_state.resize(0);
Tom Tsou76764272016-06-24 14:25:39 -070083
84 RadioInterface::close();
85}
86
Harald Welte82c72212020-09-11 09:59:33 +020087/*! we re-map the physical channels from the filter bank to logical per-TRX channels
88 * \param[in] pchan physical channel number within the channelizer
89 * \param[in] chans total number of narrow-band ARFCN channels
90 * \returns logical (TRX) channel number, or -1 in case there is none */
Tom Tsou76764272016-06-24 14:25:39 -070091static int getLogicalChan(size_t pchan, size_t chans)
92{
93 switch (chans) {
94 case 1:
95 if (pchan == 0)
96 return 0;
97 else
98 return -1;
99 break;
100 case 2:
101 if (pchan == 0)
102 return 0;
103 if (pchan == 3)
104 return 1;
105 else
106 return -1;
107 break;
108 case 3:
109 if (pchan == 1)
110 return 0;
111 if (pchan == 0)
112 return 1;
113 if (pchan == 3)
114 return 2;
115 else
116 return -1;
117 break;
118 default:
119 break;
120 };
121
122 return -1;
123}
124
Harald Welte82c72212020-09-11 09:59:33 +0200125/*! do we need to frequency shift our spectrum or not?
126 * \param chans total number of channels
127 * \returns 1 if we need to shift; 0 if not; -1 on error */
Tom Tsou76764272016-06-24 14:25:39 -0700128static int getFreqShift(size_t chans)
129{
130 switch (chans) {
131 case 1:
132 return 0;
133 case 2:
134 return 0;
135 case 3:
136 return 1;
137 default:
138 break;
139 };
140
141 return -1;
142}
143
144/* Initialize I/O specific objects */
145bool RadioInterfaceMulti::init(int type)
146{
147 float cutoff = 1.0f;
148 size_t inchunk = 0, outchunk = 0;
149
150 if (mChans > MCHANS - 1) {
151 LOG(ALERT) << "Invalid channel configuration " << mChans;
152 return false;
153 }
154
155 close();
156
157 sendBuffer.resize(mChans);
158 recvBuffer.resize(mChans);
159 convertSendBuffer.resize(1);
160 convertRecvBuffer.resize(1);
161
162 mReceiveFIFO.resize(mChans);
163 powerScaling.resize(mChans);
164 history.resize(mChans);
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100165 rx_freq_state.resize(mChans);
166 tx_freq_state.resize(mChans);
Tom Tsou76764272016-06-24 14:25:39 -0700167 active.resize(MCHANS, false);
168
Harald Welte82c72212020-09-11 09:59:33 +0200169 /* 4 == sps */
Tom Tsou76764272016-06-24 14:25:39 -0700170 inchunk = RESAMP_INRATE * 4;
171 outchunk = RESAMP_OUTRATE * 4;
172
173 if (inchunk * NUMCHUNKS < 625 * 2) {
174 LOG(ALERT) << "Invalid inner chunk size " << inchunk;
175 return false;
176 }
177
178 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
179 if (!dnsampler->init(1.0)) {
180 LOG(ALERT) << "Rx resampler failed to initialize";
181 return false;
182 }
183
184 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
185 if (!upsampler->init(cutoff)) {
186 LOG(ALERT) << "Tx resampler failed to initialize";
187 return false;
188 }
189
190 channelizer = new Channelizer(MCHANS, outchunk);
191 if (!channelizer->init()) {
192 LOG(ALERT) << "Rx channelizer failed to initialize";
193 return false;
194 }
195
196 synthesis = new Synthesis(MCHANS, outchunk);
197 if (!synthesis->init()) {
198 LOG(ALERT) << "Tx synthesis filter failed to initialize";
199 return false;
200 }
201
202 /*
203 * Allocate high and low rate buffers. The high rate receive
204 * buffer and low rate transmit vectors feed into the resampler
205 * and requires headroom equivalent to the filter length. Low
206 * rate buffers are allocated in the main radio interface code.
207 */
208 for (size_t i = 0; i < mChans; i++) {
209 sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
210 upsampler->len(), true);
211 recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
212 0, false);
213 history[i] = new signalVector(dnsampler->len());
214
215 synthesis->resetBuffer(i);
216 }
217
218 outerSendBuffer = new signalVector(synthesis->outputLen());
219 outerRecvBuffer = new signalVector(channelizer->inputLen());
220
221 convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
222 convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
223
224 /* Configure channels */
225 switch (mChans) {
226 case 1:
227 active[0] = true;
228 break;
229 case 2:
230 active[0] = true;
231 active[3] = true;
232 break;
233 case 3:
234 active[0] = true;
235 active[1] = true;
236 active[3] = true;
237 break;
238 default:
239 LOG(ALERT) << "Unsupported channel combination";
240 return false;
241 }
242
243 return true;
244}
245
246/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200247int RadioInterfaceMulti::pullBuffer()
Tom Tsou76764272016-06-24 14:25:39 -0700248{
249 bool local_underrun;
250 size_t num;
251 float *buf;
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200252 unsigned int i;
Tom Tsou76764272016-06-24 14:25:39 -0700253
254 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200255 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700256
257 /* Outer buffer access size is fixed */
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200258 num = mDevice->readSamples(convertRecvBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700259 outerRecvBuffer->size(),
260 &overrun,
261 readTimestamp,
262 &local_underrun);
263 if (num != channelizer->inputLen()) {
264 LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200265 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700266 }
267
268 convert_short_float((float *) outerRecvBuffer->begin(),
269 convertRecvBuffer[0], 2 * outerRecvBuffer->size());
270
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200271 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700272 readTimestamp += num;
273
274 channelizer->rotate((float *) outerRecvBuffer->begin(),
275 outerRecvBuffer->size());
276
277 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
278 if (!active[pchan])
279 continue;
280
281 int lchan = getLogicalChan(pchan, mChans);
282 if (lchan < 0) {
283 LOG(ALERT) << "Invalid logical channel " << pchan;
284 continue;
285 }
286
287 /*
288 * Update history by writing into the head portion of the
289 * channelizer output buffer. For this to work, filter length of
290 * the polyphase channelizer partition filter should be equal to
291 * or larger than the resampling filter.
292 */
293 buf = channelizer->outputBuffer(pchan);
294 size_t cLen = channelizer->outputLen();
295 size_t hLen = dnsampler->len();
Tom Tsou76764272016-06-24 14:25:39 -0700296
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200297 float *fdst = &buf[2 * -hLen];
298 complex *src = history[lchan]->begin();
299 for (i = 0; i < hLen; i++) {
300 fdst[0] = src->real();
301 fdst[1] = src->imag();
302 src++;
303 fdst += 2;
304 }
305 complex *dst = history[lchan]->begin();
306 float *fsrc = &buf[2 * (cLen - hLen)];
307 for (i = 0; i < hLen; i++) {
Timo Jacobus18a61512019-09-05 12:12:16 +0200308 *dst = complex(fsrc[0], fsrc[1]);
Pau Espin Pedrol1f151522018-08-30 20:52:22 +0200309 fsrc += 2;
310 dst++;
311 }
Tom Tsou76764272016-06-24 14:25:39 -0700312
313 float *wr_segment = recvBuffer[lchan]->getWriteSegment();
314
315 /* Write to the end of the inner receive buffer */
316 if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
317 channelizer->outputLen(),
318 wr_segment,
319 recvBuffer[lchan]->getSegmentLen())) {
320 LOG(ALERT) << "Sample rate upsampling error";
321 }
322 }
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200323 return 0;
Tom Tsou76764272016-06-24 14:25:39 -0700324}
325
326/* Send a timestamped chunk to the device */
327bool RadioInterfaceMulti::pushBuffer()
328{
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200329 bool local_underrun;
Tom Tsou76764272016-06-24 14:25:39 -0700330 if (sendBuffer[0]->getAvailSegments() <= 0)
331 return false;
332
333 for (size_t pchan = 0; pchan < MCHANS; pchan++) {
334 if (!active[pchan]) {
335 synthesis->resetBuffer(pchan);
336 continue;
337 }
338
339 int lchan = getLogicalChan(pchan, mChans);
340 if (lchan < 0) {
341 LOG(ALERT) << "Invalid logical channel " << pchan;
342 continue;
343 }
344
345 if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
346 sendBuffer[lchan]->getSegmentLen(),
347 synthesis->inputBuffer(pchan),
348 synthesis->inputLen())) {
349 LOG(ALERT) << "Sample rate downsampling error";
350 }
351 }
352
353 synthesis->rotate((float *) outerSendBuffer->begin(),
354 outerSendBuffer->size());
355
356 convert_float_short(convertSendBuffer[0],
357 (float *) outerSendBuffer->begin(),
358 1.0 / (float) mChans, 2 * outerSendBuffer->size());
359
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200360 size_t num = mDevice->writeSamples(convertSendBuffer,
Tom Tsou76764272016-06-24 14:25:39 -0700361 outerSendBuffer->size(),
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200362 &local_underrun,
Tom Tsou76764272016-06-24 14:25:39 -0700363 writeTimestamp);
364 if (num != outerSendBuffer->size()) {
365 LOG(ALERT) << "Transmit error " << num;
366 }
367
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200368 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou76764272016-06-24 14:25:39 -0700369 writeTimestamp += num;
370
371 return true;
372}
373
374/* Frequency comparison limit */
375#define FREQ_DELTA_LIMIT 10.0
376
377static bool fltcmp(double a, double b)
378{
379 return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
380}
381
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100382bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
383{
384 double freq_i;
385 std::string str_dir = tx ? "Tx" : "Rx";
386 std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
387
388 for (size_t i = 0; i < mChans; i++) {
389 if (i == chan)
390 continue;
391 if (!v[i].set)
392 continue;
393
394 freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
395 if (!fltcmp(freq, freq_i)) {
396 LOGCHAN(chan, DMAIN, ERROR)
397 << "Setting " << str_dir << " frequency " << freq
398 << " is incompatible: already configured channel "
399 << i << " uses frequency " << v[i].freq_hz
400 << " (expected " << freq_i << ")";
401 return false;
402 }
403 }
404 v[chan].set = true;
405 v[chan].freq_hz = freq;
406 return true;
407}
408
Tom Tsou76764272016-06-24 14:25:39 -0700409bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
410{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100411 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700412
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100413 if (chan >= mChans)
414 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700415
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100416 if (!verify_arfcn_consistency(freq, chan, true))
417 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700418
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100419 if (chan == 0) {
420 shift = (double) getFreqShift(mChans);
421 return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
422 }
Tom Tsou76764272016-06-24 14:25:39 -0700423
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100424 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700425}
426
427bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
428{
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100429 double shift;
Tom Tsou76764272016-06-24 14:25:39 -0700430
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100431 if (chan >= mChans)
432 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700433
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100434 if (!verify_arfcn_consistency(freq, chan, false))
435 return false;
Tom Tsou76764272016-06-24 14:25:39 -0700436
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100437 if (chan == 0) {
438 shift = (double) getFreqShift(mChans);
439 return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
440 }
Tom Tsou76764272016-06-24 14:25:39 -0700441
Pau Espin Pedrolfd99c6c2019-12-21 00:40:09 +0100442 return true;
Tom Tsou76764272016-06-24 14:25:39 -0700443}
444
445double RadioInterfaceMulti::setRxGain(double db, size_t chan)
446{
Harald Weltefd885642020-09-11 09:59:44 +0200447 if (chan == 0)
448 return mDevice->setRxGain(db);
449 else
450 return mDevice->getRxGain();
Tom Tsou76764272016-06-24 14:25:39 -0700451}
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200452
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200453double RadioInterfaceMulti::rssiOffset(size_t chan)
454{
455 return mDevice->rssiOffset(0);
456}
457
Pau Espin Pedrol992c9bd2020-06-08 13:44:24 +0200458int RadioInterfaceMulti::setPowerAttenuation(int atten, size_t chan)
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200459{
Harald Weltefd885642020-09-11 09:59:44 +0200460 return RadioInterface::setPowerAttenuation(atten, 0);
Pau Espin Pedrol62845242019-09-13 17:29:21 +0200461}