mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-04 06:03:17 +00:00 
			
		
		
		
	Previous checks on multi-channel TSC and ARFCN settings would fail if channels were initialized out of order. Namely, if channel 0 was not configured first, osmo-trx would error on the control interface leading osmo-bts to fail. Allow global TSC setting on all channels with added logging notice. Notify if channel frequency is unexpected - which may happen if channels are setup out of order - but do no report as error. Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
		
			
				
	
	
		
			392 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			392 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Multi-carrier radio interface
 | 
						|
 *
 | 
						|
 * Copyright (C) 2016 Ettus Research LLC 
 | 
						|
 *
 | 
						|
 * Author: Tom Tsou <tom.tsou@ettus.com>
 | 
						|
 *
 | 
						|
 * This program is free software: you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU Affero General Public License as published by
 | 
						|
 * the Free Software Foundation, either version 3 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU Affero General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Affero General Public License
 | 
						|
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 * See the COPYING file in the main directory for details.
 | 
						|
 */
 | 
						|
 | 
						|
#include <radioInterface.h>
 | 
						|
#include <Logger.h>
 | 
						|
 | 
						|
#include "Resampler.h"
 | 
						|
 | 
						|
extern "C" {
 | 
						|
#include "convert.h"
 | 
						|
}
 | 
						|
 | 
						|
/* Resampling parameters for 64 MHz clocking */
 | 
						|
#define RESAMP_INRATE			65
 | 
						|
#define RESAMP_OUTRATE			(96 / 2)
 | 
						|
 | 
						|
/* Universal resampling parameters */
 | 
						|
#define NUMCHUNKS				24
 | 
						|
 | 
						|
#define MCHANS					4
 | 
						|
 | 
						|
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
 | 
						|
					 size_t rx_sps, size_t chans)
 | 
						|
	: RadioInterface(radio, tx_sps, rx_sps, chans),
 | 
						|
	  outerSendBuffer(NULL), outerRecvBuffer(NULL),
 | 
						|
	  dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
RadioInterfaceMulti::~RadioInterfaceMulti()
 | 
						|
{
 | 
						|
	close();
 | 
						|
}
 | 
						|
 | 
						|
void RadioInterfaceMulti::close()
 | 
						|
{
 | 
						|
	delete outerSendBuffer;
 | 
						|
	delete outerRecvBuffer;
 | 
						|
	delete dnsampler;
 | 
						|
	delete upsampler;
 | 
						|
	delete channelizer;
 | 
						|
	delete synthesis;
 | 
						|
 | 
						|
	outerSendBuffer = NULL;
 | 
						|
	outerRecvBuffer = NULL;
 | 
						|
	dnsampler = NULL;
 | 
						|
	upsampler = NULL;
 | 
						|
	channelizer = NULL;
 | 
						|
	synthesis = NULL;
 | 
						|
 | 
						|
	mReceiveFIFO.resize(0);
 | 
						|
	powerScaling.resize(0);
 | 
						|
	history.resize(0);
 | 
						|
	active.resize(0);
 | 
						|
 | 
						|
	RadioInterface::close();
 | 
						|
}
 | 
						|
 | 
						|
