[feat](trx-wefax): save images as YYYY-MM-DD_HH-MM-SS-freq_kHz_MODE.png
Include rig dial frequency and mode in WEFAX image filenames, matching fldigi's approach of capturing tuning at save time. Images are saved to ~/.cache/trx-rs/wefax/. Server passes current rig state to the decoder via set_tuning() before each processing block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -80,6 +80,10 @@ pub struct WefaxDecoder {
|
||||
signal_detect_count: u32,
|
||||
/// Accumulator for computing luminance variance within the current window.
|
||||
signal_detect_buf: Vec<f32>,
|
||||
/// Current rig dial frequency in Hz (for image filenames).
|
||||
freq_hz: u64,
|
||||
/// Current rig mode name (for image filenames).
|
||||
mode: String,
|
||||
}
|
||||
|
||||
impl WefaxDecoder {
|
||||
@@ -102,6 +106,8 @@ impl WefaxDecoder {
|
||||
sent_idle_event: false,
|
||||
signal_detect_count: 0,
|
||||
signal_detect_buf: Vec::with_capacity(INTERNAL_RATE as usize / 2),
|
||||
freq_hz: 0,
|
||||
mode: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +325,12 @@ impl WefaxDecoder {
|
||||
events
|
||||
}
|
||||
|
||||
/// Update the current rig tuning (used for image filenames).
|
||||
pub fn set_tuning(&mut self, freq_hz: u64, mode: &str) {
|
||||
self.freq_hz = freq_hz;
|
||||
self.mode = mode.to_string();
|
||||
}
|
||||
|
||||
/// Check if the decoder is currently receiving an image.
|
||||
pub fn is_receiving(&self) -> bool {
|
||||
matches!(
|
||||
@@ -400,7 +412,7 @@ impl WefaxDecoder {
|
||||
// Save PNG if output directory is configured.
|
||||
if let Some(ref dir) = self.config.output_dir {
|
||||
let output_path = PathBuf::from(dir);
|
||||
match image.save_png(&output_path, ioc, lpm) {
|
||||
match image.save_png(&output_path, self.freq_hz, &self.mode) {
|
||||
Ok(p) => {
|
||||
path_str = Some(p.to_string_lossy().into_owned());
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ impl ImageAssembler {
|
||||
pub fn save_png(
|
||||
&self,
|
||||
output_dir: &Path,
|
||||
ioc: u16,
|
||||
lpm: u16,
|
||||
freq_hz: u64,
|
||||
mode: &str,
|
||||
) -> Result<PathBuf, String> {
|
||||
if self.lines.is_empty() {
|
||||
return Err("no image lines to save".into());
|
||||
@@ -53,7 +53,7 @@ impl ImageAssembler {
|
||||
std::fs::create_dir_all(output_dir)
|
||||
.map_err(|e| format!("create output dir: {}", e))?;
|
||||
|
||||
let filename = generate_filename(ioc, lpm);
|
||||
let filename = generate_filename(freq_hz, mode);
|
||||
let path = output_dir.join(&filename);
|
||||
|
||||
let file = std::fs::File::create(&path)
|
||||
@@ -89,7 +89,7 @@ impl ImageAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_filename(ioc: u16, lpm: u16) -> String {
|
||||
fn generate_filename(freq_hz: u64, mode: &str) -> String {
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
@@ -97,10 +97,11 @@ fn generate_filename(ioc: u16, lpm: u16) -> String {
|
||||
|
||||
// Convert to UTC datetime components manually (avoid chrono dependency).
|
||||
let (year, month, day, hour, min, sec) = unix_to_utc(secs);
|
||||
let freq_khz = freq_hz / 1000;
|
||||
|
||||
format!(
|
||||
"WEFAX-{:04}-{:02}-{:02}T{:02}{:02}{:02}-IOC{}-{}lpm.png",
|
||||
year, month, day, hour, min, sec, ioc, lpm
|
||||
"{:04}-{:02}-{:02}_{:02}-{:02}-{:02}-{}_kHz_{}.png",
|
||||
year, month, day, hour, min, sec, freq_khz, mode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -179,7 +180,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let dir = std::env::temp_dir().join("trx-wefax-test");
|
||||
let result = asm.save_png(&dir, 576, 120);
|
||||
let result = asm.save_png(&dir, 7880000, "USB");
|
||||
assert!(result.is_ok(), "save_png failed: {:?}", result.err());
|
||||
let path = result.unwrap();
|
||||
assert!(path.exists());
|
||||
|
||||
@@ -2780,6 +2780,13 @@ pub async fn run_wefax_decoder(
|
||||
};
|
||||
|
||||
was_active = true;
|
||||
{
|
||||
let state = state_rx.borrow();
|
||||
decoder.set_tuning(
|
||||
state.status.freq.hz,
|
||||
&format!("{:?}", state.status.mode),
|
||||
);
|
||||
}
|
||||
let events = tokio::task::block_in_place(|| {
|
||||
let _span = info_span!("wefax_decode").entered();
|
||||
decoder.process_samples(&mono)
|
||||
|
||||
Reference in New Issue
Block a user