Version 0.5.6, final tweaks to new soapy config

Config file should now be version 0.6
Slight tweaks to example config
Program now prints version number on startup
Formatting changes
This commit is contained in:
Wouter Bokslag
2026-03-25 16:57:08 +01:00
parent 98cefd855f
commit a73ed88fbc
8 changed files with 122 additions and 109 deletions

18
Cargo.lock generated
View File

@@ -150,7 +150,7 @@ dependencies = [
[[package]] [[package]]
name = "bluestation-bs" name = "bluestation-bs"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"clap", "clap",
"ctrlc", "ctrlc",
@@ -719,7 +719,7 @@ dependencies = [
[[package]] [[package]]
name = "net-tnmm-test" name = "net-tnmm-test"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"clap", "clap",
"tetra-config", "tetra-config",
@@ -734,7 +734,7 @@ dependencies = [
[[package]] [[package]]
name = "net-tnmm-test-quic" name = "net-tnmm-test-quic"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"quinn", "quinn",
"rcgen", "rcgen",
@@ -886,7 +886,7 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]] [[package]]
name = "pdu-tool" name = "pdu-tool"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"clap", "clap",
"tetra-config", "tetra-config",
@@ -1501,7 +1501,7 @@ dependencies = [
[[package]] [[package]]
name = "tetra-config" name = "tetra-config"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"chrono-tz", "chrono-tz",
"serde", "serde",
@@ -1511,7 +1511,7 @@ dependencies = [
[[package]] [[package]]
name = "tetra-core" name = "tetra-core"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"const_format", "const_format",
"git-version", "git-version",
@@ -1523,7 +1523,7 @@ dependencies = [
[[package]] [[package]]
name = "tetra-entities" name = "tetra-entities"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"as-any", "as-any",
"bitcode", "bitcode",
@@ -1553,7 +1553,7 @@ dependencies = [
[[package]] [[package]]
name = "tetra-pdus" name = "tetra-pdus"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"tetra-config", "tetra-config",
"tetra-core", "tetra-core",
@@ -1563,7 +1563,7 @@ dependencies = [
[[package]] [[package]]
name = "tetra-saps" name = "tetra-saps"
version = "0.5.5" version = "0.5.6"
dependencies = [ dependencies = [
"tetra-core", "tetra-core",
"tracing", "tracing",

View File

@@ -17,7 +17,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.5.5" version = "0.5.6"
edition = "2024" edition = "2024"
authors = ["Wouter Bokslag / Midnight Blue"] authors = ["Wouter Bokslag / Midnight Blue"]
license = "MIT" license = "MIT"

View File

@@ -92,9 +92,9 @@ fn main() {
eprintln!("░▀█▀░█▀▀░▀█▀░█▀▄░█▀█░░░░░█▀▄░█░░░█░█░█▀▀░█▀▀░▀█▀░█▀█░▀█▀░▀█▀░█▀█░█▀█"); eprintln!("░▀█▀░█▀▀░▀█▀░█▀▄░█▀█░░░░░█▀▄░█░░░█░█░█▀▀░█▀▀░▀█▀░█▀█░▀█▀░▀█▀░█▀█░█▀█");
eprintln!("░░█░░█▀▀░░█░░█▀▄░█▀█░▄▄▄░█▀▄░█░░░█░█░█▀▀░▀▀█░░█░░█▀█░░█░░░█░░█░█░█░█"); eprintln!("░░█░░█▀▀░░█░░█▀▄░█▀█░▄▄▄░█▀▄░█░░░█░█░█▀▀░▀▀█░░█░░█▀█░░█░░░█░░█░█░█░█");
eprintln!("░░▀░░▀▀▀░░▀░░▀░▀░▀░▀░░░░░▀▀░░▀▀▀░▀▀▀░▀▀▀░▀▀▀░░▀░░▀░▀░░▀░░▀▀▀░▀▀▀░▀░▀\n"); eprintln!("░░▀░░▀▀▀░░▀░░▀░▀░▀░▀░░░░░▀▀░░▀▀▀░▀▀▀░▀▀▀░▀▀▀░░▀░░▀░▀░░▀░░▀▀▀░▀▀▀░▀░▀\n");
eprintln!(" Wouter Bokslag / Midnight Blue"); eprintln!(" Wouter Bokslag / Midnight Blue");
eprintln!(" -> https://github.com/MidnightBlueLabs/tetra-bluestation"); eprintln!(" https://github.com/MidnightBlueLabs/tetra-bluestation");
eprintln!(" -> https://midnightblue.nl\n"); eprintln!(" Version: {}", tetra_core::STACK_VERSION);
let args = Args::parse(); let args = Args::parse();
let mut cfg = load_config_from_toml(&args.config); let mut cfg = load_config_from_toml(&args.config);

View File

@@ -17,7 +17,7 @@ pub fn from_toml_str(toml_str: &str) -> Result<SharedConfig, Box<dyn std::error:
let root: TomlConfigRoot = toml::from_str(toml_str)?; let root: TomlConfigRoot = toml::from_str(toml_str)?;
// Various sanity checks // Various sanity checks
let expected_config_version = "0.5"; let expected_config_version = "0.6";
if !root.config_version.eq(expected_config_version) { if !root.config_version.eq(expected_config_version) {
return Err(format!( return Err(format!(
"Unrecognized config_version: {}, expect {}", "Unrecognized config_version: {}, expect {}",
@@ -34,9 +34,10 @@ pub fn from_toml_str(toml_str: &str) -> Result<SharedConfig, Box<dyn std::error:
} }
if let Some(ref soapy) = root.phy_io.soapysdr { if let Some(ref soapy) = root.phy_io.soapysdr {
let extra_keys = sorted_keys(&soapy.extra); let extra_keys = sorted_keys(&soapy.extra);
let extra_keys_filtered = extra_keys.iter().filter(|key| { let extra_keys_filtered = extra_keys
!(key.starts_with("rx_gain_") || key.starts_with("tx_gain_")) .iter()
}).collect::<Vec<&&str>>(); .filter(|key| !(key.starts_with("rx_gain_") || key.starts_with("tx_gain_")))
.collect::<Vec<&&str>>();
if !extra_keys_filtered.is_empty() { if !extra_keys_filtered.is_empty() {
return Err(format!("Unrecognized fields: phy_io.soapysdr::{:?}", extra_keys_filtered).into()); return Err(format!("Unrecognized fields: phy_io.soapysdr::{:?}", extra_keys_filtered).into());
} }

View File

@@ -56,26 +56,40 @@ pub fn phy_dto_to_cfg(src: PhyIoDto) -> CfgPhyIo {
tx_ch: soapy_dto.tx_channel, tx_ch: soapy_dto.tx_channel,
rx_ant: soapy_dto.rx_antenna, rx_ant: soapy_dto.rx_antenna,
tx_ant: soapy_dto.tx_antenna, tx_ant: soapy_dto.tx_antenna,
rx_gains: soapy_dto.extra.iter().filter_map(|(key, value)| { rx_gains: soapy_dto
key.strip_prefix("rx_gain_").map(|gain_name| { .extra
(gain_name.to_string().to_lowercase(), match value { .iter()
Value::Integer(v) => *v as f64, .filter_map(|(key, value)| {
Value::Float(v) => *v, key.strip_prefix("rx_gain_").map(|gain_name| {
// TODO: should this error be returned somehow? (
_ => panic!("RX gain value must be a number"), gain_name.to_string().to_lowercase(),
match value {
Value::Integer(v) => *v as f64,
Value::Float(v) => *v,
// TODO: should this error be returned somehow?
_ => panic!("RX gain value must be a number"),
},
)
}) })
}) })
}).collect(), .collect(),
tx_gains: soapy_dto.extra.iter().filter_map(|(key, value)| { tx_gains: soapy_dto
key.strip_prefix("tx_gain_").map(|gain_name| { .extra
(gain_name.to_string().to_lowercase(), match value { .iter()
Value::Integer(v) => *v as f64, .filter_map(|(key, value)| {
Value::Float(v) => *v, key.strip_prefix("tx_gain_").map(|gain_name| {
// TODO: should this error be returned somehow? (
_ => panic!("TX gain value must be a number"), gain_name.to_string().to_lowercase(),
match value {
Value::Integer(v) => *v as f64,
Value::Float(v) => *v,
// TODO: should this error be returned somehow?
_ => panic!("TX gain value must be a number"),
},
)
}) })
}) })
}).collect(), .collect(),
} }
}); });

View File

@@ -2,7 +2,6 @@
use tetra_config::bluestation::{StackMode, sec_phy_soapy::*}; use tetra_config::bluestation::{StackMode, sec_phy_soapy::*};
/// Enum of all supported devices /// Enum of all supported devices
pub enum SupportedDevice { pub enum SupportedDevice {
LimeSdr(LimeSdrModel), LimeSdr(LimeSdrModel),
@@ -34,43 +33,31 @@ impl SupportedDevice {
/// Return None if the device is not supported. /// Return None if the device is not supported.
pub fn detect(driver_key: &str, hardware_key: &str) -> Option<Self> { pub fn detect(driver_key: &str, hardware_key: &str) -> Option<Self> {
match (driver_key, hardware_key) { match (driver_key, hardware_key) {
("FX3", "LimeSDR-USB") => ("FX3", "LimeSDR-USB") => Some(Self::LimeSdr(LimeSdrModel::LimeSdrUsb)),
Some(Self::LimeSdr(LimeSdrModel::LimeSdrUsb)), ("FX3", _) => Some(Self::LimeSdr(LimeSdrModel::OtherFx3)),
("FX3", _) =>
Some(Self::LimeSdr(LimeSdrModel::OtherFx3)),
("FT601", "LimeSDR-Mini_v2") => ("FT601", "LimeSDR-Mini_v2") => Some(Self::LimeSdr(LimeSdrModel::LimeSdrMiniV2)),
Some(Self::LimeSdr(LimeSdrModel::LimeSdrMiniV2)), ("FT601", "LimeNET-Micro") => Some(Self::LimeSdr(LimeSdrModel::LimeNetMicro)),
("FT601", "LimeNET-Micro") => ("FT601", _) => Some(Self::LimeSdr(LimeSdrModel::OtherFt601)),
Some(Self::LimeSdr(LimeSdrModel::LimeNetMicro)),
("FT601", _) =>
Some(Self::LimeSdr(LimeSdrModel::OtherFt601)),
("sx", _) => ("sx", _) => Some(Self::SXceiver),
Some(Self::SXceiver),
("PlutoSDR", _) => ("PlutoSDR", _) => Some(Self::PlutoSdr),
Some(Self::PlutoSdr),
// USRP B210 seems to report as ("b200", "B210"), // USRP B210 seems to report as ("b200", "B210"),
// but the driver key is also known to be "uhd" in some cases. // but the driver key is also known to be "uhd" in some cases.
// The reason is unknown but might be due to // The reason is unknown but might be due to
// gateware, firmware or driver version differences. // gateware, firmware or driver version differences.
// Try to detect USRP correctly in all cases. // Try to detect USRP correctly in all cases.
("b200", "B200") | ("uhd", "B200") => ("b200", "B200") | ("uhd", "B200") => Some(Self::Usrp(UsrpModel::B200)),
Some(Self::Usrp(UsrpModel::B200)), ("b200", "B210") | ("uhd", "B210") => Some(Self::Usrp(UsrpModel::B210)),
("b200", "B210") | ("uhd", "B210") => ("b200", _) | ("uhd", _) => Some(Self::Usrp(UsrpModel::Other)),
Some(Self::Usrp(UsrpModel::B210)),
("b200", _) | ("uhd", _) =>
Some(Self::Usrp(UsrpModel::Other)),
// TODO: add other USRP models if needed // TODO: add other USRP models if needed
_ => None, _ => None,
} }
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SdrSettings { pub struct SdrSettings {
/// Settings template, holding which SDR is used /// Settings template, holding which SDR is used
@@ -137,7 +124,7 @@ impl SdrSettings {
} }
if !cfg_gains.is_empty() { if !cfg_gains.is_empty() {
tracing::error!("Unsupported RX gains for {}: {:?}", settings.name, cfg_gains); tracing::error!("Unsupported RX gains for {}: {:?}", settings.name, cfg_gains);
return Err(Error::InvalidConfiguration) return Err(Error::InvalidConfiguration);
} }
let mut cfg_gains = cfg.tx_gains.clone(); let mut cfg_gains = cfg.tx_gains.clone();
@@ -148,7 +135,7 @@ impl SdrSettings {
} }
if !cfg_gains.is_empty() { if !cfg_gains.is_empty() {
tracing::error!("Unsupported TX gains for {}: {:?}", settings.name, cfg_gains); tracing::error!("Unsupported TX gains for {}: {:?}", settings.name, cfg_gains);
return Err(Error::InvalidConfiguration) return Err(Error::InvalidConfiguration);
} }
// TODO: check for extra gain fields in cfg // TODO: check for extra gain fields in cfg
@@ -159,17 +146,13 @@ impl SdrSettings {
/// Get default settings based on SDR type /// Get default settings based on SDR type
fn get_defaults(cfg: &CfgSoapySdr, device: SupportedDevice, mode: StackMode) -> Self { fn get_defaults(cfg: &CfgSoapySdr, device: SupportedDevice, mode: StackMode) -> Self {
match device { match device {
SupportedDevice::LimeSdr(model) => SupportedDevice::LimeSdr(model) => Self::settings_limesdr(mode, model),
Self::settings_limesdr(mode, model),
SupportedDevice::SXceiver => SupportedDevice::SXceiver => Self::settings_sxceiver(mode, cfg.fs),
Self::settings_sxceiver(mode, cfg.fs),
SupportedDevice::PlutoSdr => SupportedDevice::PlutoSdr => Self::settings_pluto(mode),
Self::settings_pluto(mode),
SupportedDevice::Usrp(model) => SupportedDevice::Usrp(model) => Self::settings_usrp(mode, model),
Self::settings_usrp(mode, model),
} }
} }
@@ -220,25 +203,24 @@ impl SdrSettings {
} }
.to_string(), .to_string(),
rx_ant: Some(match model { rx_ant: Some(
LimeSdrModel::LimeSdrUsb => "LNAL", match model {
_ => "LNAW", LimeSdrModel::LimeSdrUsb => "LNAL",
}.to_string()), _ => "LNAW",
}
.to_string(),
),
tx_ant: Some(match model { tx_ant: Some(
LimeSdrModel::LimeSdrUsb => "BAND1", match model {
_ => "BAND2", LimeSdrModel::LimeSdrUsb => "BAND1",
}.to_string()), _ => "BAND2",
}
.to_string(),
),
rx_gain: vec![ rx_gain: vec![("LNA".to_string(), 18.0), ("TIA".to_string(), 6.0), ("PGA".to_string(), 10.0)],
("LNA".to_string(), 18.0), tx_gain: vec![("PAD".to_string(), 22.0), ("IAMP".to_string(), 6.0)],
("TIA".to_string(), 6.0),
("PGA".to_string(), 10.0),
],
tx_gain: vec![
("PAD".to_string(), 22.0),
("IAMP".to_string(), 6.0),
],
// Minimum latency for BS/MS, maximum throughput for monitor // Minimum latency for BS/MS, maximum throughput for monitor
rx_args: vec![("latency".to_string(), if mode == StackMode::Mon { "1" } else { "0" }.to_string())], rx_args: vec![("latency".to_string(), if mode == StackMode::Mon { "1" } else { "0" }.to_string())],
@@ -265,14 +247,8 @@ impl SdrSettings {
rx_ant: Some("RX".to_string()), rx_ant: Some("RX".to_string()),
tx_ant: Some("TX".to_string()), tx_ant: Some("TX".to_string()),
rx_gain: vec![ rx_gain: vec![("LNA".to_string(), 42.0), ("PGA".to_string(), 16.0)],
("LNA".to_string(), 42.0), tx_gain: vec![("DAC".to_string(), 9.0), ("MIXER".to_string(), 30.0)],
("PGA".to_string(), 16.0),
],
tx_gain: vec![
("DAC".to_string(), 9.0),
("MIXER".to_string(), 30.0),
],
rx_args: vec![("period".to_string(), block_size(fs).to_string())], rx_args: vec![("period".to_string(), block_size(fs).to_string())],
tx_args: vec![("period".to_string(), block_size(fs).to_string())], tx_args: vec![("period".to_string(), block_size(fs).to_string())],
@@ -327,7 +303,6 @@ impl SdrSettings {
} }
} }
/// Get processing block size in samples for a given sample rate. /// Get processing block size in samples for a given sample rate.
/// This can be used to optimize performance for some SDRs. /// This can be used to optimize performance for some SDRs.
pub fn block_size(fs: f64) -> usize { pub fn block_size(fs: f64) -> usize {

View File

@@ -336,10 +336,8 @@ impl SoapyIo {
} }
} }
// Messy logic related to opening a device follows... // Messy logic related to opening a device follows...
/// Struct to temporarily hold stuff related to opening and detecting a device /// Struct to temporarily hold stuff related to opening and detecting a device
struct OpenedDevice { struct OpenedDevice {
dev_args: soapysdr::Args, dev_args: soapysdr::Args,
@@ -370,7 +368,11 @@ fn open_given_device(dev_args: soapysdr::Args) -> Result<OpenedDevice, soapysdr:
// Check whether the device is supported // Check whether the device is supported
if let Some(detected_device) = SupportedDevice::detect(&driver_key, &hardware_key) { if let Some(detected_device) = SupportedDevice::detect(&driver_key, &hardware_key) {
tracing::info!("Found supported device with driver_key '{}' hardware_key '{}'", driver_key, hardware_key); tracing::info!(
"Found supported device with driver_key '{}' hardware_key '{}'",
driver_key,
hardware_key
);
Ok(OpenedDevice { Ok(OpenedDevice {
dev_args, dev_args,
dev, dev,
@@ -380,7 +382,11 @@ fn open_given_device(dev_args: soapysdr::Args) -> Result<OpenedDevice, soapysdr:
soapyremote_used, soapyremote_used,
}) })
} else { } else {
tracing::info!("Skipping unsupported device with driver_key '{}' hardware_key '{}'", driver_key, hardware_key); tracing::info!(
"Skipping unsupported device with driver_key '{}' hardware_key '{}'",
driver_key,
hardware_key
);
Err(soapysdr::Error { Err(soapysdr::Error {
code: soapysdr::ErrorCode::NotSupported, code: soapysdr::ErrorCode::NotSupported,
message: "Unsupported device".to_string(), message: "Unsupported device".to_string(),
@@ -394,7 +400,7 @@ fn find_supported_device(filter_args: soapysdr::Args) -> Result<OpenedDevice, so
//tracing::info!("Trying to open a device with arguments: {}", args_formatted); //tracing::info!("Trying to open a device with arguments: {}", args_formatted);
match open_given_device(dev_args) { match open_given_device(dev_args) {
Ok(opened_device) => return Ok(opened_device), Ok(opened_device) => return Ok(opened_device),
Err(_) => {}, Err(_) => {}
} }
} }
return Err(soapysdr::Error { return Err(soapysdr::Error {
@@ -414,10 +420,12 @@ fn open_device(soapy_cfg: &CfgSoapySdr, mode: StackMode) -> Result<(soapysdr::De
let mut sdr_settings = match SdrSettings::get_settings(&soapy_cfg, opened_device.detected_device, mode) { let mut sdr_settings = match SdrSettings::get_settings(&soapy_cfg, opened_device.detected_device, mode) {
Ok(sdr_settings) => sdr_settings, Ok(sdr_settings) => sdr_settings,
Err(soapy_settings::Error::InvalidConfiguration) => return Err(soapysdr::Error { Err(soapy_settings::Error::InvalidConfiguration) => {
code: soapysdr::ErrorCode::Other, return Err(soapysdr::Error {
message: "Invalid SDR device configuration".to_string(), code: soapysdr::ErrorCode::Other,
}), message: "Invalid SDR device configuration".to_string(),
});
}
}; };
if opened_device.soapyremote_used { if opened_device.soapyremote_used {
@@ -439,7 +447,10 @@ fn open_device(soapy_cfg: &CfgSoapySdr, mode: StackMode) -> Result<(soapysdr::De
// Make sure device gets closed first. Not sure if needed. // Make sure device gets closed first. Not sure if needed.
std::mem::drop(opened_device.dev); std::mem::drop(opened_device.dev);
opened_device.dev = soapycheck!("open SoapySDR device with additional arguments", soapysdr::Device::new(opened_device.dev_args)); opened_device.dev = soapycheck!(
"open SoapySDR device with additional arguments",
soapysdr::Device::new(opened_device.dev_args)
);
// Make sure it is still the same device. // Make sure it is still the same device.
// Unlikely to change, but who knows if a device got connected just in between, // Unlikely to change, but who knows if a device got connected just in between,
// or if the device broke from first opening attempt and something else got opened // or if the device broke from first opening attempt and something else got opened
@@ -447,8 +458,13 @@ fn open_device(soapy_cfg: &CfgSoapySdr, mode: StackMode) -> Result<(soapysdr::De
let new_driver_key = opened_device.dev.driver_key().unwrap_or_default(); let new_driver_key = opened_device.dev.driver_key().unwrap_or_default();
let new_hardware_key = opened_device.dev.hardware_key().unwrap_or_default(); let new_hardware_key = opened_device.dev.hardware_key().unwrap_or_default();
if new_driver_key != opened_device.driver_key || new_hardware_key != opened_device.hardware_key { if new_driver_key != opened_device.driver_key || new_hardware_key != opened_device.hardware_key {
tracing::info!("Expected the same driver_key='{}' hardware_key='{}' after reopen, got driver_key='{}' hardware_key='{}'", tracing::info!(
opened_device.driver_key, opened_device.hardware_key, new_driver_key, new_hardware_key); "Expected the same driver_key='{}' hardware_key='{}' after reopen, got driver_key='{}' hardware_key='{}'",
opened_device.driver_key,
opened_device.hardware_key,
new_driver_key,
new_hardware_key
);
return Err(soapysdr::Error { return Err(soapysdr::Error {
code: soapysdr::ErrorCode::Other, code: soapysdr::ErrorCode::Other,
message: "Reopened a different device".to_string(), message: "Reopened a different device".to_string(),

View File

@@ -2,7 +2,7 @@
# This is an example configuration file for the TETRA base station stack # This is an example configuration file for the TETRA base station stack
# DO NOT RUN without editing to stay within legal limits of your jurisdiction # DO NOT RUN without editing to stay within legal limits of your jurisdiction
config_version = "0.5" config_version = "0.6"
# Stack operation mode: "Bs" (Base Station), "Ms" (Mobile Station), or "Mon" (Monitor) # Stack operation mode: "Bs" (Base Station), "Ms" (Mobile Station), or "Mon" (Monitor)
stack_mode = "Bs" stack_mode = "Bs"
@@ -28,7 +28,15 @@ backend = "SoapySdr"
# !!! Make sure to also edit all related fields in the cell_info section to fit this frequency. # !!! Make sure to also edit all related fields in the cell_info section to fit this frequency.
tx_freq = 438025000 tx_freq = 438025000
rx_freq = 433025000 rx_freq = 433025000
ppm_err = 0.0 # Adjust if your SDR has a non-negligible tuning error
# Adjust if your SDR has a non-negligible tuning error
# ppm_err = 0.0
################################################################################################
## OPTIONAL: specific device, antenna, gain selection ##
## Make sure to check your device for valid settings using SoapySDRUtil --probe ##
## or check: https://github.com/MidnightBlueLabs/tetra-bluestation-docs/wiki/03-Configuration ##
################################################################################################
# Optional device selection arguments. # Optional device selection arguments.
# If not specified, the first supported device found is selected automatically. # If not specified, the first supported device found is selected automatically.
@@ -40,18 +48,17 @@ ppm_err = 0.0 # Adjust if your SDR has a non-negligible tu
# device = "driver=lime,serial=123456789" # device = "driver=lime,serial=123456789"
# Optional antenna selection to override device-specific defaults # Optional antenna selection to override device-specific defaults
# Check antenna names with SoapySDRUtil --probe
# rx_antenna = "LNAW" # rx_antenna = "LNAW"
# tx_antenna = "BAND2" # tx_antenna = "BAND2"
# Optional gain values to override device-specific defaults. # Optional gain values to override device-specific defaults.
# Check valid gain names with SoapySDRUtil --probe
# For example, to increase transmit power on a LimeSDR: # For example, to increase transmit power on a LimeSDR:
# tx_gain_pad = 50.0 # tx_gain_pad = 50.0
# To adjust LNA gain to optimize RX performance on a LimeSDR or SXceiver: # To adjust LNA gain to optimize RX performance on a LimeSDR or SXceiver:
# rx_gain_lna = 30.0 # rx_gain_lna = 30.0
# Antennas, gain names and ranges of gain values are device dependent.
# To list them, try something like: SoapySDRUtil --probe=driver=lime
############################################################################### ###############################################################################
# Network Information # Network Information