static int getLogicalChan(size_t pchan, size_t chans)
 | 
						|
{
 | 
						|
	switch (chans) {
 | 
						|
	case 1:
 | 
						|
		if (pchan == 0)
 | 
						|
			return 0;
 | 
						|
		else
 | 
						|
			return -1;
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		if (pchan == 0)
 | 
						|
			return 0;
 | 
						|
		if (pchan == 3)
 | 
						|
			return 1;
 | 
						|
		else
 | 
						|
			return -1;
 | 
						|
		break;
 | 
						|
	case 3:
 | 
						|
		if (pchan == 1)
 | 
						|
			return 0;
 | 
						|
		if (pchan == 0)
 | 
						|
			return 1;
 | 
						|
		if (pchan == 3)
 | 
						|
			return 2;
 | 
						|
		else
 | 
						|
			return -1;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	};
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int getFreqShift(size_t chans)
 | 
						|
{
 | 
						|
	switch (chans) {
 | 
						|
	case 1:
 | 
						|
		return 0;
 | 
						|
	case 2:
 | 
						|
		return 0;
 | 
						|
	case 3:
 | 
						|
		return 1;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	};
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Initialize I/O specific objects */
 | 
						|
bool RadioInterfaceMulti::init(int type)
 | 
						|
{
 | 
						|
	float cutoff = 1.0f;
 | 
						|
	size_t inchunk = 0, outchunk = 0;
 | 
						|
 | 
						|
	if (mChans > MCHANS - 1) {
 | 
						|
		LOG(ALERT) << "Invalid channel configuration " << mChans;
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	close();
 | 
						|
 | 
						|
	sendBuffer.resize(mChans);
 | 
						|
	recvBuffer.resize(mChans);
 | 
						|
	convertSendBuffer.resize(1);
 | 
						|
	convertRecvBuffer.resize(1);
 | 
						|
 | 
						|
	mReceiveFIFO.resize(mChans);
 | 
						|
	powerScaling.resize(mChans);
 | 
						|
	history.resize(mChans);
 | 
						|
	active.resize(MCHANS, false);
 | 
						|
 | 
						|
	inchunk = RESAMP_INRATE * 4;
 | 
						|
	outchunk = RESAMP_OUTRATE * 4;
 | 
						|
 | 
						|
	if (inchunk  * NUMCHUNKS < 625 * 2) {
 | 
						|
		LOG(ALERT) << "Invalid inner chunk size " << inchunk;
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
 | 
						|
	if (!dnsampler->init(1.0)) {
 | 
						|
		LOG(ALERT) << "Rx resampler failed to initialize";
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
 | 
						|
	if (!upsampler->init(cutoff)) {
 | 
						|
		LOG(ALERT) << "Tx resampler failed to initialize";
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	channelizer = new Channelizer(MCHANS, outchunk);
 | 
						|
	if (!channelizer->init()) {
 | 
						|
		LOG(ALERT) << "Rx channelizer failed to initialize";
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	synthesis = new Synthesis(MCHANS, outchunk);
 | 
						|
	if (!synthesis->init()) {
 | 
						|
		LOG(ALERT) << "Tx synthesis filter failed to initialize";
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Allocate high and low rate buffers. The high rate receive
 | 
						|
	 * buffer and low rate transmit vectors feed into the resampler
 | 
						|
	 * and requires headroom equivalent to the filter length. Low
 | 
						|
	 * rate buffers are allocated in the main radio interface code.
 | 
						|
	 */
 | 
						|
	for (size_t i = 0; i < mChans; i++) {
 | 
						|
		sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
 | 
						|
					        upsampler->len(), true);
 | 
						|
		recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
 | 
						|
		                                0, false);
 | 
						|
		history[i] = new signalVector(dnsampler->len());
 | 
						|
 | 
						|
		synthesis->resetBuffer(i);
 | 
						|
	}
 | 
						|
 | 
						|
	outerSendBuffer = new signalVector(synthesis->outputLen());
 | 
						|
	outerRecvBuffer = new signalVector(channelizer->inputLen());
 | 
						|
 | 
						|
	convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
 | 
						|
	convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
 | 
						|
 | 
						|
	/* Configure channels */
 | 
						|
	switch (mChans) {
 | 
						|
	case 1:
 | 
						|
		active[0] = true;
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		active[0] = true;
 | 
						|
		active[3] = true;
 | 
						|
		break;
 | 
						|
	case 3:
 | 
						|
		active[0] = true;
 | 
						|
		active[1] = true;
 | 
						|
		active[3] = true;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		LOG(ALERT) << "Unsupported channel combination";
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* Receive a timestamped chunk from the device */
 | 
						|
void RadioInterfaceMulti::pullBuffer()
 | 
						|
{
 | 
						|
	bool local_underrun;
 | 
						|
	size_t num;
 | 
						|
	float *buf;
 | 
						|
 | 
						|
	if (recvBuffer[0]->getFreeSegments() <= 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Outer buffer access size is fixed */
 | 
						|
	num = mRadio->readSamples(convertRecvBuffer,
 | 
						|
				  outerRecvBuffer->size(),
 | 
						|
				  &overrun,
 | 
						|
				  readTimestamp,
 | 
						|
				  &local_underrun);
 | 
						|
	if (num != channelizer->inputLen()) {
 | 
						|
		LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	convert_short_float((float *) outerRecvBuffer->begin(),
 | 
						|
			    convertRecvBuffer[0], 2 * outerRecvBuffer->size());
 | 
						|
 | 
						|
	underrun |= local_underrun;
 | 
						|
	readTimestamp += num;
 | 
						|
 | 
						|
	channelizer->rotate((float *) outerRecvBuffer->begin(),
 | 
						|
			    outerRecvBuffer->size());
 | 
						|
 | 
						|
	for (size_t pchan = 0; pchan < MCHANS; pchan++) {
 | 
						|
		if (!active[pchan])
 | 
						|
			continue;
 | 
						|
 | 
						|
		int lchan = getLogicalChan(pchan, mChans);
 | 
						|
		if (lchan < 0) {
 | 
						|
			LOG(ALERT) << "Invalid logical channel " << pchan;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Update history by writing into the head portion of the
 | 
						|
		 * channelizer output buffer. For this to work, filter length of
 | 
						|
		 * the polyphase channelizer partition filter should be equal to
 | 
						|
		 * or larger than the resampling filter.
 | 
						|
		 */
 | 
						|
		buf = channelizer->outputBuffer(pchan);
 | 
						|
		size_t cLen = channelizer->outputLen();
 | 
						|
		size_t hLen = dnsampler->len();
 | 
						|
		size_t hSize = 2 * hLen * sizeof(float);
 | 
						|
 | 
						|
		memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
 | 
						|
		memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
 | 
						|
 | 
						|
		float *wr_segment = recvBuffer[lchan]->getWriteSegment();
 | 
						|
 | 
						|
		/* Write to the end of the inner receive buffer */
 | 
						|
		if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
 | 
						|
				       channelizer->outputLen(),
 | 
						|
				       wr_segment,
 | 
						|
				       recvBuffer[lchan]->getSegmentLen())) {
 | 
						|
			LOG(ALERT) << "Sample rate upsampling error";
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Send a timestamped chunk to the device */
 | 
						|
bool RadioInterfaceMulti::pushBuffer()
 | 
						|
{
 | 
						|
	if (sendBuffer[0]->getAvailSegments() <= 0)
 | 
						|
		return false;
 | 
						|
 | 
						|
	for (size_t pchan = 0; pchan < MCHANS; pchan++) {
 | 
						|
		if (!active[pchan]) {
 | 
						|
			synthesis->resetBuffer(pchan);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		int lchan = getLogicalChan(pchan, mChans);
 | 
						|
		if (lchan < 0) {
 | 
						|
			LOG(ALERT) << "Invalid logical channel " << pchan;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
 | 
						|
				       sendBuffer[lchan]->getSegmentLen(),
 | 
						|
				       synthesis->inputBuffer(pchan),
 | 
						|
				       synthesis->inputLen())) {
 | 
						|
			LOG(ALERT) << "Sample rate downsampling error";
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	synthesis->rotate((float *) outerSendBuffer->begin(),
 | 
						|
			  outerSendBuffer->size());
 | 
						|
 | 
						|
	convert_float_short(convertSendBuffer[0],
 | 
						|
			    (float *) outerSendBuffer->begin(),
 | 
						|
			    1.0 / (float) mChans, 2 * outerSendBuffer->size());
 | 
						|
 | 
						|
	size_t num = mRadio->writeSamples(convertSendBuffer,
 | 
						|
					  outerSendBuffer->size(),
 | 
						|
					  &underrun,
 | 
						|
					  writeTimestamp);
 | 
						|
	if (num != outerSendBuffer->size()) {
 | 
						|
		LOG(ALERT) << "Transmit error " << num;
 | 
						|
	}
 | 
						|
 | 
						|
	writeTimestamp += num;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* Frequency comparison limit */
 | 
						|
#define FREQ_DELTA_LIMIT		10.0
 | 
						|
 | 
						|
static bool fltcmp(double a, double b)
 | 
						|
{
 | 
						|
	return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
 | 
						|
}
 | 
						|
 | 
						|
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
 | 
						|
{
 | 
						|
  if (chan >= mChans)
 | 
						|
    return false;
 | 
						|
 | 
						|
  double shift = (double) getFreqShift(mChans);
 | 
						|
 | 
						|
  if (!chan)
 | 
						|
    return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
 | 
						|
 | 
						|
  double center = mRadio->getTxFreq();
 | 
						|
  if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
 | 
						|
    LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
 | 
						|
                << freq / 1e6 << " MHz";
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
 | 
						|
{
 | 
						|
  if (chan >= mChans)
 | 
						|
    return false;
 | 
						|
 | 
						|
  double shift = (double) getFreqShift(mChans);
 | 
						|
 | 
						|
  if (!chan)
 | 
						|
    return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
 | 
						|
 | 
						|
  double center = mRadio->getRxFreq();
 | 
						|
  if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
 | 
						|
    LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
 | 
						|
                << freq / 1e6 << " MHz";
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
 | 
						|
{
 | 
						|
  if (!chan)
 | 
						|
    return mRadio->setRxGain(db);
 | 
						|
  else
 | 
						|
    return mRadio->getRxGain();
 | 
						|
}
 |