Files
osmo-trx/Transceiver52M/radioInterface.cpp
Pau Espin Pedrol e91544d740 Calculate RSSI offset based on RxGain configuration
Prior to this patch, osmo-trx relied totally on proper VTY configuration
being set in "rssi-offset" together with the RxGain set through TRXC in
order to provide correct Uplink RSSI measurements to bts-trx.

With this patch, RSSI is now by default calculated (in LMS and UHD
backends) based on the currently set RxGain, by providing empirically
discovered values. Still, for backward compatibility, the old
"rssi-offset" command will overwrite completely the per-default
calculated rssi offset.
A new optional parameter "relative" is added at the end of the
"rssi-offset" VTY command to flag the value as relative to the newly
per-default calculated value. This way specific setups (like adding a
LNA / RF fronted) can still be expressed while still keeping the
automatic per-default offset.

Related: OS#4468
Change-Id: I8ef78fd20c22c60d61bfb18d80a4a36df4fd6c20
2020-10-14 12:53:04 +02:00

389 lines
9.7 KiB
C++

/*
* Radio device interface
*
* Copyright (C) 2008-2014 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include "radioInterface.h"
#include "Resampler.h"
#include <Logger.h>
#include <Threads.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include "convert.h"
}
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
size_t rx_sps, size_t chans,
int wReceiveOffset, GSM::Time wStartTime)
: mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
RadioInterface::~RadioInterface(void)
{
close();
}
bool RadioInterface::init(int type)
{
if ((type != RadioDevice::NORMAL) || !mChans) {
LOG(ALERT) << "Invalid configuration";
return false;
}
close();
sendBuffer.resize(mChans);
recvBuffer.resize(mChans);
convertSendBuffer.resize(mChans);
convertRecvBuffer.resize(mChans);
mReceiveFIFO.resize(mChans);
powerScaling.resize(mChans);
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
powerScaling[i] = 1.0;
}
return true;
}
void RadioInterface::close()
{
for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it)
delete *it;
for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it)
delete *it;
for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it)
delete[] *it;
for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it)
delete[] *it;
sendBuffer.resize(0);
recvBuffer.resize(0);
convertSendBuffer.resize(0);
convertRecvBuffer.resize(0);
}
double RadioInterface::fullScaleInputValue(void) {
return mDevice->fullScaleInputValue();
}
double RadioInterface::fullScaleOutputValue(void) {
return mDevice->fullScaleOutputValue();
}
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
{
double rfAtten, digAtten;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return -1;
}
if (atten < 0.0)
atten = 0.0;
rfAtten = mDevice->setPowerAttenuation((double) atten, chan);
digAtten = (double) atten - rfAtten;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
else
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
return atten;
}
int RadioInterface::getNominalTxPower(size_t chan)
{
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return -1;
}
return mDevice->getNominalTxPower(chan);
}
int RadioInterface::radioifyVector(signalVector &wVector,
size_t chan, bool zero)
{
if (zero)
sendBuffer[chan]->zero(wVector.size());
else
sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
return wVector.size();
}
int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
{
if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
LOG(ALERT) << "Insufficient number of samples in receive buffer";
return -1;
}
recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
return newVector->size();
}
bool RadioInterface::tuneTx(double freq, size_t chan)
{
return mDevice->setTxFreq(freq, chan);
}
bool RadioInterface::tuneRx(double freq, size_t chan)
{
return mDevice->setRxFreq(freq, chan);
}
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
set_selfthread_name("AlignRadio");
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
while (1) {
sleep(60);
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
mDevice->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
bool RadioInterface::start()
{
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
if (mDevice->requiresRadioAlign())
mAlignRadioServiceLoopThread.start(
(void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
if (!mDevice->start())
return false;
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i]->reset();
recvBuffer[i]->reset();
}
writeTimestamp = mDevice->initialWriteTimestamp();
readTimestamp = mDevice->initialReadTimestamp();
mDevice->updateAlignment(writeTimestamp-10000);
mDevice->updateAlignment(writeTimestamp-10000);
mOn = true;
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 || !mDevice->stop())
return false;
mOn = false;
return true;
}
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros)
{
if (!mOn)
return;
for (size_t i = 0; i < mChans; i++)
radioifyVector(*bursts[i], i, zeros[i]);
while (pushBuffer());
}
int RadioInterface::driveReceiveRadio()
{
radioVector *burst = NULL;
if (!mOn)
return 0;
if (pullBuffer() < 0)
return -1;
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int recvSz = recvBuffer[0]->getAvailSamples();
const int symbolsPerSlot = gSlotLen + 8;
int burstSize;
if (mSPSRx == 4)
burstSize = 625;
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
* Pre-allocate head room for the largest correlation size
* so we can later avoid a re-allocation and copy
* */
size_t head = GSM::gRACHSynchSequenceTS0.size();
/*
* Form receive bursts and pass up to transceiver. Use repeating
* pattern of 157-156-156-156 symbols per timeslot
*/
while (recvSz > burstSize) {
for (size_t i = 0; i < mChans; i++) {
burst = new radioVector(rcvClock, burstSize, head);
unRadioifyVector(burst->getVector(), i);
if (mReceiveFIFO[i].size() < 32)
mReceiveFIFO[i].write(burst);
else
delete burst;
}
mClock.incTN();
rcvClock.incTN();
recvSz -= burstSize;
tN = rcvClock.TN();
if (mSPSRx != 4)
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
return 1;
}
bool RadioInterface::isUnderrun()
{
bool retVal;
/* atomically get previous value of "underrun" and set the var to false */
retVal = osmo_trx_sync_fetch_and_and(&underrun, false);
return retVal;
}
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
{
if (chan >= mReceiveFIFO.size())
return NULL;
return &mReceiveFIFO[chan];
}
double RadioInterface::setRxGain(double dB, size_t chan)
{
return mDevice->setRxGain(dB, chan);
}
double RadioInterface::rssiOffset(size_t chan)
{
return mDevice->rssiOffset(chan);
}
/* Receive a timestamped chunk from the device */
int RadioInterface::pullBuffer()
{
bool local_underrun;
int numRecv;
size_t segmentLen = recvBuffer[0]->getSegmentLen();
if (recvBuffer[0]->getFreeSegments() <= 0)
return -1;
/* Outer buffer access size is fixed */
numRecv = mDevice->readSamples(convertRecvBuffer,
segmentLen,
&overrun,
readTimestamp,
&local_underrun);
if ((size_t) numRecv != segmentLen) {
LOG(ALERT) << "Receive error " << numRecv;
return -1;
}
for (size_t i = 0; i < mChans; i++) {
convert_short_float(recvBuffer[i]->getWriteSegment(),
convertRecvBuffer[i],
segmentLen * 2);
}
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
readTimestamp += numRecv;
return 0;
}
/* Send timestamped chunk to the device with arbitrary size */
bool RadioInterface::pushBuffer()
{
bool local_underrun;
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
if (sendBuffer[0]->getAvailSegments() < 1)
return false;
for (size_t i = 0; i < mChans; i++) {
convert_float_short(convertSendBuffer[i],
(float *) sendBuffer[i]->getReadSegment(),
powerScaling[i],
segmentLen * 2);
}
/* Send the all samples in the send buffer */
numSent = mDevice->writeSamples(convertSendBuffer,
segmentLen,
&local_underrun,
writeTimestamp);
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += numSent;
return true;
}