[fix](workspace): resolve clippy warnings
Clean up the workspace so cargo clippy passes across all targets and features. Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -631,7 +631,7 @@ mod tests {
|
|||||||
let mut candidate = Candidate::new(240_000.0, 0.0);
|
let mut candidate = Candidate::new(240_000.0, 0.0);
|
||||||
let pi = 0x52ab;
|
let pi = 0x52ab;
|
||||||
let block_a = encode_block(pi, OFFSET_A);
|
let block_a = encode_block(pi, OFFSET_A);
|
||||||
let block_b = encode_block((10 << 5) | 0, OFFSET_B);
|
let block_b = encode_block(10 << 5, OFFSET_B);
|
||||||
let block_d = encode_block(u16::from_be_bytes(*b"AB"), OFFSET_D);
|
let block_d = encode_block(u16::from_be_bytes(*b"AB"), OFFSET_D);
|
||||||
|
|
||||||
for bit_idx in (0..26).rev() {
|
for bit_idx in (0..26).rev() {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use trx_core::audio::{
|
|||||||
use trx_core::decode::DecodedMessage;
|
use trx_core::decode::DecodedMessage;
|
||||||
|
|
||||||
/// Run the audio client with auto-reconnect.
|
/// Run the audio client with auto-reconnect.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn run_audio_client(
|
pub async fn run_audio_client(
|
||||||
server_host: String,
|
server_host: String,
|
||||||
default_port: u16,
|
default_port: u16,
|
||||||
@@ -102,6 +103,7 @@ pub async fn run_audio_client(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn handle_audio_connection(
|
async fn handle_audio_connection(
|
||||||
stream: TcpStream,
|
stream: TcpStream,
|
||||||
server_host: &str,
|
server_host: &str,
|
||||||
|
|||||||
@@ -523,7 +523,7 @@ fn parse_port(port_str: &str) -> Result<u16, String> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{parse_remote_url, RemoteClientConfig, RemoteEndpoint};
|
use super::{parse_remote_url, RemoteClientConfig, RemoteEndpoint, SharedSpectrum};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ const LOGO_BYTES: &[u8] =
|
|||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/trx-logo.png"));
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/trx-logo.png"));
|
||||||
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
|
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
|
||||||
|
|
||||||
|
struct FrontendMeta {
|
||||||
|
http_clients: usize,
|
||||||
|
rigctl_clients: usize,
|
||||||
|
rigctl_addr: Option<String>,
|
||||||
|
active_rig_id: Option<String>,
|
||||||
|
rig_ids: Vec<String>,
|
||||||
|
owner_callsign: Option<String>,
|
||||||
|
show_sdr_gain_control: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/status")]
|
#[get("/status")]
|
||||||
pub async fn status_api(
|
pub async fn status_api(
|
||||||
state: web::Data<watch::Receiver<RigState>>,
|
state: web::Data<watch::Receiver<RigState>>,
|
||||||
@@ -39,13 +49,7 @@ pub async fn status_api(
|
|||||||
let json = serde_json::to_string(&state).map_err(actix_web::error::ErrorInternalServerError)?;
|
let json = serde_json::to_string(&state).map_err(actix_web::error::ErrorInternalServerError)?;
|
||||||
let json = inject_frontend_meta(
|
let json = inject_frontend_meta(
|
||||||
&json,
|
&json,
|
||||||
clients.load(Ordering::Relaxed),
|
frontend_meta_from_context(clients.load(Ordering::Relaxed), context.get_ref().as_ref()),
|
||||||
context.rigctl_clients.load(Ordering::Relaxed),
|
|
||||||
rigctl_addr_from_context(context.get_ref().as_ref()),
|
|
||||||
active_rig_id_from_context(context.get_ref().as_ref()),
|
|
||||||
rig_ids_from_context(context.get_ref().as_ref()),
|
|
||||||
owner_callsign_from_context(context.get_ref().as_ref()),
|
|
||||||
show_sdr_gain_control_from_context(context.get_ref().as_ref()),
|
|
||||||
);
|
);
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||||
@@ -53,16 +57,7 @@ pub async fn status_api(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Inject `"clients": N` into a JSON object string.
|
/// Inject `"clients": N` into a JSON object string.
|
||||||
fn inject_frontend_meta(
|
fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String {
|
||||||
json: &str,
|
|
||||||
http_clients: usize,
|
|
||||||
rigctl_clients: usize,
|
|
||||||
rigctl_addr: Option<String>,
|
|
||||||
active_rig_id: Option<String>,
|
|
||||||
rig_ids: Vec<String>,
|
|
||||||
owner_callsign: Option<String>,
|
|
||||||
show_sdr_gain_control: bool,
|
|
||||||
) -> String {
|
|
||||||
let mut value: serde_json::Value = match serde_json::from_str(json) {
|
let mut value: serde_json::Value = match serde_json::from_str(json) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return json.to_string(),
|
Err(_) => return json.to_string(),
|
||||||
@@ -71,29 +66,41 @@ fn inject_frontend_meta(
|
|||||||
let Some(map) = value.as_object_mut() else {
|
let Some(map) = value.as_object_mut() else {
|
||||||
return json.to_string();
|
return json.to_string();
|
||||||
};
|
};
|
||||||
map.insert("clients".to_string(), serde_json::json!(http_clients));
|
map.insert("clients".to_string(), serde_json::json!(meta.http_clients));
|
||||||
map.insert(
|
map.insert(
|
||||||
"rigctl_clients".to_string(),
|
"rigctl_clients".to_string(),
|
||||||
serde_json::json!(rigctl_clients),
|
serde_json::json!(meta.rigctl_clients),
|
||||||
);
|
);
|
||||||
if let Some(addr) = rigctl_addr {
|
if let Some(addr) = meta.rigctl_addr {
|
||||||
map.insert("rigctl_addr".to_string(), serde_json::json!(addr));
|
map.insert("rigctl_addr".to_string(), serde_json::json!(addr));
|
||||||
}
|
}
|
||||||
if let Some(rig_id) = active_rig_id {
|
if let Some(rig_id) = meta.active_rig_id {
|
||||||
map.insert("active_rig_id".to_string(), serde_json::json!(rig_id));
|
map.insert("active_rig_id".to_string(), serde_json::json!(rig_id));
|
||||||
}
|
}
|
||||||
map.insert("rig_ids".to_string(), serde_json::json!(rig_ids));
|
map.insert("rig_ids".to_string(), serde_json::json!(meta.rig_ids));
|
||||||
if let Some(owner) = owner_callsign {
|
if let Some(owner) = meta.owner_callsign {
|
||||||
map.insert("owner_callsign".to_string(), serde_json::json!(owner));
|
map.insert("owner_callsign".to_string(), serde_json::json!(owner));
|
||||||
}
|
}
|
||||||
map.insert(
|
map.insert(
|
||||||
"show_sdr_gain_control".to_string(),
|
"show_sdr_gain_control".to_string(),
|
||||||
serde_json::json!(show_sdr_gain_control),
|
serde_json::json!(meta.show_sdr_gain_control),
|
||||||
);
|
);
|
||||||
|
|
||||||
serde_json::to_string(&value).unwrap_or_else(|_| json.to_string())
|
serde_json::to_string(&value).unwrap_or_else(|_| json.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn frontend_meta_from_context(http_clients: usize, context: &FrontendRuntimeContext) -> FrontendMeta {
|
||||||
|
FrontendMeta {
|
||||||
|
http_clients,
|
||||||
|
rigctl_clients: context.rigctl_clients.load(Ordering::Relaxed),
|
||||||
|
rigctl_addr: rigctl_addr_from_context(context),
|
||||||
|
active_rig_id: active_rig_id_from_context(context),
|
||||||
|
rig_ids: rig_ids_from_context(context),
|
||||||
|
owner_callsign: owner_callsign_from_context(context),
|
||||||
|
show_sdr_gain_control: show_sdr_gain_control_from_context(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rigctl_addr_from_context(context: &FrontendRuntimeContext) -> Option<String> {
|
fn rigctl_addr_from_context(context: &FrontendRuntimeContext) -> Option<String> {
|
||||||
context
|
context
|
||||||
.rigctl_listen_addr
|
.rigctl_listen_addr
|
||||||
@@ -144,13 +151,7 @@ pub async fn events(
|
|||||||
serde_json::to_string(&initial).map_err(actix_web::error::ErrorInternalServerError)?;
|
serde_json::to_string(&initial).map_err(actix_web::error::ErrorInternalServerError)?;
|
||||||
let initial_json = inject_frontend_meta(
|
let initial_json = inject_frontend_meta(
|
||||||
&initial_json,
|
&initial_json,
|
||||||
count,
|
frontend_meta_from_context(count, context.get_ref().as_ref()),
|
||||||
context.rigctl_clients.load(Ordering::Relaxed),
|
|
||||||
rigctl_addr_from_context(context.get_ref().as_ref()),
|
|
||||||
active_rig_id_from_context(context.get_ref().as_ref()),
|
|
||||||
rig_ids_from_context(context.get_ref().as_ref()),
|
|
||||||
owner_callsign_from_context(context.get_ref().as_ref()),
|
|
||||||
show_sdr_gain_control_from_context(context.get_ref().as_ref()),
|
|
||||||
);
|
);
|
||||||
let initial_stream =
|
let initial_stream =
|
||||||
once(async move { Ok::<Bytes, Error>(Bytes::from(format!("data: {initial_json}\n\n"))) });
|
once(async move { Ok::<Bytes, Error>(Bytes::from(format!("data: {initial_json}\n\n"))) });
|
||||||
@@ -165,13 +166,10 @@ pub async fn events(
|
|||||||
serde_json::to_string(&v).ok().map(|json| {
|
serde_json::to_string(&v).ok().map(|json| {
|
||||||
let json = inject_frontend_meta(
|
let json = inject_frontend_meta(
|
||||||
&json,
|
&json,
|
||||||
counter.load(Ordering::Relaxed),
|
frontend_meta_from_context(
|
||||||
context.rigctl_clients.load(Ordering::Relaxed),
|
counter.load(Ordering::Relaxed),
|
||||||
rigctl_addr_from_context(context.as_ref()),
|
context.as_ref(),
|
||||||
active_rig_id_from_context(context.as_ref()),
|
),
|
||||||
rig_ids_from_context(context.as_ref()),
|
|
||||||
owner_callsign_from_context(context.as_ref()),
|
|
||||||
show_sdr_gain_control_from_context(context.as_ref()),
|
|
||||||
);
|
);
|
||||||
Ok::<Bytes, Error>(Bytes::from(format!("data: {json}\n\n")))
|
Ok::<Bytes, Error>(Bytes::from(format!("data: {json}\n\n")))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ pub fn spawn_audio_capture(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn run_capture(
|
fn run_capture(
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
channels: u16,
|
channels: u16,
|
||||||
@@ -1306,6 +1307,7 @@ pub async fn run_audio_listener(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn handle_audio_client(
|
async fn handle_audio_client(
|
||||||
socket: TcpStream,
|
socket: TcpStream,
|
||||||
peer: SocketAddr,
|
peer: SocketAddr,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use trx_core::rig::state::RigMode;
|
|||||||
/// `[behavior]` / `[decode_logs]` fields are still supported via
|
/// `[behavior]` / `[decode_logs]` fields are still supported via
|
||||||
/// `ServerConfig::resolved_rigs()` which synthesises a single-element list
|
/// `ServerConfig::resolved_rigs()` which synthesises a single-element list
|
||||||
/// with `id = "default"` when `rigs` is empty.
|
/// with `id = "default"` when `rigs` is empty.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct RigInstanceConfig {
|
pub struct RigInstanceConfig {
|
||||||
/// Stable rig identifier used in protocol routing.
|
/// Stable rig identifier used in protocol routing.
|
||||||
@@ -51,22 +51,6 @@ pub struct RigInstanceConfig {
|
|||||||
pub decode_logs: DecodeLogsConfig,
|
pub decode_logs: DecodeLogsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RigInstanceConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
id: String::new(), // Empty by default so auto-generation triggers in resolved_rigs()
|
|
||||||
name: None,
|
|
||||||
rig: RigConfig::default(),
|
|
||||||
behavior: BehaviorConfig::default(),
|
|
||||||
audio: AudioConfig::default(),
|
|
||||||
sdr: SdrConfig::default(),
|
|
||||||
pskreporter: PskReporterConfig::default(),
|
|
||||||
aprsfi: AprsFiConfig::default(),
|
|
||||||
decode_logs: DecodeLogsConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RigInstanceConfig {
|
impl RigInstanceConfig {
|
||||||
/// Get the display name for this rig.
|
/// Get the display name for this rig.
|
||||||
/// Returns the configured name if set, otherwise the id.
|
/// Returns the configured name if set, otherwise the id.
|
||||||
@@ -520,18 +504,14 @@ impl ServerConfig {
|
|||||||
let mut seen_ports: std::collections::HashSet<u16> = std::collections::HashSet::new();
|
let mut seen_ports: std::collections::HashSet<u16> = std::collections::HashSet::new();
|
||||||
for rig in &self.rigs {
|
for rig in &self.rigs {
|
||||||
// Check for explicit duplicate IDs (empty IDs are auto-generated later).
|
// Check for explicit duplicate IDs (empty IDs are auto-generated later).
|
||||||
if !rig.id.trim().is_empty() {
|
if !rig.id.trim().is_empty() && !seen_ids.insert(rig.id.clone()) {
|
||||||
if !seen_ids.insert(rig.id.clone()) {
|
return Err(format!("[[rigs]] duplicate rig id: \"{}\"", rig.id));
|
||||||
return Err(format!("[[rigs]] duplicate rig id: \"{}\"", rig.id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if rig.audio.enabled {
|
if rig.audio.enabled && !seen_ports.insert(rig.audio.port) {
|
||||||
if !seen_ports.insert(rig.audio.port) {
|
return Err(format!(
|
||||||
return Err(format!(
|
"[[rigs]] duplicate audio port {} (rig id: \"{}\")",
|
||||||
"[[rigs]] duplicate audio port {} (rig id: \"{}\")",
|
rig.audio.port, rig.id
|
||||||
rig.audio.port, rig.id
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(max_gain) = rig.sdr.gain.max_value {
|
if let Some(max_gain) = rig.sdr.gain.max_value {
|
||||||
if !max_gain.is_finite() {
|
if !max_gain.is_finite() {
|
||||||
@@ -979,12 +959,13 @@ tokens = ["secret123"]
|
|||||||
stream_opus: bool,
|
stream_opus: bool,
|
||||||
decoders: Vec<String>,
|
decoders: Vec<String>,
|
||||||
) {
|
) {
|
||||||
let mut ch = SdrChannelConfig::default();
|
cfg.sdr.channels.push(SdrChannelConfig {
|
||||||
ch.id = id.to_string();
|
id: id.to_string(),
|
||||||
ch.offset_hz = offset_hz;
|
offset_hz,
|
||||||
ch.stream_opus = stream_opus;
|
stream_opus,
|
||||||
ch.decoders = decoders;
|
decoders,
|
||||||
cfg.sdr.channels.push(ch);
|
..SdrChannelConfig::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+14
-13
@@ -269,14 +269,18 @@ fn parse_rig_mode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a `SoapySdrRig` with full channel config from a `RigInstanceConfig`.
|
|
||||||
#[cfg(feature = "soapysdr")]
|
#[cfg(feature = "soapysdr")]
|
||||||
fn build_sdr_rig_from_instance(
|
type SdrRigBuildResult = DynResult<(
|
||||||
rig_cfg: &RigInstanceConfig,
|
|
||||||
) -> DynResult<(
|
|
||||||
Box<dyn trx_core::rig::RigCat>,
|
Box<dyn trx_core::rig::RigCat>,
|
||||||
tokio::sync::broadcast::Receiver<Vec<f32>>,
|
tokio::sync::broadcast::Receiver<Vec<f32>>,
|
||||||
)> {
|
)>;
|
||||||
|
|
||||||
|
type OptionalSdrRig = Option<Box<dyn trx_core::rig::RigCat>>;
|
||||||
|
type OptionalSdrPcmRx = Option<broadcast::Receiver<Vec<f32>>>;
|
||||||
|
|
||||||
|
/// Build a `SoapySdrRig` with full channel config from a `RigInstanceConfig`.
|
||||||
|
#[cfg(feature = "soapysdr")]
|
||||||
|
fn build_sdr_rig_from_instance(rig_cfg: &RigInstanceConfig) -> SdrRigBuildResult {
|
||||||
use trx_core::radio::freq::Freq;
|
use trx_core::radio::freq::Freq;
|
||||||
use trx_core::rig::AudioSource;
|
use trx_core::rig::AudioSource;
|
||||||
|
|
||||||
@@ -332,6 +336,7 @@ fn build_sdr_rig_from_instance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a `RigTaskConfig` for a single rig instance.
|
/// Build a `RigTaskConfig` for a single rig instance.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_rig_task_config(
|
fn build_rig_task_config(
|
||||||
rig_cfg: &RigInstanceConfig,
|
rig_cfg: &RigInstanceConfig,
|
||||||
rig_model: String,
|
rig_model: String,
|
||||||
@@ -391,6 +396,7 @@ fn build_rig_task_config(
|
|||||||
///
|
///
|
||||||
/// `sdr_pcm_rx` carries a live SDR PCM receiver when the rig uses the
|
/// `sdr_pcm_rx` carries a live SDR PCM receiver when the rig uses the
|
||||||
/// SoapySDR backend; `None` selects the cpal capture path.
|
/// SoapySDR backend; `None` selects the cpal capture path.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn spawn_rig_audio_stack(
|
fn spawn_rig_audio_stack(
|
||||||
rig_cfg: &RigInstanceConfig,
|
rig_cfg: &RigInstanceConfig,
|
||||||
state_rx: watch::Receiver<RigState>,
|
state_rx: watch::Receiver<RigState>,
|
||||||
@@ -812,10 +818,8 @@ async fn main() -> DynResult<()> {
|
|||||||
|
|
||||||
// Build SDR rig when applicable.
|
// Build SDR rig when applicable.
|
||||||
#[cfg(feature = "soapysdr")]
|
#[cfg(feature = "soapysdr")]
|
||||||
let (sdr_prebuilt_rig, sdr_pcm_rx): (
|
let (sdr_prebuilt_rig, sdr_pcm_rx): (OptionalSdrRig, OptionalSdrPcmRx) =
|
||||||
Option<Box<dyn trx_core::rig::RigCat>>,
|
if rig_cfg.rig.access.access_type.as_deref() == Some("sdr") {
|
||||||
Option<broadcast::Receiver<Vec<f32>>>,
|
|
||||||
) = if rig_cfg.rig.access.access_type.as_deref() == Some("sdr") {
|
|
||||||
let (rig, pcm_rx) = build_sdr_rig_from_instance(rig_cfg)?;
|
let (rig, pcm_rx) = build_sdr_rig_from_instance(rig_cfg)?;
|
||||||
(Some(rig), Some(pcm_rx))
|
(Some(rig), Some(pcm_rx))
|
||||||
} else {
|
} else {
|
||||||
@@ -823,10 +827,7 @@ async fn main() -> DynResult<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "soapysdr"))]
|
#[cfg(not(feature = "soapysdr"))]
|
||||||
let (sdr_prebuilt_rig, sdr_pcm_rx): (
|
let (sdr_prebuilt_rig, sdr_pcm_rx): (OptionalSdrRig, OptionalSdrPcmRx) = (None, None);
|
||||||
Option<Box<dyn trx_core::rig::RigCat>>,
|
|
||||||
Option<broadcast::Receiver<Vec<f32>>>,
|
|
||||||
) = (None, None);
|
|
||||||
|
|
||||||
let histories = DecoderHistories::new();
|
let histories = DecoderHistories::new();
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ impl DummyRig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::items_after_test_module)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use trx_core::rig::Rig;
|
use trx_core::rig::Rig;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ fn fast_atan2(y: f32, x: f32) -> f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let angle = if x > 0.0 {
|
if x > 0.0 {
|
||||||
fast_atan(y / x)
|
fast_atan(y / x)
|
||||||
} else if x < 0.0 {
|
} else if x < 0.0 {
|
||||||
if y >= 0.0 {
|
if y >= 0.0 {
|
||||||
@@ -164,8 +164,7 @@ fn fast_atan2(y: f32, x: f32) -> f32 {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
}
|
||||||
angle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 7th-order minimax atan approximation for |z| ≤ 1.
|
/// 7th-order minimax atan approximation for |z| ≤ 1.
|
||||||
@@ -1511,14 +1510,14 @@ mod tests {
|
|||||||
let pilot_freq = 19_000.0_f32;
|
let pilot_freq = 19_000.0_f32;
|
||||||
let carrier_freq = 38_000.0_f32;
|
let carrier_freq = 38_000.0_f32;
|
||||||
let mut composite = vec![0.0_f32; num_samples];
|
let mut composite = vec![0.0_f32; num_samples];
|
||||||
for n in 0..num_samples {
|
for (n, sample) in composite.iter_mut().enumerate() {
|
||||||
let t = n as f32 / fs;
|
let t = n as f32 / fs;
|
||||||
let audio = (TAU * audio_freq * t).sin(); // L = audio, R = 0
|
let audio = (TAU * audio_freq * t).sin(); // L = audio, R = 0
|
||||||
let sum = audio; // L + R
|
let sum = audio; // L + R
|
||||||
let diff = audio; // L - R
|
let diff = audio; // L - R
|
||||||
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
||||||
let carrier = (TAU * carrier_freq * t).cos();
|
let carrier = (TAU * carrier_freq * t).cos();
|
||||||
composite[n] = sum + pilot + diff * carrier;
|
*sample = sum + pilot + diff * carrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- FM-modulate composite → IQ samples ---
|
// --- FM-modulate composite → IQ samples ---
|
||||||
@@ -1613,7 +1612,7 @@ mod tests {
|
|||||||
// Test both L-only (diff = +audio) and R-only (diff = -audio).
|
// Test both L-only (diff = +audio) and R-only (diff = -audio).
|
||||||
for (label, diff_sign) in [("L-only", 1.0_f32), ("R-only", -1.0_f32)] {
|
for (label, diff_sign) in [("L-only", 1.0_f32), ("R-only", -1.0_f32)] {
|
||||||
let mut composite = vec![0.0_f32; num_samples];
|
let mut composite = vec![0.0_f32; num_samples];
|
||||||
for n in 0..num_samples {
|
for (n, sample) in composite.iter_mut().enumerate() {
|
||||||
let t = n as f32 / fs;
|
let t = n as f32 / fs;
|
||||||
let audio: f32 = freqs.iter().map(|&f| (TAU * f * t).sin()).sum::<f32>()
|
let audio: f32 = freqs.iter().map(|&f| (TAU * f * t).sin()).sum::<f32>()
|
||||||
/ freqs.len() as f32;
|
/ freqs.len() as f32;
|
||||||
@@ -1621,7 +1620,7 @@ mod tests {
|
|||||||
let diff = audio * diff_sign; // L - R
|
let diff = audio * diff_sign; // L - R
|
||||||
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
||||||
let carrier = (TAU * carrier_freq * t).cos();
|
let carrier = (TAU * carrier_freq * t).cos();
|
||||||
composite[n] = sum + pilot + diff * carrier;
|
*sample = sum + pilot + diff * carrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
let peak_composite = 2.1_f32;
|
let peak_composite = 2.1_f32;
|
||||||
@@ -1928,14 +1927,14 @@ mod tests {
|
|||||||
let pilot_freq = 19_000.0_f32;
|
let pilot_freq = 19_000.0_f32;
|
||||||
let carrier_freq = 38_000.0_f32;
|
let carrier_freq = 38_000.0_f32;
|
||||||
let mut composite = vec![0.0_f32; num_samples];
|
let mut composite = vec![0.0_f32; num_samples];
|
||||||
for n in 0..num_samples {
|
for (n, sample) in composite.iter_mut().enumerate() {
|
||||||
let t = n as f32 / fs;
|
let t = n as f32 / fs;
|
||||||
let audio = (TAU * audio_freq * t).sin();
|
let audio = (TAU * audio_freq * t).sin();
|
||||||
let sum = audio;
|
let sum = audio;
|
||||||
let diff = audio;
|
let diff = audio;
|
||||||
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
let pilot = 0.1 * (TAU * pilot_freq * t).cos();
|
||||||
let carrier = (TAU * carrier_freq * t).cos();
|
let carrier = (TAU * carrier_freq * t).cos();
|
||||||
composite[n] = sum + pilot + diff * carrier;
|
*sample = sum + pilot + diff * carrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
let peak_composite = 2.1_f32;
|
let peak_composite = 2.1_f32;
|
||||||
|
|||||||
@@ -178,16 +178,18 @@ pub struct BlockFirFilterPair {
|
|||||||
scratch_freq: Vec<FftComplex<f32>>,
|
scratch_freq: Vec<FftComplex<f32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_fir_kernel(
|
type FirKernel = (
|
||||||
cutoff_norm: f32,
|
|
||||||
taps: usize,
|
|
||||||
block_size: usize,
|
|
||||||
) -> (
|
|
||||||
Vec<FftComplex<f32>>,
|
Vec<FftComplex<f32>>,
|
||||||
usize,
|
usize,
|
||||||
Arc<dyn Fft<f32>>,
|
Arc<dyn Fft<f32>>,
|
||||||
Arc<dyn Fft<f32>>,
|
Arc<dyn Fft<f32>>,
|
||||||
) {
|
);
|
||||||
|
|
||||||
|
fn build_fir_kernel(
|
||||||
|
cutoff_norm: f32,
|
||||||
|
taps: usize,
|
||||||
|
block_size: usize,
|
||||||
|
) -> FirKernel {
|
||||||
let coeffs = windowed_sinc_coeffs(cutoff_norm, taps);
|
let coeffs = windowed_sinc_coeffs(cutoff_norm, taps);
|
||||||
let fft_size = (block_size + taps - 1).next_power_of_two();
|
let fft_size = (block_size + taps - 1).next_power_of_two();
|
||||||
|
|
||||||
@@ -943,6 +945,7 @@ pub struct SdrPipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SdrPipeline {
|
impl SdrPipeline {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn start(
|
pub fn start(
|
||||||
source: Box<dyn IqSource>,
|
source: Box<dyn IqSource>,
|
||||||
sdr_sample_rate: u32,
|
sdr_sample_rate: u32,
|
||||||
|
|||||||
Reference in New Issue
Block a user