blob: 40bf32d206cecc090ce9efd50c6a15b5e08f3ce9 [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 Tsou03e6ecf2013-08-20 20:54:54 -040031/* Resampling parameters for 100 MHz clocking */
32#define RESAMP_INRATE 52
33#define RESAMP_OUTRATE 75
Thomas Tsou0e44ab32013-09-17 20:12:26 -040034
35/*
36 * Resampling filter bandwidth scaling factor
37 * This narrows the filter cutoff relative to the output bandwidth
38 * of the polyphase resampler. At 4 samples-per-symbol using the
39 * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
40 * RMS phase error at the resampler output.
41 */
42#define RESAMP_TX4_FILTER 0.45
kurtis.heimerlce317332011-11-26 03:18:39 +000043
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040044#define INCHUNK (RESAMP_INRATE * 4)
45#define OUTCHUNK (RESAMP_OUTRATE * 4)
kurtis.heimerlce317332011-11-26 03:18:39 +000046
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040047static Resampler *upsampler = NULL;
48static Resampler *dnsampler = NULL;
49short *convertRecvBuffer = NULL;
50short *convertSendBuffer = NULL;
kurtis.heimerlce317332011-11-26 03:18:39 +000051
Thomas Tsoucb69f082013-04-08 14:18:26 -040052RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
53 int wReceiveOffset,
54 int wSPS,
55 GSM::Time wStartTime)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040056 : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime),
57 innerSendBuffer(NULL), outerSendBuffer(NULL),
58 innerRecvBuffer(NULL), outerRecvBuffer(NULL)
Thomas Tsoucb69f082013-04-08 14:18:26 -040059{
60}
61
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040062RadioInterfaceResamp::~RadioInterfaceResamp()
63{
64 close();
65}
66
67void RadioInterfaceResamp::close()
68{
69 RadioInterface::close();
70
71 delete innerSendBuffer;
72 delete outerSendBuffer;
73 delete innerRecvBuffer;
74 delete outerRecvBuffer;
75
76 delete upsampler;
77 delete dnsampler;
78
79 innerSendBuffer = NULL;
80 outerSendBuffer = NULL;
81 innerRecvBuffer = NULL;
82 outerRecvBuffer = NULL;
83
84 upsampler = NULL;
85 dnsampler = NULL;
86}
87
88/* Initialize I/O specific objects */
89bool RadioInterfaceResamp::init()
90{
91 float cutoff = 1.0f;
92
93 close();
94
Thomas Tsou0e44ab32013-09-17 20:12:26 -040095 if (mSPSTx == 4)
96 cutoff = RESAMP_TX4_FILTER;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040097
98 dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
Thomas Tsouc1f7c422013-10-11 13:49:55 -040099 if (!dnsampler->init()) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400100 LOG(ALERT) << "Rx resampler failed to initialize";
101 return false;
102 }
103
104 upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
105 if (!upsampler->init(cutoff)) {
106 LOG(ALERT) << "Tx resampler failed to initialize";
107 return false;
108 }
109
110 /*
111 * Allocate high and low rate buffers. The high rate receive
112 * buffer and low rate transmit vectors feed into the resampler
113 * and requires headroom equivalent to the filter length. Low
114 * rate buffers are allocated in the main radio interface code.
115 */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400116 innerSendBuffer = new signalVector(INCHUNK * 20, upsampler->len());
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400117 outerSendBuffer = new signalVector(OUTCHUNK * 20);
118
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400119 outerRecvBuffer = new signalVector(OUTCHUNK * 2, dnsampler->len());
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400120 innerRecvBuffer = new signalVector(INCHUNK * 20);
121
122 convertSendBuffer = new short[OUTCHUNK * 2 * 20];
123 convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
124
125 sendBuffer = innerSendBuffer;
126 recvBuffer = innerRecvBuffer;
127
128 return true;
129}
130
131/* Receive a timestamped chunk from the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400132void RadioInterfaceResamp::pullBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000133{
kurtis.heimerlce317332011-11-26 03:18:39 +0000134 bool local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400135 int rc, num_recv;
136 int inner_len = INCHUNK;
137 int outer_len = OUTCHUNK;
kurtis.heimerlce317332011-11-26 03:18:39 +0000138
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400139 /* Outer buffer access size is fixed */
140 num_recv = mRadio->readSamples(convertRecvBuffer,
141 outer_len,
142 &overrun,
143 readTimestamp,
144 &local_underrun);
145 if (num_recv != outer_len) {
146 LOG(ALERT) << "Receive error " << num_recv;
147 return;
148 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000149
Thomas Tsou9471d762013-08-20 21:24:24 -0400150 convert_short_float((float *) outerRecvBuffer->begin(),
151 convertRecvBuffer, 2 * outer_len);
kurtis.heimerlce317332011-11-26 03:18:39 +0000152
153 underrun |= local_underrun;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400154 readTimestamp += (TIMESTAMP) num_recv;
kurtis.heimerlce317332011-11-26 03:18:39 +0000155
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400156 /* Write to the end of the inner receive buffer */
157 rc = dnsampler->rotate((float *) outerRecvBuffer->begin(), outer_len,
158 (float *) (innerRecvBuffer->begin() + recvCursor),
159 inner_len);
160 if (rc < 0) {
161 LOG(ALERT) << "Sample rate upsampling error";
162 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000163
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400164 recvCursor += inner_len;
kurtis.heimerlce317332011-11-26 03:18:39 +0000165}
166
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400167/* Send a timestamped chunk to the device */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400168void RadioInterfaceResamp::pushBuffer()
kurtis.heimerlce317332011-11-26 03:18:39 +0000169{
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400170 int rc, chunks, num_sent;
171 int inner_len, outer_len;
kurtis.heimerlce317332011-11-26 03:18:39 +0000172
173 if (sendCursor < INCHUNK)
174 return;
175
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400176 chunks = sendCursor / INCHUNK;
177 if (chunks > 8)
178 chunks = 8;
kurtis.heimerlce317332011-11-26 03:18:39 +0000179
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400180 inner_len = chunks * INCHUNK;
181 outer_len = chunks * OUTCHUNK;
kurtis.heimerlce317332011-11-26 03:18:39 +0000182
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400183 /* Always send from the beginning of the buffer */
184 rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
185 (float *) outerSendBuffer->begin(), outer_len);
186 if (rc < 0) {
187 LOG(ALERT) << "Sample rate downsampling error";
188 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000189
Thomas Tsou9471d762013-08-20 21:24:24 -0400190 convert_float_short(convertSendBuffer,
191 (float *) outerSendBuffer->begin(),
192 powerScaling, 2 * outer_len);
kurtis.heimerlce317332011-11-26 03:18:39 +0000193
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400194 num_sent = mRadio->writeSamples(convertSendBuffer,
195 outer_len,
196 &underrun,
197 writeTimestamp);
198 if (num_sent != outer_len) {
199 LOG(ALERT) << "Transmit error " << num_sent;
200 }
201
202 /* Shift remaining samples to beginning of buffer */
203 memmove(innerSendBuffer->begin(),
204 innerSendBuffer->begin() + inner_len,
205 (sendCursor - inner_len) * 2 * sizeof(float));
206
207 writeTimestamp += outer_len;
208 sendCursor -= inner_len;
209 assert(sendCursor >= 0);
kurtis.heimerlce317332011-11-26 03:18:39 +0000210}