11// Based on the simple_fm example from https://github.com/ccostes/rtl-sdr-rs/
22
33use clap:: Parser ;
4- use core:: alloc:: Layout ;
5- use ctrlc;
64use log:: info;
75use num_complex:: Complex ;
86use rtl_sdr_rs:: { error:: Result , DeviceId , RtlSdr , DEFAULT_BUF_LENGTH } ;
97use std:: f64:: consts:: PI ;
10- use std:: fs:: File ;
11- use std:: os:: fd:: AsRawFd ;
128use std:: io:: Write ;
139use std:: sync:: atomic:: { AtomicBool , Ordering } ;
1410use std:: sync:: mpsc:: { self , Receiver , Sender } ;
@@ -26,16 +22,14 @@ struct Args {
2622}
2723
2824// Radio and demodulation config
29- const FREQUENCY : u32 = 101_900_000 ; // Frequency in Hz
3025const SAMPLE_RATE : u32 = 170_000 ; // Demodulation sample rate, 170kHz
3126const RATE_RESAMPLE : u32 = 32_000 ; // Output sample rate, 32kHz
3227
33- // RTL Device Index
34- const RTL_INDEX : usize = 0 ;
35-
3628fn main ( ) {
3729 let args = Args :: parse ( ) ;
3830
31+ let frequency = parse_frequency ( args. frequency ) ;
32+
3933 // Printing to stdout will break audio output, so use this to log to stderr instead
4034 stderrlog:: new ( ) . verbosity ( log:: Level :: Info ) . init ( ) . unwrap ( ) ;
4135
@@ -47,7 +41,7 @@ fn main() {
4741 . unwrap ( ) ;
4842
4943 // Get radio and demodulation settings for given frequency and sample rate
50- let ( radio_config, demod_config) = optimal_settings ( FREQUENCY , SAMPLE_RATE ) ;
44+ let ( radio_config, demod_config) = optimal_settings ( frequency , SAMPLE_RATE ) ;
5145
5246 // Channel to pass receive data from receiver thread to processor thread
5347 let ( tx, rx) = mpsc:: channel ( ) ;
@@ -62,6 +56,24 @@ fn main() {
6256 receive_thread. join ( ) . unwrap ( ) ;
6357}
6458
59+ fn parse_frequency ( frequency : String ) -> u32 {
60+ let suffix = frequency. chars ( ) . last ( ) . unwrap ( ) ;
61+ let stripped = frequency. strip_suffix ( [ 'G' , 'g' , 'M' , 'm' , 'K' , 'k' ] ) ;
62+
63+ match suffix {
64+ 'G' | 'g' => {
65+ ( stripped. unwrap ( ) . parse :: < f32 > ( ) . unwrap ( ) * 1_000_000_000.0 ) as u32
66+ } ,
67+ 'M' | 'm' => {
68+ ( stripped. unwrap ( ) . parse :: < f32 > ( ) . unwrap ( ) * 1_000_000.0 ) as u32
69+ } ,
70+ 'K' | 'k' => {
71+ ( stripped. unwrap ( ) . parse :: < f32 > ( ) . unwrap ( ) * 1_000.0 ) as u32
72+ } ,
73+ _ => frequency. parse ( ) . unwrap ( ) ,
74+ }
75+ }
76+
6577/// Thread to open SDR device and send received data to the demod thread until
6678/// SHUTDOWN flag is set to true.
6779fn receive ( shutdown : & AtomicBool , radio_config : RadioConfig , tx : Sender < Vec < u8 > > ) {
@@ -103,7 +115,7 @@ fn receive(shutdown: &AtomicBool, radio_config: RadioConfig, tx: Sender<Vec<u8>>
103115 break ;
104116 }
105117 // Send received data through the channel to the processor thread
106- tx. send ( buf. to_vec ( ) ) ;
118+ let _ = tx. send ( buf. to_vec ( ) ) ;
107119 }
108120 // Shut down the device and exit
109121 info ! ( "Close" ) ;
@@ -179,15 +191,15 @@ fn optimal_settings(freq: u32, rate: u32) -> (RadioConfig, DemodConfig) {
179191 }
180192 (
181193 RadioConfig {
182- capture_freq : capture_freq ,
183- capture_rate : capture_rate ,
194+ capture_freq,
195+ capture_rate,
184196 } ,
185197 DemodConfig {
186198 rate_in : SAMPLE_RATE ,
187199 rate_out : SAMPLE_RATE ,
188200 rate_resample : RATE_RESAMPLE ,
189- downsample : downsample ,
190- output_scale : output_scale ,
201+ downsample,
202+ output_scale,
191203 } ,
192204 )
193205}
@@ -221,7 +233,7 @@ struct Demod {
221233impl Demod {
222234 fn new ( config : DemodConfig ) -> Self {
223235 Demod {
224- config : config ,
236+ config,
225237 prev_index : 0 ,
226238 now_lpr : 0 ,
227239 prev_lpr_index : 0 ,
@@ -243,8 +255,7 @@ impl Demod {
243255 let demodulated = self . fm_demod ( lowpassed) ;
244256
245257 // Resample and return result
246- let output = self . low_pass_real ( demodulated) ;
247- output
258+ self . low_pass_real ( demodulated)
248259 }
249260
250261 /// Performs a 90-degree rotation in the complex plane on a vector of bytes
@@ -273,8 +284,8 @@ impl Demod {
273284 /// Applies a low-pass filter on a vector of complex values
274285 fn low_pass_complex ( & mut self , buf : Vec < Complex < i32 > > ) -> Vec < Complex < i32 > > {
275286 let mut res = vec ! [ ] ;
276- for orig in 0 .. buf. len ( ) {
277- self . lp_now += buf [ orig ] ;
287+ for i in & buf {
288+ self . lp_now += i ;
278289
279290 self . prev_index += 1 ;
280291 if self . prev_index < self . config . downsample as usize {
@@ -325,20 +336,19 @@ impl Demod {
325336 if x == 0 && y == 0 {
326337 return 0 ;
327338 }
328- let mut yabs = y;
329- if yabs < 0 {
330- yabs = -yabs;
331- }
332- let angle;
333- if x >= 0 {
334- angle = pi4 - ( pi4 as i64 * ( x - yabs) as i64 ) as i32 / ( x + yabs) ;
339+ let yabs = y. abs ( ) ;
340+
341+ let angle = if x >= 0 {
342+ pi4 - ( pi4 as i64 * ( x - yabs) as i64 ) as i32 / ( x + yabs)
335343 } else {
336- angle = pi34 - ( pi4 as i64 * ( x + yabs) as i64 ) as i32 / ( yabs - x) ;
337- }
344+ pi34 - ( pi4 as i64 * ( x + yabs) as i64 ) as i32 / ( yabs - x)
345+ } ;
346+
338347 if y < 0 {
339348 return -angle;
340349 }
341- return angle;
350+
351+ angle
342352 }
343353
344354 /// Applies a low-pass filter to a vector of real-valued data
@@ -370,8 +380,8 @@ fn output(buf: Vec<i16>) {
370380 let slice_u8: & [ u8 ] = unsafe {
371381 slice:: from_raw_parts ( buf. as_ptr ( ) as * const u8 , buf. len ( ) * mem:: size_of :: < i16 > ( ) )
372382 } ;
373- out. write_all ( slice_u8) ;
374- out. flush ( ) ;
383+ let _ = out. write_all ( slice_u8) ;
384+ let _ = out. flush ( ) ;
375385}
376386
377387/// Convert a vector of i16 complex components (real and imaginary) to a vector of i32 Complex values
@@ -388,6 +398,24 @@ fn buf_to_complex(buf: Vec<i16>) -> Vec<Complex<i32>> {
388398#[ cfg( test) ]
389399mod tests {
390400 use super :: * ;
401+ #[ test]
402+ fn test_parse_frequency ( ) {
403+ let freq = parse_frequency ( "10" . to_string ( ) ) ;
404+ assert_eq ! ( freq, 10 ) ;
405+
406+ let freq = parse_frequency ( "20K" . to_string ( ) ) ;
407+ assert_eq ! ( freq, 20_000 ) ;
408+
409+ let freq = parse_frequency ( "90M" . to_string ( ) ) ;
410+ assert_eq ! ( freq, 90_000_000 ) ;
411+
412+ let freq = parse_frequency ( "1.9M" . to_string ( ) ) ;
413+ assert_eq ! ( freq, 1_900_000 ) ;
414+
415+ let freq = parse_frequency ( "2.4G" . to_string ( ) ) ;
416+ assert_eq ! ( freq, 2_400_000_000 ) ;
417+ }
418+
391419 #[ test]
392420 fn test_lowpass ( ) {
393421 // Based on data from rtl_fm
@@ -401,7 +429,7 @@ mod tests {
401429 ] ;
402430 let lp_complex = buf_to_complex ( lowpass) ;
403431
404- let ( _, demod_config) = optimal_settings ( FREQUENCY , SAMPLE_RATE ) ;
432+ let ( _, demod_config) = optimal_settings ( 100_000_000 , SAMPLE_RATE ) ;
405433 let mut demod = Demod :: new ( demod_config) ;
406434
407435 let buf_signed = vec ! [
@@ -454,9 +482,8 @@ mod tests {
454482 76 , 1228 , 2517 , 3241 , 3490 , -6608 , -11786 , -1057 , 3088 , 805 , -14996 , -783 , -12842 ,
455483 9551 , 11213 ,
456484 ] ;
457- let result = vec ! [ 2588 , 4030 , -1212 , -3430 , 2585 , 2110 , -6110 ] ;
458485
459- let ( _, demod_config) = optimal_settings ( FREQUENCY , SAMPLE_RATE ) ;
486+ let ( _, demod_config) = optimal_settings ( 100_000_000 , SAMPLE_RATE ) ;
460487 let mut demod = Demod :: new ( demod_config) ;
461488
462489 let demodulated = demod. fm_demod ( lp_complex) ;
@@ -473,7 +500,7 @@ mod tests {
473500 ] ;
474501 let result = vec ! [ 2588 , 4030 , -1212 , -3430 , 2585 , 2110 , -6110 ] ;
475502
476- let ( _, demod_config) = optimal_settings ( FREQUENCY , SAMPLE_RATE ) ;
503+ let ( _, demod_config) = optimal_settings ( 100_000_000 , SAMPLE_RATE ) ;
477504 let mut demod = Demod :: new ( demod_config) ;
478505
479506 let output = demod. low_pass_real ( demodulated) ;
0 commit comments