2011-10-12 07:44:40 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
|
|
|
*
|
2019-07-22 10:05:52 +00:00
|
|
|
* SPDX-License-Identifier: GPL-3.0+
|
|
|
|
*
|
2011-10-12 07:44:40 +00:00
|
|
|
* This software is distributed under the terms of the GNU 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 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2019-08-07 10:45:53 +00:00
|
|
|
#include <netinet/in.h>
|
2015-06-04 03:47:56 +00:00
|
|
|
#include <iomanip> // std::setprecision
|
2015-06-12 04:15:31 +00:00
|
|
|
#include <fstream>
|
2011-10-12 07:44:40 +00:00
|
|
|
#include "Transceiver.h"
|
|
|
|
#include <Logger.h>
|
|
|
|
|
2018-09-03 14:50:49 +00:00
|
|
|
extern "C" {
|
|
|
|
#include "osmo_signal.h"
|
2019-06-28 11:27:24 +00:00
|
|
|
#include "proto_trxd.h"
|
|
|
|
|
2019-07-01 18:41:55 +00:00
|
|
|
#include <osmocom/core/utils.h>
|
2019-07-01 17:03:49 +00:00
|
|
|
#include <osmocom/core/socket.h>
|
2019-07-15 16:53:08 +00:00
|
|
|
#include <osmocom/core/bits.h>
|
2018-09-03 14:50:49 +00:00
|
|
|
}
|
|
|
|
|
2012-08-08 00:51:31 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2013-06-16 10:30:58 +00:00
|
|
|
using namespace GSM;
|
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
Transceiver *transceiver;
|
|
|
|
|
Alexander's patches:
1)I did an experiment and compiled OpenBTS with clang yesterday, which
immediately highlighted two potential bugs in the Transceiver52 code.
I'm not sure they are indeed bugs and not the intended behavior, but
they look very much like that. The first one is below and the second
one is in the following mail.
GSM::Time() arguments are defined like #define USB_LATENCY_INTRVL
(10,0), which means that they are expanded into GSM::Time((10,0)).
This expression is a GSM::Time() with a single parameter where (10,0)
return value of the last argument, 0 in this case. I.e.
GSM::Time((10,0)) is equivalent to GSM::Time(0). I think this was not
the intention.
2) Printing \n after every complex number breaks output when you want to
print it in a single line, e.g. in many debug output.
I do not claim any copyright over this change, as it's very basic.
Looking forward to see it merged into mainline.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4515 19bc5d8c-e614-43d4-8b26-e1612bc8e597
2012-11-23 08:37:32 +00:00
|
|
|
#define USB_LATENCY_INTRVL 10,0
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-18 01:23:34 +00:00
|
|
|
/* Number of running values use in noise average */
|
|
|
|
#define NOISE_CNT 20
|
2012-12-06 15:43:55 +00:00
|
|
|
|
2013-10-29 19:55:47 +00:00
|
|
|
TransceiverState::TransceiverState()
|
2014-11-25 23:46:56 +00:00
|
|
|
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
|
2013-10-29 19:55:47 +00:00
|
|
|
{
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
chanType[i] = Transceiver::NONE;
|
|
|
|
fillerModulus[i] = 26;
|
|
|
|
chanResponse[i] = NULL;
|
|
|
|
DFEForward[i] = NULL;
|
|
|
|
DFEFeedback[i] = NULL;
|
|
|
|
|
|
|
|
for (int n = 0; n < 102; n++)
|
|
|
|
fillerTable[n][i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TransceiverState::~TransceiverState()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
delete chanResponse[i];
|
|
|
|
delete DFEForward[i];
|
|
|
|
delete DFEFeedback[i];
|
|
|
|
|
|
|
|
for (int n = 0; n < 102; n++)
|
|
|
|
delete fillerTable[n][i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-21 13:59:19 +00:00
|
|
|
bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
2015-05-20 01:26:31 +00:00
|
|
|
{
|
|
|
|
signalVector *burst;
|
|
|
|
|
|
|
|
if ((sps != 1) && (sps != 4))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (size_t n = 0; n < 8; n++) {
|
|
|
|
for (size_t i = 0; i < 102; i++) {
|
|
|
|
switch (filler) {
|
2018-02-21 13:59:19 +00:00
|
|
|
case FILLER_DUMMY:
|
2016-03-07 04:57:34 +00:00
|
|
|
burst = generateDummyBurst(sps, n);
|
2015-05-20 01:26:31 +00:00
|
|
|
break;
|
2018-02-21 13:59:19 +00:00
|
|
|
case FILLER_NORM_RAND:
|
2016-03-07 04:57:34 +00:00
|
|
|
burst = genRandNormalBurst(rtsc, sps, n);
|
2015-05-20 01:26:31 +00:00
|
|
|
break;
|
2018-02-21 13:59:19 +00:00
|
|
|
case FILLER_EDGE_RAND:
|
2016-03-07 06:19:15 +00:00
|
|
|
burst = generateEdgeBurst(rtsc);
|
|
|
|
break;
|
2018-02-21 13:59:19 +00:00
|
|
|
case FILLER_ACCESS_RAND:
|
2016-03-25 15:28:34 +00:00
|
|
|
burst = genRandAccessBurst(rach_delay, sps, n);
|
2016-03-23 14:06:32 +00:00
|
|
|
break;
|
2018-02-21 13:59:19 +00:00
|
|
|
case FILLER_ZERO:
|
2015-05-20 01:26:31 +00:00
|
|
|
default:
|
2016-03-07 04:57:34 +00:00
|
|
|
burst = generateEmptyBurst(sps, n);
|
2015-05-20 01:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
scaleVector(*burst, scale);
|
|
|
|
fillerTable[i][n] = burst;
|
|
|
|
}
|
|
|
|
|
2018-02-21 13:59:19 +00:00
|
|
|
if ((filler == FILLER_NORM_RAND) ||
|
|
|
|
(filler == FILLER_EDGE_RAND)) {
|
2017-03-17 22:00:34 +00:00
|
|
|
chanType[n] = TSC;
|
2016-03-07 06:19:15 +00:00
|
|
|
}
|
2014-01-25 07:34:03 +00:00
|
|
|
}
|
2015-05-20 01:26:31 +00:00
|
|
|
|
|
|
|
return false;
|
2013-10-29 19:55:47 +00:00
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
Transceiver::Transceiver(int wBasePort,
|
2017-08-16 14:53:23 +00:00
|
|
|
const char *TRXAddress,
|
|
|
|
const char *GSMcoreAddress,
|
2016-03-06 09:28:40 +00:00
|
|
|
size_t tx_sps, size_t rx_sps, size_t chans,
|
2015-06-04 03:47:56 +00:00
|
|
|
GSM::Time wTransmitLatency,
|
|
|
|
RadioInterface *wRadioInterface,
|
2019-06-17 11:02:44 +00:00
|
|
|
double wRssiOffset, int wStackSize)
|
2017-08-16 14:53:23 +00:00
|
|
|
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
2019-07-01 17:03:49 +00:00
|
|
|
mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
2019-06-17 11:02:44 +00:00
|
|
|
rssiOffset(wRssiOffset), stackSize(wStackSize),
|
2019-07-16 19:55:03 +00:00
|
|
|
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
|
|
|
|
mOn(false), mForceClockInterface(false),
|
2016-03-19 18:16:22 +00:00
|
|
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
2019-07-22 15:47:02 +00:00
|
|
|
mWriteBurstToDiskMask(0)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
|
|
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
2015-06-19 22:38:47 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
|
|
mHandover[i][j] = false;
|
|
|
|
}
|
2013-08-20 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Transceiver::~Transceiver()
|
|
|
|
{
|
2014-11-26 00:06:32 +00:00
|
|
|
stop();
|
2013-10-29 19:17:34 +00:00
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
sigProcLibDestroy();
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2019-07-01 17:03:49 +00:00
|
|
|
if (mClockSocket >= 0)
|
|
|
|
close(mClockSocket);
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
mTxPriorityQueues[i].clear();
|
2019-07-01 17:03:49 +00:00
|
|
|
if (mDataSockets[i] >= 0)
|
|
|
|
close(mDataSockets[i]);
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
2013-08-20 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
|
|
|
|
|
|
|
|
if (flags & OSMO_FD_READ)
|
|
|
|
rc = transceiver->ctrl_sock_handle_rx(chan);
|
|
|
|
if (rc < 0)
|
|
|
|
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
|
|
|
|
|
|
|
if (flags & OSMO_FD_WRITE)
|
|
|
|
rc = transceiver->ctrl_sock_write(chan);
|
|
|
|
if (rc < 0)
|
|
|
|
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-10-22 00:52:18 +00:00
|
|
|
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
|
|
|
bool edge, bool ext_rach)
|
2013-08-20 20:10:01 +00:00
|
|
|
{
|
2013-11-14 03:40:44 +00:00
|
|
|
int d_srcport, d_dstport, c_srcport, c_dstport;
|
2013-10-29 19:55:47 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mChans) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOG(FATAL) << "No channels assigned";
|
2013-10-29 22:34:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-06 08:58:56 +00:00
|
|
|
if (!sigProcLibSetup()) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOG(FATAL) << "Failed to initialize signal processing library";
|
2013-08-20 20:10:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-22 00:52:18 +00:00
|
|
|
mExtRACH = ext_rach;
|
2016-07-01 10:46:46 +00:00
|
|
|
mEdge = edge;
|
|
|
|
|
2019-07-01 17:03:49 +00:00
|
|
|
mDataSockets.resize(mChans, -1);
|
2020-04-07 23:39:17 +00:00
|
|
|
mCtrlSockets.resize(mChans);
|
2013-10-29 22:34:16 +00:00
|
|
|
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
|
|
|
mRxServiceLoopThreads.resize(mChans);
|
|
|
|
|
|
|
|
mTxPriorityQueues.resize(mChans);
|
|
|
|
mReceiveFIFO.resize(mChans);
|
|
|
|
mStates.resize(mChans);
|
2019-07-22 15:47:02 +00:00
|
|
|
mVersionTRXD.resize(mChans);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2014-04-15 21:41:28 +00:00
|
|
|
/* Filler table retransmissions - support only on channel 0 */
|
2015-05-20 01:26:31 +00:00
|
|
|
if (filler == FILLER_DUMMY)
|
2014-04-15 21:41:28 +00:00
|
|
|
mStates[0].mRetrans = true;
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/* Setup sockets */
|
2019-07-01 17:03:49 +00:00
|
|
|
mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
mLocalAddr.c_str(), mBasePort,
|
|
|
|
mRemoteAddr.c_str(), mBasePort + 100,
|
|
|
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
2019-08-26 08:55:04 +00:00
|
|
|
if (mClockSocket < 0)
|
|
|
|
return false;
|
2019-07-01 17:03:49 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
2020-04-07 23:39:17 +00:00
|
|
|
int rv;
|
2013-11-14 03:40:44 +00:00
|
|
|
c_srcport = mBasePort + 2 * i + 1;
|
|
|
|
c_dstport = mBasePort + 2 * i + 101;
|
|
|
|
d_srcport = mBasePort + 2 * i + 2;
|
|
|
|
d_dstport = mBasePort + 2 * i + 102;
|
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
2019-07-01 17:03:49 +00:00
|
|
|
mLocalAddr.c_str(), c_srcport,
|
|
|
|
mRemoteAddr.c_str(), c_dstport,
|
|
|
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
2020-04-07 23:39:17 +00:00
|
|
|
if (rv < 0)
|
2019-07-01 17:03:49 +00:00
|
|
|
return false;
|
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
|
|
|
|
mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
|
|
|
|
|
|
|
|
|
2019-07-01 17:03:49 +00:00
|
|
|
mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
mLocalAddr.c_str(), d_srcport,
|
|
|
|
mRemoteAddr.c_str(), d_dstport,
|
|
|
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
2020-04-12 22:03:01 +00:00
|
|
|
if (mDataSockets[i] < 0)
|
2019-07-01 17:03:49 +00:00
|
|
|
return false;
|
2020-04-07 23:39:17 +00:00
|
|
|
|
|
|
|
if (i && filler == FILLER_DUMMY)
|
|
|
|
filler = FILLER_ZERO;
|
|
|
|
|
|
|
|
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/* Randomize the central clock */
|
|
|
|
GSM::Time startTime(random() % gHyperframe, 0);
|
|
|
|
mRadioInterface->getClock()->set(startTime);
|
|
|
|
mTransmitDeadlineClock = startTime;
|
|
|
|
mLastClockUpdateTime = startTime;
|
|
|
|
mLatencyUpdateTime = startTime;
|
|
|
|
|
2013-08-20 20:10:01 +00:00
|
|
|
return true;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/*
|
|
|
|
* 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";
|
2015-04-23 14:08:27 +00:00
|
|
|
return true;
|
2014-11-26 00:06:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG(NOTICE) << "Starting the transceiver";
|
|
|
|
|
|
|
|
GSM::Time time = mRadioInterface->getClock()->get();
|
|
|
|
mTransmitDeadlineClock = time;
|
|
|
|
mLastClockUpdateTime = time;
|
|
|
|
mLatencyUpdateTime = time;
|
|
|
|
|
|
|
|
if (!mRadioInterface->start()) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOG(FATAL) << "Device failed to start";
|
2014-11-26 00:06:32 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Device is running - launch I/O threads */
|
2019-06-17 11:02:44 +00:00
|
|
|
mRxLowerLoopThread = new Thread(stackSize);
|
|
|
|
mTxLowerLoopThread = new Thread(stackSize);
|
2014-11-26 00:06:32 +00:00
|
|
|
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++) {
|
2019-07-22 16:10:52 +00:00
|
|
|
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
|
|
|
|
params->trx = this;
|
|
|
|
params->num = i;
|
2019-06-17 11:02:44 +00:00
|
|
|
mRxServiceLoopThreads[i] = new Thread(stackSize);
|
2014-11-26 00:06:32 +00:00
|
|
|
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
2019-07-22 16:10:52 +00:00
|
|
|
RxUpperLoopAdapter, (void*) params);
|
2014-11-26 00:06:32 +00:00
|
|
|
|
2019-07-22 16:10:52 +00:00
|
|
|
params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
|
|
|
|
params->trx = this;
|
|
|
|
params->num = i;
|
2019-06-17 11:02:44 +00:00
|
|
|
mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
|
2014-11-26 00:06:32 +00:00
|
|
|
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
2019-07-22 16:10:52 +00:00
|
|
|
TxUpperLoopAdapter, (void*) params);
|
2014-11-26 00:06:32 +00:00
|
|
|
}
|
|
|
|
|
2017-07-04 14:25:20 +00:00
|
|
|
mForceClockInterface = true;
|
2014-11-26 00:06:32 +00:00
|
|
|
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();
|
2017-06-15 22:35:02 +00:00
|
|
|
mTxLowerLoopThread->join();
|
|
|
|
mRxLowerLoopThread->join();
|
|
|
|
delete mTxLowerLoopThread;
|
|
|
|
delete mRxLowerLoopThread;
|
2014-11-26 00:06:32 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
mOn = false;
|
|
|
|
LOG(NOTICE) << "Transceiver stopped";
|
|
|
|
}
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
2013-10-29 22:34:16 +00:00
|
|
|
int RSSI, GSM::Time &wTime)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-11-14 03:48:11 +00:00
|
|
|
signalVector *burst;
|
|
|
|
radioVector *radio_burst;
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
if (chan >= mTxPriorityQueues.size()) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
|
2013-10-29 22:34:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-14 20:28:23 +00:00
|
|
|
if (wTime.TN() > 7) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
|
2013-11-14 20:28:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-06 11:44:34 +00:00
|
|
|
/* Use the number of bits as the EDGE burst indicator */
|
|
|
|
if (bits.size() == EDGE_BURST_NBITS)
|
|
|
|
burst = modulateEdgeBurst(bits, mSPSTx);
|
|
|
|
else
|
|
|
|
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
|
|
|
|
|
|
|
radio_burst = new radioVector(wTime, burst);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
mTxPriorityQueues[chan].write(radio_burst);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-25 07:34:03 +00:00
|
|
|
void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
|
|
|
|
{
|
|
|
|
int TN, modFN;
|
|
|
|
TransceiverState *state = &mStates[chan];
|
|
|
|
|
|
|
|
TN = burst->getTime().TN();
|
|
|
|
modFN = burst->getTime().FN() % state->fillerModulus[TN];
|
|
|
|
|
|
|
|
delete state->fillerTable[modFN][TN];
|
|
|
|
state->fillerTable[modFN][TN] = burst->getVector();
|
|
|
|
burst->setVector(NULL);
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
int TN, modFN;
|
|
|
|
radioVector *burst;
|
|
|
|
TransceiverState *state;
|
|
|
|
std::vector<signalVector *> bursts(mChans);
|
|
|
|
std::vector<bool> zeros(mChans);
|
2014-01-25 07:34:03 +00:00
|
|
|
std::vector<bool> filler(mChans, true);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i ++) {
|
|
|
|
state = &mStates[i];
|
|
|
|
|
|
|
|
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
|
2018-04-25 16:01:27 +00:00
|
|
|
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
|
2014-01-25 07:34:03 +00:00
|
|
|
if (state->mRetrans)
|
|
|
|
updateFillerTable(i, burst);
|
2013-11-14 03:48:11 +00:00
|
|
|
delete burst;
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TN = nowTime.TN();
|
|
|
|
modFN = nowTime.FN() % state->fillerModulus[TN];
|
|
|
|
|
|
|
|
bursts[i] = state->fillerTable[modFN][TN];
|
|
|
|
zeros[i] = state->chanType[TN] == NONE;
|
|
|
|
|
|
|
|
if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
|
2013-11-14 03:48:11 +00:00
|
|
|
bursts[i] = burst->getVector();
|
2014-01-25 07:34:03 +00:00
|
|
|
|
|
|
|
if (state->mRetrans) {
|
|
|
|
updateFillerTable(i, burst);
|
|
|
|
} else {
|
|
|
|
burst->setVector(NULL);
|
|
|
|
filler[i] = false;
|
|
|
|
}
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
delete burst;
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mRadioInterface->driveTransmitRadio(bursts, zeros);
|
|
|
|
|
2014-01-25 07:34:03 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
if (!filler[i])
|
|
|
|
delete bursts[i];
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::setModulus(size_t timeslot, size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
|
|
|
|
|
|
|
switch (state->chanType[timeslot]) {
|
2011-10-12 07:44:40 +00:00
|
|
|
case NONE:
|
|
|
|
case I:
|
|
|
|
case II:
|
|
|
|
case III:
|
|
|
|
case FILL:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 26;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
case IV:
|
|
|
|
case VI:
|
|
|
|
case V:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 51;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
2017-07-04 14:24:06 +00:00
|
|
|
//case V:
|
2011-10-12 07:44:40 +00:00
|
|
|
case VII:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 102;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
2013-06-09 22:38:18 +00:00
|
|
|
case XIII:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 52;
|
2013-06-09 22:38:18 +00:00
|
|
|
break;
|
2011-10-12 07:44:40 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-17 22:00:34 +00:00
|
|
|
CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|
|
|
size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2015-06-19 22:38:47 +00:00
|
|
|
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
|
|
|
|
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
|
|
|
|
3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
|
|
|
|
static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
|
|
|
|
1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
|
2013-10-29 22:34:16 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
2011-10-12 07:44:40 +00:00
|
|
|
unsigned burstTN = currTime.TN();
|
|
|
|
unsigned burstFN = currTime.FN();
|
2015-06-19 22:38:47 +00:00
|
|
|
int subch;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
switch (state->chanType[burstTN]) {
|
2011-10-12 07:44:40 +00:00
|
|
|
case NONE:
|
|
|
|
return OFF;
|
|
|
|
break;
|
|
|
|
case FILL:
|
|
|
|
return IDLE;
|
|
|
|
break;
|
|
|
|
case I:
|
2015-06-19 22:38:47 +00:00
|
|
|
// TODO: Are we expecting RACH on an IDLE frame?
|
|
|
|
/* if (burstFN % 26 == 25)
|
|
|
|
return IDLE;*/
|
|
|
|
if (mHandover[burstTN][0])
|
|
|
|
return RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
case II:
|
2015-06-19 22:38:47 +00:00
|
|
|
subch = tchh_subslot[burstFN % 26];
|
|
|
|
if (subch == 1)
|
|
|
|
return IDLE;
|
|
|
|
if (mHandover[burstTN][0])
|
|
|
|
return RACH;
|
2013-03-27 22:00:25 +00:00
|
|
|
return TSC;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
case III:
|
2015-06-19 22:38:47 +00:00
|
|
|
subch = tchh_subslot[burstFN % 26];
|
|
|
|
if (mHandover[burstTN][subch])
|
|
|
|
return RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
case IV:
|
|
|
|
case VI:
|
2018-10-22 00:52:18 +00:00
|
|
|
return mExtRACH ? EXT_RACH : RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
case V: {
|
|
|
|
int mod51 = burstFN % 51;
|
|
|
|
if ((mod51 <= 36) && (mod51 >= 14))
|
2018-10-22 00:52:18 +00:00
|
|
|
return mExtRACH ? EXT_RACH : RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
else if ((mod51 == 4) || (mod51 == 5))
|
2018-10-22 00:52:18 +00:00
|
|
|
return mExtRACH ? EXT_RACH : RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
else if ((mod51 == 45) || (mod51 == 46))
|
2018-10-22 00:52:18 +00:00
|
|
|
return mExtRACH ? EXT_RACH : RACH;
|
2015-06-19 22:38:47 +00:00
|
|
|
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
|
|
|
|
return RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VII:
|
|
|
|
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
|
|
|
|
return IDLE;
|
2015-06-19 22:38:47 +00:00
|
|
|
else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
|
|
|
|
return RACH;
|
2011-10-12 07:44:40 +00:00
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
2013-06-09 22:38:18 +00:00
|
|
|
case XIII: {
|
|
|
|
int mod52 = burstFN % 52;
|
|
|
|
if ((mod52 == 12) || (mod52 == 38))
|
2018-10-22 00:52:18 +00:00
|
|
|
return mExtRACH ? EXT_RACH : RACH;
|
2013-06-09 22:38:18 +00:00
|
|
|
else if ((mod52 == 25) || (mod52 == 51))
|
|
|
|
return IDLE;
|
2019-08-26 14:34:45 +00:00
|
|
|
else /* Enable 8-PSK burst detection if EDGE is enabled */
|
|
|
|
return mEdge ? EDGE : TSC;
|
2013-06-09 22:38:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
case LOOPBACK:
|
|
|
|
if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
|
|
|
|
return IDLE;
|
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return OFF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2015-06-12 04:15:31 +00:00
|
|
|
void writeToFile(radioVector *radio_burst, size_t chan)
|
|
|
|
{
|
|
|
|
GSM::Time time = radio_burst->getTime();
|
|
|
|
std::ostringstream fname;
|
|
|
|
fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
|
|
|
|
std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
|
|
|
|
outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
|
|
|
|
outfile.close();
|
|
|
|
}
|
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/*
|
|
|
|
* Pull bursts from the FIFO and handle according to the slot
|
2017-07-04 14:24:06 +00:00
|
|
|
* and burst correlation type. Equalzation is currently disabled.
|
2019-09-06 13:01:26 +00:00
|
|
|
* returns 0 on success (bi filled), negative on error (bi content undefined):
|
|
|
|
* -ENOENT: timeslot is off (fn and tn in bi are filled),
|
|
|
|
* -EIO: read error
|
2013-11-14 04:14:48 +00:00
|
|
|
*/
|
2019-09-06 13:01:26 +00:00
|
|
|
int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
2013-11-14 04:14:48 +00:00
|
|
|
{
|
2016-03-06 11:44:34 +00:00
|
|
|
int rc;
|
2019-07-04 11:02:12 +00:00
|
|
|
struct estim_burst_params ebp;
|
|
|
|
float max = -1.0, avg = 0.0;
|
2019-07-03 13:43:03 +00:00
|
|
|
unsigned max_toa;
|
2013-11-14 04:38:09 +00:00
|
|
|
int max_i = -1;
|
2013-11-14 04:14:48 +00:00
|
|
|
signalVector *burst;
|
2019-07-01 14:36:10 +00:00
|
|
|
GSM::Time burstTime;
|
2019-07-01 15:55:01 +00:00
|
|
|
SoftVector *rxBurst;
|
2013-11-14 20:52:04 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
2013-11-14 04:14:48 +00:00
|
|
|
|
|
|
|
/* Blocking FIFO read */
|
|
|
|
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
2019-09-06 11:48:35 +00:00
|
|
|
if (!radio_burst) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
|
2019-09-06 13:01:26 +00:00
|
|
|
return -EIO;
|
2019-09-06 11:48:35 +00:00
|
|
|
}
|
2013-11-14 04:14:48 +00:00
|
|
|
|
|
|
|
/* Set time and determine correlation type */
|
2019-07-01 14:36:10 +00:00
|
|
|
burstTime = radio_burst->getTime();
|
|
|
|
CorrType type = expectedCorrType(burstTime, chan);
|
2013-11-14 04:14:48 +00:00
|
|
|
|
2019-07-03 14:01:12 +00:00
|
|
|
/* Initialize struct bi */
|
|
|
|
bi->nbits = 0;
|
|
|
|
bi->fn = burstTime.FN();
|
|
|
|
bi->tn = burstTime.TN();
|
|
|
|
bi->rssi = 0.0;
|
|
|
|
bi->toa = 0.0;
|
|
|
|
bi->noise = 0.0;
|
|
|
|
bi->idle = false;
|
2019-07-01 18:42:53 +00:00
|
|
|
bi->modulation = MODULATION_GMSK;
|
|
|
|
bi->tss = 0; /* TODO: we only support tss 0 right now */
|
|
|
|
bi->tsc = 0;
|
|
|
|
bi->ci = 0.0;
|
2019-07-03 14:01:12 +00:00
|
|
|
|
2019-09-09 08:20:58 +00:00
|
|
|
/* Debug: dump bursts to disk */
|
|
|
|
/* bits 0-7 - chan 0 timeslots
|
|
|
|
* bits 8-15 - chan 1 timeslots */
|
|
|
|
if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
|
|
|
|
writeToFile(radio_burst, chan);
|
|
|
|
|
|
|
|
/* No processing if the timeslot is off.
|
|
|
|
* Not even power level or noise calculation. */
|
|
|
|
if (type == OFF) {
|
|
|
|
delete radio_burst;
|
2019-09-06 13:01:26 +00:00
|
|
|
return -ENOENT;
|
2019-09-09 08:20:58 +00:00
|
|
|
}
|
|
|
|
|
2013-11-14 04:38:09 +00:00
|
|
|
/* Select the diversity channel with highest energy */
|
|
|
|
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
2017-03-15 20:23:36 +00:00
|
|
|
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
|
2013-11-14 04:38:09 +00:00
|
|
|
if (pow > max) {
|
|
|
|
max = pow;
|
|
|
|
max_i = i;
|
|
|
|
}
|
|
|
|
avg += pow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_i < 0) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
|
2019-07-03 14:01:12 +00:00
|
|
|
goto ret_idle;
|
2013-11-14 04:38:09 +00:00
|
|
|
}
|
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Average noise on diversity paths and update global levels */
|
2013-11-14 04:38:09 +00:00
|
|
|
burst = radio_burst->getVector(max_i);
|
2013-11-14 20:31:24 +00:00
|
|
|
avg = sqrt(avg / radio_burst->chans());
|
2015-06-09 02:46:38 +00:00
|
|
|
|
|
|
|
if (type == IDLE) {
|
|
|
|
/* Update noise levels */
|
|
|
|
state->mNoises.insert(avg);
|
|
|
|
state->mNoiseLev = state->mNoises.avg();
|
2019-07-03 13:00:56 +00:00
|
|
|
}
|
2015-06-09 02:46:38 +00:00
|
|
|
|
2019-07-03 13:00:56 +00:00
|
|
|
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
|
|
|
|
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
|
|
|
|
|
2019-07-03 14:01:12 +00:00
|
|
|
if (type == IDLE)
|
|
|
|
goto ret_idle;
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2019-07-03 13:43:03 +00:00
|
|
|
max_toa = (type == RACH || type == EXT_RACH) ?
|
|
|
|
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
|
2018-10-22 00:52:18 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Detect normal or RACH bursts */
|
2019-07-04 11:02:12 +00:00
|
|
|
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
|
2019-07-03 13:23:56 +00:00
|
|
|
if (rc <= 0) {
|
|
|
|
if (rc == -SIGERR_CLIP)
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
|
2019-07-03 13:23:56 +00:00
|
|
|
else if (rc != SIGERR_NONE)
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
|
2019-07-03 14:01:12 +00:00
|
|
|
goto ret_idle;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2019-07-03 13:23:56 +00:00
|
|
|
type = (CorrType) rc;
|
2019-07-04 11:02:12 +00:00
|
|
|
bi->toa = ebp.toa;
|
2019-07-01 18:42:53 +00:00
|
|
|
bi->tsc = ebp.tsc;
|
|
|
|
bi->ci = ebp.ci;
|
2019-07-04 11:02:12 +00:00
|
|
|
rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
|
2013-11-14 20:31:24 +00:00
|
|
|
|
2019-07-02 12:59:47 +00:00
|
|
|
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */
|
2019-07-01 18:42:53 +00:00
|
|
|
if (rxBurst->size() == EDGE_BURST_NBITS) {
|
|
|
|
bi->modulation = MODULATION_8PSK;
|
2019-07-02 12:59:47 +00:00
|
|
|
bi->nbits = EDGE_BURST_NBITS;
|
2019-07-01 18:42:53 +00:00
|
|
|
} else { /* size() here is actually gSlotLen + 8, due to guard periods */
|
|
|
|
bi->modulation = MODULATION_GMSK;
|
2019-07-02 12:59:47 +00:00
|
|
|
bi->nbits = gSlotLen;
|
2019-07-01 18:42:53 +00:00
|
|
|
}
|
2019-07-02 12:59:47 +00:00
|
|
|
|
2019-07-01 14:40:44 +00:00
|
|
|
// Convert -1..+1 soft bits to 0..1 soft bits
|
2019-07-01 15:55:01 +00:00
|
|
|
vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
|
2019-07-01 14:40:44 +00:00
|
|
|
|
2019-07-01 15:55:01 +00:00
|
|
|
delete rxBurst;
|
2013-11-14 04:14:48 +00:00
|
|
|
delete radio_burst;
|
2019-09-06 13:01:26 +00:00
|
|
|
return 0;
|
2019-07-03 14:01:12 +00:00
|
|
|
|
|
|
|
ret_idle:
|
|
|
|
bi->idle = true;
|
|
|
|
delete radio_burst;
|
2019-09-06 13:01:26 +00:00
|
|
|
return 0;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Transceiver::reset()
|
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
|
|
|
mTxPriorityQueues[i].clear();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2017-07-04 14:24:06 +00:00
|
|
|
|
2018-03-08 20:24:08 +00:00
|
|
|
/**
|
|
|
|
* Matches a buffer with a command.
|
|
|
|
* @param buf a buffer to look command in
|
|
|
|
* @param cmd a command to look in buffer
|
|
|
|
* @param params pointer to arguments, or NULL
|
|
|
|
* @return true if command matches, otherwise false
|
|
|
|
*/
|
|
|
|
static bool match_cmd(char *buf,
|
|
|
|
const char *cmd, char **params)
|
|
|
|
{
|
|
|
|
size_t cmd_len = strlen(cmd);
|
|
|
|
|
|
|
|
/* Check a command itself */
|
|
|
|
if (strncmp(buf, cmd, cmd_len))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* A command has arguments */
|
|
|
|
if (params != NULL) {
|
|
|
|
/* Make sure there is a space */
|
|
|
|
if (buf[cmd_len] != ' ')
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Update external pointer */
|
|
|
|
*params = buf + cmd_len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2020-04-07 23:39:17 +00:00
|
|
|
ctrl_sock_state& s = mCtrlSockets[chan];
|
|
|
|
struct osmo_fd *conn_bfd = &s.conn_bfd;
|
|
|
|
|
|
|
|
s.txmsgqueue.push_back(m);
|
|
|
|
conn_bfd->when |= OSMO_FD_WRITE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Transceiver::ctrl_sock_write(int chan)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
ctrl_sock_state& s = mCtrlSockets[chan];
|
|
|
|
|
|
|
|
if (s.conn_bfd.fd < 0) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (s.txmsgqueue.size()) {
|
|
|
|
const ctrl_msg m = s.txmsgqueue.front();
|
|
|
|
|
|
|
|
s.conn_bfd.when &= ~OSMO_FD_WRITE;
|
|
|
|
|
|
|
|
/* try to send it over the socket */
|
|
|
|
rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
|
|
|
|
if (rc == 0)
|
|
|
|
goto close;
|
|
|
|
if (rc < 0) {
|
|
|
|
if (errno == EAGAIN) {
|
|
|
|
s.conn_bfd.when |= OSMO_FD_WRITE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
s.txmsgqueue.pop_front();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
close:
|
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Transceiver::ctrl_sock_handle_rx(int chan)
|
|
|
|
{
|
|
|
|
ctrl_msg cmd_received;
|
|
|
|
ctrl_msg cmd_to_send;
|
|
|
|
char *buffer = cmd_received.data;
|
|
|
|
char *response = cmd_to_send.data;
|
2018-03-08 20:24:08 +00:00
|
|
|
char *command, *params;
|
2018-03-08 19:54:45 +00:00
|
|
|
int msgLen;
|
2020-04-07 23:39:17 +00:00
|
|
|
ctrl_sock_state& s = mCtrlSockets[chan];
|
2013-10-29 19:17:34 +00:00
|
|
|
|
2018-03-08 19:54:45 +00:00
|
|
|
/* Attempt to read from control socket */
|
2020-04-07 23:39:17 +00:00
|
|
|
msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
|
|
|
|
if (msgLen < 0 && errno == EAGAIN)
|
|
|
|
return 0; /* Try again later */
|
2019-07-01 17:03:49 +00:00
|
|
|
if (msgLen <= 0) {
|
2020-04-07 23:39:17 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
|
|
|
|
return -EIO;
|
2019-07-01 17:03:49 +00:00
|
|
|
}
|
2018-03-08 19:54:45 +00:00
|
|
|
|
2020-04-07 23:39:17 +00:00
|
|
|
|
2018-03-08 19:54:45 +00:00
|
|
|
/* Zero-terminate received string */
|
|
|
|
buffer[msgLen] = '\0';
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2018-03-08 20:24:08 +00:00
|
|
|
/* Verify a command signature */
|
|
|
|
if (strncmp(buffer, "CMD ", 4)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
|
2020-04-07 23:39:17 +00:00
|
|
|
return -EIO;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2018-03-08 20:24:08 +00:00
|
|
|
/* Set command pointer */
|
|
|
|
command = buffer + 4;
|
2019-05-03 17:40:00 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
|
2018-03-08 20:24:08 +00:00
|
|
|
|
|
|
|
if (match_cmd(command, "POWEROFF", NULL)) {
|
2014-11-26 00:06:32 +00:00
|
|
|
stop();
|
|
|
|
sprintf(response,"RSP POWEROFF 0");
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "POWERON", NULL)) {
|
2016-10-19 22:26:04 +00:00
|
|
|
if (!start()) {
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP POWERON 1");
|
2016-10-19 22:26:04 +00:00
|
|
|
} else {
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP POWERON 0");
|
2015-06-19 22:38:47 +00:00
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
|
|
mHandover[i][j] = false;
|
|
|
|
}
|
2016-10-19 22:26:04 +00:00
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "HANDOVER", ¶ms)) {
|
2018-03-08 22:08:23 +00:00
|
|
|
unsigned ts = 0, ss = 0;
|
|
|
|
sscanf(params, "%u %u", &ts, &ss);
|
|
|
|
if (ts > 7 || ss > 7) {
|
2019-11-27 12:17:59 +00:00
|
|
|
sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
|
2018-03-08 22:08:23 +00:00
|
|
|
} else {
|
|
|
|
mHandover[ts][ss] = true;
|
|
|
|
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
|
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "NOHANDOVER", ¶ms)) {
|
2018-03-08 22:08:23 +00:00
|
|
|
unsigned ts = 0, ss = 0;
|
|
|
|
sscanf(params, "%u %u", &ts, &ss);
|
|
|
|
if (ts > 7 || ss > 7) {
|
|
|
|
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
|
|
|
|
} else {
|
|
|
|
mHandover[ts][ss] = false;
|
|
|
|
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
|
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETMAXDLY", ¶ms)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int maxDelay;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &maxDelay);
|
2016-03-19 18:16:22 +00:00
|
|
|
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) {
|
2016-03-19 18:16:22 +00:00
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int maxDelay;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &maxDelay);
|
2016-03-19 18:16:22 +00:00
|
|
|
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
|
|
|
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETRXGAIN", ¶ms)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int newGain;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &newGain);
|
2013-10-29 22:34:16 +00:00
|
|
|
newGain = mRadioInterface->setRxGain(newGain, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "NOISELEV", NULL)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
if (mOn) {
|
2013-11-14 20:52:04 +00:00
|
|
|
float lev = mStates[chan].mNoiseLev;
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP NOISELEV 0 %d",
|
2013-11-14 20:52:04 +00:00
|
|
|
(int) round(20.0 * log10(rxFullScale / lev)));
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
sprintf(response,"RSP NOISELEV 1 0");
|
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETPOWER", ¶ms)) {
|
2014-11-25 23:46:56 +00:00
|
|
|
int power;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &power);
|
2014-11-25 23:46:56 +00:00
|
|
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
|
|
|
mStates[chan].mPower = power;
|
|
|
|
sprintf(response, "RSP SETPOWER 0 %d", power);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "ADJPOWER", ¶ms)) {
|
2014-11-25 23:46:56 +00:00
|
|
|
int power, step;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &step);
|
2014-11-25 23:46:56 +00:00
|
|
|
power = mStates[chan].mPower + step;
|
|
|
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
|
|
|
mStates[chan].mPower = power;
|
|
|
|
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "RXTUNE", ¶ms)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
// tune receiver
|
|
|
|
int freqKhz;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &freqKhz);
|
2013-11-15 21:13:59 +00:00
|
|
|
mRxFreq = freqKhz * 1e3;
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "TXTUNE", ¶ms)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
// tune txmtr
|
|
|
|
int freqKhz;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &freqKhz);
|
2013-11-15 21:13:59 +00:00
|
|
|
mTxFreq = freqKhz * 1e3;
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETTSC", ¶ms)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
// set TSC
|
2013-11-15 21:32:54 +00:00
|
|
|
unsigned TSC;
|
2018-03-08 22:01:21 +00:00
|
|
|
sscanf(params, "%u", &TSC);
|
2017-03-31 02:36:41 +00:00
|
|
|
if (TSC > 7) {
|
2013-11-15 19:22:53 +00:00
|
|
|
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
2017-03-31 02:36:41 +00:00
|
|
|
} else {
|
2018-12-04 15:37:24 +00:00
|
|
|
LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
|
2011-10-12 07:44:40 +00:00
|
|
|
mTSC = TSC;
|
2013-08-20 20:10:01 +00:00
|
|
|
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "SETSLOT", ¶ms)) {
|
2015-05-20 19:02:29 +00:00
|
|
|
// set slot type
|
2011-10-12 07:44:40 +00:00
|
|
|
int corrCode;
|
|
|
|
int timeslot;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d %d", ×lot, &corrCode);
|
2011-10-12 07:44:40 +00:00
|
|
|
if ((timeslot < 0) || (timeslot > 7)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
2020-04-07 23:39:17 +00:00
|
|
|
return 0;
|
2017-07-04 14:24:06 +00:00
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
|
|
|
setModulus(timeslot, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
2019-07-03 14:17:06 +00:00
|
|
|
} else if (match_cmd(command, "SETFORMAT", ¶ms)) {
|
|
|
|
// set TRXD protocol version
|
|
|
|
unsigned version_recv;
|
|
|
|
sscanf(params, "%u", &version_recv);
|
2019-07-22 15:47:02 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
|
2019-07-03 14:17:06 +00:00
|
|
|
if (version_recv > TRX_DATA_FORMAT_VER) {
|
2019-07-22 15:47:02 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
|
|
|
|
<< "in favor of " << TRX_DATA_FORMAT_VER;
|
2019-07-03 14:17:06 +00:00
|
|
|
sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
|
|
|
|
} else {
|
2019-07-22 15:47:02 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
|
|
|
|
mVersionTRXD[chan] = version_recv;
|
2019-07-03 14:17:06 +00:00
|
|
|
sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
|
|
|
|
}
|
2018-03-08 20:24:08 +00:00
|
|
|
} else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) {
|
2019-10-13 17:08:00 +00:00
|
|
|
// debug command! may change or disappear without notice
|
2015-06-12 04:15:31 +00:00
|
|
|
// set a mask which bursts to dump to disk
|
|
|
|
int mask;
|
2018-03-08 20:24:08 +00:00
|
|
|
sscanf(params, "%d", &mask);
|
2015-06-12 04:15:31 +00:00
|
|
|
mWriteBurstToDiskMask = mask;
|
|
|
|
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
|
2018-03-08 20:24:08 +00:00
|
|
|
} else {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
|
2013-04-07 22:14:08 +00:00
|
|
|
sprintf(response,"RSP ERR 1");
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2019-05-03 17:40:00 +00:00
|
|
|
LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
|
2020-04-07 23:39:17 +00:00
|
|
|
transceiver->ctrl_sock_send(cmd_to_send, chan);
|
|
|
|
return 0;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2019-07-01 17:03:49 +00:00
|
|
|
int msgLen;
|
2016-07-01 09:46:04 +00:00
|
|
|
int burstLen;
|
2019-08-26 09:21:55 +00:00
|
|
|
struct trxd_hdr_v01_dl *dl;
|
|
|
|
char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
|
2019-07-15 16:53:08 +00:00
|
|
|
uint32_t fn;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
// check data socket
|
2019-07-01 17:03:49 +00:00
|
|
|
msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
|
|
|
|
if (msgLen <= 0) {
|
2020-04-12 22:03:01 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
|
2019-07-01 17:03:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-08-26 09:21:55 +00:00
|
|
|
switch (msgLen) {
|
|
|
|
case sizeof(*dl) + gSlotLen: /* GSM burst */
|
|
|
|
burstLen = gSlotLen;
|
|
|
|
break;
|
|
|
|
case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
|
|
|
|
if (mSPSTx != 4) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
|
2019-08-26 09:21:55 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
burstLen = EDGE_BURST_NBITS;
|
|
|
|
break;
|
|
|
|
default:
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
|
2016-07-01 09:46:04 +00:00
|
|
|
return false;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 09:21:55 +00:00
|
|
|
dl = (struct trxd_hdr_v01_dl *) buffer;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-07-15 16:53:08 +00:00
|
|
|
/* Convert TDMA FN to the host endianness */
|
2019-08-26 09:21:55 +00:00
|
|
|
fn = osmo_load32be(&dl->common.fn);
|
2019-07-15 16:53:08 +00:00
|
|
|
|
2019-07-15 16:56:56 +00:00
|
|
|
/* Make sure we support the received header format */
|
2019-08-26 09:21:55 +00:00
|
|
|
switch (dl->common.version) {
|
2019-07-15 16:56:56 +00:00
|
|
|
case 0:
|
|
|
|
/* Version 1 has the same format */
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
default:
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
|
2019-07-15 16:56:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
|
|
|
|
<< "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
|
2017-07-04 14:24:06 +00:00
|
|
|
|
2016-07-19 18:20:59 +00:00
|
|
|
BitVector newBurst(burstLen);
|
2011-10-12 07:44:40 +00:00
|
|
|
BitVector::iterator itr = newBurst.begin();
|
2019-08-26 09:21:55 +00:00
|
|
|
uint8_t *bufferItr = dl->soft_bits;
|
2017-07-04 14:24:06 +00:00
|
|
|
while (itr < newBurst.end())
|
2011-10-12 07:44:40 +00:00
|
|
|
*itr++ = *bufferItr++;
|
2017-07-04 14:24:06 +00:00
|
|
|
|
2019-08-26 09:21:55 +00:00
|
|
|
GSM::Time currTime = GSM::Time(fn, dl->common.tn);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2019-08-26 09:21:55 +00:00
|
|
|
addRadioVector(chan, newBurst, dl->tx_att, currTime);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2019-08-26 10:05:48 +00:00
|
|
|
bool Transceiver::driveReceiveRadio()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2018-09-03 14:50:49 +00:00
|
|
|
int rc = mRadioInterface->driveReceiveRadio();
|
|
|
|
if (rc == 0) {
|
2013-10-29 22:34:16 +00:00
|
|
|
usleep(100000);
|
2019-08-26 10:05:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (rc < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
2017-07-04 14:25:20 +00:00
|
|
|
mForceClockInterface = false;
|
2019-08-26 10:05:48 +00:00
|
|
|
return writeClockInterface();
|
2015-05-24 23:28:09 +00:00
|
|
|
}
|
2019-08-26 10:05:48 +00:00
|
|
|
return true;
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-07-01 11:56:17 +00:00
|
|
|
void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
|
2016-03-06 11:44:34 +00:00
|
|
|
{
|
2019-07-01 15:55:01 +00:00
|
|
|
std::ostringstream os;
|
|
|
|
for (size_t i=0; i < bi->nbits; i++) {
|
|
|
|
if (bi->rx_burst[i] > 0.5) os << "1";
|
|
|
|
else if (bi->rx_burst[i] > 0.25) os << "|";
|
|
|
|
else if (bi->rx_burst[i] > 0.0) os << "'";
|
|
|
|
else os << "-";
|
|
|
|
}
|
|
|
|
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
|
2019-09-06 12:22:31 +00:00
|
|
|
<< " time: " << unsigned(bi->tn) << ":" << bi->fn
|
2019-07-01 11:56:17 +00:00
|
|
|
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
|
|
|
|
<< "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
|
|
|
|
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
|
|
|
|
<< "dBFS/" << std::setw(6) << -bi->noise << "dBm"
|
2019-06-28 15:01:16 +00:00
|
|
|
<< " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
|
2019-07-01 18:42:53 +00:00
|
|
|
<< " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
|
2019-07-01 15:55:01 +00:00
|
|
|
<< " bits: " << os;
|
2016-03-06 11:44:34 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 10:05:48 +00:00
|
|
|
bool Transceiver::driveReceiveFIFO(size_t chan)
|
2013-10-29 22:34:16 +00:00
|
|
|
{
|
2019-06-28 15:01:16 +00:00
|
|
|
struct trx_ul_burst_ind bi;
|
2019-09-06 13:01:26 +00:00
|
|
|
int rc;
|
2019-06-28 15:01:16 +00:00
|
|
|
|
2019-09-06 13:01:26 +00:00
|
|
|
if ((rc = pullRadioVector(chan, &bi)) < 0) {
|
|
|
|
if (rc == -ENOENT) { /* timeslot off, continue processing */
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
|
2019-09-06 13:01:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false; /* other errors: we want to stop the process */
|
|
|
|
}
|
2019-08-26 10:05:48 +00:00
|
|
|
|
2019-07-01 18:41:55 +00:00
|
|
|
if (!bi.idle)
|
2019-08-26 10:05:48 +00:00
|
|
|
logRxBurst(chan, &bi);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-07-22 15:47:02 +00:00
|
|
|
switch (mVersionTRXD[chan]) {
|
2019-07-01 18:41:55 +00:00
|
|
|
case 0:
|
2019-08-26 10:05:48 +00:00
|
|
|
return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
|
2019-07-01 18:42:53 +00:00
|
|
|
case 1:
|
2019-08-26 10:05:48 +00:00
|
|
|
return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
|
2019-07-01 18:41:55 +00:00
|
|
|
default:
|
|
|
|
OSMO_ASSERT(false);
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::driveTxFIFO()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
2017-07-04 14:24:06 +00:00
|
|
|
Features a carefully controlled latency mechanism, to
|
2011-10-12 07:44:40 +00:00
|
|
|
assure that transmit packets arrive at the radio/USRP
|
|
|
|
before they need to be transmitted.
|
|
|
|
|
|
|
|
Deadline clock indicates the burst that needs to be
|
|
|
|
pushed into the FIFO right NOW. If transmit queue does
|
|
|
|
not have a burst, stick in filler data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
RadioClock *radioClock = (mRadioInterface->getClock());
|
2017-07-04 14:24:06 +00:00
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
if (mOn) {
|
|
|
|
//radioClock->wait(); // wait until clock updates
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
|
2011-10-12 07:44:40 +00:00
|
|
|
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
|
|
|
// if underrun, then we're not providing bursts to radio/USRP fast
|
|
|
|
// enough. Need to increase latency by one GSM frame.
|
2013-04-05 19:36:30 +00:00
|
|
|
if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
|
2011-11-26 03:18:55 +00:00
|
|
|
if (mRadioInterface->isUnderrun()) {
|
2012-08-08 00:51:31 +00:00
|
|
|
// only update latency at the defined frame interval
|
|
|
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
2011-11-26 03:18:55 +00:00
|
|
|
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
|
|
|
|
<< radioClock->get() << " vs "
|
|
|
|
<< mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
|
2011-11-26 03:18:55 +00:00
|
|
|
mLatencyUpdateTime = radioClock->get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if underrun hasn't occurred in the last sec (216 frames) drop
|
|
|
|
// transmit latency by a timeslot
|
2018-04-24 16:43:51 +00:00
|
|
|
if (mTransmitLatency > mRadioInterface->minLatency()) {
|
2011-11-26 03:18:55 +00:00
|
|
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
|
|
|
mTransmitLatency.decTN();
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
|
2011-11-26 03:18:55 +00:00
|
|
|
mLatencyUpdateTime = radioClock->get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
// time to push burst to transmit FIFO
|
|
|
|
pushRadioVector(mTransmitDeadlineClock);
|
|
|
|
mTransmitDeadlineClock.incTN();
|
|
|
|
}
|
|
|
|
}
|
2013-09-28 22:04:19 +00:00
|
|
|
|
|
|
|
radioClock->wait();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-08-26 10:05:48 +00:00
|
|
|
bool Transceiver::writeClockInterface()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2019-07-01 17:03:49 +00:00
|
|
|
int msgLen;
|
2011-10-12 07:44:40 +00:00
|
|
|
char command[50];
|
|
|
|
// FIXME -- This should be adaptive.
|
|
|
|
sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
|
|
|
|
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXCLK, INFO) << "sending " << command;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-07-01 17:03:49 +00:00
|
|
|
msgLen = write(mClockSocket, command, strlen(command) + 1);
|
2019-08-26 10:05:48 +00:00
|
|
|
if (msgLen <= 0) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
|
2019-08-26 10:05:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
mLastClockUpdateTime = mTransmitDeadlineClock;
|
2019-08-26 10:05:48 +00:00
|
|
|
return true;
|
2013-09-28 22:04:19 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2019-07-22 16:10:52 +00:00
|
|
|
void *RxUpperLoopAdapter(TrxChanThParams *params)
|
2013-10-29 22:34:16 +00:00
|
|
|
{
|
2018-09-20 16:04:46 +00:00
|
|
|
char thread_name[16];
|
2019-07-22 16:10:52 +00:00
|
|
|
Transceiver *trx = params->trx;
|
|
|
|
size_t num = params->num;
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2019-07-22 16:10:52 +00:00
|
|
|
free(params);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2018-09-20 16:04:46 +00:00
|
|
|
snprintf(thread_name, 16, "RxUpper%zu", num);
|
|
|
|
set_selfthread_name(thread_name);
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
while (1) {
|
2019-08-26 10:05:48 +00:00
|
|
|
if (!trx->driveReceiveFIFO(num)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
2019-08-26 10:05:48 +00:00
|
|
|
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
|
|
|
break;
|
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2018-09-20 16:04:46 +00:00
|
|
|
set_selfthread_name("RxLower");
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
while (1) {
|
2019-08-26 10:05:48 +00:00
|
|
|
if (!transceiver->driveReceiveRadio()) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
|
2019-08-26 10:05:48 +00:00
|
|
|
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
|
|
|
break;
|
|
|
|
}
|
2013-09-28 22:04:19 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
2013-09-28 22:04:19 +00:00
|
|
|
{
|
2018-09-20 16:04:46 +00:00
|
|
|
set_selfthread_name("TxLower");
|
|
|
|
|
2013-09-28 22:04:19 +00:00
|
|
|
while (1) {
|
2013-10-29 22:34:16 +00:00
|
|
|
transceiver->driveTxFIFO();
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-07-22 16:10:52 +00:00
|
|
|
void *TxUpperLoopAdapter(TrxChanThParams *params)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2018-09-20 16:04:46 +00:00
|
|
|
char thread_name[16];
|
2019-07-22 16:10:52 +00:00
|
|
|
Transceiver *trx = params->trx;
|
|
|
|
size_t num = params->num;
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2019-07-22 16:10:52 +00:00
|
|
|
free(params);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
2018-09-20 16:04:46 +00:00
|
|
|
snprintf(thread_name, 16, "TxUpper%zu", num);
|
|
|
|
set_selfthread_name(thread_name);
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
while (1) {
|
2019-08-26 10:05:48 +00:00
|
|
|
if (!trx->driveTxPriorityQueue(num)) {
|
2019-12-19 20:03:03 +00:00
|
|
|
LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
2019-08-26 10:05:48 +00:00
|
|
|
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
|
|
|
break;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|