Transceiver52M: Implement POWEROFF command
Add stop and restart capability through the POWEROFF and POWERON commands. Calling stop causes receive streaming to cease, and I/O threads to shutdown leaving only the control handling thread running. Upon receiving a POWERON command, I/O threads and device streaming are restarted. Proper shutdown of the transceiver is now initiated by the destructor, which calls the stop command internally to wind down and deallocate threads. Signed-off-by: Tom Tsou <tom@tsou.cc>
This commit is contained in:
parent
a4d1a41244
commit
eb54bddf47
|
@ -84,42 +84,46 @@ void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::Transceiver(int wBasePort,
|
Transceiver::Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *wTRXAddress,
|
||||||
size_t wSPS, size_t wChans,
|
size_t wSPS, size_t wChans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface)
|
RadioInterface *wRadioInterface)
|
||||||
: mBasePort(wBasePort), mAddr(TRXAddress),
|
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||||
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
|
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
||||||
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mMaxExpectedDelay(0)
|
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
||||||
|
mTxFreq(0.0), mRxFreq(0.0), mMaxExpectedDelay(0)
|
||||||
{
|
{
|
||||||
GSM::Time startTime(random() % gHyperframe,0);
|
|
||||||
|
|
||||||
mRxLowerLoopThread = new Thread(32768);
|
|
||||||
mTxLowerLoopThread = new Thread(32768);
|
|
||||||
|
|
||||||
mTransmitDeadlineClock = startTime;
|
|
||||||
mLastClockUpdateTime = startTime;
|
|
||||||
mLatencyUpdateTime = startTime;
|
|
||||||
mRadioInterface->getClock()->set(startTime);
|
|
||||||
|
|
||||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::~Transceiver()
|
Transceiver::~Transceiver()
|
||||||
{
|
{
|
||||||
|
stop();
|
||||||
|
|
||||||
sigProcLibDestroy();
|
sigProcLibDestroy();
|
||||||
|
|
||||||
delete mClockSocket;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mControlServiceLoopThreads[i]->cancel();
|
||||||
|
mControlServiceLoopThreads[i]->join();
|
||||||
|
delete mControlServiceLoopThreads[i];
|
||||||
|
|
||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
delete mCtrlSockets[i];
|
delete mCtrlSockets[i];
|
||||||
delete mDataSockets[i];
|
delete mDataSockets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize transceiver
|
||||||
|
*
|
||||||
|
* Start or restart the control loop. Any further control is handled through the
|
||||||
|
* socket API. Randomize the central radio clock set the downlink burst
|
||||||
|
* counters. Note that the clock will not update until the radio starts, but we
|
||||||
|
* are still expected to report clock indications through control channel
|
||||||
|
* activity.
|
||||||
|
*/
|
||||||
bool Transceiver::init(bool filler)
|
bool Transceiver::init(bool filler)
|
||||||
{
|
{
|
||||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||||
|
@ -150,8 +154,7 @@ bool Transceiver::init(bool filler)
|
||||||
if (filler)
|
if (filler)
|
||||||
mStates[0].mRetrans = true;
|
mStates[0].mRetrans = true;
|
||||||
|
|
||||||
mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
|
/* Setup sockets */
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
c_srcport = mBasePort + 2 * i + 1;
|
c_srcport = mBasePort + 2 * i + 1;
|
||||||
c_dstport = mBasePort + 2 * i + 101;
|
c_dstport = mBasePort + 2 * i + 101;
|
||||||
|
@ -162,10 +165,19 @@ bool Transceiver::init(bool filler)
|
||||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Randomize the central clock */
|
||||||
|
GSM::Time startTime(random() % gHyperframe, 0);
|
||||||
|
mRadioInterface->getClock()->set(startTime);
|
||||||
|
mTransmitDeadlineClock = startTime;
|
||||||
|
mLastClockUpdateTime = startTime;
|
||||||
|
mLatencyUpdateTime = startTime;
|
||||||
|
|
||||||
|
/* Start control threads */
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||||
mControlServiceLoopThreads[i] = new Thread(32768);
|
mControlServiceLoopThreads[i] = new Thread(32768);
|
||||||
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
|
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
mRxServiceLoopThreads[i] = new Thread(32768);
|
ControlServiceLoopAdapter, (void*) chan);
|
||||||
|
|
||||||
for (size_t n = 0; n < 8; n++) {
|
for (size_t n = 0; n < 8; n++) {
|
||||||
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
|
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
|
||||||
|
@ -178,6 +190,106 @@ bool Transceiver::init(bool filler)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the transceiver
|
||||||
|
*
|
||||||
|
* Submit command(s) to the radio device to commence streaming samples and
|
||||||
|
* launch threads to handle sample I/O. Re-synchronize the transmit burst
|
||||||
|
* counters to the central radio clock here as well.
|
||||||
|
*/
|
||||||
|
bool Transceiver::start()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
|
||||||
|
if (mOn) {
|
||||||
|
LOG(ERR) << "Transceiver already running";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Starting the transceiver";
|
||||||
|
|
||||||
|
GSM::Time time = mRadioInterface->getClock()->get();
|
||||||
|
mTransmitDeadlineClock = time;
|
||||||
|
mLastClockUpdateTime = time;
|
||||||
|
mLatencyUpdateTime = time;
|
||||||
|
|
||||||
|
if (!mRadioInterface->start()) {
|
||||||
|
LOG(ALERT) << "Device failed to start";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Device is running - launch I/O threads */
|
||||||
|
mRxLowerLoopThread = new Thread(32768);
|
||||||
|
mTxLowerLoopThread = new Thread(32768);
|
||||||
|
mTxLowerLoopThread->start((void * (*)(void*))
|
||||||
|
TxLowerLoopAdapter,(void*) this);
|
||||||
|
mRxLowerLoopThread->start((void * (*)(void*))
|
||||||
|
RxLowerLoopAdapter,(void*) this);
|
||||||
|
|
||||||
|
/* Launch uplink and downlink burst processing threads */
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||||
|
mRxServiceLoopThreads[i] = new Thread(32768);
|
||||||
|
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
|
RxUpperLoopAdapter, (void*) chan);
|
||||||
|
|
||||||
|
chan = new TransceiverChannel(this, i);
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
|
TxUpperLoopAdapter, (void*) chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeClockInterface();
|
||||||
|
mOn = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the transceiver
|
||||||
|
*
|
||||||
|
* Perform stopping by disabling receive streaming and issuing cancellation
|
||||||
|
* requests to running threads. Most threads will timeout and terminate once
|
||||||
|
* device is disabled, but the transmit loop may block waiting on the central
|
||||||
|
* UMTS clock. Explicitly signal the clock to make sure that the transmit loop
|
||||||
|
* makes it to the thread cancellation point.
|
||||||
|
*/
|
||||||
|
void Transceiver::stop()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
|
||||||
|
if (!mOn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Stopping the transceiver";
|
||||||
|
mTxLowerLoopThread->cancel();
|
||||||
|
mRxLowerLoopThread->cancel();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mRxServiceLoopThreads[i]->cancel();
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Stopping the device";
|
||||||
|
mRadioInterface->stop();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mRxServiceLoopThreads[i]->join();
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->join();
|
||||||
|
delete mRxServiceLoopThreads[i];
|
||||||
|
delete mTxPriorityQueueServiceLoopThreads[i];
|
||||||
|
|
||||||
|
mTxPriorityQueues[i].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
mTxLowerLoopThread->join();
|
||||||
|
mRxLowerLoopThread->join();
|
||||||
|
delete mTxLowerLoopThread;
|
||||||
|
delete mRxLowerLoopThread;
|
||||||
|
|
||||||
|
mOn = false;
|
||||||
|
LOG(NOTICE) << "Transceiver stopped";
|
||||||
|
}
|
||||||
|
|
||||||
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
||||||
int RSSI, GSM::Time &wTime)
|
int RSSI, GSM::Time &wTime)
|
||||||
{
|
{
|
||||||
|
@ -525,17 +637,6 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::start()
|
|
||||||
{
|
|
||||||
TransceiverChannel *chan;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
|
|
||||||
chan = new TransceiverChannel(this, i);
|
|
||||||
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
ControlServiceLoopAdapter, (void*) chan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transceiver::reset()
|
void Transceiver::reset()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
||||||
|
@ -574,39 +675,14 @@ void Transceiver::driveControl(size_t chan)
|
||||||
LOG(INFO) << "command is " << buffer;
|
LOG(INFO) << "command is " << buffer;
|
||||||
|
|
||||||
if (strcmp(command,"POWEROFF")==0) {
|
if (strcmp(command,"POWEROFF")==0) {
|
||||||
// turn off transmitter/demod
|
stop();
|
||||||
sprintf(response,"RSP POWEROFF 0");
|
sprintf(response,"RSP POWEROFF 0");
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"POWERON")==0) {
|
else if (strcmp(command,"POWERON")==0) {
|
||||||
// turn on transmitter/demod
|
if (!start())
|
||||||
if (!mTxFreq || !mRxFreq)
|
|
||||||
sprintf(response,"RSP POWERON 1");
|
sprintf(response,"RSP POWERON 1");
|
||||||
else {
|
else
|
||||||
sprintf(response,"RSP POWERON 0");
|
sprintf(response,"RSP POWERON 0");
|
||||||
if (!chan && !mOn) {
|
|
||||||
// Prepare for thread start
|
|
||||||
mRadioInterface->start();
|
|
||||||
|
|
||||||
// Start radio interface threads.
|
|
||||||
mTxLowerLoopThread->start((void * (*)(void*))
|
|
||||||
TxLowerLoopAdapter,(void*) this);
|
|
||||||
mRxLowerLoopThread->start((void * (*)(void*))
|
|
||||||
RxLowerLoopAdapter,(void*) this);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
|
||||||
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
|
||||||
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
RxUpperLoopAdapter, (void*) chan);
|
|
||||||
|
|
||||||
chan = new TransceiverChannel(this, i);
|
|
||||||
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
TxUpperLoopAdapter, (void*) chan);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeClockInterface();
|
|
||||||
mOn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"SETMAXDLY")==0) {
|
else if (strcmp(command,"SETMAXDLY")==0) {
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
|
@ -855,7 +931,7 @@ void Transceiver::writeClockInterface()
|
||||||
|
|
||||||
LOG(INFO) << "ClockInterface: sending " << command;
|
LOG(INFO) << "ClockInterface: sending " << command;
|
||||||
|
|
||||||
mClockSocket->write(command, strlen(command) + 1);
|
mClockSocket.write(command, strlen(command) + 1);
|
||||||
|
|
||||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
mLastClockUpdateTime = mTransmitDeadlineClock;
|
||||||
|
|
||||||
|
@ -923,15 +999,7 @@ void *TxUpperLoopAdapter(TransceiverChannel *chan)
|
||||||
trx->setPriority(0.40);
|
trx->setPriority(0.40);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
bool stale = false;
|
trx->driveTxPriorityQueue(num);
|
||||||
// Flush the UDP packets until a successful transfer.
|
|
||||||
while (!trx->driveTxPriorityQueue(num)) {
|
|
||||||
stale = true;
|
|
||||||
}
|
|
||||||
if (!num && stale) {
|
|
||||||
// If a packet was stale, remind the GSM stack of the clock.
|
|
||||||
trx->writeClockInterface();
|
|
||||||
}
|
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -91,12 +91,10 @@ class Transceiver {
|
||||||
private:
|
private:
|
||||||
int mBasePort;
|
int mBasePort;
|
||||||
std::string mAddr;
|
std::string mAddr;
|
||||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
|
||||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
|
||||||
|
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
UDPSocket *mClockSocket; ///< socket for writing clock updates to GSM core
|
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||||
|
|
||||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||||
|
@ -107,6 +105,8 @@ private:
|
||||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||||
|
|
||||||
|
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||||
|
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||||
|
|
||||||
|
@ -173,6 +173,13 @@ private:
|
||||||
|
|
||||||
std::vector<TransceiverState> mStates;
|
std::vector<TransceiverState> mStates;
|
||||||
|
|
||||||
|
/** Start and stop I/O threads through the control socket API */
|
||||||
|
bool start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/** Protect destructor accessable stop call */
|
||||||
|
Mutex mLock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Transceiver constructor
|
/** Transceiver constructor
|
||||||
|
@ -191,8 +198,7 @@ public:
|
||||||
/** Destructor */
|
/** Destructor */
|
||||||
~Transceiver();
|
~Transceiver();
|
||||||
|
|
||||||
/** start the Transceiver */
|
/** Start the control loop */
|
||||||
void start();
|
|
||||||
bool init(bool filler);
|
bool init(bool filler);
|
||||||
|
|
||||||
/** attach the radioInterface receive FIFO */
|
/** attach the radioInterface receive FIFO */
|
||||||
|
|
|
@ -40,6 +40,14 @@
|
||||||
#define TX_AMPL 0.3
|
#define TX_AMPL 0.3
|
||||||
#define SAMPLE_BUF_SZ (1 << 20)
|
#define SAMPLE_BUF_SZ (1 << 20)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UHD timeout value on streaming (re)start
|
||||||
|
*
|
||||||
|
* Allow some time for streaming to commence after the start command is issued,
|
||||||
|
* but consider a wait beyond one second to be a definite error condition.
|
||||||
|
*/
|
||||||
|
#define UHD_RESTART_TIMEOUT 1.0
|
||||||
|
|
||||||
enum uhd_dev_type {
|
enum uhd_dev_type {
|
||||||
USRP1,
|
USRP1,
|
||||||
USRP2,
|
USRP2,
|
||||||
|
@ -268,7 +276,7 @@ public:
|
||||||
int open(const std::string &args, bool extref);
|
int open(const std::string &args, bool extref);
|
||||||
bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
void restart();
|
bool restart();
|
||||||
void setPriority(float prio);
|
void setPriority(float prio);
|
||||||
enum TxWindowType getWindowType() { return tx_window; }
|
enum TxWindowType getWindowType() { return tx_window; }
|
||||||
|
|
||||||
|
@ -313,8 +321,9 @@ public:
|
||||||
|
|
||||||
enum err_code {
|
enum err_code {
|
||||||
ERROR_TIMING = -1,
|
ERROR_TIMING = -1,
|
||||||
ERROR_UNRECOVERABLE = -2,
|
ERROR_TIMEOUT = -2,
|
||||||
ERROR_UNHANDLED = -3,
|
ERROR_UNRECOVERABLE = -3,
|
||||||
|
ERROR_UNHANDLED = -4,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -358,7 +367,7 @@ private:
|
||||||
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
||||||
bool set_freq(double freq, size_t chan, bool tx);
|
bool set_freq(double freq, size_t chan, bool tx);
|
||||||
|
|
||||||
Thread async_event_thrd;
|
Thread *async_event_thrd;
|
||||||
bool diversity;
|
bool diversity;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -396,6 +405,12 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void thread_enable_cancel(bool cancel)
|
||||||
|
{
|
||||||
|
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
||||||
: tx_gain_min(0.0), tx_gain_max(0.0),
|
: tx_gain_min(0.0), tx_gain_max(0.0),
|
||||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||||
|
@ -727,7 +742,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||||
{
|
{
|
||||||
uhd::rx_metadata_t md;
|
uhd::rx_metadata_t md;
|
||||||
size_t num_smpls;
|
size_t num_smpls;
|
||||||
float timeout = 0.1f;
|
float timeout = UHD_RESTART_TIMEOUT;
|
||||||
|
|
||||||
std::vector<std::vector<short> >
|
std::vector<std::vector<short> >
|
||||||
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
|
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
|
||||||
|
@ -743,6 +758,8 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||||
if (!num_smpls) {
|
if (!num_smpls) {
|
||||||
switch (md.error_code) {
|
switch (md.error_code) {
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||||
|
LOG(ALERT) << "Device timed out";
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -756,7 +773,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uhd_device::restart()
|
bool uhd_device::restart()
|
||||||
{
|
{
|
||||||
/* Allow 100 ms delay to align multi-channel streams */
|
/* Allow 100 ms delay to align multi-channel streams */
|
||||||
double delay = 0.1;
|
double delay = 0.1;
|
||||||
|
@ -771,7 +788,7 @@ void uhd_device::restart()
|
||||||
|
|
||||||
usrp_dev->issue_stream_cmd(cmd);
|
usrp_dev->issue_stream_cmd(cmd);
|
||||||
|
|
||||||
flush_recv(1);
|
return flush_recv(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uhd_device::start()
|
bool uhd_device::start()
|
||||||
|
@ -787,10 +804,12 @@ bool uhd_device::start()
|
||||||
uhd::msg::register_handler(&uhd_msg_handler);
|
uhd::msg::register_handler(&uhd_msg_handler);
|
||||||
|
|
||||||
// Start asynchronous event (underrun check) loop
|
// Start asynchronous event (underrun check) loop
|
||||||
async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
|
async_event_thrd = new Thread();
|
||||||
|
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||||
|
|
||||||
// Start streaming
|
// Start streaming
|
||||||
restart();
|
if (!restart())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Display usrp time
|
// Display usrp time
|
||||||
double time_now = usrp_dev->get_time_now().get_real_secs();
|
double time_now = usrp_dev->get_time_now().get_real_secs();
|
||||||
|
@ -810,6 +829,10 @@ bool uhd_device::stop()
|
||||||
|
|
||||||
usrp_dev->issue_stream_cmd(stream_cmd);
|
usrp_dev->issue_stream_cmd(stream_cmd);
|
||||||
|
|
||||||
|
async_event_thrd->cancel();
|
||||||
|
async_event_thrd->join();
|
||||||
|
delete async_event_thrd;
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -830,6 +853,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||||
switch (md.error_code) {
|
switch (md.error_code) {
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||||
LOG(ALERT) << "UHD: Receive timed out";
|
LOG(ALERT) << "UHD: Receive timed out";
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
||||||
|
@ -899,8 +923,11 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||||
|
|
||||||
// Receive samples from the usrp until we have enough
|
// Receive samples from the usrp until we have enough
|
||||||
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
||||||
|
thread_enable_cancel(false);
|
||||||
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
|
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
|
||||||
metadata, 0.1, true);
|
metadata, 0.1, true);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
rx_pkt_cnt++;
|
rx_pkt_cnt++;
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
|
@ -910,6 +937,9 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||||
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
||||||
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
case ERROR_TIMEOUT:
|
||||||
|
// Assume stopping condition
|
||||||
|
return 0;
|
||||||
case ERROR_TIMING:
|
case ERROR_TIMING:
|
||||||
restart();
|
restart();
|
||||||
case ERROR_UNHANDLED:
|
case ERROR_UNHANDLED:
|
||||||
|
@ -988,7 +1018,10 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_enable_cancel(false);
|
||||||
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
if (num_smpls != (unsigned) len) {
|
if (num_smpls != (unsigned) len) {
|
||||||
LOG(ALERT) << "UHD: Device send timed out";
|
LOG(ALERT) << "UHD: Device send timed out";
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1157,11 @@ double uhd_device::getRxFreq(size_t chan)
|
||||||
bool uhd_device::recv_async_msg()
|
bool uhd_device::recv_async_msg()
|
||||||
{
|
{
|
||||||
uhd::async_metadata_t md;
|
uhd::async_metadata_t md;
|
||||||
if (!usrp_dev->get_device()->recv_async_msg(md))
|
|
||||||
|
thread_enable_cancel(false);
|
||||||
|
bool rc = usrp_dev->get_device()->recv_async_msg(md);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
if (!rc)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Assume that any error requires resynchronization
|
// Assume that any error requires resynchronization
|
||||||
|
|
|
@ -391,8 +391,6 @@ int main(int argc, char *argv[])
|
||||||
if (!trx)
|
if (!trx)
|
||||||
goto shutdown;
|
goto shutdown;
|
||||||
|
|
||||||
trx->start();
|
|
||||||
|
|
||||||
chans = trx->numChans();
|
chans = trx->numChans();
|
||||||
std::cout << "-- Transceiver active with "
|
std::cout << "-- Transceiver active with "
|
||||||
<< chans << " channel(s)" << std::endl;
|
<< chans << " channel(s)" << std::endl;
|
||||||
|
|
|
@ -23,32 +23,27 @@
|
||||||
|
|
||||||
void RadioClock::set(const GSM::Time& wTime)
|
void RadioClock::set(const GSM::Time& wTime)
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
mClock = wTime;
|
mClock = wTime;
|
||||||
updateSignal.signal();
|
updateSignal.signal();
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioClock::incTN()
|
void RadioClock::incTN()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
mClock.incTN();
|
mClock.incTN();
|
||||||
updateSignal.signal();
|
updateSignal.signal();
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSM::Time RadioClock::get()
|
GSM::Time RadioClock::get()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
GSM::Time retVal = mClock;
|
GSM::Time retVal = mClock;
|
||||||
mLock.unlock();
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioClock::wait()
|
void RadioClock::wait()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
updateSignal.wait(mLock,1);
|
updateSignal.wait(mLock,1);
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,15 +171,20 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
||||||
return mRadio->setRxFreq(freq, chan);
|
return mRadio->setRxFreq(freq, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RadioInterface::start()
|
||||||
void RadioInterface::start()
|
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Starting radio";
|
if (mOn)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG(INFO) << "Starting radio device";
|
||||||
#ifdef USRP1
|
#ifdef USRP1
|
||||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||||
(void*)this);
|
(void*)this);
|
||||||
#endif
|
#endif
|
||||||
mRadio->start();
|
|
||||||
|
if (!mRadio->start())
|
||||||
|
return false;
|
||||||
|
|
||||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||||
readTimestamp = mRadio->initialReadTimestamp();
|
readTimestamp = mRadio->initialReadTimestamp();
|
||||||
|
|
||||||
|
@ -188,6 +193,23 @@ void RadioInterface::start()
|
||||||
|
|
||||||
mOn = true;
|
mOn = true;
|
||||||
LOG(INFO) << "Radio started";
|
LOG(INFO) << "Radio started";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the radio device
|
||||||
|
*
|
||||||
|
* This is a pass-through call to the device interface. Because the underlying
|
||||||
|
* stop command issuance generally doesn't return confirmation on device status,
|
||||||
|
* this call will only return false if the device is already stopped.
|
||||||
|
*/
|
||||||
|
bool RadioInterface::stop()
|
||||||
|
{
|
||||||
|
if (!mOn || !mRadio->stop())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mOn = false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USRP1
|
#ifdef USRP1
|
||||||
|
|
|
@ -78,7 +78,8 @@ private:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** start the interface */
|
/** start the interface */
|
||||||
void start();
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
/** intialization */
|
/** intialization */
|
||||||
virtual bool init(int type);
|
virtual bool init(int type);
|
||||||
|
|
Loading…
Reference in New Issue