[style](trx-rs): reformat codebase

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
2026-03-17 22:36:11 +01:00
parent a823e66816
commit b533d704a1
22 changed files with 348 additions and 252 deletions
@@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: BSD-2-Clause
use num_complex::Complex;
use super::DcBlocker;
use num_complex::Complex;
/// C-QUAM (Compatible Quadrature AM) stereo demodulator.
///
@@ -57,8 +57,7 @@ impl CquamDemod {
self.carrier_im = alpha * self.carrier_im + one_minus_alpha * s.im;
// Rotate s by −φ to phase-align I with (1 + m_s) and Q with m_d.
let mag_sq =
self.carrier_re * self.carrier_re + self.carrier_im * self.carrier_im;
let mag_sq = self.carrier_re * self.carrier_re + self.carrier_im * self.carrier_im;
let (i_corr, q_corr) = if mag_sq > 1e-8 {
let inv = mag_sq.sqrt().recip();
let cos_phi = self.carrier_re * inv;
@@ -94,7 +93,10 @@ mod tests {
let out = demod.demodulate_stereo(&samples);
assert_eq!(out.len(), 512);
for &s in &out {
assert!(s.abs() < 1e-5, "silence should produce near-zero output, got {s}");
assert!(
s.abs() < 1e-5,
"silence should produce near-zero output, got {s}"
);
}
}
@@ -8,9 +8,7 @@ use num_complex::Complex;
/// 7th-order minimax atan approximation for |z| <= 1.
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
unsafe fn atan_poly_neon(
z: std::arch::aarch64::float32x4_t,
) -> std::arch::aarch64::float32x4_t {
unsafe fn atan_poly_neon(z: std::arch::aarch64::float32x4_t) -> std::arch::aarch64::float32x4_t {
use std::arch::aarch64::*;
let c0 = vdupq_n_f32(0.999_999_5_f32);
let c1 = vdupq_n_f32(-0.333_326_1_f32);
@@ -249,7 +249,10 @@ impl SdrPipeline {
channel_if_hz: f64,
mode: &RigMode,
bandwidth_hz: u32,
) -> (broadcast::Sender<Vec<f32>>, broadcast::Sender<Vec<Complex<f32>>>) {
) -> (
broadcast::Sender<Vec<f32>>,
broadcast::Sender<Vec<Complex<f32>>>,
) {
const PCM_BROADCAST_CAPACITY: usize = 32;
const IQ_BROADCAST_CAPACITY: usize = 64;
let (pcm_tx, _) = broadcast::channel::<Vec<f32>>(PCM_BROADCAST_CAPACITY);
@@ -456,9 +459,7 @@ fn iq_read_loop(
// Hold a read lock only for the duration of this block's DSP pass.
// Write lock (add/remove channel) waits at most one block (~2 ms).
{
let dsps = channel_dsps
.read()
.expect("channel_dsps RwLock poisoned");
let dsps = channel_dsps.read().expect("channel_dsps RwLock poisoned");
for dsp_arc in dsps.iter() {
match dsp_arc.lock() {
Ok(mut dsp) => dsp.process_block(samples),
@@ -272,7 +272,12 @@ impl ChannelDsp {
} else {
(cutoff_hz / self.sdr_sample_rate as f32).min(0.499)
};
self.lpf_iq = BlockFirFilterPair::new(cutoff_norm, ssb_shift_norm(&self.mode, cutoff_norm), auto_taps(cutoff_norm), IQ_BLOCK_SIZE);
self.lpf_iq = BlockFirFilterPair::new(
cutoff_norm,
ssb_shift_norm(&self.mode, cutoff_norm),
auto_taps(cutoff_norm),
IQ_BLOCK_SIZE,
);
let rate_changed = self.decim_factor != next_decim_factor;
self.decim_factor = next_decim_factor;
self.decim_counter = 0;
@@ -352,7 +357,12 @@ impl ChannelDsp {
channel_if_hz,
demodulator: Demodulator::for_mode(mode),
mode: mode.clone(),
lpf_iq: BlockFirFilterPair::new(cutoff_norm, ssb_shift_norm(mode, cutoff_norm), auto_taps(cutoff_norm), IQ_BLOCK_SIZE),
lpf_iq: BlockFirFilterPair::new(
cutoff_norm,
ssb_shift_norm(mode, cutoff_norm),
auto_taps(cutoff_norm),
IQ_BLOCK_SIZE,
),
sdr_sample_rate,
audio_sample_rate,
audio_bandwidth_hz,
@@ -109,7 +109,12 @@ type FirKernel = (
/// Setting `shift_norm = +cutoff_norm` produces a one-sided USB filter
/// `[0, BW]`; `shift_norm = -cutoff_norm` produces a one-sided LSB filter
/// `[-BW, 0]`; `shift_norm = 0` leaves the kernel symmetric (AM/FM/WFM).
fn build_fir_kernel(cutoff_norm: f32, shift_norm: f32, taps: usize, block_size: usize) -> FirKernel {
fn build_fir_kernel(
cutoff_norm: f32,
shift_norm: f32,
taps: usize,
block_size: usize,
) -> FirKernel {
let coeffs = windowed_sinc_coeffs(cutoff_norm, taps);
let fft_size = (block_size + taps - 1).next_power_of_two();
@@ -210,8 +215,14 @@ unsafe fn mul_freq_domain_neon(
let (h_re, h_im) = (h_ri.0, h_ri.1);
// Complex multiply: out.re = x.re*h.re - x.im*h.im, out.im = x.re*h.im + x.im*h.re
let out_re = vmulq_f32(vsubq_f32(vmulq_f32(x_re, h_re), vmulq_f32(x_im, h_im)), scale_v);
let out_im = vmulq_f32(vaddq_f32(vmulq_f32(x_re, h_im), vmulq_f32(x_im, h_re)), scale_v);
let out_re = vmulq_f32(
vsubq_f32(vmulq_f32(x_re, h_re), vmulq_f32(x_im, h_im)),
scale_v,
);
let out_im = vmulq_f32(
vaddq_f32(vmulq_f32(x_re, h_im), vmulq_f32(x_im, h_re)),
scale_v,
);
// Reinterleave: .0 = [re0,im0,re1,im1], .1 = [re2,im2,re3,im3]
let out = vzipq_f32(out_re, out_im);
@@ -313,7 +324,8 @@ impl BlockFirFilterPair {
/// `-cutoff_norm` for LSB/CWR.
pub fn new(cutoff_norm: f32, shift_norm: f32, taps: usize, block_size: usize) -> Self {
let taps = taps.max(1);
let (h_buf, fft_size, fft, ifft) = build_fir_kernel(cutoff_norm, shift_norm, taps, block_size);
let (h_buf, fft_size, fft, ifft) =
build_fir_kernel(cutoff_norm, shift_norm, taps, block_size);
Self {
h_freq: h_buf,
overlap: vec![FftComplex::new(0.0, 0.0); taps.saturating_sub(1)],
@@ -7,10 +7,10 @@ pub mod dsp;
pub mod real_iq_source;
pub mod vchan_impl;
use dsp::IqSource as _;
use std::pin::Pin;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use dsp::IqSource as _;
use trx_core::radio::freq::{Band, Freq};
use trx_core::rig::response::RigError;
use trx_core::rig::state::{RigFilterState, SpectrumData, VchanRdsEntry, WfmDenoiseLevel};
@@ -257,10 +257,7 @@ impl SoapySdrRig {
};
// Initialise filter state from primary channel config (index 0), or defaults.
let bandwidth_hz = channels
.first()
.map(|&(_, _, bw)| bw)
.unwrap_or(3000);
let bandwidth_hz = channels.first().map(|&(_, _, bw)| bw).unwrap_or(3000);
let spectrum_buf = pipeline.spectrum_buf.clone();
let retune_cmd = pipeline.retune_cmd.clone();
@@ -359,10 +356,7 @@ impl SoapySdrRig {
let dsps = self.pipeline.channel_dsps.read().unwrap();
for idx in [ais_a_idx, ais_b_idx] {
if let Some(dsp_arc) = dsps.get(idx) {
dsp_arc
.lock()
.unwrap()
.set_filter(self.bandwidth_hz);
dsp_arc.lock().unwrap().set_filter(self.bandwidth_hz);
}
}
}
@@ -749,10 +743,7 @@ impl RigCat for SoapySdrRig {
{
let dsps = self.pipeline.channel_dsps.read().unwrap();
if let Some(dsp_arc) = dsps.get(self.primary_channel_idx) {
dsp_arc
.lock()
.unwrap()
.set_filter(bandwidth_hz);
dsp_arc.lock().unwrap().set_filter(bandwidth_hz);
}
}
self.apply_ais_channel_filters();
@@ -100,11 +100,7 @@ impl SdrVirtualChannelManager {
/// - `fixed_slot_count`: number of fixed pipeline slots (primary + AIS),
/// i.e. the index of the first slot available for virtual channels.
/// - `max_total`: maximum total channels including primary (e.g. 4).
pub fn new(
pipeline: Arc<SdrPipeline>,
fixed_slot_count: usize,
max_total: usize,
) -> Self {
pub fn new(pipeline: Arc<SdrPipeline>, fixed_slot_count: usize, max_total: usize) -> Self {
// Seed the channel list with a synthetic primary-channel entry.
// We use the first PCM sender from the pipeline (index 0).
let primary_pcm_tx = pipeline
@@ -177,9 +173,9 @@ impl SdrVirtualChannelManager {
}
let bandwidth_hz = default_bandwidth_hz(mode);
let (pcm_tx, iq_tx) =
self.pipeline
.add_virtual_channel(if_hz as f64, mode, bandwidth_hz);
let (pcm_tx, iq_tx) = self
.pipeline
.add_virtual_channel(if_hz as f64, mode, bandwidth_hz);
let pipeline_slot = self
.pipeline