blob: 869acd0ba238049f4bbcfeaf6d6718d36cb4ec3f [file] [log] [blame]
kurtis.heimerlce317332011-11-26 03:18:39 +00001/*
2 * Radio device interface with sample rate conversion
kurtis.heimerlce317332011-11-26 03:18:39 +00003 *
Tom Tsou28670fb2015-08-21 19:32:58 -07004 * Copyright (C) 2011-2014 Free Software Foundation, Inc.
5 * Copyright (C) 2015 Ettus Research LLC
6 *
7 * Author: Tom Tsou <tom@tsou.cc>
kurtis.heimerlce317332011-11-26 03:18:39 +00008 *
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02009 * SPDX-License-Identifier: AGPL-3.0+
10 *
kurtis.heimerlce317332011-11-26 03:18:39 +000011 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Affero General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Affero General Public License for more details.
20 *
21 * You should have received a copy of the GNU Affero General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * See the COPYING file in the main directory for details.
24 */
25
26#include <radioInterface.h>
27#include <Logger.h>
28
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040029#include "Resampler.h"
30
31extern "C" {
32#include "convert.h"
33}
34
Thomas Tsoufe269fe2013-10-14 23:56:51 -040035/* Resampling parameters for 64 MHz clocking */
36#define RESAMP_64M_INRATE 65
37#define RESAMP_64M_OUTRATE 96
38
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040039/* Resampling parameters for 100 MHz clocking */
Thomas Tsoufe269fe2013-10-14 23:56:51 -040040#define RESAMP_100M_INRATE 52
41#define RESAMP_100M_OUTRATE 75
Thomas Tsou0e44ab32013-09-17 20:12:26 -040042
Thomas Tsou3952d802013-10-15 15:12:24 -040043/* Universal resampling parameters */
44#define NUMCHUNKS 24
45
Thomas Tsou0e44ab32013-09-17 20:12:26 -040046/*
47 * Resampling filter bandwidth scaling factor
48 * This narrows the filter cutoff relative to the output bandwidth
49 * of the polyphase resampler. At 4 samples-per-symbol using the
50 * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
51 * RMS phase error at the resampler output.
52 */
53#define RESAMP_TX4_FILTER 0.45
kurtis.heimerlce317332011-11-26 03:18:39 +000054
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040055static Resampler *upsampler = NULL;
56static Resampler *dnsampler = NULL;
Thomas Tsoue90a42b2013-11-13 23:38:09 -050057static size_t resamp_inrate = 0;
58static size_t resamp_inchunk = 0;
59static size_t resamp_outrate = 0;
60static size_t resamp_outchunk = 0;
Thomas Tsoufe269fe2013-10-14 23:56:51 -040061
Pau Espin Pedrola801ae52019-09-13 15:59:29 +020062RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wDevice,
Tom Tsou8f0ccf62016-07-20 16:35:03 -070063 size_t tx_sps, size_t rx_sps)
Pau Espin Pedrola801ae52019-09-13 15:59:29 +020064 : RadioInterface(wDevice, tx_sps, rx_sps, 1),
Tom Tsou8f0ccf62016-07-20 16:35:03 -070065 outerSendBuffer(NULL), outerRecvBuffer(NULL)
Thomas Tsoucb69f082013-04-08 14:18:26 -040066{
67}
68
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040069RadioInterfaceResamp::~RadioInterfaceResamp()
70{
71 close();
72}
73
74void RadioInterfaceResamp::close()
75{
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040076 delete outerSendBuffer;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040077 delete outerRecvBuffer;
78
79 delete upsampler;
80 delete dnsampler;
81
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040082 outerSendBuffer = NULL;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040083 outerRecvBuffer = NULL;
84
85 upsampler = NULL;
86 dnsampler = NULL;
Thomas Tsoude1648c2013-10-15 16:18:58 -040087
Thomas Tsou204a9f12013-10-29 18:34:16 -040088 if (sendBuffer.size())
89 sendBuffer[0] = NULL;
90 if (recvBuffer.size())
91 recvBuffer[0] = NULL;
92
Thomas Tsoude1648c2013-10-15 16:18:58 -040093 RadioInterface::close();
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040094}
95
96/* Initialize I/O specific objects */
Thomas Tsoufe269fe2013-10-14 23:56:51 -040097bool RadioInterfaceResamp::init(int type)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040098{
99 float cutoff = 1.0f;
100
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400101 switch (type) {
102 case RadioDevice::RESAMP_64M:
103 resamp_inrate = RESAMP_64M_INRATE;
104 resamp_outrate = RESAMP_64M_OUTRATE;
105 break;
106 case RadioDevice::RESAMP_100M:
107 resamp_inrate = RESAMP_100M_INRATE;
108 resamp_outrate = RESAMP_100M_OUTRATE;
109 break;
110 case RadioDevice::NORMAL:
111 default:
112 LOG(ALERT) << "Invalid device configuration";
113 return false;
114 }
115
Tom Tsou8f0ccf62016-07-20 16:35:03 -0700116 resamp_inchunk = resamp_inrate * 4 * mSPSRx;
117 resamp_outchunk = resamp_outrate * 4 * mSPSRx;
Thomas Tsou3952d802013-10-15 15:12:24 -0400118
Thomas Tsou0e44ab32013-09-17 20:12:26 -0400119 if (mSPSTx == 4)
120 cutoff = RESAMP_TX4_FILTER;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400121
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400122 dnsampler = new Resampler(resamp_inrate, resamp_outrate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400123 if (!dnsampler->init()) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400124 LOG(ALERT) << "Rx resampler failed to initialize";
125 return false;
126 }
127
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400128 upsampler = new Resampler(resamp_outrate, resamp_inrate);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400129 if (!upsampler->init(cutoff)) {
130 LOG(ALERT) << "Tx resampler failed to initialize";
131 return false;
132 }
133
134 /*
135 * Allocate high and low rate buffers. The high rate receive
136 * buffer and low rate transmit vectors feed into the resampler
137 * and requires headroom equivalent to the filter length. Low
138 * rate buffers are allocated in the main radio interface code.
139 */
Tom Tsou28670fb2015-08-21 19:32:58 -0700140 sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk,
141 upsampler->len(), true);
142 recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false);
143
Thomas Tsou3952d802013-10-15 15:12:24 -0400144 outerSendBuffer =
145 new signalVector(NUMCHUNKS * resamp_outchunk);
146 outerRecvBuffer =
147 new signalVector(resamp_outchunk, dnsampler->len());
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400148
Thomas Tsou204a9f12013-10-29 18:34:16 -0400149 convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
150 convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400151
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400152 return true;
153}
154
155/* Receive a timestamped chunk from the device */
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200156int RadioInterfaceResamp::pullBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000157{
kurtis.heimerlce317332011-11-26 03:18:39 +0000158 bool local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400159 int rc, num_recv;
Thomas Tsou3952d802013-10-15 15:12:24 -0400160
Tom Tsou28670fb2015-08-21 19:32:58 -0700161 if (recvBuffer[0]->getFreeSegments() <= 0)
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200162 return -1;
kurtis.heimerlce317332011-11-26 03:18:39 +0000163
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400164 /* Outer buffer access size is fixed */
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200165 num_recv = mDevice->readSamples(convertRecvBuffer,
Thomas Tsou3952d802013-10-15 15:12:24 -0400166 resamp_outchunk,
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400167 &overrun,
168 readTimestamp,
169 &local_underrun);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500170 if (num_recv != (int) resamp_outchunk) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400171 LOG(ALERT) << "Receive error " << num_recv;
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200172 return -1;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400173 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000174
Thomas Tsou9471d762013-08-20 21:24:24 -0400175 convert_short_float((float *) outerRecvBuffer->begin(),
Thomas Tsou204a9f12013-10-29 18:34:16 -0400176 convertRecvBuffer[0], 2 * resamp_outchunk);
kurtis.heimerlce317332011-11-26 03:18:39 +0000177
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200178 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Thomas Tsou3952d802013-10-15 15:12:24 -0400179 readTimestamp += (TIMESTAMP) resamp_outchunk;
kurtis.heimerlce317332011-11-26 03:18:39 +0000180
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400181 /* Write to the end of the inner receive buffer */
Thomas Tsou3952d802013-10-15 15:12:24 -0400182 rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
183 resamp_outchunk,
Tom Tsou28670fb2015-08-21 19:32:58 -0700184 recvBuffer[0]->getWriteSegment(),
Thomas Tsou3952d802013-10-15 15:12:24 -0400185 resamp_inchunk);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400186 if (rc < 0) {
187 LOG(ALERT) << "Sample rate upsampling error";
188 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000189
Tom Tsou28670fb2015-08-21 19:32:58 -0700190 /* Set history for the next chunk */
191 outerRecvBuffer->updateHistory();
Pau Espin Pedrol8e498bf2018-09-03 16:45:15 +0200192 return 0;
kurtis.heimerlce317332011-11-26 03:18:39 +0000193}
194
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400195/* Send a timestamped chunk to the device */
Tom Tsou28670fb2015-08-21 19:32:58 -0700196bool RadioInterfaceResamp::pushBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000197{
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200198 bool local_underrun;
Tom Tsou28670fb2015-08-21 19:32:58 -0700199 int rc;
200 size_t numSent;
kurtis.heimerlce317332011-11-26 03:18:39 +0000201
Tom Tsou28670fb2015-08-21 19:32:58 -0700202 if (sendBuffer[0]->getAvailSegments() <= 0)
203 return false;
kurtis.heimerlce317332011-11-26 03:18:39 +0000204
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400205 /* Always send from the beginning of the buffer */
Tom Tsou28670fb2015-08-21 19:32:58 -0700206 rc = upsampler->rotate(sendBuffer[0]->getReadSegment(),
207 resamp_inchunk,
208 (float *) outerSendBuffer->begin(),
209 resamp_outchunk);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400210 if (rc < 0) {
211 LOG(ALERT) << "Sample rate downsampling error";
212 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000213
Thomas Tsou204a9f12013-10-29 18:34:16 -0400214 convert_float_short(convertSendBuffer[0],
Thomas Tsou9471d762013-08-20 21:24:24 -0400215 (float *) outerSendBuffer->begin(),
Tom Tsou28670fb2015-08-21 19:32:58 -0700216 powerScaling[0], 2 * resamp_outchunk);
kurtis.heimerlce317332011-11-26 03:18:39 +0000217
Pau Espin Pedrola801ae52019-09-13 15:59:29 +0200218 numSent = mDevice->writeSamples(convertSendBuffer,
Tom Tsou28670fb2015-08-21 19:32:58 -0700219 resamp_outchunk,
Pau Espin Pedrol25383a32019-09-13 18:23:47 +0200220 &local_underrun,
Tom Tsou28670fb2015-08-21 19:32:58 -0700221 writeTimestamp);
222 if (numSent != resamp_outchunk) {
223 LOG(ALERT) << "Transmit error " << numSent;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400224 }
225
Pau Espin Pedrole503c982019-09-13 18:56:08 +0200226 osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
Tom Tsou28670fb2015-08-21 19:32:58 -0700227 writeTimestamp += resamp_outchunk;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400228
Tom Tsou28670fb2015-08-21 19:32:58 -0700229 return true;
kurtis.heimerlce317332011-11-26 03:18:39 +0000230}