mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
e3xx: add support for power calibration api
This commit adds support for the UHD power calibration API on the E3xx family of radios. It also updates the documentation to reflect this support, and it adds support to the uhd_power_cal.py utility by adding a "calibrator" subclass for E3xx radios.
This commit is contained in:
parent
f6f1dec85a
commit
51d4411ec1
@ -599,6 +599,15 @@ The following daughterboard sensors are always available:
|
||||
- `ad9361_temperature`: Temperature sensor of the RFIC die
|
||||
- `rssi`: RSSI value as returned from the AD9361 (RX only)
|
||||
|
||||
\subsection e3xx_power_api Power API
|
||||
|
||||
The E3xx series support the UHD power calibration API (see: \ref page_power).
|
||||
The TX path and the two RX paths per channel each have their own calibration
|
||||
data, resulting in 6 sets of calibration data per E3xx device.
|
||||
|
||||
Devices have to be manually calibrated using a calibrated power meter or
|
||||
signal generator.
|
||||
|
||||
\section e3xx_rasm Remote Management
|
||||
|
||||
\subsection e3xx_rasm_mender Mender: Remote update capability
|
||||
|
||||
@ -215,6 +215,9 @@ void e3xx_radio_control_impl::set_rx_antenna(const std::string& ant, const size_
|
||||
|
||||
radio_control_impl::set_rx_antenna(ant, chan);
|
||||
_set_atr_bits(chan);
|
||||
|
||||
// Upon changing antenna, update gain setting if in power tracking mode
|
||||
_rx_pwr_mgr.at(chan)->update_power();
|
||||
}
|
||||
|
||||
double e3xx_radio_control_impl::set_tx_frequency(const double freq, const size_t chan)
|
||||
@ -230,6 +233,8 @@ double e3xx_radio_control_impl::set_tx_frequency(const double freq, const size_t
|
||||
// frequency, we change the other, too
|
||||
for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; ++chan_idx) {
|
||||
radio_control_impl::set_tx_frequency(coerced_freq, chan_idx);
|
||||
// Upon retune, update gain setting if in power tracking mode
|
||||
_tx_pwr_mgr.at(chan_idx)->update_power();
|
||||
}
|
||||
|
||||
// Front-end switching
|
||||
@ -251,6 +256,8 @@ double e3xx_radio_control_impl::set_rx_frequency(const double freq, const size_t
|
||||
// frequency, we change the other, too
|
||||
for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; ++chan_idx) {
|
||||
radio_control_impl::set_rx_frequency(coerced_freq, chan_idx);
|
||||
// Upon retune, update gain setting if in power tracking mode
|
||||
_rx_pwr_mgr.at(chan_idx)->update_power();
|
||||
}
|
||||
// Front-end switching
|
||||
_set_atr_bits(chan);
|
||||
@ -263,6 +270,10 @@ void e3xx_radio_control_impl::set_rx_agc(const bool enb, const size_t chan)
|
||||
std::lock_guard<std::recursive_mutex> l(_set_lock);
|
||||
RFNOC_LOG_TRACE("set_rx_agc(enb=" << enb << ", chan=" << chan << ")");
|
||||
const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
|
||||
// Upon enabling AGC, stop tracking by power
|
||||
if (enb) {
|
||||
_rx_pwr_mgr.at(chan)->set_tracking_mode(pwr_cal_mgr::tracking_mode::TRACK_GAIN);
|
||||
}
|
||||
_ad9361->set_agc(rx_fe, enb);
|
||||
}
|
||||
|
||||
@ -291,6 +302,9 @@ double e3xx_radio_control_impl::set_tx_gain(const double gain, const size_t chan
|
||||
double clip_gain = uhd::clip(gain, AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN);
|
||||
_ad9361->set_gain(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clip_gain);
|
||||
radio_control_impl::set_tx_gain(clip_gain, chan);
|
||||
// Upon setting gain, stop tracking by power. Power manager also invokes
|
||||
// this function and will clobber its mode, but then restores TRACK_POWER
|
||||
_tx_pwr_mgr.at(chan)->set_tracking_mode(pwr_cal_mgr::tracking_mode::TRACK_GAIN);
|
||||
return clip_gain;
|
||||
}
|
||||
|
||||
@ -302,6 +316,9 @@ double e3xx_radio_control_impl::set_rx_gain(const double gain, const size_t chan
|
||||
double clip_gain = uhd::clip(gain, AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN);
|
||||
_ad9361->set_gain(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clip_gain);
|
||||
radio_control_impl::set_rx_gain(clip_gain, chan);
|
||||
// Upon setting gain, stop tracking by power. Power manager also invokes
|
||||
// this function and will clobber its mode, but then restores TRACK_POWER
|
||||
_rx_pwr_mgr.at(chan)->set_tracking_mode(pwr_cal_mgr::tracking_mode::TRACK_GAIN);
|
||||
return clip_gain;
|
||||
}
|
||||
|
||||
|
||||
@ -128,9 +128,14 @@ void e3xx_radio_control_impl::_init_frontend_subtree(
|
||||
{
|
||||
const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
|
||||
const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
|
||||
constexpr char HW_GAIN_STAGE[] = "hw";
|
||||
uhd::eeprom_map_t eeprom_map = get_db_eeprom();
|
||||
const std::string db_serial(eeprom_map["serial"].begin(), eeprom_map["serial"].end());
|
||||
|
||||
RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel "
|
||||
<< chan_idx << " to prop tree path " << tx_fe_path << " and "
|
||||
<< rx_fe_path);
|
||||
|
||||
// TX Standard attributes
|
||||
subtree->create<std::string>(tx_fe_path / "name").set("E3xx");
|
||||
subtree->create<std::string>(tx_fe_path / "connection").set("IQ");
|
||||
@ -159,6 +164,56 @@ void e3xx_radio_control_impl::_init_frontend_subtree(
|
||||
.add_coerced_subscriber([](const std::vector<std::string>&) {
|
||||
throw uhd::runtime_error("Attempting to update antenna options!");
|
||||
});
|
||||
// TX Power Calibration
|
||||
auto tx_cal_ggroup = uhd::gain_group::make();
|
||||
tx_cal_ggroup->register_fcns(HW_GAIN_STAGE,
|
||||
{[this, chan_idx]() { return get_tx_gain_range(chan_idx); },
|
||||
[this, chan_idx]() { return get_tx_gain(chan_idx); },
|
||||
[this, chan_idx](const double gain) { this->set_tx_gain(gain, chan_idx); }},
|
||||
10 /* High priority */);
|
||||
_tx_pwr_mgr.insert(_tx_pwr_mgr.begin() + chan_idx,
|
||||
uhd::usrp::pwr_cal_mgr::make(
|
||||
// Cal serial is the DB serial plus the FE name
|
||||
db_serial + "#" + subtree->access<std::string>(tx_fe_path / "name").get(),
|
||||
// Logger prefix
|
||||
"E3XX-CAL-TX",
|
||||
// Current frequency getter
|
||||
[this, chan_idx]() { return get_tx_frequency(chan_idx); },
|
||||
// Current calibration key getter (format is "e3xx_pwr_$dir_$chan_$ant")
|
||||
[this, chan_idx]() {
|
||||
const std::string ant = get_tx_antenna(chan_idx);
|
||||
return std::string("e3xx_pwr_tx_") + std::to_string(chan_idx) + "_"
|
||||
+ pwr_cal_mgr::sanitize_antenna_name(ant);
|
||||
},
|
||||
tx_cal_ggroup));
|
||||
// Provide current FE temperature to power manager
|
||||
_tx_pwr_mgr.at(chan_idx)->set_temperature(
|
||||
get_tx_sensor("ad9361_temperature", chan_idx).to_int());
|
||||
// RX Power Calibration
|
||||
auto rx_cal_ggroup = uhd::gain_group::make();
|
||||
rx_cal_ggroup->register_fcns(HW_GAIN_STAGE,
|
||||
{[this, chan_idx]() { return get_rx_gain_range(chan_idx); },
|
||||
[this, chan_idx]() { return get_rx_gain(chan_idx); },
|
||||
[this, chan_idx](const double gain) { this->set_rx_gain(gain, chan_idx); }},
|
||||
10 /* High priority */);
|
||||
_rx_pwr_mgr.insert(_rx_pwr_mgr.begin() + chan_idx,
|
||||
uhd::usrp::pwr_cal_mgr::make(
|
||||
// Cal serial is the DB serial plus the FE name
|
||||
db_serial + "#" + subtree->access<std::string>(rx_fe_path / "name").get(),
|
||||
// Logger prefix
|
||||
"E3XX-CAL-RX",
|
||||
// Current frequency getter
|
||||
[this, chan_idx]() { return get_rx_frequency(chan_idx); },
|
||||
// Current calibration key getter (format is "e3xx_pwr_$dir_$chan_$ant")
|
||||
[this, chan_idx]() {
|
||||
const std::string ant = get_rx_antenna(chan_idx);
|
||||
return std::string("e3xx_pwr_rx_") + std::to_string(chan_idx) + "_"
|
||||
+ pwr_cal_mgr::sanitize_antenna_name(ant);
|
||||
},
|
||||
rx_cal_ggroup));
|
||||
// Provide current FE temperature to power manager
|
||||
_rx_pwr_mgr.at(chan_idx)->set_temperature(
|
||||
get_rx_sensor("ad9361_temperature", chan_idx).to_int());
|
||||
// TX frequency
|
||||
subtree->create<double>(tx_fe_path / "freq" / "value")
|
||||
.set_coercer([this, chan_idx](const double freq) {
|
||||
|
||||
@ -407,6 +407,41 @@ class X410Calibrator(USRPCalibratorBase):
|
||||
# time to clear the command queue
|
||||
tune_settling_time = .5
|
||||
|
||||
class E3XXCalibrator(USRPCalibratorBase):
|
||||
"""
|
||||
E3XX calibration
|
||||
"""
|
||||
mboard_ids = ('e320', 'e310', 'e31x', 'e3xx')
|
||||
# Choosing 4 MHz: It is a small rate, but carries enough bandwidth to receive
|
||||
# a tone. Compatible with the default MCR of 16 MHz.
|
||||
default_rate = 4e6
|
||||
# Choosing an LO offset of 8 MHz: At 4 Msps, the LO will never be within
|
||||
# our estimate. E3XX generally has good DC offset / IQ balance performance,
|
||||
# but we still try and avoid DC as much as possible.
|
||||
lo_offset = 8e6
|
||||
tune_settling_time = .01 # Probably not needed??
|
||||
|
||||
def __init__(self, usrp, meas_dev, direction, **kwargs):
|
||||
super().__init__(usrp, meas_dev, direction, **kwargs)
|
||||
self._start_temp = None
|
||||
self._stop_temp = None
|
||||
|
||||
def start(self):
|
||||
# query and remember starting temperature
|
||||
get_sensor_fn = getattr(self._usrp, 'get_{}_sensor'.format(self._dir))
|
||||
self._start_temp = get_sensor_fn("ad9361_temperature").to_real()
|
||||
super().start()
|
||||
|
||||
def stop(self, store=True):
|
||||
# save average temperature over the calibration cycle
|
||||
get_sensor_fn = getattr(self._usrp, 'get_{}_sensor'.format(self._dir))
|
||||
self._stop_temp = get_sensor_fn("ad9361_temperature").to_real()
|
||||
print("===== Temperature range was {:.1f} to {:.1f} C"
|
||||
.format(self._start_temp, self._stop_temp))
|
||||
self.temp = round((self._start_temp + self._stop_temp) / 2)
|
||||
super().stop(store)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# The dispatch function
|
||||
###############################################################################
|
||||
|
||||
Loading…
Reference in New Issue
Block a user