mirror of
https://github.com/fairwaves/openbts-2.8.git
synced 2025-10-23 07:42:01 +00:00
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2242 19bc5d8c-e614-43d4-8b26-e1612bc8e597
402 lines
10 KiB
C++
402 lines
10 KiB
C++
/*
|
|
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
|
*
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "rnrad1.h"
|
|
|
|
#include "ad9862.h"
|
|
|
|
using namespace ad9862;
|
|
|
|
|
|
rnrad1Tx::rnrad1Tx (int whichBoard,
|
|
unsigned int wInterpRate,
|
|
const std::string fpgaFilename = "",
|
|
const std::string firmwareFilename = "")
|
|
: rnrad1Core(whichBoard, RAD1_TX_INTERFACE, RAD1_TX_ALTINTERFACE, fpgaFilename, firmwareFilename, false)
|
|
{
|
|
mSwMux = 0x8;
|
|
mHwMux = 0x81;
|
|
mInterpRate = wInterpRate;
|
|
|
|
mDevHandle = 0;
|
|
mEndptHandle = 0;
|
|
mBytesSeen = 0;
|
|
mEnabled = true;
|
|
|
|
// initialize rx specific registers
|
|
bool result=true;
|
|
result &= write9862(REG_TX_PWR_DN,0);
|
|
result &= write9862(REG_TX_A_OFFSET_LO,0);
|
|
result &= write9862(REG_TX_A_OFFSET_HI,0);
|
|
result &= write9862(REG_TX_B_OFFSET_LO,0);
|
|
result &= write9862(REG_TX_B_OFFSET_HI,0);
|
|
result &= write9862(REG_TX_A_GAIN,TX_X_GAIN_COARSE_FULL);
|
|
result &= write9862(REG_TX_B_GAIN,TX_X_GAIN_COARSE_FULL);
|
|
result &= write9862(REG_TX_PGA,0xff); // maximum gain (0 dB)
|
|
result &= write9862(REG_TX_MISC,0);
|
|
result &= write9862(REG_TX_IF,TX_IF_USE_CLKOUT1
|
|
| TX_IF_I_FIRST
|
|
| TX_IF_INV_TX_SYNC
|
|
| TX_IF_2S_COMP
|
|
| TX_IF_INTERLEAVED);
|
|
result &= write9862(REG_TX_DIGITAL,TX_DIGITAL_2_DATA_PATHS
|
|
| TX_DIGITAL_INTERPOLATE_4X);
|
|
result &= write9862(REG_TX_MODULATOR,TX_MODULATOR_DISABLE_NCO
|
|
| TX_MODULATOR_COARSE_MODULATION_NONE),
|
|
result &= write9862(REG_TX_NCO_FTW_7_0,0);
|
|
result &= write9862(REG_TX_NCO_FTW_15_8,0);
|
|
result &= write9862(REG_TX_NCO_FTW_23_16,0);
|
|
if (!result) {
|
|
LOG(ERR) << "Failed to init AD9862 TX regs";
|
|
exit(1);
|
|
}
|
|
|
|
// Reset the tx path and leave it disabled.
|
|
enable (false);
|
|
|
|
sendRqst(VRQ_FPGA_SET_TX_RESET, 1);
|
|
usleep(10);
|
|
sendRqst(VRQ_FPGA_SET_TX_RESET, 0);
|
|
|
|
setSampleRateDivisor (4); // we're using interp x4
|
|
|
|
// check fusb buffering parameters
|
|
int blockSize = 4096; //fusb::default_block_size();
|
|
int numBlocks = 128; //std::max (1, fusb::default_buffer_size() / blockSize);
|
|
|
|
mDevHandle = fusb::make_devhandle (getHandle(), getContext());
|
|
mEndptHandle = mDevHandle->make_ephandle (RAD1_TX_ENDPOINT, false,
|
|
blockSize, numBlocks);
|
|
|
|
writeFpgaReg(FR_ATR_MASK_0,0);
|
|
writeFpgaReg(FR_ATR_TXVAL_0,0);
|
|
writeFpgaReg(FR_ATR_RXVAL_0,0);
|
|
writeFpgaReg(FR_ATR_MASK_2,0);
|
|
writeFpgaReg(FR_ATR_TXVAL_2,0);
|
|
writeFpgaReg(FR_ATR_RXVAL_2,0);
|
|
|
|
// initialize registers that are common to rx and tx
|
|
result=true;
|
|
result &= write9862 (REG_TX_IF,TX_IF_USE_CLKOUT1 | TX_IF_I_FIRST | TX_IF_2S_COMP | TX_IF_INTERLEAVED);
|
|
result &= write9862 (REG_TX_DIGITAL, TX_DIGITAL_2_DATA_PATHS | TX_DIGITAL_INTERPOLATE_4X);
|
|
if (!result){
|
|
LOG(ERR) << "failed to init AD9862 TX regs";
|
|
exit(1);
|
|
}
|
|
writeHwMuxReg ();
|
|
if (!setInterpRate (mInterpRate)){
|
|
LOG(ERR) << "failed to set interpolation rate";
|
|
exit(1);
|
|
}
|
|
int mux = 0x00000098;
|
|
if (!setMux (mux)){
|
|
LOG(ERR) << "failed to set tx mux";
|
|
exit(1);
|
|
}
|
|
|
|
mTxModulatorShadow = (TX_MODULATOR_DISABLE_NCO
|
|
| TX_MODULATOR_COARSE_MODULATION_NONE);
|
|
setTxFreq (0);
|
|
}
|
|
|
|
|
|
bool rnrad1Tx::enable (bool on)
|
|
{
|
|
mEnabled = on;
|
|
return (sendRqst(VRQ_FPGA_SET_TX_ENABLE, on) == 0);
|
|
}
|
|
|
|
bool rnrad1Tx::disable () // conditional disable, return prev state
|
|
{
|
|
bool enabled = enable ();
|
|
if (enabled) enable (false);
|
|
return enabled;
|
|
}
|
|
|
|
void rnrad1Tx::restore (bool on) // conditional set
|
|
{
|
|
if (on != enable ()) enable (on);
|
|
}
|
|
|
|
bool rnrad1Tx::writeHwMuxReg()
|
|
{
|
|
bool s = disable ();
|
|
bool ok = writeFpgaReg (FR_TX_MUX, mHwMux | 1);
|
|
restore (s);
|
|
return ok;
|
|
}
|
|
|
|
bool rnrad1Tx::setSampleRateDivisor (unsigned int div)
|
|
{
|
|
return writeFpgaReg (FR_TX_SAMPLE_RATE_DIV, div - 1);
|
|
}
|
|
|
|
int rnrad1Tx::write (const void *buf, int len, bool *underrun)
|
|
{
|
|
int r;
|
|
|
|
if (underrun)
|
|
*underrun = false;
|
|
|
|
if (len < 0 || (len % 512) != 0) {
|
|
LOG(ERR) << "write: invalid length = " << len;
|
|
return -1;
|
|
}
|
|
|
|
r = mEndptHandle->write (buf, len);
|
|
if (r > 0)
|
|
mBytesSeen += r;
|
|
|
|
// NOTE: may get bogus underrun during first write
|
|
if (underrun != 0 && mBytesSeen >= mBytesPerPoll){
|
|
mBytesSeen = 0;
|
|
*underrun = true;
|
|
unsigned char status;
|
|
if (checkUnderrun(&status) != 1)
|
|
LOG(ERR) << "Underrun check failed";
|
|
*underrun = status;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
bool rnrad1Tx::setPga (int amp, double gain)
|
|
{
|
|
if (amp < 0 || amp > 3)
|
|
return false;
|
|
|
|
gain = std::min(pgaMax(),std::max(pgaMin(), gain));
|
|
|
|
int intGain = (int) rint((gain - pgaMin()) / pgaDbPerStep());
|
|
|
|
// 0 and 1 are same, as are 2 and 3
|
|
return write9862(REG_TX_PGA, intGain);
|
|
|
|
}
|
|
|
|
double rnrad1Tx::pga (int amp) const
|
|
{
|
|
if (amp < 0 || amp > 3)
|
|
return READ_FAILED;
|
|
|
|
unsigned char v;
|
|
bool ok = read9862 (REG_TX_PGA, &v);
|
|
if (!ok)
|
|
return READ_FAILED;
|
|
|
|
return (pgaDbPerStep() * v) + pgaMin();
|
|
}
|
|
|
|
bool rnrad1Tx::writeOE (int value, int mask)
|
|
{
|
|
return writeFpgaReg(FR_OE_0,(mask << 16) | (value & 0xffff));
|
|
}
|
|
|
|
bool rnrad1Tx::writeIO (int value, int mask)
|
|
{
|
|
return writeFpgaReg(FR_IO_0,(mask << 16) | (value & 0xffff));
|
|
}
|
|
|
|
bool rnrad1Tx::readIO (int *value)
|
|
{
|
|
int t;
|
|
int reg = 0 + 1; // FIXME, *very* magic number (fix in serial_io.v)
|
|
bool ok = readFpgaReg(reg, &t);
|
|
if (!ok)
|
|
return false;
|
|
|
|
*value = t & 0xffff; // FIXME, more magic
|
|
return true;
|
|
}
|
|
|
|
int rnrad1Tx::readIO (void)
|
|
{
|
|
int value;
|
|
if (!readIO(&value))
|
|
return READ_FAILED;
|
|
return value;
|
|
}
|
|
|
|
|
|
bool rnrad1Tx::writeRefClk(int value)
|
|
{
|
|
return writeFpgaReg(FR_TX_A_REFCLK, value);
|
|
}
|
|
|
|
bool rnrad1Tx::writeAuxDac (int dac, int value)
|
|
{
|
|
return rnrad1Core::writeAuxDac(dac, value);
|
|
}
|
|
|
|
bool rnrad1Tx::readAuxAdc (int adc, int *value)
|
|
{
|
|
return rnrad1Core::readAuxAdc(true, adc, value);
|
|
}
|
|
|
|
int rnrad1Tx::readAuxAdc (int adc)
|
|
{
|
|
int retVal;
|
|
rnrad1Tx::readAuxAdc(adc, &retVal);
|
|
return retVal;
|
|
}
|
|
|
|
rnrad1Tx::~rnrad1Tx ()
|
|
{
|
|
delete mEndptHandle;
|
|
delete mDevHandle;
|
|
|
|
// initialize registers that are common to rx and tx
|
|
bool result=true;
|
|
result &= write9862(REG_TX_PWR_DN, TX_PWR_DN_TX_DIGITAL | TX_PWR_DN_TX_ANALOG_BOTH);
|
|
result &= write9862(REG_TX_MODULATOR, TX_MODULATOR_DISABLE_NCO | TX_MODULATOR_COARSE_MODULATION_NONE);
|
|
}
|
|
|
|
rnrad1Tx* rnrad1Tx::make(int which_board,
|
|
unsigned int wInterpRate,
|
|
const std::string fpgaFilename = "",
|
|
const std::string firmwareFilename = "")
|
|
{
|
|
try {
|
|
rnrad1Tx* u = new rnrad1Tx(which_board, wInterpRate,
|
|
fpgaFilename, firmwareFilename);
|
|
return u;
|
|
}
|
|
catch (...){
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
bool rnrad1Tx::setInterpRate (unsigned int rate)
|
|
{
|
|
if ((rate & 0x3) || rate < 4 || rate > 512){
|
|
LOG(ERR) << "interpolation rate must be in [4, 512] and a multiple of 4";
|
|
return false;
|
|
}
|
|
|
|
mInterpRate = rate;
|
|
setUsbDataRate ((dacRate () / rate) * (2 * sizeof (short)));
|
|
|
|
// We're using the interp by 4 feature of the 9862 so that we can
|
|
// use its fine modulator. Thus, we reduce the FPGA's interpolation rate
|
|
// by a factor of 4.
|
|
|
|
bool s = disable();
|
|
bool ok = writeFpgaReg (FR_INTERP_RATE, mInterpRate/4 - 1);
|
|
restore (s);
|
|
return ok;
|
|
}
|
|
|
|
bool rnrad1Tx::setMux (int mux)
|
|
{
|
|
mSwMux = mux;
|
|
mHwMux = mux << 4;
|
|
return writeHwMuxReg ();
|
|
}
|
|
|
|
bool rnrad1Tx::setTxFreq (double freq)
|
|
{
|
|
|
|
// split freq into fine and coarse components
|
|
|
|
double coarse;
|
|
|
|
double freq1 = dacRate () / 8; // First coarse frequency
|
|
double freq2 = dacRate () / 4; // Second coarse frequency
|
|
double limit1 = freq1 / 2; // Midpoint of [0 , freq1] range
|
|
double limit2 = (freq1 + freq2) / 2; // Midpoint of [freq1 , freq2] range
|
|
double highLimit = (double)44e6; // Highest meaningful frequency
|
|
|
|
if ((freq < -highLimit) || (freq > highLimit))
|
|
return false;
|
|
|
|
mTxModulatorShadow &= ~TX_MODULATOR_CM_MASK;
|
|
if (freq < -limit2) {
|
|
coarse = -freq2;
|
|
mTxModulatorShadow |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4;
|
|
mTxModulatorShadow |= TX_MODULATOR_NEG_COARSE_TUNE;
|
|
}
|
|
else if (freq < -limit1) {
|
|
coarse = -freq1;
|
|
mTxModulatorShadow |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8;
|
|
mTxModulatorShadow |= TX_MODULATOR_NEG_COARSE_TUNE;
|
|
}
|
|
else if (freq < limit1) {
|
|
coarse = 0;
|
|
}
|
|
else if (freq < limit2) {
|
|
coarse = freq1;
|
|
mTxModulatorShadow |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8;
|
|
}
|
|
else if (freq <= highLimit) {
|
|
coarse = freq2;
|
|
mTxModulatorShadow |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4;
|
|
}
|
|
|
|
|
|
double fine = freq - coarse;
|
|
double sign = 2.0*(fine > 0.0) - 1.0;
|
|
unsigned int v = (int) rint (fabs (fine) / (double) (dacRate()/4) * pow (2.0, 24.0));
|
|
mTxFreq = coarse + v * (double) (dacRate()/4) / pow (2.0, 24.0) * sign;
|
|
|
|
bool ok = true;
|
|
ok &= write9862 (REG_TX_NCO_FTW_23_16, (unsigned char) ((v >> 16) & 0xff));
|
|
ok &= write9862 (REG_TX_NCO_FTW_15_8, (unsigned char) ((v >> 8) & 0xff));
|
|
ok &= write9862 (REG_TX_NCO_FTW_7_0, (unsigned char) (v & 0xff));
|
|
|
|
mTxModulatorShadow |= TX_MODULATOR_ENABLE_NCO;
|
|
|
|
if (fine < 0)
|
|
mTxModulatorShadow |= TX_MODULATOR_NEG_FINE_TUNE;
|
|
else
|
|
mTxModulatorShadow &= ~TX_MODULATOR_NEG_FINE_TUNE;
|
|
|
|
ok &= write9862 (REG_TX_MODULATOR, mTxModulatorShadow);
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
bool rnrad1Tx::start()
|
|
{
|
|
if (!rnrad1Core::start ())
|
|
return false;
|
|
|
|
if (!enable (true)){
|
|
LOG(ERR) << "Can't enable TX";
|
|
return false;
|
|
}
|
|
|
|
if (!mEndptHandle->start ()){
|
|
LOG(ERR) << "Can't start USB TX stream";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|