Skip to content
Snippets Groups Projects
Commit f0f43d28 authored by Stephen D's avatar Stephen D
Browse files

morse code identification

parent 3b256d56
No related branches found
No related tags found
No related merge requests found
......@@ -369,12 +369,19 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "small_morse"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be47da2709ca6c5a95cbaea1275b139930c1b76c372957015732b95f79c4fdfa"
[[package]]
name = "spectrodraw"
version = "0.1.0"
dependencies = [
"image",
"rand",
"small_morse",
"wav",
]
......
......@@ -9,3 +9,4 @@ edition = "2021"
image = "0.23"
wav = "1.0.0"
rand = "0.8.4"
small_morse = "0.1.0"
mod morse;
use rand::Rng;
use std::f32::consts::PI;
use std::fs::File;
......@@ -9,6 +11,7 @@ const CENTER: f32 = 2_000.0; // Hz
const LENGTH: f32 = 2.0; // seconds
const SAMPLES: u32 = 48_000; // Hz
const FUZZ_AMOUNT: f32 = 1.0;
const CALLSIGN: &str = "N0CALL";
fn main() {
let min_freq = CENTER - BANDWIDTH / 2.0;
......@@ -22,11 +25,13 @@ fn main() {
let samples_per_pixel = ((LENGTH * SAMPLES as f32) / (height as f32)) as usize;
let mut wav_data: Vec<f32> = vec![0.0; samples_per_pixel * height as usize];
let mut rng = rand::thread_rng();
for y in 0..height {
for x in 0..width {
let freq = (x as f32 / width as f32) * BANDWIDTH + min_freq;
let freq = freq + rand::thread_rng().gen_range(-FUZZ_AMOUNT..FUZZ_AMOUNT);
// add some fuzzing to prevent the beat frequency from messing things up
let freq = freq + rng.gen_range(-FUZZ_AMOUNT..FUZZ_AMOUNT);
let amplitude = gray.get_pixel(x, y).0[0] as f32 / 255.0;
......@@ -40,20 +45,24 @@ fn main() {
}
}
let header = Header::new(WAV_FORMAT_PCM, 1, SAMPLES, 16);
let data = BitDepth::Sixteen(scale_samples(&wav_data));
let mut data = vec![];
morse::Encoder::new(SAMPLES, 50, 600.0).encode(&mut data, CALLSIGN);
scale_samples(&mut data, &wav_data);
let header = Header::new(WAV_FORMAT_PCM, 1, SAMPLES, 16);
let data = BitDepth::Sixteen(data);
let mut out_file = File::create(Path::new("amogus.wav")).unwrap();
wav::write(header, &data, &mut out_file).unwrap();
}
// scale down to prevent clipping
// we should add a way to clip the top x% of samples
fn scale_samples(data: &[f32]) -> Vec<i16> {
fn scale_samples(buf: &mut Vec<i16>, data: &[f32]) {
let mut max = 0.0;
for d in data {
max = d.max(max);
}
data.iter().map(|x| (x * 32767.0 / max) as i16).collect()
buf.extend(data.iter().map(|x| (x * 32767.0 / max) as i16));
}
use std::f32::consts::PI;
pub struct Encoder {
dot_period: u32, // in samples
scale: f32,
}
impl Encoder {
pub fn new(sample_rate: u32, dot_length_millis: u32, freq_hz: f32) -> Self {
let dot_period = dot_length_millis * sample_rate / 1000;
let scale = freq_hz * 2.0 * PI / sample_rate as f32;
Self { dot_period, scale }
}
pub fn encode(&self, buf: &mut Vec<i16>, s: &str) {
for action in small_morse::encode(s) {
match action.state {
small_morse::State::On => {
self.add_beep(buf, action.duration);
}
small_morse::State::Off => {
self.add_delay(buf, action.duration);
}
}
}
}
pub fn add_delay(&self, buf: &mut Vec<i16>, duration: u8) {
let len = duration as u32 * self.dot_period;
for _ in 0..len {
buf.push(0);
}
}
// todo add some dampening
pub fn add_beep(&self, buf: &mut Vec<i16>, duration: u8) {
let len = duration as u32 * self.dot_period;
for i in 0..len {
let mut val = (i as f32 * self.scale).sin();
if i < 1000 {
val *= i as f32 / 1000.0;
} else if i > (len - 1000) {
let diff = len - i;
val *= diff as f32 / 1000.0;
}
buf.push((val * 32767.0) as i16);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment