Skip to content

Instantly share code, notes, and snippets.

@bczhc
Created September 22, 2026 09:01
Show Gist options
  • Select an option

  • Save bczhc/39b1eec2c9476dd8be13701e8d798430 to your computer and use it in GitHub Desktop.

Select an option

Save bczhc/39b1eec2c9476dd8be13701e8d798430 to your computer and use it in GitHub Desktop.
airspy r2 FM解调代码。
//! Use command:
//!
//! ```shell
//! freq=88.6; airspy_rx -f $freq -a 10000000 -t 0 -h 10 -b 0 -r - \
//! | ~/code/radio-and-ham-tools/target/release/demo5 \
//! | ffmpeg -f f32le -ch_layout mono -ar 250000 -i pipe:0 \
//! -af "aemphasis=type=50fm:mode=reproduction,lowpass=f=15000:p=2,bandreject=f=19000:width_type=h:width=50" \
//! -ch_layout mono -ar 48000 -c:a pcm_f32le -f f32le - \
//! | pacat --format=float32le --channels=1 --rate=48000
//! ```
use byteorder::{WriteBytesExt, LE};
use num_complex::Complex64;
use radio_and_ham_tools::ReadIq;
use std::f64::consts::PI;
use std::io::{stdin, stdout, BufReader, BufWriter};
use std::process::{Command, Stdio};
use std::thread::spawn;
use crossbeam_channel::bounded;
fn main() -> anyhow::Result<()> {
// let iq_sample_rate = 768000;
let iq_sample_rate = 10000000;
let pcm_sample_rate = 48000;
let vfo_bandwidth = 250_000;
// let fc_offset = 200_000;
let fc_offset = 0;
let mut reader = stdin();
let mut writer = stdout();
let decimation_factor = iq_sample_rate / pcm_sample_rate;
let ffmpeg_cmd = format!(
"ffmpeg -hide_banner -f f32le -ar {iq_sample_rate} -ac 2 -i pipe:0 \
-af aresample=resampler=soxr \
-f f32le -ar {vfo_bandwidth} -ac 2 -"
);
let program = shell_words::split(&ffmpeg_cmd).unwrap();
let mut command = Command::new(&program[0])
.args(&program[1..])
.stderr(Stdio::inherit())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
let mut program_in = BufWriter::new(command.stdin.take().unwrap());
let mut program_out = BufReader::new(command.stdout.take().unwrap());
spawn(move || {
std::io::copy(&mut reader, &mut program_in).unwrap();
drop(program_in);
let status = command.wait().unwrap();
if !status.success() {
panic!("ffmpeg exits with non-zero status: {}", status);
}
});
let dt = 1. / vfo_bandwidth as f64;
let mut last_phase = 0.;
loop {
// this iq is down-sampled
let mut iq = program_out.read_iq()?;
let deviation = 100_000 /* WFM */;
let phase = iq.arg();
let mut diff = phase - last_phase;
if diff < -PI {
diff += 2. * PI;
} else if diff > PI {
diff -= 2. * PI;
}
let signal = diff / dt / (2. * PI) / deviation as f64;
// decimation
// if n.is_multiple_of(100) {
writer.write_f32::<LE>(signal.clamp(-1., 1.) as _)?;
// }
last_phase = phase;
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment