mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-22 23:32:00 +00:00
Replace the polyphase filter and resampler with a separate implementation using SSE enabled convolution. The USRP2 (including derived devices N200, N210) are the only supported devices that require sample rate conversion, so set the default resampling parameters for the 100 MHz FPGA clock. This changes the previous resampling ratios. 270.833 kHz -> 400 kHz (65 / 96) 270.833 kHz -> 390.625 kHz (52 / 75) The new resampling factor uses a USRP resampling factor of 256 instead of 250. On the device, this allows two halfband filters to be used rather than one. The end result is reduced distortial and aliasing effecits from CIC filter rolloff. B100 and USRP1 will no be supported at 400 ksps with these changes. Signed-off-by: Thomas Tsou <tom@tsou.cc> git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@6733 19bc5d8c-e614-43d4-8b26-e1612bc8e597
230 lines
5.8 KiB
C++
230 lines
5.8 KiB
C++
/*
|
|
* Radio device interface with sample rate conversion
|
|
* Written by Thomas Tsou <tom@tsou.cc>
|
|
*
|
|
* Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
|
|
*
|
|
* 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>
|
|
|
|
#include "Resampler.h"
|
|
|
|
extern "C" {
|
|
#include "convert.h"
|
|
}
|
|
|
|
/* New chunk sizes for resampled rate */
|
|
#ifdef INCHUNK
|
|
#undef INCHUNK
|
|
#endif
|
|
#ifdef OUTCHUNK
|
|
#undef OUTCHUNK
|
|
#endif
|
|
|
|
/* Resampling parameters for 100 MHz clocking */
|
|
#define RESAMP_INRATE 52
|
|
#define RESAMP_OUTRATE 75
|
|
#define RESAMP_FILT_LEN 16
|
|
|
|
#define INCHUNK (RESAMP_INRATE * 4)
|
|
#define OUTCHUNK (RESAMP_OUTRATE * 4)
|
|
|
|
static Resampler *upsampler = NULL;
|
|
static Resampler *dnsampler = NULL;
|
|
short *convertRecvBuffer = NULL;
|
|
short *convertSendBuffer = NULL;
|
|
|
|
/* Complex float to short conversion */
|
|
static void floatToShort(short *out, float *in, int num)
|
|
{
|
|
for (int i = 0; i < num; i++)
|
|
out[i] = (short) in[i];
|
|
}
|
|
|
|
/* Complex short to float conversion */
|
|
static void shortToFloat(float *out, short *in, int num)
|
|
{
|
|
for (int i = 0; i < num; i++)
|
|
out[i] = (float) in[i];
|
|
}
|
|
|
|
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
|
|
int wReceiveOffset,
|
|
int wSPS,
|
|
GSM::Time wStartTime)
|
|
: RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime),
|
|
innerSendBuffer(NULL), outerSendBuffer(NULL),
|
|
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
|
|
{
|
|
}
|
|
|
|
RadioInterfaceResamp::~RadioInterfaceResamp()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void RadioInterfaceResamp::close()
|
|
{
|
|
RadioInterface::close();
|
|
|
|
delete innerSendBuffer;
|
|
delete outerSendBuffer;
|
|
delete innerRecvBuffer;
|
|
delete outerRecvBuffer;
|
|
|
|
delete upsampler;
|
|
delete dnsampler;
|
|
|
|
innerSendBuffer = NULL;
|
|
outerSendBuffer = NULL;
|
|
innerRecvBuffer = NULL;
|
|
outerRecvBuffer = NULL;
|
|
|
|
upsampler = NULL;
|
|
dnsampler = NULL;
|
|
}
|
|
|
|
/* Initialize I/O specific objects */
|
|
bool RadioInterfaceResamp::init()
|
|
{
|
|
float cutoff = 1.0f;
|
|
|
|
close();
|
|
|
|
/*
|
|
* With oversampling, restrict bandwidth to 150% of base rate. This also
|
|
* provides last ditch bandwith limiting if the pulse shaping filter is
|
|
* insufficient.
|
|
*/
|
|
if (sps > 1)
|
|
cutoff = 1.5 / sps;
|
|
|
|
dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
|
|
if (!dnsampler->init(cutoff)) {
|
|
LOG(ALERT) << "Rx resampler failed to initialize";
|
|
return false;
|
|
}
|
|
|
|
upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
|
|
if (!upsampler->init(cutoff)) {
|
|
LOG(ALERT) << "Tx resampler failed to initialize";
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
outerRecvBuffer = new signalVector(OUTCHUNK * 2, RESAMP_FILT_LEN);
|
|
innerRecvBuffer = new signalVector(INCHUNK * 20);
|
|
|
|
convertSendBuffer = new short[OUTCHUNK * 2 * 20];
|
|
convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
|
|
|
|
sendBuffer = innerSendBuffer;
|
|
recvBuffer = innerRecvBuffer;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Receive a timestamped chunk from the device */
|
|
void RadioInterfaceResamp::pullBuffer()
|
|
{
|
|
bool local_underrun;
|
|
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;
|
|
}
|
|
|
|
shortToFloat((float *) outerRecvBuffer->begin(),
|
|
convertRecvBuffer, 2 * outer_len);
|
|
|
|
underrun |= local_underrun;
|
|
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";
|
|
}
|
|
|
|
recvCursor += inner_len;
|
|
}
|
|
|
|
/* Send a timestamped chunk to the device */
|
|
void RadioInterfaceResamp::pushBuffer()
|
|
{
|
|
int rc, chunks, num_sent;
|
|
int inner_len, outer_len;
|
|
|
|
if (sendCursor < INCHUNK)
|
|
return;
|
|
|
|
chunks = sendCursor / INCHUNK;
|
|
if (chunks > 8)
|
|
chunks = 8;
|
|
|
|
inner_len = chunks * INCHUNK;
|
|
outer_len = chunks * OUTCHUNK;
|
|
|
|
/* 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";
|
|
}
|
|
|
|
floatToShort(convertSendBuffer,
|
|
(float *) outerSendBuffer->begin(),
|
|
2 * outer_len);
|
|
|
|
num_sent = mRadio->writeSamples(convertSendBuffer,
|
|
outer_len,
|
|
&underrun,
|
|
writeTimestamp);
|
|
if (num_sent != outer_len) {
|
|
LOG(ALERT) << "Transmit error " << num_sent;
|
|
}
|
|
|
|
/* Shift remaining samples to beginning of buffer */
|
|
memmove(innerSendBuffer->begin(),
|
|
innerSendBuffer->begin() + inner_len,
|
|
(sendCursor - inner_len) * 2 * sizeof(float));
|
|
|
|
writeTimestamp += outer_len;
|
|
sendCursor -= inner_len;
|
|
assert(sendCursor >= 0);
|
|
}
|