[chore](trx-rs): refine pending SDR frontend and backend changes
Signed-off-by: Stan Grams <sjg@haxx.space> Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
@@ -479,9 +479,14 @@ pub fn spawn_audio_playback(
|
||||
let device_name = cfg.device.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) =
|
||||
run_playback(sample_rate, channels, frame_duration_ms, device_name, rx, shutdown_rx)
|
||||
{
|
||||
if let Err(e) = run_playback(
|
||||
sample_rate,
|
||||
channels,
|
||||
frame_duration_ms,
|
||||
device_name,
|
||||
rx,
|
||||
shutdown_rx,
|
||||
) {
|
||||
error!("Audio playback thread error: {}", e);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -54,7 +54,7 @@ pub struct RigInstanceConfig {
|
||||
impl Default for RigInstanceConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: String::new(), // Empty by default so auto-generation triggers in resolved_rigs()
|
||||
id: String::new(), // Empty by default so auto-generation triggers in resolved_rigs()
|
||||
name: None,
|
||||
rig: RigConfig::default(),
|
||||
behavior: BehaviorConfig::default(),
|
||||
@@ -634,21 +634,13 @@ impl ServerConfig {
|
||||
.map(|(idx, rig)| {
|
||||
let id = if rig.id.trim().is_empty() {
|
||||
// Generate ID from model name with counter.
|
||||
let model = rig
|
||||
.rig
|
||||
.model
|
||||
.as_deref()
|
||||
.unwrap_or("unknown")
|
||||
.to_lowercase();
|
||||
let model = rig.rig.model.as_deref().unwrap_or("unknown").to_lowercase();
|
||||
format!("{}_{}", model, idx)
|
||||
} else {
|
||||
rig.id.clone()
|
||||
};
|
||||
|
||||
RigInstanceConfig {
|
||||
id,
|
||||
..rig.clone()
|
||||
}
|
||||
RigInstanceConfig { id, ..rig.clone() }
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
+13
-16
@@ -488,11 +488,11 @@ fn spawn_rig_audio_stack(
|
||||
"[{}] using SDR audio source — cpal capture disabled",
|
||||
rig_cfg.id
|
||||
);
|
||||
let pcm_tx_clone = pcm_tx.clone();
|
||||
let rx_audio_tx_sdr = rx_audio_tx.clone();
|
||||
let sdr_sample_rate = rig_cfg.audio.sample_rate;
|
||||
let sdr_channels = rig_cfg.audio.channels;
|
||||
let sdr_bitrate_bps = rig_cfg.audio.bitrate_bps;
|
||||
let pcm_tx_clone = pcm_tx.clone();
|
||||
let rx_audio_tx_sdr = rx_audio_tx.clone();
|
||||
let sdr_sample_rate = rig_cfg.audio.sample_rate;
|
||||
let sdr_channels = rig_cfg.audio.channels;
|
||||
let sdr_bitrate_bps = rig_cfg.audio.bitrate_bps;
|
||||
handles.push(tokio::spawn(async move {
|
||||
let opus_ch = match sdr_channels {
|
||||
1 => opus::Channels::Mono,
|
||||
@@ -502,17 +502,14 @@ fn spawn_rig_audio_stack(
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut encoder = match opus::Encoder::new(
|
||||
sdr_sample_rate,
|
||||
opus_ch,
|
||||
opus::Application::Audio,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
tracing::error!("SDR audio: Opus encoder init failed: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut encoder =
|
||||
match opus::Encoder::new(sdr_sample_rate, opus_ch, opus::Application::Audio) {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
tracing::error!("SDR audio: Opus encoder init failed: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = encoder.set_bitrate(opus::Bitrate::Bits(sdr_bitrate_bps as i32)) {
|
||||
tracing::warn!("SDR audio: set_bitrate failed: {}", e);
|
||||
}
|
||||
|
||||
@@ -172,10 +172,8 @@ impl BlockFirFilter {
|
||||
let ifft = planner.plan_fft_inverse(fft_size);
|
||||
|
||||
// Pre-compute H(f) = FFT of zero-padded coefficients.
|
||||
let mut h_buf: Vec<FftComplex<f32>> = coeffs
|
||||
.iter()
|
||||
.map(|&c| FftComplex::new(c, 0.0))
|
||||
.collect();
|
||||
let mut h_buf: Vec<FftComplex<f32>> =
|
||||
coeffs.iter().map(|&c| FftComplex::new(c, 0.0)).collect();
|
||||
h_buf.resize(fft_size, FftComplex::new(0.0, 0.0));
|
||||
fft.process(&mut h_buf);
|
||||
|
||||
@@ -384,8 +382,7 @@ impl ChannelDsp {
|
||||
mixed_q[idx] = sample.re * lo_im + sample.im * lo_re;
|
||||
}
|
||||
// Advance phase with wrap to avoid precision loss.
|
||||
self.mixer_phase =
|
||||
(phase_start + n as f64 * phase_inc).rem_euclid(std::f64::consts::TAU);
|
||||
self.mixer_phase = (phase_start + n as f64 * phase_inc).rem_euclid(std::f64::consts::TAU);
|
||||
|
||||
// --- 2. FFT FIR (overlap-save) --------------------------------------
|
||||
let filtered_i = self.lpf_i.filter_block(&mixed_i);
|
||||
@@ -471,8 +468,7 @@ impl SdrPipeline {
|
||||
let thread_dsps: Vec<Arc<Mutex<ChannelDsp>>> = channel_dsps.clone();
|
||||
let spectrum_buf: Arc<Mutex<Option<Vec<f32>>>> = Arc::new(Mutex::new(None));
|
||||
let thread_spectrum_buf = spectrum_buf.clone();
|
||||
let retune_cmd: Arc<std::sync::Mutex<Option<f64>>> =
|
||||
Arc::new(std::sync::Mutex::new(None));
|
||||
let retune_cmd: Arc<std::sync::Mutex<Option<f64>>> = Arc::new(std::sync::Mutex::new(None));
|
||||
let thread_retune_cmd = retune_cmd.clone();
|
||||
|
||||
std::thread::Builder::new()
|
||||
@@ -529,9 +525,7 @@ fn iq_read_loop(
|
||||
|
||||
// Pre-compute Hann window coefficients.
|
||||
let hann_window: Vec<f32> = (0..SPECTRUM_FFT_SIZE)
|
||||
.map(|i| {
|
||||
0.5 * (1.0 - (2.0 * PI * i as f32 / (SPECTRUM_FFT_SIZE - 1) as f32).cos())
|
||||
})
|
||||
.map(|i| 0.5 * (1.0 - (2.0 * PI * i as f32 / (SPECTRUM_FFT_SIZE - 1) as f32).cos()))
|
||||
.collect();
|
||||
|
||||
let mut planner = FftPlanner::<f32>::new();
|
||||
@@ -683,16 +677,7 @@ mod tests {
|
||||
#[test]
|
||||
fn channel_dsp_processes_silence() {
|
||||
let (pcm_tx, _pcm_rx) = broadcast::channel::<Vec<f32>>(8);
|
||||
let mut dsp = ChannelDsp::new(
|
||||
0.0,
|
||||
&RigMode::USB,
|
||||
48_000,
|
||||
8_000,
|
||||
20,
|
||||
3000,
|
||||
31,
|
||||
pcm_tx,
|
||||
);
|
||||
let mut dsp = ChannelDsp::new(0.0, &RigMode::USB, 48_000, 8_000, 20, 3000, 31, pcm_tx);
|
||||
let block = vec![Complex::new(0.0_f32, 0.0_f32); 4096];
|
||||
dsp.process_block(&block);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user