use std::{ f32::consts::PI, fs::File, io::{Read, Write}, ops::{Add, Div, Mul, Sub}, }; mod bfsk; mod complex; pub mod fft; mod nco; use bfsk::BFSKMod; use complex::Complex; use complex::Complex32; use fft::rader; use nco::Nco; use plotters::prelude::*; use crate::fft::{ DFT, create_fft, dft::NaiveDFT, mixed_radix::MixedRadixFFT, prime_factors, rader::{RaderFFT, compute_prime_primitive_root, exp_mod}, rader2::{Rader2FFT, next_pow2}, radix2::Radix2FFT, windows, }; // Utilities fn map(input: T, in_min: T, in_max: T, out_min: T, out_max: T) -> T where T: Clone + Add + Mul + Sub + Div, { ((input - in_min.clone()) / (in_max - in_min)) * (out_max - out_min.clone()) + out_min } fn euclid_mod(a: f32, m: f32) -> f32 { let r = a % m; if r < 0.0 { r + m } else { r } } fn main() { test(); } fn test() { let freq1 = 2. * PI / 4.0; let freq2 = 2. * PI / 8.0; //let sample_count = 71*71; //let sample_count = 71 * 71; //let sample_count = 4804; let sample_count = 4799; let mut o1 = Nco::new(freq1); let mut o2 = Nco::new(freq2); let mut fft = RaderFFT::create(sample_count); let mut dft = RaderFFT::create(sample_count); for (x, y) in fft.get_input().iter_mut().zip(dft.get_input().iter_mut()) { *y = o1.cexp();// + o2.cexp(); //*y = *x; o1.step(); o2.step(); } //fft.execute(windows::rectanguar); dft.execute(windows::rectanguar); let root = BitMapBackend::new("out.png", (640, 480)).into_drawing_area(); root.fill(&WHITE).unwrap(); let mut chart = ChartBuilder::on(&root) .caption("fft", ("sans-serif", 50).into_font()) .margin(5) .x_label_area_size(30) .y_label_area_size(30) .build_cartesian_2d(0f32..(sample_count as f32), -PI..PI) .unwrap(); //chart.configure_mesh().draw()?; chart .draw_series(LineSeries::new( (0..sample_count) .zip(dft.get_output().iter()) .map(|(x, y)| (x as f32, (*y).arg() * (*y).mag())), &RED, )) .unwrap() .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED)); chart .draw_series(LineSeries::new( (0..sample_count) .zip(dft.get_output().iter()) .map(|(x, y)| (x as f32, (*y).mag() / sample_count as f32)), &BLUE, )) .unwrap() .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE)); chart .configure_series_labels() .background_style(&WHITE.mix(0.8)) .border_style(&BLACK) .draw() .unwrap(); root.present().unwrap(); } fn modulate() { let sample_rate = 44100; let mut frequency = 2000.0; //HZ let mut bandwidth = 500.0; //HZ let path = "a.jpg"; let file = File::open(path).unwrap(); let mut bit_stream = file.bytes().flat_map(|byte| { let byte = byte.unwrap(); [ byte & 1 == 1, (byte >> 1) & 1 == 1, (byte >> 2) & 1 == 1, (byte >> 3) & 1 == 1, (byte >> 4) & 1 == 1, (byte >> 5) & 1 == 1, (byte >> 6) & 1 == 1, (byte >> 7) & 1 == 1, ] }); //let mut bit_stream = (0..22000).map(|_| false).chain((0..22000).map(|_| true)); //let mut bit_stream = (0..22000).flat_map(|_| [true, false]); let baud_rate = 400; println!("{} samples/bit", sample_rate / baud_rate); let mut bfsk = BFSKMod::new( sample_rate / baud_rate, 2. * PI * (bandwidth / sample_rate as f32), &mut bit_stream, ); let spec = hound::WavSpec { channels: 1, sample_rate, bits_per_sample: 16, sample_format: hound::SampleFormat::Int, }; let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap(); let mut lo = Nco::new(2. * PI * (frequency / sample_rate as f32)); let prev = Complex::new(0., 0.); let alpha = 1.0 - (-2.0 * PI * ((1.5 * 0.5 * bandwidth) / sample_rate as f32)); while let Some(sample) = bfsk.step_modulate() { let amplitude = i16::MAX as f32; let c_sample = lo.cexp() * sample; let filtered = prev + (c_sample - prev) * alpha; writer .write_sample((amplitude * c_sample.re) as i16) .unwrap(); lo.step(); } writer.finalize().unwrap(); }