blob: 03cde30d5ea9f0569f6fbadf1a97d6612c44de85 [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
kurtis.heimerlce317332011-11-26 03:18:39 +000031/* New chunk sizes for resampled rate */
32#ifdef INCHUNK
33 #undef INCHUNK
34#endif
35#ifdef OUTCHUNK
36 #undef OUTCHUNK
37#endif
38
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040039/* Resampling parameters for 100 MHz clocking */
40#define RESAMP_INRATE 52
41#define RESAMP_OUTRATE 75
Thomas Tsou0e44ab32013-09-17 20:12:26 -040042
43/*
44 * Resampling filter bandwidth scaling factor
45 * This narrows the filter cutoff relative to the output bandwidth
46 * of the polyphase resampler. At 4 samples-per-symbol using the
47 * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
48 * RMS phase error at the resampler output.
49 */
50#define RESAMP_TX4_FILTER 0.45
kurtis.heimerlce317332011-11-26 03:18:39 +000051
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040052#define INCHUNK (RESAMP_INRATE * 4)
53#define OUTCHUNK (RESAMP_OUTRATE * 4)
kurtis.heimerlce317332011-11-26 03:18:39 +000054
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040055static Resampler *upsampler = NULL;
56static Resampler *dnsampler = NULL;
57short *convertRecvBuffer = NULL;
58short *convertSendBuffer = NULL;
kurtis.heimerlce317332011-11-26 03:18:39 +000059
Thomas Tsoucb69f082013-04-08 14:18:26 -040060RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
61 int wReceiveOffset,
62 int wSPS,
63 GSM::Time wStartTime)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040064 : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime),
65 innerSendBuffer(NULL), outerSendBuffer(NULL),
66 innerRecvBuffer(NULL), outerRecvBuffer(NULL)
Thomas Tsoucb69f082013-04-08 14:18:26 -040067{
68}
69
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040070RadioInterfaceResamp::~RadioInterfaceResamp()
71{
72 close();
73}
74
75void RadioInterfaceResamp::close()
76{
77 RadioInterface::close();
78
79 delete innerSendBuffer;
80 delete outerSendBuffer;
81 delete innerRecvBuffer;
82 delete outerRecvBuffer;
83
84 delete upsampler;
85 delete dnsampler;
86
87 innerSendBuffer = NULL;
88 outerSendBuffer = NULL;
89 innerRecvBuffer = NULL;
90 outerRecvBuffer = NULL;
91
92 upsampler = NULL;
93 dnsampler = NULL;
94}
95
96/* Initialize I/O specific objects */
97bool RadioInterfaceResamp::init()
98{
99 float cutoff = 1.0f;
100
101 close();
102
Thomas Tsou0e44ab32013-09-17 20:12:26 -0400103 if (mSPSTx == 4)
104 cutoff = RESAMP_TX4_FILTER;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400105
106 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
107 if (!dnsampler->init(cutoff)) {
108 LOG(ALERT) << "Rx resampler failed to initialize";
109 return false;
110 }
111
112 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
113 if (!upsampler->init(cutoff)) {
114 LOG(ALERT) << "Tx resampler failed to initialize";
115 return false;
116 }
117
118 /*
119 * Allocate high and low rate buffers. The high rate receive
120 * buffer and low rate transmit vectors feed into the resampler
121 * and requires headroom equivalent to the filter length. Low
122 * rate buffers are allocated in the main radio interface code.
123 */
124 innerSendBuffer = new signalVector(INCHUNK * 20, RESAMP_FILT_LEN);
125 outerSendBuffer = new signalVector(OUTCHUNK * 20);
126
127 outerRecvBuffer = new signalVector(OUTCHUNK * 2, RESAMP_FILT_LEN);
128 innerRecvBuffer = new signalVector(INCHUNK * 20);
129
130 convertSendBuffer = new short[OUTCHUNK * 2 * 20];
131 convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
132
133 sendBuffer = innerSendBuffer;
134 recvBuffer = innerRecvBuffer;
135
136 return true;
137}
138
139/* Receive a timestamped chunk from the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400140void RadioInterfaceResamp::pullBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000141{
kurtis.heimerlce317332011-11-26 03:18:39 +0000142 bool local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400143 int rc, num_recv;
144 int inner_len = INCHUNK;
145 int outer_len = OUTCHUNK;
kurtis.heimerlce317332011-11-26 03:18:39 +0000146
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400147 /* Outer buffer access size is fixed */
148 num_recv = mRadio->readSamples(convertRecvBuffer,
149 outer_len,
150 &overrun,
151 readTimestamp,
152 &local_underrun);
153 if (num_recv != outer_len) {
154 LOG(ALERT) << "Receive error " << num_recv;
155 return;
156 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000157
Thomas Tsou9471d762013-08-20 21:24:24 -0400158 convert_short_float((float *) outerRecvBuffer->begin(),
159 convertRecvBuffer, 2 * outer_len);
kurtis.heimerlce317332011-11-26 03:18:39 +0000160
161 underrun |= local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400162 readTimestamp += (TIMESTAMP) num_recv;
kurtis.heimerlce317332011-11-26 03:18:39 +0000163
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400164 /* Write to the end of the inner receive buffer */
165 rc = dnsampler->rotate((float *) outerRecvBuffer->begin(), outer_len,
166 (float *) (innerRecvBuffer->begin() + recvCursor),
167 inner_len);
168 if (rc < 0) {
169 LOG(ALERT) << "Sample rate upsampling error";
170 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000171
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400172 recvCursor += inner_len;
kurtis.heimerlce317332011-11-26 03:18:39 +0000173}
174
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400175/* Send a timestamped chunk to the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400176void RadioInterfaceResamp::pushBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000177{
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400178 int rc, chunks, num_sent;
179 int inner_len, outer_len;
kurtis.heimerlce317332011-11-26 03:18:39 +0000180
181 if (sendCursor < INCHUNK)
182 return;
183
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400184 chunks = sendCursor / INCHUNK;
185 if (chunks > 8)
186 chunks = 8;
kurtis.heimerlce317332011-11-26 03:18:39 +0000187
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400188 inner_len = chunks * INCHUNK;
189 outer_len = chunks * OUTCHUNK;
kurtis.heimerlce317332011-11-26 03:18:39 +0000190
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400191 /* Always send from the beginning of the buffer */
192 rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
193 (float *) outerSendBuffer->begin(), outer_len);
194 if (rc < 0) {
195 LOG(ALERT) << "Sample rate downsampling error";
196 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000197
Thomas Tsou9471d762013-08-20 21:24:24 -0400198 convert_float_short(convertSendBuffer,
199 (float *) outerSendBuffer->begin(),
200 powerScaling, 2 * outer_len);
kurtis.heimerlce317332011-11-26 03:18:39 +0000201
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400202 num_sent = mRadio->writeSamples(convertSendBuffer,
203 outer_len,
204 &underrun,
205 writeTimestamp);
206 if (num_sent != outer_len) {
207 LOG(ALERT) << "Transmit error " << num_sent;
208 }
209
210 /* Shift remaining samples to beginning of buffer */
211 memmove(innerSendBuffer->begin(),
212 innerSendBuffer->begin() + inner_len,
213 (sendCursor - inner_len) * 2 * sizeof(float));
214
215 writeTimestamp += outer_len;
216 sendCursor -= inner_len;
217 assert(sendCursor >= 0);
kurtis.heimerlce317332011-11-26 03:18:39 +0000218}