2011-11-26 03:18:39 +00:00
|
|
|
/*
|
|
|
|
* Radio device interface with sample rate conversion
|
2013-08-21 00:54:54 +00:00
|
|
|
* Written by Thomas Tsou <tom@tsou.cc>
|
2011-11-26 03:18:39 +00:00
|
|
|
*
|
2013-08-21 00:54:54 +00:00
|
|
|
* Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
|
2011-11-26 03:18:39 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
* See the COPYING file in the main directory for details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <radioInterface.h>
|
|
|
|
#include <Logger.h>
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
#include "Resampler.h"
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include "convert.h"
|
|
|
|
}
|
|
|
|
|
2011-11-26 03:18:39 +00:00
|
|
|
/* New chunk sizes for resampled rate */
|
|
|
|
#ifdef INCHUNK
|
|
|
|
#undef INCHUNK
|
|
|
|
#endif
|
|
|
|
#ifdef OUTCHUNK
|
|
|
|
#undef OUTCHUNK
|
|
|
|
#endif
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Resampling parameters for 100 MHz clocking */
|
|
|
|
#define RESAMP_INRATE 52
|
|
|
|
#define RESAMP_OUTRATE 75
|
2013-09-18 00:12:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Resampling filter bandwidth scaling factor
|
|
|
|
* This narrows the filter cutoff relative to the output bandwidth
|
|
|
|
* of the polyphase resampler. At 4 samples-per-symbol using the
|
|
|
|
* 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
|
|
|
|
* RMS phase error at the resampler output.
|
|
|
|
*/
|
|
|
|
#define RESAMP_TX4_FILTER 0.45
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
#define INCHUNK (RESAMP_INRATE * 4)
|
|
|
|
#define OUTCHUNK (RESAMP_OUTRATE * 4)
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
static Resampler *upsampler = NULL;
|
|
|
|
static Resampler *dnsampler = NULL;
|
|
|
|
short *convertRecvBuffer = NULL;
|
|
|
|
short *convertSendBuffer = NULL;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
|
|
|
|
int wReceiveOffset,
|
|
|
|
int wSPS,
|
|
|
|
GSM::Time wStartTime)
|
|
|
|
: RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime),
|
|
|
|
innerSendBuffer(NULL), outerSendBuffer(NULL),
|
|
|
|
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
RadioInterfaceResamp::~RadioInterfaceResamp()
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
2013-08-21 00:54:54 +00:00
|
|
|
close();
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
void RadioInterfaceResamp::close()
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
2013-08-21 00:54:54 +00:00
|
|
|
RadioInterface::close();
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
delete innerSendBuffer;
|
|
|
|
delete outerSendBuffer;
|
|
|
|
delete innerRecvBuffer;
|
|
|
|
delete outerRecvBuffer;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
delete upsampler;
|
|
|
|
delete dnsampler;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
innerSendBuffer = NULL;
|
|
|
|
outerSendBuffer = NULL;
|
|
|
|
innerRecvBuffer = NULL;
|
|
|
|
outerRecvBuffer = NULL;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
upsampler = NULL;
|
|
|
|
dnsampler = NULL;
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Initialize I/O specific objects */
|
|
|
|
bool RadioInterfaceResamp::init()
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
2013-08-21 00:54:54 +00:00
|
|
|
float cutoff = 1.0f;
|
|
|
|
|
|
|
|
close();
|
|
|
|
|
2013-09-18 00:12:26 +00:00
|
|
|
if (mSPSTx == 4)
|
|
|
|
cutoff = RESAMP_TX4_FILTER;
|
2013-08-21 00:54:54 +00:00
|
|
|
|
|
|
|
dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
|
|
|
|
if (!dnsampler->init(cutoff)) {
|
|
|
|
LOG(ALERT) << "Rx resampler failed to initialize";
|
|
|
|
return false;
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
|
|
|
|
if (!upsampler->init(cutoff)) {
|
|
|
|
LOG(ALERT) << "Tx resampler failed to initialize";
|
|
|
|
return false;
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/*
|
|
|
|
* Allocate high and low rate buffers. The high rate receive
|
|
|
|
* buffer and low rate transmit vectors feed into the resampler
|
|
|
|
* and requires headroom equivalent to the filter length. Low
|
|
|
|
* rate buffers are allocated in the main radio interface code.
|
|
|
|
*/
|
|
|
|
innerSendBuffer = new signalVector(INCHUNK * 20, RESAMP_FILT_LEN);
|
|
|
|
outerSendBuffer = new signalVector(OUTCHUNK * 20);
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
outerRecvBuffer = new signalVector(OUTCHUNK * 2, RESAMP_FILT_LEN);
|
|
|
|
innerRecvBuffer = new signalVector(INCHUNK * 20);
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
convertSendBuffer = new short[OUTCHUNK * 2 * 20];
|
|
|
|
convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
sendBuffer = innerSendBuffer;
|
|
|
|
recvBuffer = innerRecvBuffer;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
return true;
|
2013-04-08 18:18:26 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Receive a timestamped chunk from the device */
|
2013-04-08 18:18:26 +00:00
|
|
|
void RadioInterfaceResamp::pullBuffer()
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
|
|
|
bool local_underrun;
|
2013-08-21 00:54:54 +00:00
|
|
|
int rc, num_recv;
|
|
|
|
int inner_len = INCHUNK;
|
|
|
|
int outer_len = OUTCHUNK;
|
|
|
|
|
|
|
|
/* Outer buffer access size is fixed */
|
|
|
|
num_recv = mRadio->readSamples(convertRecvBuffer,
|
|
|
|
outer_len,
|
|
|
|
&overrun,
|
|
|
|
readTimestamp,
|
|
|
|
&local_underrun);
|
|
|
|
if (num_recv != outer_len) {
|
|
|
|
LOG(ALERT) << "Receive error " << num_recv;
|
|
|
|
return;
|
|
|
|
}
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 01:24:24 +00:00
|
|
|
convert_short_float((float *) outerRecvBuffer->begin(),
|
|
|
|
convertRecvBuffer, 2 * outer_len);
|
2011-11-26 03:18:39 +00:00
|
|
|
|
|
|
|
underrun |= local_underrun;
|
2013-08-21 00:54:54 +00:00
|
|
|
readTimestamp += (TIMESTAMP) num_recv;
|
|
|
|
|
|
|
|
/* Write to the end of the inner receive buffer */
|
|
|
|
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(), outer_len,
|
|
|
|
(float *) (innerRecvBuffer->begin() + recvCursor),
|
|
|
|
inner_len);
|
|
|
|
if (rc < 0) {
|
|
|
|
LOG(ALERT) << "Sample rate upsampling error";
|
|
|
|
}
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
recvCursor += inner_len;
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Send a timestamped chunk to the device */
|
2013-04-08 18:18:26 +00:00
|
|
|
void RadioInterfaceResamp::pushBuffer()
|
2011-11-26 03:18:39 +00:00
|
|
|
{
|
2013-08-21 00:54:54 +00:00
|
|
|
int rc, chunks, num_sent;
|
|
|
|
int inner_len, outer_len;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
|
|
|
if (sendCursor < INCHUNK)
|
|
|
|
return;
|
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
chunks = sendCursor / INCHUNK;
|
|
|
|
if (chunks > 8)
|
|
|
|
chunks = 8;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
inner_len = chunks * INCHUNK;
|
|
|
|
outer_len = chunks * OUTCHUNK;
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Always send from the beginning of the buffer */
|
|
|
|
rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
|
|
|
|
(float *) outerSendBuffer->begin(), outer_len);
|
|
|
|
if (rc < 0) {
|
|
|
|
LOG(ALERT) << "Sample rate downsampling error";
|
|
|
|
}
|
|
|
|
|
2013-08-21 01:24:24 +00:00
|
|
|
convert_float_short(convertSendBuffer,
|
|
|
|
(float *) outerSendBuffer->begin(),
|
|
|
|
powerScaling, 2 * outer_len);
|
2013-08-21 00:54:54 +00:00
|
|
|
|
|
|
|
num_sent = mRadio->writeSamples(convertSendBuffer,
|
|
|
|
outer_len,
|
|
|
|
&underrun,
|
|
|
|
writeTimestamp);
|
|
|
|
if (num_sent != outer_len) {
|
|
|
|
LOG(ALERT) << "Transmit error " << num_sent;
|
|
|
|
}
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
/* Shift remaining samples to beginning of buffer */
|
|
|
|
memmove(innerSendBuffer->begin(),
|
|
|
|
innerSendBuffer->begin() + inner_len,
|
|
|
|
(sendCursor - inner_len) * 2 * sizeof(float));
|
2011-11-26 03:18:39 +00:00
|
|
|
|
2013-08-21 00:54:54 +00:00
|
|
|
writeTimestamp += outer_len;
|
|
|
|
sendCursor -= inner_len;
|
|
|
|
assert(sendCursor >= 0);
|
2011-11-26 03:18:39 +00:00
|
|
|
}
|