2011-10-12 07:44:40 +00:00
|
|
|
/*
|
2012-03-08 00:55:59 +00:00
|
|
|
* Copyright 2008, 2009, 2012 Free Software Foundation, Inc.
|
2011-10-12 07:44:40 +00:00
|
|
|
*
|
|
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
|
|
* See the COPYING file in the main directory for details.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "radioInterface.h"
|
|
|
|
#include <Logger.h>
|
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
bool started = false;
|
|
|
|
|
2013-04-08 18:41:11 +00:00
|
|
|
/* Device side buffers */
|
2013-04-26 23:30:55 +00:00
|
|
|
static short *rx_buf[CHAN_MAX];
|
|
|
|
static short *tx_buf[CHAN_MAX];
|
2013-04-08 18:41:11 +00:00
|
|
|
|
|
|
|
/* Complex float to short conversion */
|
|
|
|
static void floatToShort(short *out, float *in, int num)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
|
|
out[2 * i + 0] = (short) in[2 * i + 0];
|
|
|
|
out[2 * i + 1] = (short) in[2 * i + 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Complex short to float conversion */
|
|
|
|
static void shortToFloat(float *out, short *in, int num)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
|
|
out[2 * i + 0] = (float) in[2 * i + 0];
|
|
|
|
out[2 * i + 1] = (float) in[2 * i + 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
RadioInterface::RadioInterface(RadioDevice *wRadio,
|
2012-06-06 00:31:28 +00:00
|
|
|
int wChanM,
|
2013-04-08 17:45:55 +00:00
|
|
|
int wSPS,
|
2013-04-26 23:30:55 +00:00
|
|
|
int wReceiveOffset,
|
2011-10-12 07:44:40 +00:00
|
|
|
GSM::Time wStartTime)
|
2012-06-06 00:31:28 +00:00
|
|
|
: mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
|
2013-04-26 23:30:55 +00:00
|
|
|
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
|
|
|
|
powerScaling(1.0), loadTest(false)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
mClock.set(wStartTime);
|
|
|
|
}
|
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
RadioInterface::~RadioInterface(void)
|
|
|
|
{
|
2012-03-17 00:26:46 +00:00
|
|
|
if (mOn) {
|
|
|
|
mRadio->stop();
|
|
|
|
close();
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
for (int i = 0; i < mChanM; i++) {
|
2012-03-17 00:26:46 +00:00
|
|
|
if (rcvBuffer[i] != NULL)
|
|
|
|
delete rcvBuffer[i];
|
|
|
|
if (sendBuffer[i] != NULL)
|
|
|
|
delete sendBuffer[i];
|
|
|
|
}
|
2012-03-08 00:55:59 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double RadioInterface::fullScaleInputValue(void) {
|
|
|
|
return mRadio->fullScaleInputValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
double RadioInterface::fullScaleOutputValue(void) {
|
|
|
|
return mRadio->fullScaleOutputValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-13 16:29:08 +00:00
|
|
|
void RadioInterface::setPowerAttenuation(double atten, int chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2011-11-26 03:18:11 +00:00
|
|
|
double rfGain, digAtten;
|
2011-11-26 03:18:05 +00:00
|
|
|
|
2013-07-13 16:29:08 +00:00
|
|
|
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
|
2011-11-26 03:18:11 +00:00
|
|
|
digAtten = atten - mRadio->maxTxGain() + rfGain;
|
2011-11-26 03:18:05 +00:00
|
|
|
|
|
|
|
if (digAtten < 1.0)
|
|
|
|
powerScaling = 1.0;
|
|
|
|
else
|
|
|
|
powerScaling = 1.0/sqrt(pow(10, (digAtten/10.0)));
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
int RadioInterface::radioifyVector(signalVector &wVector,
|
|
|
|
float *retVector,
|
|
|
|
float scale,
|
|
|
|
bool zero)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2011-11-26 03:18:34 +00:00
|
|
|
int i;
|
2011-10-12 07:44:40 +00:00
|
|
|
signalVector::iterator itr = wVector.begin();
|
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
if (zero) {
|
|
|
|
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
|
|
|
|
return wVector.size();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
for (i = 0; i < wVector.size(); i++) {
|
|
|
|
retVector[2 * i + 0] = itr->real() * scale;
|
|
|
|
retVector[2 * i + 1] = itr->imag() * scale;
|
|
|
|
itr++;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
return wVector.size();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
int RadioInterface::unRadioifyVector(float *floatVector, int offset,
|
|
|
|
signalVector &newVector)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2011-11-26 03:18:34 +00:00
|
|
|
int i;
|
|
|
|
signalVector::iterator itr = newVector.begin();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
for (i = 0; i < newVector.size(); i++) {
|
2012-03-08 00:55:59 +00:00
|
|
|
*itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
|
|
|
|
floatVector[offset + 2 * i + 1]);
|
2011-11-26 03:18:34 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
return newVector.size();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
bool RadioInterface::tuneTx(double freq, int chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-04-26 23:30:55 +00:00
|
|
|
return mRadio->setTxFreq(freq, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
bool RadioInterface::tuneRx(double freq, int chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-04-26 23:30:55 +00:00
|
|
|
return mRadio->setRxFreq(freq, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
bool RadioInterface::start()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2012-03-08 00:55:59 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (mOn)
|
|
|
|
return false;
|
|
|
|
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mOn = true;
|
2013-04-08 17:35:36 +00:00
|
|
|
#ifdef USRP1
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mAlignRadioServiceLoopThread = new Thread(32768);
|
|
|
|
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
|
|
|
(void*)this);
|
2013-04-08 17:35:36 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
|
|
|
readTimestamp = mRadio->initialReadTimestamp();
|
2012-06-06 00:31:28 +00:00
|
|
|
for (i = 0; i < mChanM; i++) {
|
2012-03-08 00:55:59 +00:00
|
|
|
sendBuffer[i] = new float[8*2*INCHUNK];
|
|
|
|
rcvBuffer[i] = new float[8*2*OUTCHUNK];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init I/O specific variables if applicable */
|
|
|
|
init();
|
|
|
|
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mRadio->start();
|
|
|
|
LOG(DEBUG) << "Radio started";
|
|
|
|
mRadio->updateAlignment(writeTimestamp-10000);
|
|
|
|
mRadio->updateAlignment(writeTimestamp-10000);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
return true;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2012-03-17 19:02:32 +00:00
|
|
|
bool RadioInterface::stop()
|
|
|
|
{
|
|
|
|
if (!mOn)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mOn = false;
|
|
|
|
mRadio->stop();
|
|
|
|
}
|
|
|
|
|
2013-04-08 17:35:36 +00:00
|
|
|
#ifdef USRP1
|
2011-10-12 07:44:40 +00:00
|
|
|
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
|
|
|
|
{
|
2012-03-17 19:02:32 +00:00
|
|
|
while (radioInterface->on()) {
|
2011-10-12 07:44:40 +00:00
|
|
|
radioInterface->alignRadio();
|
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RadioInterface::alignRadio() {
|
|
|
|
sleep(60);
|
|
|
|
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
|
|
|
|
}
|
2013-04-08 17:35:36 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
|
|
|
|
{
|
|
|
|
int i;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
if (!mOn)
|
|
|
|
return;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-06-06 00:31:28 +00:00
|
|
|
for (i = 0; i < mChanM; i++) {
|
2013-04-26 23:30:55 +00:00
|
|
|
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
|
|
|
|
powerScaling, zeroBurst[i]);
|
2012-03-08 00:55:59 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
/*
|
|
|
|
* All bursts should be the same size since all transceivers are
|
|
|
|
* tied with a single clock in the radio interface.
|
|
|
|
*/
|
|
|
|
sendCursor += radioBurst[0]->size();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
pushBuffer();
|
|
|
|
}
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
|
2012-03-08 00:55:59 +00:00
|
|
|
{
|
2013-04-26 23:30:55 +00:00
|
|
|
for (int i = 0; i < chanM; i++)
|
2012-03-08 00:55:59 +00:00
|
|
|
memmove(buf[i], buf[i] + offset, sizeof(float) * len);
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-08 00:55:59 +00:00
|
|
|
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
|
|
|
|
int idx, GSM::Time rxClock)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2012-06-06 00:31:28 +00:00
|
|
|
for (i = 0; i < mChanM; i++) {
|
2013-04-26 23:30:55 +00:00
|
|
|
signalVector rxVector(samplesPerBurst);
|
|
|
|
unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
|
|
|
|
radioVector *rxBurst = new radioVector(rxVector, rxClock);
|
|
|
|
mReceiveFIFO[i].write(rxBurst);
|
2012-03-08 00:55:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RadioInterface::driveReceiveRadio()
|
|
|
|
{
|
|
|
|
if (!mOn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (mReceiveFIFO[0].size() > 8)
|
|
|
|
return;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
pullBuffer();
|
|
|
|
|
|
|
|
GSM::Time rcvClock = mClock.get();
|
|
|
|
rcvClock.decTN(receiveOffset);
|
|
|
|
unsigned tN = rcvClock.TN();
|
2011-11-26 03:18:34 +00:00
|
|
|
int rcvSz = rcvCursor;
|
2011-10-12 07:44:40 +00:00
|
|
|
int readSz = 0;
|
|
|
|
const int symbolsPerSlot = gSlotLen + 8;
|
2012-03-08 00:55:59 +00:00
|
|
|
int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
// while there's enough data in receive buffer, form received
|
|
|
|
// GSM bursts and pass up to Transceiver
|
|
|
|
// Using the 157-156-156-156 symbols per timeslot format.
|
2012-03-08 00:55:59 +00:00
|
|
|
while (rcvSz >= samplesPerBurst) {
|
2011-10-12 07:44:40 +00:00
|
|
|
if (rcvClock.FN() >= 0) {
|
2012-03-08 00:55:59 +00:00
|
|
|
loadVectors(tN, samplesPerBurst, readSz, rcvClock);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2012-03-08 00:55:59 +00:00
|
|
|
|
|
|
|
mClock.incTN();
|
2011-10-12 07:44:40 +00:00
|
|
|
rcvClock.incTN();
|
2012-03-08 00:55:59 +00:00
|
|
|
|
|
|
|
readSz += samplesPerBurst;
|
|
|
|
rcvSz -= samplesPerBurst;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
tN = rcvClock.TN();
|
2012-03-08 00:55:59 +00:00
|
|
|
samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2011-11-26 03:18:34 +00:00
|
|
|
if (readSz > 0) {
|
|
|
|
rcvCursor -= readSz;
|
2013-04-26 23:30:55 +00:00
|
|
|
shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2011-11-26 03:18:46 +00:00
|
|
|
}
|
|
|
|
|
2013-07-13 16:29:08 +00:00
|
|
|
double RadioInterface::setRxGain(double dB, int chan)
|
2011-11-26 03:18:46 +00:00
|
|
|
{
|
|
|
|
if (mRadio)
|
2013-07-13 16:29:08 +00:00
|
|
|
return mRadio->setRxGain(dB, chan);
|
2011-11-26 03:18:46 +00:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-13 16:29:08 +00:00
|
|
|
double RadioInterface::getRxGain(int chan)
|
2011-11-26 03:18:46 +00:00
|
|
|
{
|
|
|
|
if (mRadio)
|
2013-07-13 16:29:08 +00:00
|
|
|
return mRadio->getRxGain(chan);
|
2011-11-26 03:18:46 +00:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2013-04-08 18:41:11 +00:00
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
bool RadioInterface::init()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < CHAN_MAX; i++) {
|
|
|
|
rx_buf[i] = new short[2 * OUTCHUNK];
|
|
|
|
tx_buf[i] = new short[4 * 2 * INCHUNK];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RadioInterface::close()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < CHAN_MAX; i++) {
|
|
|
|
delete rx_buf[i];
|
|
|
|
delete tx_buf[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-08 18:41:11 +00:00
|
|
|
/* Receive a timestamped chunk from the device */
|
|
|
|
void RadioInterface::pullBuffer()
|
|
|
|
{
|
|
|
|
bool local_underrun;
|
|
|
|
|
|
|
|
/* Read samples. Fail if we don't get what we want. */
|
2013-04-26 23:30:55 +00:00
|
|
|
int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
|
2013-04-08 18:41:11 +00:00
|
|
|
|
|
|
|
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
|
|
|
|
assert(num_rd == OUTCHUNK);
|
|
|
|
|
|
|
|
underrun |= local_underrun;
|
|
|
|
readTimestamp += (TIMESTAMP) num_rd;
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
for (int i = 0; i < mChanM; i++)
|
|
|
|
shortToFloat(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd);
|
|
|
|
|
2013-04-08 18:41:11 +00:00
|
|
|
rcvCursor += num_rd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send timestamped chunk to the device with arbitrary size */
|
|
|
|
void RadioInterface::pushBuffer()
|
|
|
|
{
|
|
|
|
if (sendCursor < INCHUNK)
|
|
|
|
return;
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
for (int i = 0; i < mChanM; i++)
|
|
|
|
floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
|
2013-04-08 18:41:11 +00:00
|
|
|
|
|
|
|
/* Write samples. Fail if we don't get what we want. */
|
2013-04-26 23:30:55 +00:00
|
|
|
int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
|
|
|
|
writeTimestamp, &underrun);
|
2013-04-08 18:41:11 +00:00
|
|
|
assert(num_smpls == sendCursor);
|
|
|
|
|
|
|
|
writeTimestamp += (TIMESTAMP) num_smpls;
|
|
|
|
sendCursor = 0;
|
|
|
|
}
|