mcbts: Add multi-ARFCN radio support
Add new radio interface "radioInterfaceMulti" for multi-carrier support. Only USRP B200/B210 devices are supported because of sample rate requirements (3.2 Msps). Only 4 SPS operation Tx/RX is supported. 8-PSK is supported. Other options may be added at a later time Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
This commit is contained in:
parent
35222296fe
commit
7676427816
|
@ -67,6 +67,7 @@ libtransceiver_la_SOURCES = \
|
|||
$(COMMON_SOURCES) \
|
||||
Resampler.cpp \
|
||||
radioInterfaceResamp.cpp \
|
||||
radioInterfaceMulti.cpp \
|
||||
radioInterfaceDiversity.cpp
|
||||
|
||||
bin_PROGRAMS = osmo-trx
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#endif
|
||||
|
||||
#define B2XX_CLK_RT 26e6
|
||||
#define B2XX_MCBTS_CLK_RT 3.2e6
|
||||
#define E1XX_CLK_RT 52e6
|
||||
#define B100_BASE_RT 400000
|
||||
#define USRP2_BASE_RT 390625
|
||||
|
@ -61,6 +62,7 @@ enum uhd_dev_type {
|
|||
B100,
|
||||
B200,
|
||||
B210,
|
||||
B2XX_MCBTS,
|
||||
E1XX,
|
||||
E3XX,
|
||||
X3XX,
|
||||
|
@ -110,6 +112,7 @@ static struct uhd_dev_offset uhd_offsets[] = {
|
|||
{ B200, 4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" },
|
||||
{ B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
|
||||
{ B210, 4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" },
|
||||
{ B2XX_MCBTS, 4, 4, 1.07188e-4, "B200/B210 4 SPS Multi-ARFCN" },
|
||||
{ E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||
{ E1XX, 4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" },
|
||||
{ E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
|
||||
|
@ -150,9 +153,20 @@ static double select_rate(uhd_dev_type type, int sps,
|
|||
return -9999.99;
|
||||
}
|
||||
|
||||
|
||||
if ((sps != 4) && (sps != 1))
|
||||
return -9999.99;
|
||||
|
||||
if (iface == RadioDevice::MULTI_ARFCN) {
|
||||
switch (type) {
|
||||
case B2XX_MCBTS:
|
||||
return 4 * MCBTS_SPACING;
|
||||
default:
|
||||
LOG(ALERT) << "Invalid device combination";
|
||||
return -9999.99;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case USRP2:
|
||||
case X3XX:
|
||||
|
@ -542,12 +556,15 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
|||
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
|
||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
else if (dev_type == E1XX) {
|
||||
} else if (dev_type == E1XX) {
|
||||
if (set_master_clk(E1XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
} else if (dev_type == B2XX_MCBTS) {
|
||||
if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Set sample rates
|
||||
try {
|
||||
usrp_dev->set_tx_rate(tx_rate);
|
||||
|
@ -574,6 +591,9 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
|||
|
||||
double uhd_device::setTxGain(double db, size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel" << chan;
|
||||
return 0.0f;
|
||||
|
@ -620,6 +640,9 @@ double uhd_device::setRxGain(double db, size_t chan)
|
|||
|
||||
double uhd_device::getRxGain(size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= rx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
|
@ -762,11 +785,24 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
|||
}
|
||||
|
||||
// Verify and set channels
|
||||
if ((dev_type == B210) && (chans == 2)) {
|
||||
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
if (iface == MULTI_ARFCN) {
|
||||
if ((dev_type != B200) && (dev_type != B210)) {
|
||||
LOG(ALERT) << "Unsupported device configuration";
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev_type = B2XX_MCBTS;
|
||||
chans = 1;
|
||||
} else if (chans == 2) {
|
||||
if (dev_type == B210) {
|
||||
} else if (dev_type == UMTRX) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
} else {
|
||||
LOG(ALERT) << "Invalid device configuration";
|
||||
return -1;
|
||||
}
|
||||
} else if (chans != 1) {
|
||||
LOG(ALERT) << "Invalid channel combination for device";
|
||||
return -1;
|
||||
|
@ -839,6 +875,8 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
|||
|
||||
if (iface == DIVERSITY)
|
||||
return DIVERSITY;
|
||||
if (iface == MULTI_ARFCN)
|
||||
return MULTI_ARFCN;
|
||||
|
||||
switch (dev_type) {
|
||||
case B100:
|
||||
|
@ -1295,7 +1333,7 @@ double uhd_device::getRxFreq(size_t chan)
|
|||
*/
|
||||
TIMESTAMP uhd_device::initialWriteTimestamp()
|
||||
{
|
||||
if (rx_sps == tx_sps)
|
||||
if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
|
||||
return ts_initial;
|
||||
else
|
||||
return ts_initial * tx_sps;
|
||||
|
|
|
@ -74,6 +74,7 @@ struct trx_config {
|
|||
bool extref;
|
||||
Transceiver::FillerType filler;
|
||||
bool diversity;
|
||||
bool mcbts;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
|
@ -127,7 +128,7 @@ bool testConfig()
|
|||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr, edgestr;
|
||||
std::string refstr, fillstr, divstr, mcstr, edgestr;
|
||||
|
||||
if (!testConfig())
|
||||
return false;
|
||||
|
@ -163,13 +164,29 @@ bool trx_setup_config(struct trx_config *config)
|
|||
config->diversity = DEFAULT_DIVERSITY;
|
||||
}
|
||||
|
||||
/* Diversity only supported on 2 channels */
|
||||
if (config->diversity)
|
||||
if (!config->chans)
|
||||
config->chans = DEFAULT_CHANS;
|
||||
|
||||
if (config->mcbts && ((config->chans < 0) || (config->chans > 5))) {
|
||||
std::cout << "Unsupported number of channels" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Diversity only supported on 2 channels without multi-carrier */
|
||||
if (config->diversity && config->mcbts) {
|
||||
std::cout << "Multi-carrier diversity unsupported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (config->diversity && (config->chans != 2)) {
|
||||
std::cout << "Setting channels to 2 for diversity" << std::endl;
|
||||
config->chans = 2;
|
||||
}
|
||||
|
||||
edgestr = config->edge ? "Enabled" : "Disabled";
|
||||
refstr = config->extref ? "Enabled" : "Disabled";
|
||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||
mcstr = config->mcbts ? "Enabled" : "Disabled";
|
||||
|
||||
switch (config->filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
fillstr = "Dummy bursts";
|
||||
|
@ -200,6 +217,7 @@ bool trx_setup_config(struct trx_config *config)
|
|||
ost << " EDGE support............ " << edgestr << std::endl;
|
||||
ost << " External Reference...... " << refstr << std::endl;
|
||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||
ost << " Multi-Carrier........... " << mcstr << std::endl;
|
||||
ost << " Diversity............... " << divstr << std::endl;
|
||||
ost << " Tuning offset........... " << config->offset << std::endl;
|
||||
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
|
||||
|
@ -235,6 +253,10 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
|||
radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
|
||||
config->chans);
|
||||
break;
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
return NULL;
|
||||
|
@ -309,6 +331,7 @@ static void print_help()
|
|||
" -p Base port number\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -d Enable dual channel diversity receiver\n"
|
||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||
" -x Enable external 10 MHz reference\n"
|
||||
" -s Tx samples-per-symbol (1 or 4)\n"
|
||||
" -b Rx samples-per-symbol (1 or 4)\n"
|
||||
|
@ -334,13 +357,14 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||
config->rach_delay = 0;
|
||||
config->extref = false;
|
||||
config->filler = Transceiver::FILLER_ZERO;
|
||||
config->mcbts = false;
|
||||
config->diversity = false;
|
||||
config->offset = 0.0;
|
||||
config->rssi_offset = 0.0;
|
||||
config->swap_channels = false;
|
||||
config->edge = false;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:b:r:A:R:Se")) != -1) {
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxfo:s:b:r:A:R:Se")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
|
@ -361,6 +385,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||
case 'c':
|
||||
config->chans = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
config->mcbts = true;
|
||||
break;
|
||||
case 'd':
|
||||
config->diversity = true;
|
||||
break;
|
||||
|
@ -403,8 +430,8 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||
}
|
||||
}
|
||||
|
||||
/* Force 4 SPS for EDGE configurations */
|
||||
if (config->edge) {
|
||||
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
|
||||
if ((config->edge) || (config->mcbts)) {
|
||||
config->tx_sps = 4;
|
||||
config->rx_sps = 4;
|
||||
}
|
||||
|
@ -412,7 +439,8 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||
if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
|
||||
config->filler = Transceiver::FILLER_EDGE_RAND;
|
||||
|
||||
if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
|
||||
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
|
||||
(config->rx_sps != 1) && (config->rx_sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
|
||||
print_help();
|
||||
exit(0);
|
||||
|
@ -455,6 +483,9 @@ int main(int argc, char *argv[])
|
|||
srandom(time(NULL));
|
||||
|
||||
/* Create the low level device object */
|
||||
if (config.mcbts)
|
||||
iface = RadioDevice::MULTI_ARFCN;
|
||||
|
||||
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface,
|
||||
config.chans, config.offset);
|
||||
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#endif
|
||||
|
||||
#define GSMRATE (1625e3/6)
|
||||
#define MCBTS_SPACING 800000.0
|
||||
|
||||
/** a 64-bit virtual timestamp for radio data */
|
||||
typedef unsigned long long TIMESTAMP;
|
||||
|
@ -39,6 +40,7 @@ class RadioDevice {
|
|||
NORMAL,
|
||||
RESAMP_64M,
|
||||
RESAMP_100M,
|
||||
MULTI_ARFCN,
|
||||
DIVERSITY,
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "radioClock.h"
|
||||
#include "radioBuffer.h"
|
||||
#include "Resampler.h"
|
||||
#include "Channelizer.h"
|
||||
#include "Synthesis.h"
|
||||
|
||||
static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
|
||||
|
||||
|
@ -101,7 +103,7 @@ public:
|
|||
RadioClock* getClock(void) { return &mClock;};
|
||||
|
||||
/** set transmit frequency */
|
||||
bool tuneTx(double freq, size_t chan = 0);
|
||||
virtual bool tuneTx(double freq, size_t chan = 0);
|
||||
|
||||
/** set receive frequency */
|
||||
virtual bool tuneRx(double freq, size_t chan = 0);
|
||||
|
@ -164,6 +166,34 @@ public:
|
|||
void close();
|
||||
};
|
||||
|
||||
class RadioInterfaceMulti : public RadioInterface {
|
||||
private:
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
std::vector<signalVector *> history;
|
||||
std::vector<bool> active;
|
||||
|
||||
Resampler *dnsampler;
|
||||
Resampler *upsampler;
|
||||
Channelizer *channelizer;
|
||||
Synthesis *synthesis;
|
||||
|
||||
public:
|
||||
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans = 1);
|
||||
~RadioInterfaceMulti();
|
||||
|
||||
bool init(int type);
|
||||
void close();
|
||||
|
||||
bool tuneTx(double freq, size_t chan);
|
||||
bool tuneRx(double freq, size_t chan);
|
||||
double setRxGain(double dB, size_t chan);
|
||||
};
|
||||
|
||||
class RadioInterfaceDiversity : public RadioInterface {
|
||||
public:
|
||||
RadioInterfaceDiversity(RadioDevice* wRadio,
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* Multi-carrier radio interface
|
||||
*
|
||||
* Copyright (C) 2016 Ettus Research LLC
|
||||
*
|
||||
* Author: Tom Tsou <tom.tsou@ettus.com>
|
||||
*
|
||||
* 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"
|
||||
}
|
||||
|
||||
/* Resampling parameters for 64 MHz clocking */
|
||||
#define RESAMP_INRATE 65
|
||||
#define RESAMP_OUTRATE (96 / 2)
|
||||
|
||||
/* Universal resampling parameters */
|
||||
#define NUMCHUNKS 24
|
||||
|
||||
#define MCHANS 4
|
||||
|
||||
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans)
|
||||
: RadioInterface(radio, tx_sps, rx_sps, chans),
|
||||
outerSendBuffer(NULL), outerRecvBuffer(NULL),
|
||||
dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RadioInterfaceMulti::~RadioInterfaceMulti()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void RadioInterfaceMulti::close()
|
||||
{
|
||||
delete outerSendBuffer;
|
||||
delete outerRecvBuffer;
|
||||
delete dnsampler;
|
||||
delete upsampler;
|
||||
delete channelizer;
|
||||
delete synthesis;
|
||||
|
||||
outerSendBuffer = NULL;
|
||||
outerRecvBuffer = NULL;
|
||||
dnsampler = NULL;
|
||||
upsampler = NULL;
|
||||
channelizer = NULL;
|
||||
synthesis = NULL;
|
||||
|
||||
mReceiveFIFO.resize(0);
|
||||
powerScaling.resize(0);
|
||||
history.resize(0);
|
||||
active.resize(0);
|
||||
|
||||
RadioInterface::close();
|
||||
}
|
||||
|
||||
static int getLogicalChan(size_t pchan, size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
case 1:
|
||||
if (pchan == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
if (pchan == 0)
|
||||
return 0;
|
||||
if (pchan == 3)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 3:
|
||||
if (pchan == 1)
|
||||
return 0;
|
||||
if (pchan == 0)
|
||||
return 1;
|
||||
if (pchan == 3)
|
||||
return 2;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int getFreqShift(size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 0;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize I/O specific objects */
|
||||
bool RadioInterfaceMulti::init(int type)
|
||||
{
|
||||
float cutoff = 1.0f;
|
||||
size_t inchunk = 0, outchunk = 0;
|
||||
|
||||
if (mChans > MCHANS - 1) {
|
||||
LOG(ALERT) << "Invalid channel configuration " << mChans;
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
sendBuffer.resize(mChans);
|
||||
recvBuffer.resize(mChans);
|
||||
convertSendBuffer.resize(1);
|
||||
convertRecvBuffer.resize(1);
|
||||
|
||||
mReceiveFIFO.resize(mChans);
|
||||
powerScaling.resize(mChans);
|
||||
history.resize(mChans);
|
||||
active.resize(MCHANS, false);
|
||||
|
||||
inchunk = RESAMP_INRATE * 4;
|
||||
outchunk = RESAMP_OUTRATE * 4;
|
||||
|
||||
if (inchunk * NUMCHUNKS < 625 * 2) {
|
||||
LOG(ALERT) << "Invalid inner chunk size " << inchunk;
|
||||
return false;
|
||||
}
|
||||
|
||||
dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
|
||||
if (!dnsampler->init(1.0)) {
|
||||
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;
|
||||
}
|
||||
|
||||
channelizer = new Channelizer(MCHANS, outchunk);
|
||||
if (!channelizer->init()) {
|
||||
LOG(ALERT) << "Rx channelizer failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
synthesis = new Synthesis(MCHANS, outchunk);
|
||||
if (!synthesis->init()) {
|
||||
LOG(ALERT) << "Tx synthesis filter 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.
|
||||
*/
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
|
||||
upsampler->len(), true);
|
||||
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
|
||||
0, false);
|
||||
history[i] = new signalVector(dnsampler->len());
|
||||
|
||||
synthesis->resetBuffer(i);
|
||||
}
|
||||
|
||||
outerSendBuffer = new signalVector(synthesis->outputLen());
|
||||
outerRecvBuffer = new signalVector(channelizer->inputLen());
|
||||
|
||||
convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
|
||||
convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
|
||||
|
||||
/* Configure channels */
|
||||
switch (mChans) {
|
||||
case 1:
|
||||
active[0] = true;
|
||||
break;
|
||||
case 2:
|
||||
active[0] = true;
|
||||
active[3] = true;
|
||||
break;
|
||||
case 3:
|
||||
active[0] = true;
|
||||
active[1] = true;
|
||||
active[3] = true;
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported channel combination";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceMulti::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
size_t num;
|
||||
float *buf;
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
outerRecvBuffer->size(),
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num != channelizer->inputLen()) {
|
||||
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
|
||||
return;
|
||||
}
|
||||
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
convertRecvBuffer[0], 2 * outerRecvBuffer->size());
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += num;
|
||||
|
||||
channelizer->rotate((float *) outerRecvBuffer->begin(),
|
||||
outerRecvBuffer->size());
|
||||
|
||||
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
|
||||
if (!active[pchan])
|
||||
continue;
|
||||
|
||||
int lchan = getLogicalChan(pchan, mChans);
|
||||
if (lchan < 0) {
|
||||
LOG(ALERT) << "Invalid logical channel " << pchan;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update history by writing into the head portion of the
|
||||
* channelizer output buffer. For this to work, filter length of
|
||||
* the polyphase channelizer partition filter should be equal to
|
||||
* or larger than the resampling filter.
|
||||
*/
|
||||
buf = channelizer->outputBuffer(pchan);
|
||||
size_t cLen = channelizer->outputLen();
|
||||
size_t hLen = dnsampler->len();
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
|
||||
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
|
||||
|
||||
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
|
||||
|
||||
/* Write to the end of the inner receive buffer */
|
||||
if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
|
||||
channelizer->outputLen(),
|
||||
wr_segment,
|
||||
recvBuffer[lchan]->getSegmentLen())) {
|
||||
LOG(ALERT) << "Sample rate upsampling error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a timestamped chunk to the device */
|
||||
bool RadioInterfaceMulti::pushBuffer()
|
||||
{
|
||||
if (sendBuffer[0]->getAvailSegments() <= 0)
|
||||
return false;
|
||||
|
||||
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
|
||||
if (!active[pchan]) {
|
||||
synthesis->resetBuffer(pchan);
|
||||
continue;
|
||||
}
|
||||
|
||||
int lchan = getLogicalChan(pchan, mChans);
|
||||
if (lchan < 0) {
|
||||
LOG(ALERT) << "Invalid logical channel " << pchan;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
|
||||
sendBuffer[lchan]->getSegmentLen(),
|
||||
synthesis->inputBuffer(pchan),
|
||||
synthesis->inputLen())) {
|
||||
LOG(ALERT) << "Sample rate downsampling error";
|
||||
}
|
||||
}
|
||||
|
||||
synthesis->rotate((float *) outerSendBuffer->begin(),
|
||||
outerSendBuffer->size());
|
||||
|
||||
convert_float_short(convertSendBuffer[0],
|
||||
(float *) outerSendBuffer->begin(),
|
||||
1.0 / (float) mChans, 2 * outerSendBuffer->size());
|
||||
|
||||
size_t num = mRadio->writeSamples(convertSendBuffer,
|
||||
outerSendBuffer->size(),
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
if (num != outerSendBuffer->size()) {
|
||||
LOG(ALERT) << "Transmit error " << num;
|
||||
}
|
||||
|
||||
writeTimestamp += num;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Frequency comparison limit */
|
||||
#define FREQ_DELTA_LIMIT 10.0
|
||||
|
||||
static bool fltcmp(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
|
||||
if (!chan)
|
||||
return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
|
||||
|
||||
double center = mRadio->getTxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
|
||||
if (!chan)
|
||||
return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
|
||||
|
||||
double center = mRadio->getRxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
|
||||
{
|
||||
if (!chan)
|
||||
return mRadio->setRxGain(db);
|
||||
else
|
||||
return mRadio->getRxGain();
|
||||
}
|
|
@ -80,7 +80,6 @@ static Complex<float> psk8_table[8] = {
|
|||
#define DOWNSAMPLE_OUT_LEN 156
|
||||
|
||||
static Resampler *dnsampler = NULL;
|
||||
static signalVector *dnsampler_in = NULL;
|
||||
|
||||
/*
|
||||
* RACH and midamble correlation waveforms. Store the buffer separately
|
||||
|
@ -163,7 +162,6 @@ void sigProcLibDestroy()
|
|||
delete GSMPulse1;
|
||||
delete GSMPulse4;
|
||||
delete dnsampler;
|
||||
delete dnsampler_in;
|
||||
|
||||
GMSKRotation1 = NULL;
|
||||
GMSKRotation4 = NULL;
|
||||
|
@ -1936,13 +1934,15 @@ int detectEdgeBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
|||
|
||||
signalVector *downsampleBurst(signalVector &burst)
|
||||
{
|
||||
size_t ilen = DOWNSAMPLE_IN_LEN, olen = DOWNSAMPLE_OUT_LEN;
|
||||
signalVector *in, *out;
|
||||
|
||||
signalVector *out = new signalVector(olen);
|
||||
memcpy(dnsampler_in->begin(), burst.begin(), ilen * 2 * sizeof(float));
|
||||
in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
||||
out = new signalVector(DOWNSAMPLE_OUT_LEN);
|
||||
memcpy(in->begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float));
|
||||
|
||||
dnsampler->rotate((float *) dnsampler_in->begin(), ilen,
|
||||
(float *) out->begin(), olen);
|
||||
dnsampler->rotate((float *) in->begin(), DOWNSAMPLE_IN_LEN,
|
||||
(float *) out->begin(), DOWNSAMPLE_OUT_LEN);
|
||||
delete in;
|
||||
return out;
|
||||
};
|
||||
|
||||
|
@ -2128,8 +2128,6 @@ bool sigProcLibSetup()
|
|||
goto fail;
|
||||
}
|
||||
|
||||
dnsampler_in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
|
|
Loading…
Reference in New Issue