blob: f898d65e8299d108c535cfb6bc9de2238507e6ba [file] [log] [blame]
kurtis.heimerlce317332011-11-26 03:18:39 +00001/*
2 * Radio device interface with sample rate conversion
Thomas Tsou03e6ecf2013-08-20 20:54:54 -04003 * Written by Thomas Tsou <tom@tsou.cc>
kurtis.heimerlce317332011-11-26 03:18:39 +00004 *
Thomas Tsou03e6ecf2013-08-20 20:54:54 -04005 * Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
kurtis.heimerlce317332011-11-26 03:18:39 +00006 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
21
22#include <radioInterface.h>
23#include <Logger.h>
24
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040025#include "Resampler.h"
26
27extern "C" {
28#include "convert.h"
29}
30
Thomas Tsoufe269fe2013-10-14 23:56:51 -040031/* Resampling parameters for 64 MHz clocking */
32#define RESAMP_64M_INRATE 65
33#define RESAMP_64M_OUTRATE 96
34
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040035/* Resampling parameters for 100 MHz clocking */
Thomas Tsoufe269fe2013-10-14 23:56:51 -040036#define RESAMP_100M_INRATE 52
37#define RESAMP_100M_OUTRATE 75
Thomas Tsou0e44ab32013-09-17 20:12:26 -040038
Thomas Tsou3952d802013-10-15 15:12:24 -040039/* Universal resampling parameters */
40#define NUMCHUNKS 24
41
Thomas Tsou0e44ab32013-09-17 20:12:26 -040042/*
43 * Resampling filter bandwidth scaling factor
44 * This narrows the filter cutoff relative to the output bandwidth
45 * of the polyphase resampler. At 4 samples-per-symbol using the
46 * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
47 * RMS phase error at the resampler output.
48 */
49#define RESAMP_TX4_FILTER 0.45
kurtis.heimerlce317332011-11-26 03:18:39 +000050
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040051static Resampler *upsampler = NULL;
52static Resampler *dnsampler = NULL;
Thomas Tsoue90a42b2013-11-13 23:38:09 -050053static size_t resamp_inrate = 0;
54static size_t resamp_inchunk = 0;
55static size_t resamp_outrate = 0;
56static size_t resamp_outchunk = 0;
Thomas Tsoufe269fe2013-10-14 23:56:51 -040057
Thomas Tsoucb69f082013-04-08 14:18:26 -040058RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
Thomas Tsoue90a42b2013-11-13 23:38:09 -050059 size_t sps, size_t chans)
60 : RadioInterface(wRadio, sps, chans),
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040061 innerSendBuffer(NULL), outerSendBuffer(NULL),
62 innerRecvBuffer(NULL), outerRecvBuffer(NULL)
Thomas Tsoucb69f082013-04-08 14:18:26 -040063{
64}
65
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040066RadioInterfaceResamp::~RadioInterfaceResamp()
67{
68 close();
69}
70
71void RadioInterfaceResamp::close()
72{
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040073 delete innerSendBuffer;
74 delete outerSendBuffer;
75 delete innerRecvBuffer;
76 delete outerRecvBuffer;
77
78 delete upsampler;
79 delete dnsampler;
80
81 innerSendBuffer = NULL;
82 outerSendBuffer = NULL;
83 innerRecvBuffer = NULL;
84 outerRecvBuffer = NULL;
85
86 upsampler = NULL;
87 dnsampler = NULL;
Thomas Tsoude1648c2013-10-15 16:18:58 -040088
Thomas Tsou204a9f12013-10-29 18:34:16 -040089 if (sendBuffer.size())
90 sendBuffer[0] = NULL;
91 if (recvBuffer.size())
92 recvBuffer[0] = NULL;
93
Thomas Tsoude1648c2013-10-15 16:18:58 -040094 RadioInterface::close();
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040095}
96
97/* Initialize I/O specific objects */
Thomas Tsoufe269fe2013-10-14 23:56:51 -040098bool RadioInterfaceResamp::init(int type)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040099{
100 float cutoff = 1.0f;
101
Thomas Tsou204a9f12013-10-29 18:34:16 -0400102 if (mChans != 1) {
103 LOG(ALERT) << "Unsupported channel configuration " << mChans;
104 return false;
105 }
106
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400107 close();
108
Thomas Tsou204a9f12013-10-29 18:34:16 -0400109 sendBuffer.resize(1);
110 recvBuffer.resize(1);
111 convertSendBuffer.resize(1);
112 convertRecvBuffer.resize(1);
113 mReceiveFIFO.resize(1);
Thomas Tsouaf506442013-11-18 01:35:22 -0500114 powerScaling.resize(1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400115
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400116 switch (type) {
117 case RadioDevice::RESAMP_64M:
118 resamp_inrate = RESAMP_64M_INRATE;
119 resamp_outrate = RESAMP_64M_OUTRATE;
120 break;
121 case RadioDevice::RESAMP_100M:
122 resamp_inrate = RESAMP_100M_INRATE;
123 resamp_outrate = RESAMP_100M_OUTRATE;
124 break;
125 case RadioDevice::NORMAL:
126 default:
127 LOG(ALERT) << "Invalid device configuration";
128 return false;
129 }
130
131 resamp_inchunk = resamp_inrate * 4;
132 resamp_outchunk = resamp_outrate * 4;
133
Thomas Tsou3952d802013-10-15 15:12:24 -0400134 if (resamp_inchunk * NUMCHUNKS < 157 * mSPSTx * 2) {
135 LOG(ALERT) << "Invalid inner chunk size " << resamp_inchunk;
136 return false;
137 }
138
Thomas Tsou0e44ab32013-09-17 20:12:26 -0400139 if (mSPSTx == 4)
140 cutoff = RESAMP_TX4_FILTER;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400141
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400142 dnsampler = new Resampler(resamp_inrate, resamp_outrate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400143 if (!dnsampler->init()) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400144 LOG(ALERT) << "Rx resampler failed to initialize";
145 return false;
146 }
147
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400148 upsampler = new Resampler(resamp_outrate, resamp_inrate);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400149 if (!upsampler->init(cutoff)) {
150 LOG(ALERT) << "Tx resampler failed to initialize";
151 return false;
152 }
153
154 /*
155 * Allocate high and low rate buffers. The high rate receive
156 * buffer and low rate transmit vectors feed into the resampler
157 * and requires headroom equivalent to the filter length. Low
158 * rate buffers are allocated in the main radio interface code.
159 */
Thomas Tsou3952d802013-10-15 15:12:24 -0400160 innerSendBuffer =
161 new signalVector(NUMCHUNKS * resamp_inchunk, upsampler->len());
162 outerSendBuffer =
163 new signalVector(NUMCHUNKS * resamp_outchunk);
164 outerRecvBuffer =
165 new signalVector(resamp_outchunk, dnsampler->len());
166 innerRecvBuffer =
167 new signalVector(NUMCHUNKS * resamp_inchunk / mSPSTx);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400168
Thomas Tsou204a9f12013-10-29 18:34:16 -0400169 convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
170 convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400171
Thomas Tsou204a9f12013-10-29 18:34:16 -0400172 sendBuffer[0] = innerSendBuffer;
173 recvBuffer[0] = innerRecvBuffer;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400174
175 return true;
176}
177
178/* Receive a timestamped chunk from the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400179void RadioInterfaceResamp::pullBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000180{
kurtis.heimerlce317332011-11-26 03:18:39 +0000181 bool local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400182 int rc, num_recv;
Thomas Tsou3952d802013-10-15 15:12:24 -0400183
184 if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
185 return;
kurtis.heimerlce317332011-11-26 03:18:39 +0000186
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400187 /* Outer buffer access size is fixed */
188 num_recv = mRadio->readSamples(convertRecvBuffer,
Thomas Tsou3952d802013-10-15 15:12:24 -0400189 resamp_outchunk,
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400190 &overrun,
191 readTimestamp,
192 &local_underrun);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500193 if (num_recv != (int) resamp_outchunk) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400194 LOG(ALERT) << "Receive error " << num_recv;
195 return;
196 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000197
Thomas Tsou9471d762013-08-20 21:24:24 -0400198 convert_short_float((float *) outerRecvBuffer->begin(),
Thomas Tsou204a9f12013-10-29 18:34:16 -0400199 convertRecvBuffer[0], 2 * resamp_outchunk);
kurtis.heimerlce317332011-11-26 03:18:39 +0000200
201 underrun |= local_underrun;
Thomas Tsou3952d802013-10-15 15:12:24 -0400202 readTimestamp += (TIMESTAMP) resamp_outchunk;
kurtis.heimerlce317332011-11-26 03:18:39 +0000203
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400204 /* Write to the end of the inner receive buffer */
Thomas Tsou3952d802013-10-15 15:12:24 -0400205 rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
206 resamp_outchunk,
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400207 (float *) (innerRecvBuffer->begin() + recvCursor),
Thomas Tsou3952d802013-10-15 15:12:24 -0400208 resamp_inchunk);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400209 if (rc < 0) {
210 LOG(ALERT) << "Sample rate upsampling error";
211 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000212
Thomas Tsou3952d802013-10-15 15:12:24 -0400213 recvCursor += resamp_inchunk;
kurtis.heimerlce317332011-11-26 03:18:39 +0000214}
215
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400216/* Send a timestamped chunk to the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400217void RadioInterfaceResamp::pushBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000218{
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400219 int rc, chunks, num_sent;
220 int inner_len, outer_len;
kurtis.heimerlce317332011-11-26 03:18:39 +0000221
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400222 if (sendCursor < resamp_inchunk)
kurtis.heimerlce317332011-11-26 03:18:39 +0000223 return;
224
Thomas Tsou3952d802013-10-15 15:12:24 -0400225 if (sendCursor > innerSendBuffer->size())
226 LOG(ALERT) << "Send buffer overflow";
227
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400228 chunks = sendCursor / resamp_inchunk;
kurtis.heimerlce317332011-11-26 03:18:39 +0000229
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400230 inner_len = chunks * resamp_inchunk;
231 outer_len = chunks * resamp_outchunk;
kurtis.heimerlce317332011-11-26 03:18:39 +0000232
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400233 /* Always send from the beginning of the buffer */
234 rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
235 (float *) outerSendBuffer->begin(), outer_len);
236 if (rc < 0) {
237 LOG(ALERT) << "Sample rate downsampling error";
238 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000239
Thomas Tsou204a9f12013-10-29 18:34:16 -0400240 convert_float_short(convertSendBuffer[0],
Thomas Tsou9471d762013-08-20 21:24:24 -0400241 (float *) outerSendBuffer->begin(),
Thomas Tsoucb269a32013-11-15 14:15:47 -0500242 powerScaling[0], 2 * outer_len);
kurtis.heimerlce317332011-11-26 03:18:39 +0000243
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400244 num_sent = mRadio->writeSamples(convertSendBuffer,
245 outer_len,
246 &underrun,
247 writeTimestamp);
248 if (num_sent != outer_len) {
249 LOG(ALERT) << "Transmit error " << num_sent;
250 }
251
252 /* Shift remaining samples to beginning of buffer */
253 memmove(innerSendBuffer->begin(),
254 innerSendBuffer->begin() + inner_len,
255 (sendCursor - inner_len) * 2 * sizeof(float));
256
257 writeTimestamp += outer_len;
258 sendCursor -= inner_len;
259 assert(sendCursor >= 0);
kurtis.heimerlce317332011-11-26 03:18:39 +0000260}