diff --git a/software/soapy/CMakeLists.txt b/software/soapy/CMakeLists.txt index c8ca0686..4b93ec55 100644 --- a/software/soapy/CMakeLists.txt +++ b/software/soapy/CMakeLists.txt @@ -12,6 +12,9 @@ if(NOT CMAKE_BUILD_TYPE) endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + ######################################################################## # Header and library resources needed to communicate with the device. # These may be found within the build tree or in an external project. diff --git a/software/soapy/src/AD9361.cpp b/software/soapy/src/AD9361.cpp index fe46a2d2..37c5e10a 100644 --- a/software/soapy/src/AD9361.cpp +++ b/software/soapy/src/AD9361.cpp @@ -1082,12 +1082,6 @@ void AD9361::reset() void AD9361::clear() { this->current_table = -1; - this->bypass_tx_fir = true; - this->bypass_rx_fir = true; - this->rate_governor = 1; - this->rfdc_track_en = true; - this->bbdc_track_en = true; - this->quad_track_en = true; this->prev_ensm_state = 0; this->curr_ensm_state = 0; this->auto_cal_en = false; @@ -1936,7 +1930,7 @@ void AD9361::setupGainControl(AD9361::GainControl *ctrl) this->writeRegField(AD9361_REG_RX1_MANUAL_LPF_GAIN, POWER_MEAS_IN_STATE_5(~0), reg); this->writeRegField(AD9361_REG_RX1_MANUAL_LMT_FULL_GAIN, POWER_MEAS_IN_STATE_5_MSB, reg >> 3); - return this->updateGainControl(); + this->updateGainControl(); } void AD9361::updateGainControl() { @@ -2205,6 +2199,35 @@ void AD9361::setupRSSI(AD9361::RSSIControl *ctrl, bool is_update) this->writeReg(AD9361_REG_RSSI_CONFIG, temp); // RSSI Mode Select } +void AD9361::readRSSI(uint8_t rx_id, double *symbol, double *preamble) +{ + if(rx_id != 1 && rx_id != 2) + throw std::invalid_argument("AD9361: Unknown RX path " + std::to_string(rx_id)); + + if(!symbol && !preamble) + throw std::invalid_argument("AD9361: At least one of symbol or preamble must be provided"); + + uint8_t reg[6]; + + this->readReg(AD9361_REG_PREAMBLE_LSB, reg, 6); + + if(rx_id == 1) + { + if(symbol) + *symbol = RSSI_OFFSET + RSSI_RESOLUTION * (double)((reg[5] << RSSI_LSB_SHIFT) + (reg[1] & RSSI_LSB_MASK1)); + + if(preamble) + *preamble = RSSI_OFFSET + RSSI_RESOLUTION * (double)((reg[4] << RSSI_LSB_SHIFT) + (reg[0] & RSSI_LSB_MASK1)); + } + else if(rx_id == 2) + { + if(symbol) + *symbol = RSSI_OFFSET + RSSI_RESOLUTION * (double)((reg[3] << RSSI_LSB_SHIFT) + ((reg[1] & RSSI_LSB_MASK2) >> 1)); + + if(preamble) + *preamble = RSSI_OFFSET + RSSI_RESOLUTION * (double)((reg[2] << RSSI_LSB_SHIFT) + ((reg[0] & RSSI_LSB_MASK2) >> 1)); + } +} double AD9361::getTemperature() { @@ -2588,7 +2611,7 @@ void AD9361::setBBPLLRate(AD9361::ClockScale *clk_scale, uint32_t rate, uint32_t this->writeReg(AD9361_REG_VCO_PROGRAM_2, 0x01); // Increase BBPLL KV and phase margin this->writeReg(AD9361_REG_VCO_PROGRAM_2, 0x05); // Increase BBPLL KV and phase margin - this->checkCalibrationDone(AD9361_REG_CH_1_OVERFLOW, BBPLL_LOCK, 1); + this->checkCalibrationDone(AD9361_REG_CH_1_OVERFLOW, BBPLL_LOCK, 1, 1000); } uint64_t AD9361::roundIntRFPLLRate(AD9361::ClockScale *clk_scale, uint64_t rate, uint32_t prate) @@ -2999,7 +3022,7 @@ void AD9361::setClockScaler(AD9361::ClockScale *clk_scale, bool set) if(clk_scale->mult != 1 || clk_scale->div > 4 || clk_scale->div < 1 || clk_scale->div == 3) throw std::invalid_argument("AD9361: RX_SAMPL clock divider must be 1, 2 or 4"); - uint8_t val = this->bypass_rx_fir ? 0 : (Utils::Ilog2((uint8_t)clk_scale->div) + 1); + uint8_t val = this->bypass_rx_fir ? 0 : ((clk_scale->div == 4) ? 3 : clk_scale->div); if(set) this->writeRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), val); @@ -3046,7 +3069,7 @@ void AD9361::setClockScaler(AD9361::ClockScale *clk_scale, bool set) if(clk_scale->mult != 1 || clk_scale->div > 4 || clk_scale->div < 1 || clk_scale->div == 3) throw std::invalid_argument("AD9361: TX_SAMPL clock divider must be 1, 2 or 4"); - uint8_t val = this->bypass_tx_fir ? 0 : (Utils::Ilog2((uint8_t)clk_scale->div) + 1); + uint8_t val = this->bypass_tx_fir ? 0 : ((clk_scale->div == 4) ? 3 : clk_scale->div); if(set) this->writeRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), val); @@ -3416,6 +3439,195 @@ void AD9361::getClockChain(uint32_t *rx_clocks, uint32_t *tx_clocks) } } +void AD9361::verifyFIRCoefficients(AD9361::FIRDest dest, const int16_t *coefs, uint8_t count) +{ + uint8_t gain = 0; + uint32_t offs = 0; + + if(dest & FIR_IS_RX) + { + gain = this->readReg(AD9361_REG_RX_FILTER_GAIN); + offs = AD9361_REG_RX_FILTER_COEF_ADDR - AD9361_REG_TX_FILTER_COEF_ADDR; + + this->writeReg(AD9361_REG_RX_FILTER_GAIN, 0); + } + + uint8_t conf = this->readReg(AD9361_REG_TX_FILTER_CONF + offs); + uint8_t sel; + uint8_t cnt; + + if((dest & 3) == 3) + { + sel = 1; + cnt = 2; + } + else + { + sel = (dest & 3); + cnt = 1; + } + + uint8_t mismatch = 0; + + for(; cnt > 0; cnt--, sel++) + { + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, FIR_NUM_TAPS(count / 16 - 1) | FIR_SELECT(sel) | FIR_START_CLK); + + for(uint8_t val = 0; val < count; val++) + { + this->writeReg(AD9361_REG_TX_FILTER_COEF_ADDR + offs, val); + + int16_t tmp = ((int16_t)this->readReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) | ((int16_t)this->readReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8); + + if(tmp != coefs[val]) + { + mismatch = val | 0x80; + + break; + } + } + + if(mismatch) + break; + } + + if(dest & FIR_IS_RX) + this->writeReg(AD9361_REG_RX_FILTER_GAIN, gain); + + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, conf); + + if(mismatch) + throw std::runtime_error("AD9361: FIR coefficient mismatch at index " + std::to_string(mismatch & 0x7F)); +} +void AD9361::loadFIRCoefficients(AD9361::FIRDest dest, const int16_t *coefs, uint8_t count, uint8_t int_dec, int8_t gain) +{ + if(!coefs) + throw std::invalid_argument("AD9361: Invalid FIR coefficients"); + + if(!count || count > 128 || count % 16) + throw std::invalid_argument("AD9361: Invalid FIR coefficient count"); + + if(dest & FIR_IS_RX && gain != -12 && gain != -6 && gain != 0 && gain != 6) + throw std::invalid_argument("AD9361: Invalid FIR gain"); + + if(!(dest & FIR_IS_RX) && gain != -6 && gain != 0) + throw std::invalid_argument("AD9361: Invalid FIR gain"); + + if(int_dec != 1 && int_dec != 2 && int_dec != 4) + throw std::invalid_argument("AD9361: Invalid FIR interpolation/decimation"); + + this->forceENSMState(ENSM_STATE_ALERT); + + uint8_t fir_conf = 0; + uint8_t fir_enable; + uint32_t offs = 0; + + if(dest & FIR_IS_RX) + { + this->writeReg(AD9361_REG_RX_FILTER_GAIN, (3 - (gain + 12) / 6) & 0x3); + + offs = AD9361_REG_RX_FILTER_COEF_ADDR - AD9361_REG_TX_FILTER_COEF_ADDR; + + this->rx_fir_ntaps = count; + this->rx_fir_dec = int_dec; + + fir_enable = this->readRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); + + this->writeRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), (int_dec == 4) ? 3 : int_dec); + } + else + { + if(gain == -6) + fir_conf = TX_FIR_GAIN_6DB; + + this->tx_fir_ntaps = count; + this->tx_fir_int = int_dec; + + fir_enable = this->readRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); + this->writeRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), (int_dec == 4) ? 3 : int_dec); + } + + fir_conf |= FIR_NUM_TAPS(count / 16 - 1) | FIR_SELECT(dest) | FIR_START_CLK; + + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); + + for(uint8_t val = 0; val < count; val++) + { + this->writeReg(AD9361_REG_TX_FILTER_COEF_ADDR + offs, val); + this->writeReg(AD9361_REG_TX_FILTER_COEF_WRITE_DATA_1 + offs, coefs[val] & 0xFF); + this->writeReg(AD9361_REG_TX_FILTER_COEF_WRITE_DATA_2 + offs, coefs[val] >> 8); + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf | FIR_WRITE); + this->writeReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + this->writeReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + } + + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); + + fir_conf &= ~FIR_START_CLK; + + this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); + + if(dest & FIR_IS_RX) + this->writeRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), fir_enable); + else + this->writeRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable); + + this->restorePrevENSMState(); +} +void AD9361::validateAndEnableFIR() +{ + if(!this->bypass_tx_fir) + { + if(!(this->tx_fir_int == 1 || this->tx_fir_int == 2 || this->tx_fir_int == 4)) + throw new std::runtime_error("AD9361: Invalid interpolation " + std::to_string(this->tx_fir_int) + " in filter config"); + + if(this->tx_fir_int == 1 && this->tx_fir_ntaps > 64) + throw new std::runtime_error("AD9361: Invalid: ntaps > 64 and interpolation = 1"); + } + + if(!this->bypass_rx_fir) + { + if(!(this->rx_fir_dec == 1 || this->rx_fir_dec == 2 || this->rx_fir_dec == 4)) + throw new std::runtime_error("AD9361: Invalid decimation " + std::to_string(this->rx_fir_dec) + " in filter config"); + } + + uint32_t rx[6], tx[6]; + + try + { + this->calcClockChain(this->getClockRate(this->ref_clk_scale[TX_SAMPL_CLK]), this->rate_governor, rx, tx); + } + catch(const std::exception &e) + { + this->calcClockChain(MIN_BASEBAND_RATE_NOFIR, this->rate_governor, rx, tx); // If this fails we are out of luck + } + + if(!this->bypass_tx_fir) + { + uint32_t max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16; + + if(this->tx_fir_ntaps > max) + throw new std::runtime_error("AD9361: Invalid: ratio DAC / TX_SAMPL * 16 > ntaps (max " + std::to_string(max) + ", dac " + std::to_string(tx[DAC_FREQ]) + ", tx " + std::to_string(tx[TX_SAMPL_FREQ]) + ")"); + } + + if(!this->bypass_rx_fir) + { + uint32_t max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) / rx[RX_SAMPL_FREQ]) * 16; + + if(this->rx_fir_ntaps > max) + throw new std::runtime_error("AD9361: Invalid: ratio ADC/2 / RX_SAMPL * 16 > ntaps (max " + std::to_string(max) + ", adc " + std::to_string(rx[ADC_FREQ]) + ", r2 " + std::to_string(rx[R2_FREQ]) + ", rx " + std::to_string(rx[RX_SAMPL_FREQ]) + ")"); + } + + this->setClockChain(rx, tx); + + // See also: this->setClockChain() + // TODO: Re-enable this + // if(!this->pdata->dig_interface_tune_fir_disable && this->bypass_tx_fir && this->bypass_rx_fir) + // ad9361_util_dig_tune(0, RESTORE_PREVIOUS); + + this->setRFBandwidth(this->current_rx_bw_Hz, this->current_tx_bw_Hz); +} + void AD9361::checkCalibrationDone(uint16_t reg, uint8_t mask, uint8_t done_val, uint32_t timeout_ms) { uint32_t timeout_us = timeout_ms * 1000; @@ -4598,6 +4810,54 @@ void AD9361::setRXGain(uint8_t rx_id, AD9361::RFRXGain *rx_gain) else this->setFullTableGain(idx_reg, rx_gain); } +void AD9361::setRXGainMode(uint8_t rx_id, AD9361::RFGainCtrlMode mode) +{ + uint8_t gain_ctl_shift; + + if(rx_id == 1) + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + else if(rx_id == 2) + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + else + throw std::invalid_argument("AD9361: Unknown RX path " + std::to_string(rx_id)); + + uint8_t _mode; + + switch(mode) + { + case RF_GAIN_MGC: + _mode = RX_GAIN_CTL_MGC; + break; + case RF_GAIN_FASTATTACK_AGC: + _mode = RX_GAIN_CTL_AGC_FAST_ATK; + break; + case RF_GAIN_SLOWATTACK_AGC: + _mode = RX_GAIN_CTL_AGC_SLOW_ATK; + break; + case RF_GAIN_HYBRID_AGC: + _mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD; + break; + default: + throw std::invalid_argument("AD9361: Invalid RX gain control mode"); + break; + } + + uint8_t val = this->readReg(AD9361_REG_AGC_CONFIG_1); + this->enableRX(rx_id, RX_DISABLE); + + val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift); + val |= _mode << gain_ctl_shift; + + if(_mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD) + val |= SLOW_ATTACK_HYBRID_MODE; + else + val &= ~SLOW_ATTACK_HYBRID_MODE; + + this->writeReg(AD9361_REG_AGC_CONFIG_1, val); + this->enableRX(rx_id, RX_ENABLE); + + this->updateGainControl(); +} void AD9361::initRFPLLVCO(bool tx, uint64_t freq, uint32_t ref_freq) { @@ -4889,496 +5149,6 @@ void AD9361::doMCSStage(uint8_t stage) } /* -int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_gain_ctrl* gain_ctrl) -{ - int32_t rc = 0; - uint32_t gain_ctl_shift, mode; - uint8_t val; - - rc = this->readReg(AD9361_REG_AGC_CONFIG_1, &val, 1); - - if(rc < 0) - { - DBGPRINTLN_CTX("Unable to read AGC_CONFIG_1 register (%"PRIX32")", AD9361_REG_AGC_CONFIG_1); - - return rc; - } - - switch(gain_ctrl->mode) - { - case RF_GAIN_MGC: - mode = RX_GAIN_CTL_MGC; - break; - case RF_GAIN_FASTATTACK_AGC: - mode = RX_GAIN_CTL_AGC_FAST_ATK; - break; - case RF_GAIN_SLOWATTACK_AGC: - mode = RX_GAIN_CTL_AGC_SLOW_ATK; - break; - case RF_GAIN_HYBRID_AGC: - mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD; - break; - default: - return -EINVAL; - break; - } - - if(gain_ctrl->ant == 1) - { - gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; - } - else if(gain_ctrl->ant == 2) - { - gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; - } - else - { - DBGPRINTLN_CTX("Unknown Rx path %"PRIu32, gain_ctrl->ant); - - return -EINVAL; - } - - rc = this->enableRX(gain_ctrl->ant, RX_DISABLE); - - if(rc < 0) - { - DBGPRINTLN_CTX("Unable to disable Rx%"PRIu32, gain_ctrl->ant); - - return rc; - } - - val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift); - val |= mode << gain_ctl_shift; - - if(mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD) - val |= SLOW_ATTACK_HYBRID_MODE; - else - val &= ~SLOW_ATTACK_HYBRID_MODE; - - rc = this->writeReg(AD9361_REG_AGC_CONFIG_1, val); - - if(rc < 0) - { - DBGPRINTLN_CTX("Unable to write AGC_CONFIG_1 register (%"PRIX32")", AD9361_REG_AGC_CONFIG_1); - - return rc; - } - - this->enableRX(gain_ctrl->ant, RX_ENABLE); - - rc = this->updateGainControl(); - - return rc; -} - -int32_t ad9361_read_rssi(struct ad9361_rf_rssi* rssi) -{ - uint8_t reg_val_buf[6]; - int32_t rc; - - rc = this->readReg(AD9361_REG_PREAMBLE_LSB, reg_val_buf, ARRAY_SIZE(reg_val_buf)); - - if(rssi->ant == 1) - { - rssi->symbol = RSSI_RESOLUTION * ((reg_val_buf[5] << RSSI_LSB_SHIFT) + (reg_val_buf[1] & RSSI_LSB_MASK1)); - rssi->preamble = RSSI_RESOLUTION * ((reg_val_buf[4] << RSSI_LSB_SHIFT) + (reg_val_buf[0] & RSSI_LSB_MASK1)); - } - else if(rssi->ant == 2) - { - rssi->symbol = RSSI_RESOLUTION * ((reg_val_buf[3] << RSSI_LSB_SHIFT) + ((reg_val_buf[1] & RSSI_LSB_MASK2) >> 1)); - rssi->preamble = RSSI_RESOLUTION * ((reg_val_buf[2] << RSSI_LSB_SHIFT) + ((reg_val_buf[0] & RSSI_LSB_MASK2) >> 1)); - } - else - { - rc = -EFAULT; - } - - rssi->multiplier = RSSI_MULTIPLIER; - - return rc; -} - -static int32_t ad9361_verify_fir_filter_coef(enum ad9361_fir_dest dest, uint32_t ntaps, short* coef) -{ - uint32_t val, offs = 0, gain = 0, conf, sel, cnt; - int32_t ret = 0; - - //return 0; - - DBGPRINTLN_CTX("ntaps %"PRIu32", dest %d", ntaps, dest); - - if(dest & FIR_IS_RX) - { - gain = this->readReg(AD9361_REG_RX_FILTER_GAIN); - - offs = AD9361_REG_RX_FILTER_COEF_ADDR - AD9361_REG_TX_FILTER_COEF_ADDR; - - this->writeReg(AD9361_REG_RX_FILTER_GAIN, 0); - } - - conf = this->readReg(AD9361_REG_TX_FILTER_CONF + offs); - - if((dest & 3) == 3) - { - sel = 1; - cnt = 2; - } - else - { - sel = (dest & 3); - cnt = 1; - } - - for(; cnt > 0; cnt--, sel++) - { - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, FIR_NUM_TAPS(ntaps / 16 - 1) | FIR_SELECT(sel) | FIR_START_CLK); - - for(val = 0; val < ntaps; val++) - { - int16_t tmp; - - this->writeReg(AD9361_REG_TX_FILTER_COEF_ADDR + offs, val); - - tmp = (this->readReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) | (this->readReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8); - - if(tmp != coef[val]) - { - DBGPRINTLN_CTX("%s%"PRIu32" readback mismatch at tap %"PRIu32" (%d != %d)", (dest & FIR_IS_RX) ? "RX" : "TX", sel, val, tmp, coef[val]); - - ret = -EIO; - } - } - } - - if(dest & FIR_IS_RX) - this->writeReg(AD9361_REG_RX_FILTER_GAIN, gain); - - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, conf); - - return ret; -} -int32_t ad9361_load_fir_filter_coef(enum ad9361_fir_dest dest, int32_t gain_dB, uint32_t ntaps, int16_t* coef) -{ - uint32_t val, offs = 0, fir_conf = 0, fir_enable = 0; - int32_t ret; - - DBGPRINTLN_CTX("ntaps %"PRIu32", gain %"PRId32" dB, dest %d", ntaps, gain_dB, dest); - - if(coef == NULL || !ntaps || ntaps > 128 || ntaps % 16) - { - DBGPRINTLN_CTX("Invalid parameters"); - - return -EINVAL; - } - - this->forceENSMState(ENSM_STATE_ALERT); - - if(dest & FIR_IS_RX) - { - val = 3 - (gain_dB + 12) / 6; - - this->writeReg(AD9361_REG_RX_FILTER_GAIN, val & 0x3); - - offs = AD9361_REG_RX_FILTER_COEF_ADDR - AD9361_REG_TX_FILTER_COEF_ADDR; - this->rx_fir_ntaps = ntaps; - fir_enable = this->readRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); - - this->writeRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), (this->rx_fir_dec == 4) ? 3 : this->rx_fir_dec); - } - else - { - if(gain_dB == -6) - fir_conf = TX_FIR_GAIN_6DB; - - this->tx_fir_ntaps = ntaps; - - fir_enable = this->readRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); - this->writeRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), (this->tx_fir_int == 4) ? 3 : this->tx_fir_int); - } - - val = ntaps / 16 - 1; - - fir_conf |= FIR_NUM_TAPS(val) | FIR_SELECT(dest) | FIR_START_CLK; - - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); - - for(val = 0; val < ntaps; val++) - { - this->writeReg(AD9361_REG_TX_FILTER_COEF_ADDR + offs, val); - this->writeReg(AD9361_REG_TX_FILTER_COEF_WRITE_DATA_1 + offs, coef[val] & 0xFF); - this->writeReg(AD9361_REG_TX_FILTER_COEF_WRITE_DATA_2 + offs, coef[val] >> 8); - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf | FIR_WRITE); - this->writeReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); - this->writeReg(AD9361_REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); - } - - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); - - fir_conf &= ~FIR_START_CLK; - - this->writeReg(AD9361_REG_TX_FILTER_CONF + offs, fir_conf); - - ret = ad9361_verify_fir_filter_coef(dest, ntaps, coef); - - if(dest & FIR_IS_RX) - this->writeRegField(AD9361_REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), fir_enable); - else - this->writeRegField(AD9361_REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable); - - this->restorePrevENSMState(); - - return ret; -} -int32_t ad9361_parse_fir(char* data, uint32_t size) -{ - char* line; - int32_t i = 0, ret, txc, rxc; - int32_t tx = -1, tx_gain = 0, tx_int = 0; - int32_t rx = -1, rx_gain = 0, rx_dec = 0; - int32_t rtx = -1, rrx = -1; - int16_t coef_tx[128]; - int16_t coef_rx[128]; - char* ptr = data; - - this->filt_rx_bw_Hz = 0; - this->filt_tx_bw_Hz = 0; - this->filt_valid = false; - - while((line = strsep(&ptr, "\n"))) - { - if(line >= data + size) - { - break; - } - - if(line[0] == '#') - continue; - - if(tx < 0) - { - ret = sscanf(line, "TX %"PRId32" GAIN %"PRId32" INT %"PRId32, &tx, &tx_gain, &tx_int); - - if(ret == 3) - continue; - else - tx = -1; - } - if(rx < 0) - { - ret = sscanf(line, "RX %"PRId32" GAIN %"PRId32" DEC %"PRId32, &rx, &rx_gain, &rx_dec); - - if(ret == 3) - continue; - else - tx = -1; - } - - if(rtx < 0) - { - ret = sscanf(line, "RTX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, &this->filt_tx_path_clks[0], &this->filt_tx_path_clks[1], &this->filt_tx_path_clks[2], &this->filt_tx_path_clks[3], &this->filt_tx_path_clks[4], &this->filt_tx_path_clks[5]); - - if(ret == 6) - { - rtx = 0; - - continue; - } - else - { - rtx = -1; - } - } - - if(rrx < 0) - { - ret = sscanf(line, "RRX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, &this->filt_rx_path_clks[0], &this->filt_rx_path_clks[1], &this->filt_rx_path_clks[2], &this->filt_rx_path_clks[3], &this->filt_rx_path_clks[4], &this->filt_rx_path_clks[5]); - - if(ret == 6) - { - rrx = 0; - continue; - } - else - { - rrx = -1; - } - } - - if(!this->filt_rx_bw_Hz) - { - ret = sscanf(line, "BWRX %"PRId32, &this->filt_rx_bw_Hz); - - if(ret == 1) - continue; - else - this->filt_rx_bw_Hz = 0; - } - - if(!this->filt_tx_bw_Hz) - { - ret = sscanf(line, "BWTX %"PRId32, &this->filt_tx_bw_Hz); - - if(ret == 1) - continue; - else - this->filt_tx_bw_Hz = 0; - } - - ret = sscanf(line, "%"PRId32",%"PRId32, &txc, &rxc); - - if(ret == 1) - { - coef_tx[i] = coef_rx[i] = (int16_t)txc; - - i++; - - continue; - } - else if(ret == 2) - { - coef_tx[i] = (int16_t)txc; - coef_rx[i] = (int16_t)rxc; - - i++; - - continue; - } - } - - switch(tx) - { - case FIR_TX1: - case FIR_TX2: - case FIR_TX1_TX2: - this->tx_fir_int = tx_int; - ret = ad9361_load_fir_filter_coef((enum ad9361_fir_dest)tx, tx_gain, i, coef_tx); - break; - default: - ret = -EINVAL; - } - - switch(rx | FIR_IS_RX) - { - case FIR_RX1: - case FIR_RX2: - case FIR_RX1_RX2: - this->rx_fir_dec = rx_dec; - ret = ad9361_load_fir_filter_coef((enum ad9361_fir_dest)(rx | FIR_IS_RX), rx_gain, i, coef_rx); - break; - default: - ret = -EINVAL; - } - - if(ret < 0) - return ret; - - if(!(rrx | rtx)) - this->filt_valid = true; - - return size; -} -int32_t ad9361_validate_enable_fir() -{ - int32_t ret; - uint32_t rx[6], tx[6]; - uint32_t max, min, valid; - - DBGPRINTLN_CTX("TX FIR - enable: %d, ntaps: %d, int: %d", !this->bypass_tx_fir, this->tx_fir_ntaps, this->tx_fir_int); - DBGPRINTLN_CTX("RX FIR - enable: %d, ntaps: %d, dec: %d", !this->bypass_rx_fir, this->rx_fir_ntaps, this->rx_fir_dec); - - if(!this->bypass_tx_fir) - { - if(!(this->tx_fir_int == 1 || this->tx_fir_int == 2 || this->tx_fir_int == 4)) - { - DBGPRINTLN_CTX("Invalid interpolation %d in filter config", this->tx_fir_int); - - return -EINVAL; - } - - - if(this->tx_fir_int == 1 && this->tx_fir_ntaps > 64) - { - DBGPRINTLN_CTX("Invalid: ntaps > 64 and interpolation = 1"); - - return -EINVAL; - } - } - - if(!this->bypass_rx_fir) - { - if(!(this->rx_fir_dec == 1 || this->rx_fir_dec == 2 || this->rx_fir_dec == 4)) - { - DBGPRINTLN_CTX("Invalid decimation %d in filter config", this->rx_fir_dec); - - return -EINVAL; - } - } - - if(!this->filt_valid || this->bypass_rx_fir || this->bypass_tx_fir) - { - ret = this->calcClockChain(this->getClockRate(this->ref_clk_scale[TX_SAMPL_CLK]), this->rate_governor, rx, tx); - - if(ret < 0) - { - min = this->rate_governor ? 1500000U : 1000000U; - - DBGPRINTLN_CTX("Calculating filter rates failed (%"PRId32"), using min frequency", ret); - - ret = this->calcClockChain(min, this->rate_governor, rx, tx); - - if(ret < 0) - return ret; - } - - valid = false; - } - else - { - memcpy(rx, this->filt_rx_path_clks, sizeof(rx)); - memcpy(tx, this->filt_tx_path_clks, sizeof(tx)); - - valid = true; - } - - DBGPRINTLN_CTX("RX Rates - BBPLL: %"PRIu32" Hz, ADC: %"PRIu32" Hz, R2CLK: %"PRIu32" Hz, R1CLK: %"PRIu32" Hz, CLKRF: %"PRIu32" Hz, RSAMPL: %"PRIu32" Hz", rx[BBPLL_FREQ], rx[ADC_FREQ], rx[R2_FREQ], rx[R1_FREQ], rx[CLKRF_FREQ], rx[RX_SAMPL_FREQ]); - DBGPRINTLN_CTX("TX Rates - BBPLL: %"PRIu32" Hz, DAC: %"PRIu32" Hz, T2CLK: %"PRIu32" Hz, T1CLK: %"PRIu32" Hz, CLKTF: %"PRIu32" Hz, TSAMPL: %"PRIu32" Hz", tx[BBPLL_FREQ], tx[DAC_FREQ], tx[T2_FREQ], tx[T1_FREQ], tx[CLKTF_FREQ], tx[TX_SAMPL_FREQ]); - - if(!this->bypass_tx_fir) - { - max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16; - - if(this->tx_fir_ntaps > max) - { - DBGPRINTLN_CTX("Invalid: ratio DAC / TX_SAMPL * 16 > ntaps (max %"PRIu32", adc %"PRIu32", tx %"PRIu32")", max, tx[DAC_FREQ], tx[TX_SAMPL_FREQ]); - - return -EINVAL; - } - } - - if(!this->bypass_rx_fir) - { - max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) / rx[RX_SAMPL_FREQ]) * 16; - - if(this->rx_fir_ntaps > max) - { - DBGPRINTLN_CTX("Invalid: ratio ADC/2 / RX_SAMPL * 16 > ntaps (max %"PRIu32")", max); - - return -EINVAL; - } - } - - ret = this->setClockChain(rx, tx); - - if(ret < 0) - return ret; - - // See also: this->setClockChain() - if(!this->pdata->dig_interface_tune_fir_disable && this->bypass_tx_fir && this->bypass_rx_fir) - ad9361_util_dig_tune(0, RESTORE_PREVIOUS); - - return ad9361_update_rf_bandwidth(valid ? this->filt_rx_bw_Hz : this->current_rx_bw_Hz, valid ? this->filt_tx_bw_Hz : this->current_tx_bw_Hz); -} - int32_t ad9361_rssi_gain_step_calib() { uint32_t lna_error[4]; @@ -5471,5 +5241,4 @@ int32_t ad9361_rssi_gain_step_calib() return 0; } - */ \ No newline at end of file diff --git a/software/soapy/src/AXIDMAC.cpp b/software/soapy/src/AXIDMAC.cpp index 35775044..44b3e7c1 100644 --- a/software/soapy/src/AXIDMAC.cpp +++ b/software/soapy/src/AXIDMAC.cpp @@ -10,7 +10,7 @@ void AXIDMAC::ISR(void *_this) void AXIDMAC::handleIRQ() { - std::unique_lock lock(this->mutex); + std::unique_lock lock(this->mutex); uint32_t pend = this->readReg(AXI_DMAC_REG_IRQ_PENDING); this->writeReg(AXI_DMAC_REG_IRQ_PENDING, pend); // Clear pending IRQs @@ -183,7 +183,7 @@ AXIDMAC::Capabilities AXIDMAC::getCapabilities() void AXIDMAC::enable(bool enable) { - std::lock_guard lock(this->mutex); + std::lock_guard lock(this->mutex); uint32_t ctrl = this->readReg(AXI_DMAC_REG_CONTROL); @@ -206,7 +206,7 @@ bool AXIDMAC::enabled() } void AXIDMAC::pause(bool pause) { - std::lock_guard lock(this->mutex); + std::lock_guard lock(this->mutex); uint32_t ctrl = this->readReg(AXI_DMAC_REG_CONTROL); @@ -231,37 +231,42 @@ bool AXIDMAC::idle() void AXIDMAC::waitIdle(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool idle = this->idle(); - while(--timeout && !this->idle()) + while(--timeout && !idle) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->idle()) + idle = this->idle(); + } + + if(!idle) throw std::runtime_error("AXI DMAC: Timed out waiting for controller to be idle"); // Wait for all transfer callbacks to complete - bool all_complete = false; + idle = false; - while(--timeout && !all_complete) + while(--timeout && !idle) { - all_complete = true; + idle = true; for(uint8_t i = 0; i < AXI_DMAC_NUM_TRANSFERS; i++) { if(!this->transfer_callback_done[i].load()) { - all_complete = false; + idle = false; break; } } - if(all_complete) + if(idle) break; std::this_thread::sleep_for(std::chrono::microseconds(100)); } - if(!all_complete) + if(!idle) throw std::runtime_error("AXI DMAC: Timed out waiting for transfer callbacks to complete"); } @@ -270,34 +275,70 @@ void AXIDMAC::setTransferCallback(uint8_t id, AXIDMAC::Transfer::Callback callba if(id >= AXI_DMAC_NUM_TRANSFERS) throw std::invalid_argument("AXI DMAC: Invalid transfer ID"); - std::lock_guard lock(this->mutex); + std::lock_guard lock(this->mutex); this->transfers[id].cb = callback; this->transfers[id].cb_arg = arg; } void AXIDMAC::submitTransfer(AXIDMAC::Transfer &transfer) { + if(transfer.size > this->capabilities.max_transfer_size) + throw std::invalid_argument("AXI DMAC: Transfer length " + std::to_string(transfer.size) + " is out of range (max: " + std::to_string(this->capabilities.max_transfer_size) + ")"); + if(transfer.flags & AXIDMAC::Transfer::Flags::CYCLIC) { if(!this->capabilities.cyclic_support) - throw std::runtime_error("AXI DMAC: Cyclic transfers are not supported"); + throw std::invalid_argument("AXI DMAC: Cyclic transfers are not supported"); if(this->capabilities.transfer_mode != AXIDMAC::TransferMode::MEM_TO_DEV) - throw std::runtime_error("AXI DMAC: Cyclic transfers are only supported in memory to device mode"); + throw std::invalid_argument("AXI DMAC: Cyclic transfers are only supported in memory to device mode"); } - if((this->capabilities.transfer_mode & AXIDMAC::TransferMode::DEV_TO_MEM) && (transfer.dest_addr & ~this->capabilities.dest_addr_mask)) - throw std::runtime_error("AXI DMAC: Destination address 0x" + std::to_string(transfer.dest_addr) + " is out of range (mask: 0x" + std::to_string(this->capabilities.dest_addr_mask) + ")"); + if(this->capabilities.transfer_mode & AXIDMAC::TransferMode::DEV_TO_MEM) + { + if(transfer.dest_addr & ~this->capabilities.dest_addr_mask) + throw std::invalid_argument("AXI DMAC: Destination address is out of range"); + + if(transfer.dest_addr & (this->capabilities.bytes_per_burst - 1)) + throw std::invalid_argument("AXI DMAC: Destination address is not aligned with burst size"); - if((this->capabilities.transfer_mode & AXIDMAC::TransferMode::MEM_TO_DEV) && (transfer.src_addr & ~this->capabilities.src_addr_mask)) - throw std::runtime_error("AXI DMAC: Source address 0x" + std::to_string(transfer.src_addr) + " is out of range (mask: 0x" + std::to_string(this->capabilities.src_addr_mask) + ")"); + if((transfer.dest_addr & 0xFFF) + std::min(transfer.size, (uint32_t)this->capabilities.bytes_per_burst) > 0x1000) + throw std::invalid_argument("AXI DMAC: Destination address crosses 4K boundary"); - if(transfer.size > this->capabilities.max_transfer_size) - throw std::runtime_error("AXI DMAC: Transfer length " + std::to_string(transfer.size) + " is out of range (max: " + std::to_string(this->capabilities.max_transfer_size) + ")"); + if(!(this->capabilities.transfer_mode & AXIDMAC::TransferMode::MEM_TO_DEV)) // If not memory-to-memory xfer + { + if(transfer.size & ((this->capabilities.src_data_width >> 3) - 1)) + throw std::invalid_argument("AXI DMAC: Transfer length is not multiple of destination data bus"); + } + } + + if(this->capabilities.transfer_mode & AXIDMAC::TransferMode::MEM_TO_DEV) + { + if(transfer.src_addr & ~this->capabilities.src_addr_mask) + throw std::invalid_argument("AXI DMAC: Source address is out of range"); + + if(transfer.src_addr & (this->capabilities.bytes_per_burst - 1)) + throw std::invalid_argument("AXI DMAC: Source address is not aligned with burst size"); + + if((transfer.src_addr & 0xFFF) + std::min(transfer.size, (uint32_t)this->capabilities.bytes_per_burst) > 0x1000) + throw std::invalid_argument("AXI DMAC: Source address crosses 4K boundary"); + + if(!(this->capabilities.transfer_mode & AXIDMAC::TransferMode::DEV_TO_MEM)) // If not memory-to-memory xfer + { + if(transfer.size & ((this->capabilities.dest_data_width >> 3) - 1)) + throw std::invalid_argument("AXI DMAC: Transfer length is not multiple of source data bus"); + } + } + + if(this->capabilities.transfer_mode == AXIDMAC::TransferMode::DEV_TO_DEV) + { + if(transfer.size & ((std::max(this->capabilities.src_data_width, this->capabilities.dest_data_width) >> 3) - 1)) + throw std::invalid_argument("AXI DMAC: Transfer length is not multiple of widest data bus"); + } - std::lock_guard lock(this->mutex); + std::lock_guard lock(this->mutex); - if(!this->enabled()) + if(!(this->readReg(AXI_DMAC_REG_CONTROL) & AXI_DMAC_REG_CONTROL_ENABLE)) throw std::runtime_error("AXI DMAC: Cannot submit transfer while disabled"); bool available = !this->readReg(AXI_DMAC_REG_XFER_SUBMIT); diff --git a/software/soapy/src/AXIRFTStamp.cpp b/software/soapy/src/AXIRFTStamp.cpp index f82bccf7..c59edd53 100644 --- a/software/soapy/src/AXIRFTStamp.cpp +++ b/software/soapy/src/AXIRFTStamp.cpp @@ -80,11 +80,16 @@ bool AXIRFTStamp::isClockSynced() void AXIRFTStamp::waitForClockSync(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isClockSynced(); - while(--timeout && !this->isClockSynced()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isClockSynced()) + done = this->isClockSynced(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for clock synchronization"); } void AXIRFTStamp::triggerClockResync(bool wait, uint32_t timeout_ms) @@ -209,41 +214,61 @@ bool AXIRFTStamp::isTXEnabled(AXIRFTStamp::Channel ch) void AXIRFTStamp::waitTXEnabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXEnabled(); - while(--timeout && !this->isTXEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXEnabled()) + done = this->isTXEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX enable"); } void AXIRFTStamp::waitTXEnabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXEnabled(ch); - while(--timeout && !this->isTXEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXEnabled(ch)) + done = this->isTXEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " enable"); } void AXIRFTStamp::waitTXDisabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isTXEnabled(); - while(--timeout && this->isTXEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isTXEnabled()) + done = !this->isTXEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX disable"); } void AXIRFTStamp::waitTXDisabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isTXEnabled(ch); - while(--timeout && this->isTXEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isTXEnabled(ch)) + done = !this->isTXEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " disable"); } void AXIRFTStamp::enableRX(bool enable) @@ -281,41 +306,61 @@ bool AXIRFTStamp::isRXEnabled(AXIRFTStamp::Channel ch) void AXIRFTStamp::waitRXEnabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXEnabled(); - while(--timeout && !this->isRXEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXEnabled()) + done = this->isRXEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX enable"); } void AXIRFTStamp::waitRXEnabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXEnabled(ch); - while(--timeout && !this->isRXEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXEnabled(ch)) + done = this->isRXEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " enable"); } void AXIRFTStamp::waitRXDisabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isRXEnabled(); - while(--timeout && this->isRXEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isRXEnabled()) + done = !this->isRXEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX disable"); } void AXIRFTStamp::waitRXDisabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isRXEnabled(ch); - while(--timeout && this->isRXEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isRXEnabled(ch)) + done = !this->isRXEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " disable"); } @@ -353,41 +398,61 @@ bool AXIRFTStamp::isTXCounterEnabled(AXIRFTStamp::Channel ch) void AXIRFTStamp::waitTXCounterEnabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXCounterEnabled(); - while(--timeout && !this->isTXCounterEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXCounterEnabled()) + done = this->isTXCounterEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX counter enable"); } void AXIRFTStamp::waitTXCounterEnabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXCounterEnabled(ch); - while(--timeout && !this->isTXCounterEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXCounterEnabled(ch)) - throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " counter enable"); + done = this->isTXCounterEnabled(ch); + } + + if(!done) + throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " enable"); } void AXIRFTStamp::waitTXCounterDisabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isTXCounterEnabled(); - while(--timeout && this->isTXCounterEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isTXCounterEnabled()) + done = !this->isTXCounterEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX counter disable"); } void AXIRFTStamp::waitTXCounterDisabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isTXCounterEnabled(ch); - while(--timeout && this->isTXCounterEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isTXCounterEnabled(ch)) + done = !this->isTXCounterEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " counter disable"); } void AXIRFTStamp::enableRXCounter(bool enable) @@ -415,41 +480,61 @@ bool AXIRFTStamp::isRXCounterEnabled(AXIRFTStamp::Channel ch) void AXIRFTStamp::waitRXCounterEnabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXCounterEnabled(); - while(--timeout && !this->isRXCounterEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXCounterEnabled()) + done = this->isRXCounterEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX counter enable"); } void AXIRFTStamp::waitRXCounterEnabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXCounterEnabled(ch); - while(--timeout && !this->isRXCounterEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXCounterEnabled(ch)) - throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " counter enable"); + done = this->isRXCounterEnabled(ch); + } + + if(!done) + throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " enable"); } void AXIRFTStamp::waitRXCounterDisabled(uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isRXCounterEnabled(); - while(--timeout && this->isRXCounterEnabled()) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isRXCounterEnabled()) + done = !this->isRXCounterEnabled(); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX counter disable"); } void AXIRFTStamp::waitRXCounterDisabled(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = !this->isRXCounterEnabled(ch); - while(--timeout && this->isRXCounterEnabled(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(this->isRXCounterEnabled(ch)) + done = !this->isRXCounterEnabled(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " counter disable"); } @@ -585,41 +670,61 @@ bool AXIRFTStamp::isRXDataReady(AXIRFTStamp::Channel ch) void AXIRFTStamp::waitTXDMAReady(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXDMAReady(ch); - while(--timeout && !this->isTXDMAReady(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXDMAReady(ch)) + done = this->isTXDMAReady(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " DMA ready"); } void AXIRFTStamp::waitRXDMAReady(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXDMAReady(ch); - while(--timeout && !this->isRXDMAReady(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXDMAReady(ch)) + done = this->isRXDMAReady(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " DMA ready"); } void AXIRFTStamp::waitTXDataReady(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isTXDataReady(ch); - while(--timeout && !this->isTXDataReady(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isTXDataReady(ch)) + done = this->isTXDataReady(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for TX" + std::to_string(ch) + " data ready"); } void AXIRFTStamp::waitRXDataReady(AXIRFTStamp::Channel ch, uint32_t timeout_ms) { uint64_t timeout = (uint64_t)timeout_ms * 100ULL; + bool done = this->isRXDataReady(ch); - while(--timeout && !this->isRXDataReady(ch)) + while(--timeout && !done) + { std::this_thread::sleep_for(std::chrono::microseconds(10)); - if(!this->isRXDataReady(ch)) + done = this->isRXDataReady(ch); + } + + if(!done) throw std::runtime_error("AXI RF Timestamping: Timed out waiting for RX" + std::to_string(ch) + " data ready"); } diff --git a/software/soapy/src/IDT8V97003.cpp b/software/soapy/src/IDT8V97003.cpp index 9d8273b6..75d98b4b 100644 --- a/software/soapy/src/IDT8V97003.cpp +++ b/software/soapy/src/IDT8V97003.cpp @@ -1,6 +1,6 @@ #include "IDT8V97003.hpp" -static const double IDT8V97003_kVCO[] = {100e6, 120e6, 140e6, 160e6, 210e6, 165e6, 155e6, 170e6}; +static const double IDT8V97003_kVCO[] = { 100e6, 120e6, 140e6, 160e6, 210e6, 165e6, 155e6, 170e6 }; void IDT8V97003::ValidateLoopFilter(IDT8V97003::LoopFilter filter) { @@ -20,7 +20,7 @@ void IDT8V97003::ValidateLoopFilter(IDT8V97003::LoopFilter filter) throw std::invalid_argument("8V97003: Invalid loop filter C3 value"); } -void IDT8V97003::readReg(uint8_t reg, uint8_t *dst, uint8_t count) +void IDT8V97003::readReg(uint8_t reg, uint8_t* dst, uint8_t count) { if(this->spi.controller == nullptr) throw std::runtime_error("8V97003: SPI not initialized"); @@ -42,7 +42,7 @@ void IDT8V97003::readReg(uint8_t reg, uint8_t *dst, uint8_t count) this->spi.controller->selectSlave(this->spi.ss_mask, false); // This unlocks mutex } -void IDT8V97003::writeReg(uint8_t reg, uint8_t *src, uint8_t count) +void IDT8V97003::writeReg(uint8_t reg, uint8_t* src, uint8_t count) { if(this->spi.controller == nullptr) throw std::runtime_error("8V97003: SPI not initialized"); @@ -264,10 +264,10 @@ bool IDT8V97003::isMuted(IDT8V97003::RFOutput output) { case IDT8V97003::RFOutput::RFOUT_A: reg = IDT8V97003_REG_RFOUTA_PWR; - break; + break; case IDT8V97003::RFOutput::RFOUT_B: reg = IDT8V97003_REG_RFOUTB_PWR; - break; + break; default: throw std::runtime_error("8V97003: Invalid RF output"); } @@ -300,10 +300,10 @@ void IDT8V97003::mute(IDT8V97003::RFOutput output, bool mute) { case IDT8V97003::RFOutput::RFOUT_A: reg = IDT8V97003_REG_RFOUTA_PWR; - break; + break; case IDT8V97003::RFOutput::RFOUT_B: reg = IDT8V97003_REG_RFOUTB_PWR; - break; + break; default: throw std::runtime_error("8V97003: Invalid RF output"); } @@ -344,7 +344,7 @@ void IDT8V97003::setMuteUntilLocked(bool mute) { std::lock_guard lock(this->mutex); - this->rmwReg(IDT8V97003_REG_RFOUTA_ENA, IDT8V97003_REG_RFOUTA_ENA_MUTE_UNTIL_LD, mute ? IDT8V97003_REG_RFOUTA_ENA_MUTE_UNTIL_LD : 0); + this->rmwReg(IDT8V97003_REG_RFOUTA_ENA, (uint8_t)~IDT8V97003_REG_RFOUTA_ENA_MUTE_UNTIL_LD, mute ? IDT8V97003_REG_RFOUTA_ENA_MUTE_UNTIL_LD : 0); } void IDT8V97003::enableRFOutput(IDT8V97003::RFOutput output, bool enable) @@ -355,17 +355,17 @@ void IDT8V97003::enableRFOutput(IDT8V97003::RFOutput output, bool enable) { case IDT8V97003::RFOutput::RFOUT_A: reg = IDT8V97003_REG_RFOUTA_ENA; - break; + break; case IDT8V97003::RFOutput::RFOUT_B: reg = IDT8V97003_REG_RFOUTB_ENA; - break; + break; default: throw std::runtime_error("8V97003: Invalid RF output"); } std::lock_guard lock(this->mutex); - this->rmwReg(reg, IDT8V97003_REG_RFOUTA_ENA_RFOUTA_ENA, enable ? IDT8V97003_REG_RFOUTA_ENA_RFOUTA_ENA : 0); + this->rmwReg(reg, (uint8_t)~IDT8V97003_REG_RFOUTA_ENA_RFOUTA_ENA, enable ? IDT8V97003_REG_RFOUTA_ENA_RFOUTA_ENA : 0); } void IDT8V97003::setRFOutputPower(IDT8V97003::RFOutput output, uint8_t power) { @@ -378,10 +378,10 @@ void IDT8V97003::setRFOutputPower(IDT8V97003::RFOutput output, uint8_t power) { case IDT8V97003::RFOutput::RFOUT_A: reg = IDT8V97003_REG_RFOUTA_PWR; - break; + break; case IDT8V97003::RFOutput::RFOUT_B: reg = IDT8V97003_REG_RFOUTB_PWR; - break; + break; default: throw std::runtime_error("8V97003: Invalid RF output"); } @@ -400,10 +400,10 @@ uint8_t IDT8V97003::getRFOutputPower(IDT8V97003::RFOutput output) { case IDT8V97003::RFOutput::RFOUT_A: reg = IDT8V97003_REG_RFOUTA_PWR; - break; + break; case IDT8V97003::RFOutput::RFOUT_B: reg = IDT8V97003_REG_RFOUTB_PWR; - break; + break; default: throw std::runtime_error("8V97003: Invalid RF output"); } @@ -585,16 +585,16 @@ void IDT8V97003::configPFD(IDT8V97003::RefPathConfig ref_cfg, IDT8V97003::PFDPul { case IDT8V97003::PFDPulseWidth::PFD_PW_260ps: pw_val = IDT8V97003_REG_PFD_PULSE_WIDTH_PW_260ps; - break; + break; case IDT8V97003::PFDPulseWidth::PFD_PW_348ps: pw_val = IDT8V97003_REG_PFD_PULSE_WIDTH_PW_348ps; - break; + break; case IDT8V97003::PFDPulseWidth::PFD_PW_487ps: pw_val = IDT8V97003_REG_PFD_PULSE_WIDTH_PW_487ps; - break; + break; case IDT8V97003::PFDPulseWidth::PFD_PW_583ps: pw_val = IDT8V97003_REG_PFD_PULSE_WIDTH_PW_583ps; - break; + break; default: throw std::runtime_error("8V97003: Invalid PFD pulse width"); } @@ -616,7 +616,7 @@ void IDT8V97003::configPFD(IDT8V97003::RefPathConfig ref_cfg, IDT8V97003::PFDPul double mult_out_freq = this->ref_freq * (ref_cfg.doubler_en ? 2 : 1) * ref_cfg.mult; if(ref_cfg.mult > 1 && (mult_out_freq < 160e6 || mult_out_freq > 250e6)) - throw std::runtime_error("8V97003: Multiplier output frequency out of range (Valid: 160-250 MHz)"); + throw std::runtime_error("8V97003: Multiplier output frequency out of range (Valid: 160-250 MHz)"); double pfd_freq = mult_out_freq / ref_cfg.r_div; @@ -758,7 +758,7 @@ IDT8V97003::LoopFilter IDT8V97003::getLoopFilter() { IDT8V97003::ValidateLoopFilter(this->loop_filter); } - catch (const std::exception &e) + catch(const std::exception& e) { throw std::runtime_error("8V97003: Current loop filter is not valid"); } @@ -820,7 +820,7 @@ void IDT8V97003::setLoopBandwidth(double bw) { bw = this->getTargetLoopBandwidth(); } - catch (const std::exception &e) + catch(const std::exception& e) { throw std::runtime_error("8V97003: Loop bandwidth is not valid and target loop bandwidth is not set"); } @@ -930,7 +930,7 @@ void IDT8V97003::setChargePumpBleederCurrent(double current) current = std::round(current / 20e-6); current = CLAMP(current, 0, 127); - this->writeReg(IDT8V97003_REG_ICP_BLEEDER, (uint8_t)current & 0x7F); + this->rmwReg(IDT8V97003_REG_ICP_BLEEDER, IDT8V97003_REG_ICP_BLEEDER_CP_HIZ, (uint8_t)current & 0x7F); } void IDT8V97003::enableChargePump(bool enable) { @@ -966,24 +966,24 @@ IDT8V97003::LDPrecision IDT8V97003::getLockDetectPrecision() { case IDT8V97003_REG_LD_CTL1_LD_PRECISION_0p5ns: return IDT8V97003::LDPrecision::LD_PREC_0p5ns; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PRECISION_1p0ns: return IDT8V97003::LDPrecision::LD_PREC_1p0ns; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PRECISION_1p8ns: return IDT8V97003::LDPrecision::LD_PREC_1p8ns; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PRECISION_3p0ns: return IDT8V97003::LDPrecision::LD_PREC_3p0ns; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PRECISION_6p4ns: case 0x05: // Repeated setting return IDT8V97003::LDPrecision::LD_PREC_6p4ns; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PRECISION_10p4ns: case 0x07: // Repeated setting return IDT8V97003::LDPrecision::LD_PREC_10p4ns; - break; + break; default: throw std::runtime_error("8V97003: Invalid lock detect precision"); } @@ -996,22 +996,22 @@ void IDT8V97003::setLockDetectPrecision(IDT8V97003::LDPrecision prec) { case IDT8V97003::LDPrecision::LD_PREC_0p5ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_0p5ns; - break; + break; case IDT8V97003::LDPrecision::LD_PREC_1p0ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_1p0ns; - break; + break; case IDT8V97003::LDPrecision::LD_PREC_1p8ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_1p8ns; - break; + break; case IDT8V97003::LDPrecision::LD_PREC_3p0ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_3p0ns; - break; + break; case IDT8V97003::LDPrecision::LD_PREC_6p4ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_6p4ns; - break; + break; case IDT8V97003::LDPrecision::LD_PREC_10p4ns: reg = IDT8V97003_REG_LD_CTL1_LD_PRECISION_10p4ns; - break; + break; default: throw std::runtime_error("8V97003: Invalid lock detect precision"); } @@ -1028,16 +1028,16 @@ IDT8V97003::LDPinMode IDT8V97003::getLockDetectPinMode() { case IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_LD: return IDT8V97003::LDPinMode::LD_MODE_LD; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_CAL_DONE: return IDT8V97003::LDPinMode::LD_MODE_CAL_DONE; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_LOW: return IDT8V97003::LDPinMode::LD_MODE_LOW; - break; + break; case IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_HIGH: return IDT8V97003::LDPinMode::LD_MODE_HIGH; - break; + break; default: throw std::runtime_error("8V97003: Invalid lock detect pin mode"); } @@ -1050,16 +1050,16 @@ void IDT8V97003::setLockDetectPinMode(IDT8V97003::LDPinMode mode) { case IDT8V97003::LDPinMode::LD_MODE_LD: reg = IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_LD; - break; + break; case IDT8V97003::LDPinMode::LD_MODE_CAL_DONE: reg = IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_CAL_DONE; - break; + break; case IDT8V97003::LDPinMode::LD_MODE_LOW: reg = IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_LOW; - break; + break; case IDT8V97003::LDPinMode::LD_MODE_HIGH: reg = IDT8V97003_REG_LD_CTL1_LD_PIN_MODE_HIGH; - break; + break; default: throw std::runtime_error("8V97003: Invalid lock detect pin mode"); } @@ -1084,7 +1084,7 @@ double IDT8V97003::getFeedbackDivider() return (double)a + (double)b / (double)c; } -bool IDT8V97003::isFeedbackDividerFractional(double &dist) +bool IDT8V97003::isFeedbackDividerFractional(double& dist) { uint8_t buf[10]; @@ -1202,6 +1202,18 @@ void IDT8V97003::setFrequency(double freq, bool set_loop_bw, int32_t cal_timeout c /= 2.0; } + a = std::round(a); + b = std::round(b); + c = std::round(c); + + if(b == c) + { + a++; + + b = 0.0; + c = 2.0; + } + if(b > 0.0 && pfd_freq > 250e6) throw std::runtime_error("8V97003: Fractional mode not supported with PFD above 250 MHz"); @@ -1224,10 +1236,6 @@ void IDT8V97003::setFrequency(double freq, bool set_loop_bw, int32_t cal_timeout c = 2.0; } - a = std::round(a); - b = std::round(b); - c = std::round(c); - uint8_t man_ctl_reg = IDT8V97003_REG_MANUAL_CTL_FORCE_RELOCK | IDT8V97003_REG_MANUAL_CTL_MANUAL_RESYNC; uint8_t buf[10]; diff --git a/software/soapy/src/IcyRadioTool.cpp b/software/soapy/src/IcyRadioTool.cpp index 1d9fcd95..6ee8b4ec 100644 --- a/software/soapy/src/IcyRadioTool.cpp +++ b/software/soapy/src/IcyRadioTool.cpp @@ -107,8 +107,8 @@ void testToneTX(SoapyIcyRadio *sdr, double fc) sdr->setBandwidth(SOAPY_SDR_TX, 0, 12e6); sdr->setFrequency(SOAPY_SDR_TX, 0, fc); sdr->setAntenna(SOAPY_SDR_TX, 0, "TX1A"); - sdr->setGain(SOAPY_SDR_TX, 0, "TX_ATT", 30); - sdr->setGain(SOAPY_SDR_TX, 1, "TX_ATT", 30); + sdr->setGain(SOAPY_SDR_TX, 0, "TX_RF", -30); + sdr->setGain(SOAPY_SDR_TX, 1, "TX_RF", -30); double full_scale = 0; const std::string fmt = sdr->getNativeStreamFormat(SOAPY_SDR_TX, 0, full_scale); @@ -172,8 +172,8 @@ void testTimedToneTX(SoapyIcyRadio *sdr, double fc) sdr->setBandwidth(SOAPY_SDR_TX, 0, 12e6); sdr->setFrequency(SOAPY_SDR_TX, 0, fc); sdr->setAntenna(SOAPY_SDR_TX, 0, "TX1A"); - sdr->setGain(SOAPY_SDR_TX, 0, "TX_ATT", 30); - sdr->setGain(SOAPY_SDR_TX, 1, "TX_ATT", 30); + sdr->setGain(SOAPY_SDR_TX, 0, "TX_RF", -30); + sdr->setGain(SOAPY_SDR_TX, 1, "TX_RF", -30); double full_scale = 0; const std::string fmt = sdr->getNativeStreamFormat(SOAPY_SDR_TX, 0, full_scale); @@ -246,8 +246,8 @@ void testAWGNTX(SoapyIcyRadio *sdr, double fc, double fs) sdr->setBandwidth(SOAPY_SDR_TX, 0, 0.5 * fs); sdr->setFrequency(SOAPY_SDR_TX, 0, fc); sdr->setAntenna(SOAPY_SDR_TX, 0, "TX1A"); - sdr->setGain(SOAPY_SDR_TX, 0, "TX_ATT", 30); - sdr->setGain(SOAPY_SDR_TX, 1, "TX_ATT", 30); + sdr->setGain(SOAPY_SDR_TX, 0, "TX_RF", -30); + sdr->setGain(SOAPY_SDR_TX, 1, "TX_RF", -30); double full_scale = 0; const std::string fmt = sdr->getNativeStreamFormat(SOAPY_SDR_TX, 0, full_scale); @@ -312,8 +312,8 @@ void testRFDelay(SoapyIcyRadio *sdr, double fc, double fs) sdr->setFrequency(SOAPY_SDR_RX, 0, fc); sdr->setAntenna(SOAPY_SDR_TX, 0, "TX1A"); sdr->setAntenna(SOAPY_SDR_RX, 0, "RX1A"); - sdr->setGain(SOAPY_SDR_TX, 0, "TX_ATT", 10); - sdr->setGain(SOAPY_SDR_RX, 0, "RX_FE", 0); + sdr->setGain(SOAPY_SDR_TX, 0, "TX_RF", -10); + sdr->setGain(SOAPY_SDR_RX, 0, "RX_RF", 0); double tx_full_scale = 0; const std::string tx_fmt = sdr->getNativeStreamFormat(SOAPY_SDR_TX, 0, tx_full_scale); @@ -480,8 +480,8 @@ void testFullDuplex(SoapyIcyRadio *sdr, size_t n_chan, double fs) sdr->setSampleRate(SOAPY_SDR_TX, 0, fs); sdr->setAntenna(SOAPY_SDR_TX, 0, "TX1A"); sdr->setAntenna(SOAPY_SDR_RX, 0, "RX1A"); - sdr->setGain(SOAPY_SDR_TX, 0, "TX_ATT", 89); - sdr->setGain(SOAPY_SDR_RX, 0, "RX_FE", 0); + sdr->setGain(SOAPY_SDR_TX, 0, "TX_RF", -89); + sdr->setGain(SOAPY_SDR_RX, 0, "RX_RF", 0); std::vector channels(n_chan); diff --git a/software/soapy/src/LT7182S.cpp b/software/soapy/src/LT7182S.cpp index c55229b1..39dc6f6c 100644 --- a/software/soapy/src/LT7182S.cpp +++ b/software/soapy/src/LT7182S.cpp @@ -148,6 +148,21 @@ void LT7182S::writeWord(uint8_t cmd, uint16_t data) this->iic.controller->write(this->iic.addr, buf, 3, AXIIIC::Stop::STOP); } +void LT7182S::writeDWord(uint8_t cmd, uint32_t data) +{ + if(this->iic.controller == nullptr) + throw std::runtime_error("LT7182S: IIC not initialized"); + + uint8_t buf[5]; + + buf[0] = cmd; + buf[1] = data & 0xFF; + buf[2] = (data >> 8) & 0xFF; + buf[3] = (data >> 16) & 0xFF; + buf[4] = data >> 24; + + this->iic.controller->write(this->iic.addr, buf, 5, AXIIIC::Stop::STOP); +} void LT7182S::writeL11(uint8_t cmd, float data) { int8_t exp = -16; @@ -214,6 +229,22 @@ uint16_t LT7182S::readWord(uint8_t cmd) return (((uint16_t)buf[1]) << 8) | buf[0]; } +uint32_t LT7182S::readDWord(uint8_t cmd) +{ + if(this->iic.controller == nullptr) + throw std::runtime_error("LT7182S: IIC not initialized"); + + uint8_t buf[4]; + + this->iic.controller->startAtomicTransaction(); // Lock the I2C bus so the next two transactions are not interrupted + + this->iic.controller->write(this->iic.addr, cmd, AXIIIC::Stop::RESTART); + this->iic.controller->read(this->iic.addr, buf, 4, AXIIIC::Stop::STOP); + + this->iic.controller->endAtomicTransaction(); // Unlock the I2C bus + + return (((uint32_t)buf[3]) << 24) | (((uint32_t)buf[2]) << 16) | (((uint32_t)buf[1]) << 8) | buf[0]; +} float LT7182S::readL11(uint8_t cmd) { uint16_t data = this->readWord(cmd); @@ -421,6 +452,36 @@ uint16_t LT7182S::readManufacturerSpecialID() return this->readWord(0xE7); } +uint64_t LT7182S::getFaultLogTimestamp() +{ + std::lock_guard lock(this->mutex); + + uint64_t timestamp = this->readDWord(0xE8); + + timestamp = (timestamp << 32) | this->readDWord(0xE9); + + return timestamp; +} +void LT7182S::setFaultLogTimestamp(uint64_t timestamp) +{ + std::lock_guard lock(this->mutex); + + this->writeDWord(0xE8, timestamp >> 32); + this->writeDWord(0xE9, timestamp & 0xFFFFFFFF); +} +void LT7182S::storeFaultLog() +{ + std::lock_guard lock(this->mutex); + + this->writeCommand(0xEA); +} +void LT7182S::clearFaultLog() +{ + std::lock_guard lock(this->mutex); + + this->writeCommand(0xEC); +} + uint8_t LT7182S::getStatusByte(LT7182S::Chan ch) { std::lock_guard lock(this->mutex); diff --git a/software/soapy/src/SPIFlash.cpp b/software/soapy/src/SPIFlash.cpp index 1beb669b..6377802b 100644 --- a/software/soapy/src/SPIFlash.cpp +++ b/software/soapy/src/SPIFlash.cpp @@ -61,6 +61,29 @@ void SPIFlash::detectDevice() } } break; + case 0xEF4018: + { + uint16_t mf_dev = this->readMFDeviceID(); + + if(mf_dev != 0xEF17) + break; + + this->dev_size = 16 * 1024 * 1024; + + // Distinguish between JV and FV variants + uint8_t data = this->read(0x00000024); + uint8_t data_dual = this->readDualIO(0x00000024, false, true); + uint8_t data_dual_cont = this->readDualIO(0x00000024, true, false); + + if(data == data_dual) + { + if(data_dual != data_dual_cont) + this->dev = SPIFlash::DeviceID::W25Q128JV; // JV does not support continuous read + else + this->dev = SPIFlash::DeviceID::W25Q128FV; // FV supports continuous read + } + } + break; } } @@ -92,6 +115,10 @@ std::string SPIFlash::getDeviceName() return "W25Q64BV"; case SPIFlash::DeviceID::W25Q64FV: return "W25Q64FV"; + case SPIFlash::DeviceID::W25Q128JV: + return "W25Q128JV"; + case SPIFlash::DeviceID::W25Q128FV: + return "W25Q128FV"; case SPIFlash::DeviceID::SST26VF016B: return "SST26VF016B"; case SPIFlash::DeviceID::SST26VF064B: @@ -278,6 +305,8 @@ uint64_t SPIFlash::readUniqueID() case SPIFlash::DeviceID::W25Q64JV: case SPIFlash::DeviceID::W25Q64BV: case SPIFlash::DeviceID::W25Q64FV: + case SPIFlash::DeviceID::W25Q128JV: + case SPIFlash::DeviceID::W25Q128FV: break; default: throw std::runtime_error("SPI Flash: Device does not support unique ID read"); diff --git a/software/soapy/src/Si5351.cpp b/software/soapy/src/Si5351.cpp index 9e18dcbb..4b2e6e43 100644 --- a/software/soapy/src/Si5351.cpp +++ b/software/soapy/src/Si5351.cpp @@ -200,13 +200,23 @@ Si5351::MultiSynthDivider Si5351::CalculateMSDivider(double f1, double f2) c /= 2.0; } - Si5351::MultiSynthDivider div; + a = std::round(a); + b = std::round(b); + c = std::round(c); - div.a = std::round(a); - div.b = std::round(b); - div.c = std::round(c); + if(b == c) + { + a++; - return div; + b = 0.0; + c = 1.0; + } + + return { + .a = (uint32_t)a, + .b = (uint32_t)b, + .c = (uint32_t)c + }; } Si5351::MultiSynthDivider Si5351::CalculateValidPLLMSDivider(double f1, double f2) { diff --git a/software/soapy/src/SoapyIcyRadio.cpp b/software/soapy/src/SoapyIcyRadio.cpp index 3e145636..5225c928 100644 --- a/software/soapy/src/SoapyIcyRadio.cpp +++ b/software/soapy/src/SoapyIcyRadio.cpp @@ -1,5 +1,28 @@ #include "SoapyIcyRadio.hpp" +// Default FIR coefficients for decimation/interpolation when sample rates below 2.0833... MHz are needed +// Gain is 6 dB, set AD9361 gain to -6 dB to compensate +static const int16_t rf_phy_fir_128_dec4[] = { + -15, -27, -23, -6, 17, 33, 31, 9, -23, -47, -45, -13, 34, 69, 67, 21, + -49, -102, -99, -32, 69, 146, 143, 48, -96, -204, -200, -69, 129, 278, 275, 97, + -170, -372, -371, -135, 222, 494, 497, 187, -288, -654, -665, -258, 376, 875, 902, 363, + -500, -1201, -1265, -530, 699, 1748, 1906, 845, -1089, -2922, -3424, -1697, 2326, 7714, 12821, 15921, + 15921, 12821, 7714, 2326, -1697, -3424, -2922, -1089, 845, 1906, 1748, 699, -530, -1265, -1201, -500, + 363, 902, 875, 376, -258, -665, -654, -288, 187, 497, 494, 222, -135, -371, -372, -170, + 97, 275, 278, 129, -69, -200, -204, -96, 48, 143, 146, 69, -32, -99, -102, -49, + 21, 67, 69, 34, -13, -45, -47, -23, 9, 31, 33, 17, -6, -23, -27, -15 +}; +static const int16_t rf_phy_fir_128_dec2[] = { + 0, 0, 1, 0, -2, 0, 3, 0, -5, 0, 8, 0, -11, 0, 17, 0, + -24, 0, 33, 0, -45, 0, 61, 0, -80, 0, 104, 0, -134, 0, 169, 0, + -213, 0, 264, 0, -327, 0, 401, 0, -489, 0, 595, 0, -724, 0, 880, 0, + -1075, 0, 1323, 0, -1652, 0, 2114, 0, -2819, 0, 4056, 0, -6883, 0, 20837, 32767, + 20837, 0, -6883, 0, 4056, 0, -2819, 0, 2114, 0, -1652, 0, 1323, 0, -1075, 0, + 880, 0, -724, 0, 595, 0, -489, 0, 401, 0, -327, 0, 264, 0, -213, 0, + 169, 0, -134, 0, 104, 0, -80, 0, 61, 0, -45, 0, 33, 0, -24, 0, + 17, 0, -11, 0, 8, 0, -5, 0, 3, 0, -2, 0, 1, 0, -0, 0 +}; + void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) { SoapySDR::Kwargs c_args = args; @@ -33,7 +56,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) if(f < 0 || f > UINT32_MAX) { - this->config.clkin_freq = 10000000U; + this->config.clkin_freq = 10e6; DLOGF(SOAPY_SDR_WARNING, "Invalid external clock input frequency provided (%.6f), using default: 10 MHz", f); } @@ -41,7 +64,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) { this->config.clkin_freq = f; - DLOGF(SOAPY_SDR_INFO, "External clock input frequency is %u Hz", this->config.clkin_freq); + DLOGF(SOAPY_SDR_INFO, "External clock input frequency is %.6f Hz", this->config.clkin_freq); } } else @@ -49,7 +72,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) if(this->config.use_clkin) DLOGF(SOAPY_SDR_WARNING, "External clock input usage requested, but no frequency provided, using default: 10 MHz"); - this->config.clkin_freq = 10000000U; + this->config.clkin_freq = 10e6; } // en_ext_clk_out @@ -71,7 +94,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) if(f < 0 || f > UINT32_MAX) { - this->config.clkout_freq = 10000000U; + this->config.clkout_freq = 10e6; DLOGF(SOAPY_SDR_WARNING, "Invalid external clock output frequency provided (%.6f), using default: 10 MHz", f); } @@ -79,7 +102,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) { this->config.clkout_freq = f; - DLOGF(SOAPY_SDR_INFO, "Requested external clock output frequency is %u Hz", this->config.clkout_freq); + DLOGF(SOAPY_SDR_INFO, "Requested external clock output frequency is %.6f Hz", this->config.clkout_freq); } } else @@ -87,7 +110,7 @@ void SoapyIcyRadio::parseConfig(const SoapySDR::Kwargs &args) if(this->config.enable_clkout) DLOGF(SOAPY_SDR_WARNING, "External clock output enabled, but no frequency provided, using default: 10 MHz"); - this->config.clkout_freq = 10000000U; + this->config.clkout_freq = 10e6; } } @@ -1021,6 +1044,10 @@ void SoapyIcyRadio::initPeripheralsPostClocks() // Init RF Phy this->rf_phy->init(); + this->rf_phy->loadFIRCoefficients(AD9361::FIRDest::FIR_RX1_RX2, rf_phy_fir_128_dec4, ARRAY_SIZE(rf_phy_fir_128_dec4), 4, -6); + this->rf_phy->loadFIRCoefficients(AD9361::FIRDest::FIR_TX1_TX2, rf_phy_fir_128_dec4, ARRAY_SIZE(rf_phy_fir_128_dec4), 4, 0); + + // Init RF Phy HDL block this->axi_ad9361->init(this->rf_phy); { @@ -1100,11 +1127,11 @@ void SoapyIcyRadio::initPeripheralsPostClocks() this->mmw_synth->setLoopFilter( { - .rs = 62, - .cs = 220e-9, - .cp = 2.2e-9, - .r3 = 20, - .c3 = 680e-12 + .rs = 20, + .cs = 1e-6, + .cp = 10e-9, + .r3 = 62, + .c3 = 2.2e-9 } ); this->mmw_synth->setTargetLoopBandwidth(100e3); // 100 kHz @@ -1494,7 +1521,7 @@ void SoapyIcyRadio::initClocks() DLOGF(SOAPY_SDR_DEBUG, " MS in integer mode"); } - DLOGF(SOAPY_SDR_INFO, "Achieved external clock output frequency is %u Hz", this->clk_mngr->getClockFrequency(Si5351::ClockOutput::CLK_EXT_CLK_OUT)); + DLOGF(SOAPY_SDR_INFO, "Achieved external clock output frequency is %.6f Hz", this->clk_mngr->getClockFrequency(Si5351::ClockOutput::CLK_EXT_CLK_OUT)); } // Wait and global enable @@ -2052,6 +2079,10 @@ void SoapyIcyRadio::reconfigureDataPath(bool rx2tx2, size_t rx_ch, size_t tx_ch) this->rf_phy->setup(); + this->rf_phy->loadFIRCoefficients(AD9361::FIRDest::FIR_RX1_RX2, rf_phy_fir_128_dec4, ARRAY_SIZE(rf_phy_fir_128_dec4), 4, -6); + this->rf_phy->loadFIRCoefficients(AD9361::FIRDest::FIR_TX1_TX2, rf_phy_fir_128_dec4, ARRAY_SIZE(rf_phy_fir_128_dec4), 4, 0); + this->rf_phy->validateAndEnableFIR(); + this->axi_ad9361->updateActiveChannels(); this->axi_rf_tstamp->enableClockSyncBypass(!rx2tx2); @@ -2061,6 +2092,9 @@ void SoapyIcyRadio::validateSampleRateAndChannelCombination(const double rate, c { DLOGF(SOAPY_SDR_DEBUG, "validateSampleRateAndChannelCombination: Rate %u Sps, channel count %u", (size_t)rate, channel_count); + if(rate < MIN_BASEBAND_RATE) + throw std::runtime_error("validateSampleRateAndChannelCombination: Rate too low, minimum is " + std::to_string(MIN_BASEBAND_RATE) + " Sps"); + if(channel_count > 1 && rate > MAX_BASEBAND_RATE / 2) throw std::runtime_error("validateSampleRateAndChannelCombination: Rate too high for multiple channels, maximum is " + std::to_string(MAX_BASEBAND_RATE / 2) + " Sps"); @@ -2360,8 +2394,6 @@ void SoapyIcyRadio::reinitStreamChannels(SoapyIcyRadio::Stream *stream) std::vector SoapyIcyRadio::getStreams(bool active_only) const { - std::lock_guard lock(this->streams_mutex); - std::vector ret; for(SoapyIcyRadio::Stream *s : this->streams) @@ -2374,8 +2406,6 @@ std::vector SoapyIcyRadio::getStreams(bool active_only) } std::vector SoapyIcyRadio::getStreams(const int direction, bool active_only) const { - std::lock_guard lock(this->streams_mutex); - std::vector ret; for(SoapyIcyRadio::Stream *s : this->streams) @@ -2386,10 +2416,8 @@ std::vector SoapyIcyRadio::getStreams(const int directi return ret; } -SoapyIcyRadio::Stream *SoapyIcyRadio::findStream(SoapyIcyRadio::Stream *stream) const +SoapyIcyRadio::Stream *SoapyIcyRadio::_findStream(SoapyIcyRadio::Stream *stream) const { - std::lock_guard lock(this->streams_mutex); - for(SoapyIcyRadio::Stream *s : this->streams) { if(s == stream) @@ -2416,8 +2444,6 @@ bool SoapyIcyRadio::isChannelVectorValid(const int direction, const std::vector< } bool SoapyIcyRadio::isAnyChannelBusy(const int direction, const std::vector &channels) const { - std::lock_guard lock(this->streams_mutex); - for(SoapyIcyRadio::Stream *s : this->streams) { if(s->direction != direction) @@ -2432,8 +2458,6 @@ bool SoapyIcyRadio::isAnyChannelBusy(const int direction, const std::vector SoapyIcyRadio::getBusyChannels(const int direction) const { - std::lock_guard lock(this->streams_mutex); - std::vector ret; for(SoapyIcyRadio::Stream *s : this->streams) diff --git a/software/soapy/src/SoapyRegistration.cpp b/software/soapy/src/SoapyRegistration.cpp index e2b5ae87..a38a4158 100644 --- a/software/soapy/src/SoapyRegistration.cpp +++ b/software/soapy/src/SoapyRegistration.cpp @@ -92,15 +92,15 @@ SoapySDR::KwargsList findIcyRadio(const SoapySDR::Kwargs &args) } SoapySDR::Device *makeIcyRadio(const SoapySDR::Kwargs &args) { - // If path specified, use that and we're done - if(args.count("path") != 0) + // If only path is specified, use that and we're done + if(args.count("path") != 0 && args.count("serial") == 0) return new SoapyIcyRadio(args); // For anything else, make a non-const copy of args SoapySDR::Kwargs _args = args; - // If device_id specified, build a path from that and we're done - if(args.count("device_id") != 0) + // If only device_id is specified, build a path from that and we're done + if(args.count("device_id") != 0 && args.count("serial") == 0) { _args["path"] = "/dev/icyradio" + args.at("device_id"); diff --git a/software/soapy/src/SoapySettings.cpp b/software/soapy/src/SoapySettings.cpp index 8f2cda5a..580819d9 100644 --- a/software/soapy/src/SoapySettings.cpp +++ b/software/soapy/src/SoapySettings.cpp @@ -149,7 +149,7 @@ SoapySDR::Kwargs SoapyIcyRadio::getHardwareInfo() const while(!this->axi_dna->isSerialNumberReady()) // Serial is essential, so wait for it to be ready std::this_thread::sleep_for(std::chrono::milliseconds(1)); - char buf[64]; + char buf[64] = {0}; snprintf(buf, sizeof(buf), "%015lX", this->axi_dna->getSerialNumber()); info["serial"] = buf; @@ -251,8 +251,12 @@ void SoapyIcyRadio::setAntenna(const int direction, const size_t channel, const if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("setAntenna: Unknown direction"); + bool toggle_pa = true; // TODO: Allow disabling auto PA setting when TXnB selected + if(channel <= 1) { + toggle_pa = false; + // RF Channels if(direction == SOAPY_SDR_RX) { @@ -286,6 +290,12 @@ void SoapyIcyRadio::setAntenna(const int direction, const size_t channel, const } this->rf_phy->setupRFPort(false, this->rf_phy->pdata->rf_rx_input_sel, this->rf_phy->pdata->rf_tx_output_sel); + + if(toggle_pa) + { + this->rf_phy->setGPOValue(2, this->rf_phy->pdata->rf_tx_output_sel == 1); + this->rf_phy->setGPOValue(3, this->rf_phy->pdata->rf_tx_output_sel == 1); + } } } @@ -342,11 +352,11 @@ std::vector SoapyIcyRadio::listGains(const int direction, const siz if(direction == SOAPY_SDR_RX) { - gains.push_back("RX_FE"); + gains.push_back("RX_RF"); } else { - gains.push_back("TX_ATT"); + gains.push_back("TX_RF"); } return gains; @@ -366,7 +376,7 @@ void SoapyIcyRadio::setGainMode(const int direction, const size_t channel, const if(direction != SOAPY_SDR_RX) throw std::runtime_error("setGainMode: AGC is not supported for TX"); - // TODO: + this->rf_phy->setRXGainMode(BIT(channel), automatic ? AD9361::RFGainCtrlMode::RF_GAIN_SLOWATTACK_AGC : AD9361::RFGainCtrlMode::RF_GAIN_MGC); } bool SoapyIcyRadio::getGainMode(const int direction, const size_t channel) const { @@ -383,7 +393,7 @@ void SoapyIcyRadio::setGain(const int direction, const size_t channel, const dou if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("setGain: Unknown direction"); - this->setGain(direction, channel, direction == SOAPY_SDR_RX ? "RX_FE" : "TX_ATT", value); + this->setGain(direction, channel, direction == SOAPY_SDR_RX ? "RX_RF" : "TX_RF", value); } void SoapyIcyRadio::setGain(const int direction, const size_t channel, const std::string &name, const double value) { @@ -392,12 +402,12 @@ void SoapyIcyRadio::setGain(const int direction, const size_t channel, const std if(direction == SOAPY_SDR_RX) { - if(name == "RX_FE") + if(name == "RX_RF") { - SoapySDR::Range r = this->getGainRange(direction, channel, name); + // SoapySDR::Range r = this->getGainRange(direction, channel, name); - if(value < r.minimum() || value > r.maximum()) - throw std::runtime_error("setGain: Gain out of range"); + // if(value < r.minimum() || value > r.maximum()) + // throw std::runtime_error("setGain: Gain out of range"); AD9361::RFRXGain g; @@ -412,12 +422,12 @@ void SoapyIcyRadio::setGain(const int direction, const size_t channel, const std } else { - if(name == "TX_ATT") + if(name == "TX_RF") { - if(value < 0 || value > 89.75) + if(value > 0 || value < -89.75) throw std::runtime_error("setGain: Gain out of range"); - this->rf_phy->setTXAttenuation(value * 1000, channel == 0, channel == 1, true); + this->rf_phy->setTXAttenuation(value * -1000, channel == 0, channel == 1, true); } else { @@ -430,7 +440,7 @@ double SoapyIcyRadio::getGain(const int direction, const size_t channel) const if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("getGain: Unknown direction"); - return this->getGain(direction, channel, direction == SOAPY_SDR_RX ? "RX_FE" : "TX_ATT"); + return this->getGain(direction, channel, direction == SOAPY_SDR_RX ? "RX_RF" : "TX_RF"); } double SoapyIcyRadio::getGain(const int direction, const size_t channel, const std::string &name) const { @@ -439,7 +449,7 @@ double SoapyIcyRadio::getGain(const int direction, const size_t channel, const s if(direction == SOAPY_SDR_RX) { - if(name == "RX_FE") + if(name == "RX_RF") { AD9361::RFRXGain g; @@ -454,9 +464,9 @@ double SoapyIcyRadio::getGain(const int direction, const size_t channel, const s } else { - if(name == "TX_ATT") + if(name == "TX_RF") { - return (double)this->rf_phy->getTXAttenuation(BIT(channel)) / 1000.0; + return (double)this->rf_phy->getTXAttenuation(BIT(channel)) / -1000.0; } else { @@ -469,7 +479,7 @@ SoapySDR::Range SoapyIcyRadio::getGainRange(const int direction, const size_t ch if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("getGainRange: Unknown direction"); - return this->getGainRange(direction, channel, direction == SOAPY_SDR_RX ? "RX_FE" : "TX_ATT"); + return this->getGainRange(direction, channel, direction == SOAPY_SDR_RX ? "RX_RF" : "TX_RF"); } SoapySDR::Range SoapyIcyRadio::getGainRange(const int direction, const size_t channel, const std::string &name) const { @@ -478,7 +488,7 @@ SoapySDR::Range SoapyIcyRadio::getGainRange(const int direction, const size_t ch if(direction == SOAPY_SDR_RX) { - if(name == "RX_FE") + if(name == "RX_RF") { int8_t min = this->rf_phy->gt_info[this->rf_phy->getCurrentGainTable()].abs_gain_tbl[0]; int8_t max = this->rf_phy->gt_info[this->rf_phy->getCurrentGainTable()].abs_gain_tbl[this->rf_phy->gt_info[this->rf_phy->getCurrentGainTable()].max_index - 1]; @@ -492,9 +502,9 @@ SoapySDR::Range SoapyIcyRadio::getGainRange(const int direction, const size_t ch } else { - if(name == "TX_ATT") + if(name == "TX_RF") { - return SoapySDR::Range(0.0, 89.75, 0.25); + return SoapySDR::Range(-89.75, 0.0, 0.25); } else { @@ -508,7 +518,7 @@ void SoapyIcyRadio::setFrequency(const int direction, const size_t channel, cons if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("setFrequency: Unknown direction"); - this->setFrequency(direction, channel, "LO", frequency, args); + this->setFrequency(direction, channel, direction == SOAPY_SDR_RX ? "RX_LO" : "TX_LO", frequency, args); } void SoapyIcyRadio::setFrequency(const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args) { @@ -517,7 +527,7 @@ void SoapyIcyRadio::setFrequency(const int direction, const size_t channel, cons if(direction == SOAPY_SDR_RX) { - if(name == "LO") + if(name == "RX_LO") { if(frequency < 70e6 || frequency > 6e9) throw std::runtime_error("setFrequency: Frequency out of range"); @@ -531,7 +541,7 @@ void SoapyIcyRadio::setFrequency(const int direction, const size_t channel, cons } else { - if(name == "LO") + if(name == "TX_LO") { if(frequency < 47e6 || frequency > 6e9) throw std::runtime_error("setFrequency: Frequency out of range"); @@ -549,7 +559,7 @@ double SoapyIcyRadio::getFrequency(const int direction, const size_t channel) co if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("getFrequency: Unknown direction"); - return this->getFrequency(direction, channel, "LO"); + return this->getFrequency(direction, channel, direction == SOAPY_SDR_RX ? "RX_LO" : "TX_LO"); } double SoapyIcyRadio::getFrequency(const int direction, const size_t channel, const std::string &name) const { @@ -558,7 +568,7 @@ double SoapyIcyRadio::getFrequency(const int direction, const size_t channel, co if(direction == SOAPY_SDR_RX) { - if(name == "LO") + if(name == "RX_LO") { return this->rf_phy->getClockRate(this->rf_phy->ref_clk_scale[AD9361::ClockIndex::RX_RFPLL]); } @@ -569,7 +579,7 @@ double SoapyIcyRadio::getFrequency(const int direction, const size_t channel, co } else { - if(name == "LO") + if(name == "TX_LO") { return this->rf_phy->getClockRate(this->rf_phy->ref_clk_scale[AD9361::ClockIndex::TX_RFPLL]); } @@ -586,7 +596,14 @@ std::vector SoapyIcyRadio::listFrequencies(const int direction, con std::vector names; - names.push_back("LO"); + if(direction == SOAPY_SDR_RX) + { + names.push_back("RX_LO"); + } + else + { + names.push_back("TX_LO"); + } return names; } @@ -595,7 +612,7 @@ SoapySDR::RangeList SoapyIcyRadio::getFrequencyRange(const int direction, const if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("getFrequencyRange: Unknown direction"); - return this->getFrequencyRange(direction, channel, "LO"); + return this->getFrequencyRange(direction, channel, direction == SOAPY_SDR_RX ? "RX_LO" : "TX_LO"); } SoapySDR::RangeList SoapyIcyRadio::getFrequencyRange(const int direction, const size_t channel, const std::string &name) const { @@ -606,7 +623,7 @@ SoapySDR::RangeList SoapyIcyRadio::getFrequencyRange(const int direction, const if(direction == SOAPY_SDR_RX) { - if(name == "LO") + if(name == "RX_LO") { ranges.emplace_back(70e6, 6e9); } @@ -617,7 +634,7 @@ SoapySDR::RangeList SoapyIcyRadio::getFrequencyRange(const int direction, const } else { - if(name == "LO") + if(name == "TX_LO") { ranges.emplace_back(47e6, 6e9); } @@ -644,7 +661,7 @@ void SoapyIcyRadio::setSampleRate(const int direction, const size_t channel, con if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("setSampleRate: Unknown direction"); - std::lock_guard lock(this->streams_mutex); + std::lock_guard lock(this->streams_mutex); // Check if datapath needs reconfiguration and if we can do it auto rx_chs = this->getBusyChannels(SOAPY_SDR_RX); @@ -671,6 +688,32 @@ void SoapyIcyRadio::setSampleRate(const int direction, const size_t channel, con this->reinitStreamChannels(s); } + if(rate < MIN_BASEBAND_RATE_NOFIR) // Below this value requires FIR decimation/interpolation + { + if(this->rf_phy->bypass_rx_fir || this->rf_phy->bypass_tx_fir) + { + // FIR was already loaded on transceiver init + this->rf_phy->bypass_rx_fir = false; + this->rf_phy->bypass_tx_fir = false; + + // No need to call this since changes will apply when we set the clocks below + // This also validates some things that "can" be invalid, but should we really care? + // this->rf_phy->validateAndEnableFIR(); + } + } + else + { + if(!this->rf_phy->bypass_rx_fir || !this->rf_phy->bypass_tx_fir) + { + this->rf_phy->bypass_rx_fir = true; + this->rf_phy->bypass_tx_fir = true; + + // No need to call this since changes will apply when we set the clocks below + // This also validates some things that "can" be invalid, but should we really care? + // this->rf_phy->validateAndEnableFIR(); + } + } + this->rf_phy->setClockChainFreq(rate); this->rf_phy->setRFBandwidth(this->rf_phy->current_rx_bw_Hz, this->rf_phy->current_tx_bw_Hz); @@ -690,7 +733,7 @@ SoapySDR::RangeList SoapyIcyRadio::getSampleRateRange(const int direction, const SoapySDR::RangeList ranges; - ranges.emplace_back(2100000UL, 61440000UL); + ranges.emplace_back(MIN_BASEBAND_RATE, MAX_BASEBAND_RATE); // Actually supported up to 122.88 MS/s, but off-spec return ranges; } @@ -1309,9 +1352,7 @@ SoapySDR::ArgInfo SoapyIcyRadio::getSensorInfo(const std::string &key) const } std::string SoapyIcyRadio::readSensor(const std::string &key) const { - char buf[16]; - - memset(buf, 0, sizeof(buf)); + char buf[16] = {0}; // XADC sensors (FPGA) if(key == "xadc_temp") @@ -1422,6 +1463,20 @@ std::vector SoapyIcyRadio::listSensors(const int direction, const s std::vector sensors; + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + sensors.push_back("preamble_rssi"); + sensors.push_back("symbol_rssi"); + } + else + { + // No sensors + } + } + return sensors; } SoapySDR::ArgInfo SoapyIcyRadio::getSensorInfo(const int direction, const size_t channel, const std::string &key) const @@ -1431,6 +1486,38 @@ SoapySDR::ArgInfo SoapyIcyRadio::getSensorInfo(const int direction, const size_t SoapySDR::ArgInfo info; + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + if(key == "preamble_rssi") + { + info.key = "preamble_rssi"; + info.value = "0.00"; + info.name = "RX Channel " + std::to_string(channel) + " preamble phase RSSI"; + info.description = "RSSI as reported by the RF PHY for the preamble phase of RX channel " + std::to_string(channel); + info.units = "dB"; + info.type = SoapySDR::ArgInfo::FLOAT; + info.range = SoapySDR::Range(-128.0, 0.0, 0.25); + } + else if(key == "symbol_rssi") + { + info.key = "symbol_rssi"; + info.value = "0.00"; + info.name = "RX Channel " + std::to_string(channel) + " symbol phase RSSI"; + info.description = "RSSI as reported by the RF PHY for the symbol phase of RX channel " + std::to_string(channel); + info.units = "dB"; + info.type = SoapySDR::ArgInfo::FLOAT; + info.range = SoapySDR::Range(-128.0, 0.0, 0.25); + } + } + else + { + // No sensors + } + } + return info; } std::string SoapyIcyRadio::readSensor(const int direction, const size_t channel, const std::string &key) const @@ -1438,5 +1525,138 @@ std::string SoapyIcyRadio::readSensor(const int direction, const size_t channel, if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) throw std::runtime_error("readSensor: Unknown direction"); + char buf[16] = {0}; + + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + if(key == "preamble_rssi") + snprintf(buf, sizeof(buf), "%.2f", this->rf_phy->readPreambleRSSI(BIT(channel))); + else if(key == "symbol_rssi") + snprintf(buf, sizeof(buf), "%.2f", this->rf_phy->readSymbolRSSI(BIT(channel))); + } + else + { + // No sensors + } + } + + return buf; +} + + +SoapySDR::ArgInfoList SoapyIcyRadio::getSettingInfo() const +{ + SoapySDR::ArgInfoList settings; + + return settings; +} +void SoapyIcyRadio::writeSetting(const std::string &key, const std::string &value) +{ + // No settings to write +} +std::string SoapyIcyRadio::readSetting(const std::string &key) const +{ + // No settings to read + return ""; +} +SoapySDR::ArgInfoList SoapyIcyRadio::getSettingInfo(const int direction, const size_t channel) const +{ + if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) + throw std::runtime_error("setAntenna: Unknown direction"); + + SoapySDR::ArgInfoList settings; + + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + SoapySDR::ArgInfo s; + + s.key = "bias_t"; + s.value = "false"; + s.name = "Bias-Tee"; + s.description = "Enable or disable the Bias-Tee power on RX" + std::to_string(BIT(channel)) + "C"; + s.type = SoapySDR::ArgInfo::Type::BOOL; + + settings.push_back(s); + } + else + { + SoapySDR::ArgInfo s; + + s.key = "pa"; + s.value = "false"; + s.name = "Power Amplifier"; + s.description = "Enable or disable the power amplifier on TX" + std::to_string(BIT(channel)) + "B"; + s.type = SoapySDR::ArgInfo::Type::BOOL; + + settings.push_back(s); + } + } + + return settings; +} +void SoapyIcyRadio::writeSetting(const int direction, const size_t channel, const std::string &key, const std::string &value) +{ + if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) + throw std::runtime_error("writeSetting: Unknown direction"); + + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + if(key == "bias_t") + { + // GPO 0 for channel 0, GPO 1 for channel 1 + if(value == "true") + this->rf_phy->setGPOValue(channel, true); + else + this->rf_phy->setGPOValue(channel, false); + } + } + else + { + if(key == "pa") + { + // GPO 2 for channel 0, GPO 3 for channel 1 + if(value == "true") + this->rf_phy->setGPOValue(channel + 2, true); + else + this->rf_phy->setGPOValue(channel + 2, false); + } + } + } +} +std::string SoapyIcyRadio::readSetting(const int direction, const size_t channel, const std::string &key) const +{ + if(direction != SOAPY_SDR_RX && direction != SOAPY_SDR_TX) + throw std::runtime_error("readSetting: Unknown direction"); + + if(channel <= 1) + { + // RF Channels + if(direction == SOAPY_SDR_RX) + { + if(key == "bias_t") + { + // GPO 0 for channel 0, GPO 1 for channel 1 + return this->rf_phy->getGPOValue(channel) ? "true" : "false"; + } + } + else + { + if(key == "pa") + { + // GPO 2 for channel 0, GPO 3 for channel 1 + return this->rf_phy->getGPOValue(channel + 2) ? "true" : "false"; + } + } + } + return ""; } \ No newline at end of file diff --git a/software/soapy/src/SoapyStreaming.cpp b/software/soapy/src/SoapyStreaming.cpp index 6dd445b6..c037e0b6 100644 --- a/software/soapy/src/SoapyStreaming.cpp +++ b/software/soapy/src/SoapyStreaming.cpp @@ -60,7 +60,7 @@ SoapySDR::Stream *SoapyIcyRadio::setupStream(const int direction, const std::str if(!this->isChannelVectorValid(direction, channels)) throw std::runtime_error("setupStream: Invalid channel(s) specified"); - std::lock_guard lock(this->streams_mutex); + std::lock_guard lock(this->streams_mutex); DLOGF(SOAPY_SDR_DEBUG, "setupStream: %s, %u channels, %s (%s)", (direction == SOAPY_SDR_RX) ? "RX" : "TX", channels.size(), format.c_str(), SoapySDR::KwargsToString(args).c_str()); @@ -156,9 +156,9 @@ SoapySDR::Stream *SoapyIcyRadio::setupStream(const int direction, const std::str } void SoapyIcyRadio::closeStream(SoapySDR::Stream *stream) { - std::lock_guard lock(this->streams_mutex); + std::lock_guard lock(this->streams_mutex); - SoapyIcyRadio::Stream *s = this->findStream(stream); + SoapyIcyRadio::Stream *s = this->_findStream(stream); // Use unlocked version since we are already locked if(s == nullptr) throw std::runtime_error("closeStream: Stream not found"); @@ -167,7 +167,11 @@ void SoapyIcyRadio::closeStream(SoapySDR::Stream *stream) std::lock_guard lock(s->mutex); if(s->active) - this->deactivateStream(stream); + { + DLOGF(SOAPY_SDR_ERROR, "closeStream: Stream is active"); + + return; + } } std::remove(this->streams.begin(), this->streams.end(), s); @@ -663,16 +667,36 @@ int SoapyIcyRadio::writeStream(SoapySDR::Stream *stream, const void * const *buf _timeNs = s->cur_buf_time; } - this->releaseWriteBuffer(stream, s->cur_buf_handle, s->cur_buf_valid_size, _flags, _timeNs); + try + { + this->releaseWriteBuffer(stream, s->cur_buf_handle, s->cur_buf_valid_size, _flags, _timeNs); - for(size_t i = 0; i < s->cur_buf_ptrs.size(); i++) - s->cur_buf_ptrs[i] = nullptr; + DLOGF_S(SOAPY_SDR_TRACE, "writeStream: Buffer released"); - s->cur_buf_size = 0; - s->cur_buf_valid_size = 0; - s->cur_buf_handle = 0; - s->cur_buf_time = 0; - s->cur_buf_time_valid = false; + for(size_t i = 0; i < s->cur_buf_ptrs.size(); i++) + s->cur_buf_ptrs[i] = nullptr; + + s->cur_buf_size = 0; + s->cur_buf_valid_size = 0; + s->cur_buf_handle = 0; + s->cur_buf_time = 0; + s->cur_buf_time_valid = false; + } + catch(const std::exception &e) + { + DLOGF(SOAPY_SDR_ERROR, "writeStream: Failed to release buffer %s, samples lost", e.what()); + + for(size_t i = 0; i < s->cur_buf_ptrs.size(); i++) + s->cur_buf_ptrs[i] = nullptr; + + s->cur_buf_size = 0; + s->cur_buf_valid_size = 0; + s->cur_buf_handle = 0; + s->cur_buf_time = 0; + s->cur_buf_time_valid = false; + + return SOAPY_SDR_STREAM_ERROR; + } } else { @@ -739,7 +763,7 @@ int SoapyIcyRadio::readStreamStatus(SoapySDR::Stream *stream, size_t &chanMask, ret = SOAPY_SDR_UNDERFLOW; // Log underflow as warning, otherwise it will be logged on the next releaseWriteBuffer - DLOGF(SOAPY_SDR_WARNING, "readStreamStatus: Underflow on channel %u", c->num); + DLOGF(SOAPY_SDR_TRACE, "readStreamStatus: Underflow on channel %u", c->num); c->underflow = false; chanMask |= BIT(c->num); @@ -1280,22 +1304,12 @@ void SoapyIcyRadio::releaseWriteBuffer(SoapySDR::Stream *stream, const size_t ha DLOGF_S(SOAPY_SDR_TRACE, "releaseWriteBuffer: Releasing user buffer %u", handle); bool any_dma_disabled = false; - bool any_underflow = false; // Check if any of the DMAs is disabled, meaning we had an underflow and the ISR disabled them for(auto &c : s->channels) { std::lock_guard lock(c->mutex); - if(c->underflow) - { - DLOGF_S(SOAPY_SDR_TRACE, "releaseWriteBuffer: Channel %u underflowed", c->num); - - any_underflow = true; - - break; - } - if(!c->dma->enabled()) { DLOGF_S(SOAPY_SDR_TRACE, "releaseWriteBuffer: DMA Controller %u of channel %u is disabled", c->dma->getPeripheralID(), c->num); @@ -1306,13 +1320,10 @@ void SoapyIcyRadio::releaseWriteBuffer(SoapySDR::Stream *stream, const size_t ha } } - if(any_underflow || any_dma_disabled) + if(any_dma_disabled) { // If no underflow flag is set, assume the warning has already been logged - if(any_underflow) - DLOGF(SOAPY_SDR_WARNING, "releaseWriteBuffer: At least one DMA Controller is disabled, waiting for all to be"); - else - DLOGF_S(SOAPY_SDR_TRACE, "releaseWriteBuffer: At least one DMA Controller is disabled, waiting for all to be"); + DLOGF(SOAPY_SDR_WARNING, "releaseWriteBuffer: At least one DMA Controller is disabled, waiting for all to be"); for(auto &c : s->channels) { @@ -1522,6 +1533,9 @@ void SoapyIcyRadio::releaseWriteBuffer(SoapySDR::Stream *stream, const size_t ha dma_buf->xfer.size = numElems * ICYRADIO_SAMPLE_SIZE_BYTES; + if(c->dma->idle()) + DLOGF(SOAPY_SDR_WARNING, "releaseWriteBuffer: DMA Controller is idle, stream may be discontinuous"); + c->dma->submitTransfer(dma_buf->xfer); dma_buf->idle = false; diff --git a/software/soapy/src/include/AD9361.hpp b/software/soapy/src/include/AD9361.hpp index 32fed2fb..ebb0df6d 100644 --- a/software/soapy/src/include/AD9361.hpp +++ b/software/soapy/src/include/AD9361.hpp @@ -2094,8 +2094,8 @@ // AD9361 Limits -#define RSSI_MULTIPLIER 100 -#define RSSI_RESOLUTION ((int) (0.25 * RSSI_MULTIPLIER)) +#define RSSI_OFFSET -128 +#define RSSI_RESOLUTION 0.25 #define RSSI_MAX_WEIGHT 255 #define MAX_LMT_INDEX 40 @@ -2126,6 +2126,8 @@ #define MAX_TX_HB2 320000000UL #define MAX_TX_HB3 320000000UL +#define MIN_BASEBAND_RATE DIV_CEIL(MIN_ADC_CLK, 48) // Min ADC rate divided by maximum decimation +#define MIN_BASEBAND_RATE_NOFIR DIV_CEIL(MIN_ADC_CLK, 12)// Min ADC rate divided by maximum decimation without FIR #define MAX_BASEBAND_RATE 61440000UL #define RFPLL_MODULUS 8388593UL @@ -2177,11 +2179,6 @@ class AD9361 FIR_RX1_RX2 = 0x83, FIR_IS_RX = 0x80, }; - struct RFGainCtrl - { - uint32_t ant; - uint8_t mode; - }; enum RFGainCtrlMode { RF_GAIN_MGC, @@ -2493,14 +2490,6 @@ class AD9361 uint32_t mixer_index; /* MIXER Index (Split GT mode only) */ }; - struct RFRSSI - { - uint32_t ant; /* Antenna number for which RSSI is reported */ - uint32_t symbol; /* Runtime RSSI */ - uint32_t preamble; /* Initial RSSI */ - int32_t multiplier; /* Multiplier to convert reported RSSI */ - uint8_t duration; /* Duration to be considered for measuring */ - }; struct SynthLUT { uint16_t VCO_MHz; @@ -2740,6 +2729,10 @@ class AD9361 void setupGPO(AD9361::GPOControl *ctrl); void setGPOValue(uint8_t gpo, AD9361::GPOValue value); + inline void setGPOValue(uint8_t gpo, bool value) + { + this->setGPOValue(gpo, value ? AD9361::GPOValue::HIGH : AD9361::GPOValue::LOW); + } AD9361::GPOValue getGPOValue(uint8_t gpo); void setupAuxDAC(AD9361::AUXDACControl *ctrl); @@ -2771,6 +2764,27 @@ class AD9361 void setupRXADC(); void setupRSSI(AD9361::RSSIControl *ctrl, bool is_update); + void readRSSI(uint8_t rx_id, double *symbol, double *preamble = nullptr); + inline double readRSSI(uint8_t rx_id) + { + double d; + + this->readRSSI(rx_id, &d); + + return d; + } + inline double readSymbolRSSI(uint8_t rx_id) + { + return this->readRSSI(rx_id); + } + inline double readPreambleRSSI(uint8_t rx_id) + { + double d; + + this->readRSSI(rx_id, nullptr, &d); + + return d; + } double getTemperature(); @@ -2835,6 +2849,10 @@ class AD9361 this->setClockChain(rx, tx); } + void verifyFIRCoefficients(AD9361::FIRDest dest, const int16_t *coefs, uint8_t count); + void loadFIRCoefficients(AD9361::FIRDest dest, const int16_t *coefs, uint8_t count, uint8_t int_dec = 1, int8_t gain = 0); + void validateAndEnableFIR(); + inline void runCalibration(uint8_t mask, uint32_t timeout_ms = 5000) { this->writeReg(AD9361_REG_CALIBRATION_CTRL, mask); @@ -2917,6 +2935,7 @@ class AD9361 void setSplitTableGain(uint16_t idx_reg, AD9361::RFRXGain *rx_gain); void setFullTableGain(uint16_t idx_reg, AD9361::RFRXGain *rx_gain); void setRXGain(uint8_t rx_id, AD9361::RFRXGain *rx_gain); + void setRXGainMode(uint8_t rx_id, AD9361::RFGainCtrlMode mode); void initRFPLLVCO(bool tx, uint64_t freq, uint32_t ref_freq); @@ -2973,11 +2992,6 @@ class AD9361 bool bypass_rx_fir; bool bypass_tx_fir; bool rx_eq_2tx; - bool filt_valid; - uint32_t filt_rx_path_clks[NUM_RX_CLOCKS]; - uint32_t filt_tx_path_clks[NUM_TX_CLOCKS]; - uint32_t filt_rx_bw_Hz; - uint32_t filt_tx_bw_Hz; uint8_t tx_fir_int; uint8_t tx_fir_ntaps; uint8_t rx_fir_dec; diff --git a/software/soapy/src/include/AXIDMAC.hpp b/software/soapy/src/include/AXIDMAC.hpp index 66dede8f..73c6f719 100644 --- a/software/soapy/src/include/AXIDMAC.hpp +++ b/software/soapy/src/include/AXIDMAC.hpp @@ -180,7 +180,7 @@ class AXIDMAC: public AXIPeripheral std::atomic transfer_done[AXI_DMAC_NUM_TRANSFERS]; std::atomic transfer_callback_done[AXI_DMAC_NUM_TRANSFERS]; - std::recursive_mutex mutex; + std::mutex mutex; }; // Instance 0 - axi_dmac_rf_tx0 - AXI DMA Controller for RF TX channel 0 diff --git a/software/soapy/src/include/IDT8V97003.hpp b/software/soapy/src/include/IDT8V97003.hpp index fff97b8d..ee3efbf7 100644 --- a/software/soapy/src/include/IDT8V97003.hpp +++ b/software/soapy/src/include/IDT8V97003.hpp @@ -145,6 +145,7 @@ #define IDT8V97003_REG_LD_CTL1_LD_PRECISION_10p4ns 0x06 // IDT8V97003_REG_PWR_CTL +#define IDT8V97003_REG_PWR_CTL_VCO_VREG_PDOWN BIT(7) #define IDT8V97003_REG_PWR_CTL_REF_VREG_PDOWN BIT(6) #define IDT8V97003_REG_PWR_CTL_PDCP_VREG_PDOWN BIT(5) #define IDT8V97003_REG_PWR_CTL_FB_VREG_PDOWN BIT(4) diff --git a/software/soapy/src/include/LT7182S.hpp b/software/soapy/src/include/LT7182S.hpp index 5304cbfd..190d8ba4 100644 --- a/software/soapy/src/include/LT7182S.hpp +++ b/software/soapy/src/include/LT7182S.hpp @@ -83,10 +83,12 @@ class LT7182S void writeCommand(uint8_t cmd); void writeByte(uint8_t cmd, uint8_t data); void writeWord(uint8_t cmd, uint16_t data); + void writeDWord(uint8_t cmd, uint32_t data); void writeL11(uint8_t cmd, float data); void writeUL16(uint8_t cmd, float data); uint8_t readByte(uint8_t cmd); uint16_t readWord(uint8_t cmd); + uint32_t readDWord(uint8_t cmd); float readL11(uint8_t cmd); float readUL16(uint8_t cmd); uint8_t readBlock(uint8_t cmd, uint8_t *buf, uint8_t max_size); @@ -128,6 +130,12 @@ class LT7182S std::string readManufacturerSerial(); uint16_t readManufacturerSpecialID(); + uint64_t getFaultLogTimestamp(); + void setFaultLogTimestamp(uint64_t timestamp); + void storeFaultLog(); + void clearFaultLog(); + void readFaultLog(/* ... */); // TODO: + uint8_t getStatusByte(LT7182S::Chan ch = LT7182S::Chan::CH_NO_CHANGE); void clearStatusByte(uint8_t mask, LT7182S::Chan ch = LT7182S::Chan::CH_NO_CHANGE); uint16_t getStatusWord(LT7182S::Chan ch = LT7182S::Chan::CH_NO_CHANGE); diff --git a/software/soapy/src/include/MappedRegion.hpp b/software/soapy/src/include/MappedRegion.hpp index 2b96adbc..7d9ffd29 100644 --- a/software/soapy/src/include/MappedRegion.hpp +++ b/software/soapy/src/include/MappedRegion.hpp @@ -31,8 +31,8 @@ class MappedRegion uintptr_t start; // Start address of the region (Physical address) size_t size; // Size of the requested region size_t map_size; // Size of the mapped region - void* map_ptr; // Pointer to the start of the mapped region (Virtual address) - void* ptr; // Pointer to the start of the requested region (Virtual address) + void *map_ptr; // Pointer to the start of the mapped region (Virtual address) + void *ptr; // Pointer to the start of the requested region (Virtual address) bool mapped; // Whether the region is mapped std::mutex mutex; diff --git a/software/soapy/src/include/SPIFlash.hpp b/software/soapy/src/include/SPIFlash.hpp index 54da32db..ea99400c 100644 --- a/software/soapy/src/include/SPIFlash.hpp +++ b/software/soapy/src/include/SPIFlash.hpp @@ -79,6 +79,8 @@ // 0xEF4017 - W25Q64JV-IQ/JQ // 0xEF4017 - W25Q64BV-IQ/JQ // 0xEF4017 - W25Q64FV-IQ/JQ +// 0xEF4018 - W25Q128JV-IQ/JQ +// 0xEF4018 - W25Q129FV-IQ/JQ // 0xBF2641 - SST26VF016B // 0xBF2643 - SST26VF064B @@ -91,6 +93,8 @@ class SPIFlash W25Q64JV, W25Q64BV, W25Q64FV, + W25Q128JV, + W25Q128FV, SST26VF016B, SST26VF064B, diff --git a/software/soapy/src/include/SoapyIcyRadio.hpp b/software/soapy/src/include/SoapyIcyRadio.hpp index 3d091263..6d060d5f 100644 --- a/software/soapy/src/include/SoapyIcyRadio.hpp +++ b/software/soapy/src/include/SoapyIcyRadio.hpp @@ -53,9 +53,9 @@ class SoapyIcyRadio: public SoapySDR::Device struct Config { bool use_clkin; // Use external clock input (CLKIN) instead of the internal TCXO (XTAL) - uint32_t clkin_freq; // External clock input frequency in Hz + double clkin_freq; // External clock input frequency in Hz bool enable_clkout; // Enable clock output on the u.FL connector - uint32_t clkout_freq; // External clock output frequency in Hz + double clkout_freq; // External clock output frequency in Hz }; class Stream { @@ -316,12 +316,12 @@ class SoapyIcyRadio: public SoapySDR::Device /******************************************************************* * Settings API ******************************************************************/ - //SoapySDR::ArgInfoList getSettingInfo() const; - //void writeSetting(const std::string &key, const std::string &value); - //std::string readSetting(const std::string &key) const; - //SoapySDR::ArgInfoList getSettingInfo(const int direction, const size_t channel) const; - //void writeSetting(const int direction, const size_t channel, const std::string &key, const std::string &value); - //std::string readSetting(const int direction, const size_t channel, const std::string &key) const; + SoapySDR::ArgInfoList getSettingInfo() const; + void writeSetting(const std::string &key, const std::string &value); + std::string readSetting(const std::string &key) const; + SoapySDR::ArgInfoList getSettingInfo(const int direction, const size_t channel) const; + void writeSetting(const int direction, const size_t channel, const std::string &key, const std::string &value); + std::string readSetting(const int direction, const size_t channel, const std::string &key) const; /******************************************************************* * GPIO API @@ -405,10 +405,22 @@ class SoapyIcyRadio: public SoapySDR::Device std::vector getStreams(bool active_only = false) const; std::vector getStreams(const int direction, bool active_only = false) const; - SoapyIcyRadio::Stream *findStream(SoapyIcyRadio::Stream *stream) const; + SoapyIcyRadio::Stream *_findStream(SoapyIcyRadio::Stream *stream) const; + inline SoapyIcyRadio::Stream *_findStream(SoapySDR::Stream *stream) const + { + return this->_findStream(reinterpret_cast(stream)); + } + inline SoapyIcyRadio::Stream *findStream(SoapyIcyRadio::Stream *stream) const + { + std::lock_guard lock(this->streams_mutex); + + return this->_findStream(stream); + } inline SoapyIcyRadio::Stream *findStream(SoapySDR::Stream *stream) const { - return this->findStream(reinterpret_cast(stream)); + std::lock_guard lock(this->streams_mutex); + + return this->_findStream(stream); } bool isChannelVectorValid(const int direction, const std::vector &channels) const; @@ -463,5 +475,5 @@ class SoapyIcyRadio: public SoapySDR::Device private: // Streaming metadata std::vector streams; - mutable std::recursive_mutex streams_mutex; + mutable std::mutex streams_mutex; }; \ No newline at end of file