From 3d994125a19061eb7c48970bfb2840e6ba7e59cb Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Wed, 24 Jun 2020 23:21:12 +0200 Subject: [PATCH] Full encoding chain is working --- Cargo.lock | 169 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/baudot.rs | 31 +++++--- src/bitgenerator.rs | 64 ++++++++++++++++ src/bitupsampler.rs | 74 ++++++++++++++++++ src/main.rs | 20 ++++- src/symbolgenerator.rs | 55 ++++++++++++++ 7 files changed, 404 insertions(+), 10 deletions(-) create mode 100644 src/bitgenerator.rs create mode 100644 src/bitupsampler.rs create mode 100644 src/symbolgenerator.rs diff --git a/Cargo.lock b/Cargo.lock index 93440bc..3411d46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,183 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "libpulse-binding" +version = "2.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b35c3c377b537d044107ff0830b4e5ae32a5c106289977a04b3387a143e994" +dependencies = [ + "libc", + "libpulse-sys", + "winapi", +] + +[[package]] +name = "libpulse-simple-binding" +version = "2.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ef3ce750cdbce60fc96aac1f6dad84d67419d2307a05a27b73700f1f5d33e0" +dependencies = [ + "libpulse-binding", + "libpulse-simple-sys", + "libpulse-sys", +] + +[[package]] +name = "libpulse-simple-sys" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48e779375c1d6e8b8d2fcbca67cc2f3208d447f17215f8ea600d6a58ccc6a56" +dependencies = [ + "libpulse-sys", + "pkg-config", +] + +[[package]] +name = "libpulse-sys" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9559e91871e5f820c3b4e181b8c8f3ebd0b7839c92943802caf0f1477fdc27" +dependencies = [ + "libc", + "pkg-config", + "winapi", +] + +[[package]] +name = "num" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3e176191bc4faad357e3122c4747aa098ac880e88b168f106386128736cf4a" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05ad05bd8977050b171b3f6b48175fea6e0565b7981059b486075e1026a9fb5" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "rusty-dsp" +version = "0.1.0" +dependencies = [ + "libpulse-binding", + "libpulse-simple-binding", + "num", +] + [[package]] name = "rusty-tty" version = "0.1.0" dependencies = [ "lazy_static", + "rusty-dsp", ] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 870a717..127c38f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" [dependencies] lazy_static = "*" +rusty-dsp = {path = "../rusty-dsp"} diff --git a/src/baudot.rs b/src/baudot.rs index 6549e9d..418af06 100644 --- a/src/baudot.rs +++ b/src/baudot.rs @@ -1,5 +1,9 @@ use std::collections::HashMap; +extern crate rusty_dsp; + +use rusty_dsp::error::DspError; + const LETTERS: &[u8] = b"\0E\nA SIU\rDRJNFCKTZLWHYPQOBG\0MXV\0"; const FIGURES: &[u8] = b"\03\n- \x0787\r\x054',!:(5\")2#6019?&\0./;\0"; @@ -50,7 +54,7 @@ enum ShiftState { Figures, } -struct Encoder<'a> { +pub struct Encoder<'a> { shift_state: ShiftState, buffer: Option, iterator: Box + 'a>, @@ -70,7 +74,7 @@ impl<'a> Encoder<'a> { } impl<'a> Iterator for Encoder<'a> { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { if let Some(c) = self.buffer { @@ -108,17 +112,20 @@ impl<'a> Iterator for Encoder<'a> { Some(Ok(SHIFT_FIGURES)) } - (None, None, _) => Some(Err(byte)), + (None, None, _) => Some(Err(DspError::Error(format!( + "Unable to decode symbol: {:?}", + &byte + )))), } } } -pub fn encode(msg: &str) -> Result, u8> { +pub fn encode(msg: &str) -> Result, DspError> { let encoder = Encoder::from(msg.to_ascii_uppercase().into_bytes().into_iter()); encoder.collect() } -struct Decoder<'a> { +pub struct Decoder<'a> { shift_state: ShiftState, iterator: Box + 'a>, } @@ -138,7 +145,7 @@ impl<'a> Decoder<'a> { impl<'a> Iterator for Decoder<'a> { // Option> for nice error handling if somebody wants to use collect // See https://doc.rust-lang.org/stable/rust-by-example/error/iter_result.html - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { let mut byte = match self.iterator.next() { @@ -167,18 +174,24 @@ impl<'a> Iterator for Decoder<'a> { match (byte, self.shift_state) { (c, ShiftState::Letters) => match letter_from_code(c) { Some(l) => Some(Ok(l)), - None => Some(Err(c)), + None => Some(Err(DspError::Error(format!( + "Unable to decode symbol: {:?}", + c + )))), }, (c, ShiftState::Figures) => match figure_from_code(c) { Some(f) => Some(Ok(f)), - None => Some(Err(c)), + None => Some(Err(DspError::Error(format!( + "Unable to decode symbol: {:?}", + c + )))), }, (_, ShiftState::None) => panic!("Shift state is expectedly None"), } } } -pub fn decode(msg: &[u8]) -> Result { +pub fn decode(msg: &[u8]) -> Result { let decoder = Decoder::from(msg.iter().copied()); match decoder.collect() { Ok(v) => Ok(String::from_utf8(v).unwrap()), diff --git a/src/bitgenerator.rs b/src/bitgenerator.rs new file mode 100644 index 0000000..d6b8a03 --- /dev/null +++ b/src/bitgenerator.rs @@ -0,0 +1,64 @@ +extern crate rusty_dsp; + +use rusty_dsp::error::DspError; + +pub struct BitGenerator<'a> { + bit_pos: usize, + current_byte: u8, + iterator: Box> + 'a>, +} + +impl<'a> BitGenerator<'a> { + pub fn from(iterator: I) -> BitGenerator<'a> + where + I: Iterator> + 'a, + { + BitGenerator { + bit_pos: 0, + current_byte: 0, + iterator: Box::new(iterator), + } + } +} + +impl<'a> Iterator for BitGenerator<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.bit_pos == 0 { + let byte = match self.iterator.next() { + None => return None, + Some(Err(e)) => return Some(Err(e)), + Some(Ok(b)) => b, + }; + + self.current_byte = 0b11000000 | (byte << 1); + }; + + let bit = (self.current_byte & (1 << self.bit_pos)) != 0; + self.bit_pos = (self.bit_pos + 1) % 8; + + Some(Ok(bit)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bitgenerator() { + let input = vec![0b11111, 0b01010, 0b00000]; + + let generator = BitGenerator::from(input.into_iter().map(|x| Ok(x))); + + let result_data: Result, DspError> = generator.collect(); + assert_eq!( + result_data, + Ok(vec![ + true, true, true, true, true, true, true, true, true, false, true, false, true, + false, true, true, true, false, false, false, false, false, true, true + ]) + ); + } +} diff --git a/src/bitupsampler.rs b/src/bitupsampler.rs new file mode 100644 index 0000000..1f252fb --- /dev/null +++ b/src/bitupsampler.rs @@ -0,0 +1,74 @@ +extern crate rusty_dsp; + +use rusty_dsp::error::DspError; + +pub struct BitUpsampler<'a> { + bit_length: f64, + current_bit_length: usize, + pos: usize, + residual_error: f64, + current_bit: bool, + iterator: Box> + 'a>, +} + +impl<'a> BitUpsampler<'a> { + pub fn from(iterator: I, sampling_rate: f64, baud_rate: f64) -> BitUpsampler<'a> + where + I: Iterator> + 'a, + { + BitUpsampler { + bit_length: sampling_rate / baud_rate, + current_bit_length: 0, + residual_error: 0.0, + pos: 0, + current_bit: false, + iterator: Box::new(iterator), + } + } +} + +impl<'a> Iterator for BitUpsampler<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.pos >= self.current_bit_length { + self.current_bit = match self.iterator.next() { + None => return None, + Some(Err(e)) => return Some(Err(e)), + Some(Ok(b)) => b, + }; + + self.current_bit_length = self.bit_length.floor() as usize; + self.residual_error += self.bit_length - self.bit_length.floor(); + if self.residual_error >= 1.0 { + self.residual_error -= 1.0; + self.current_bit_length += 1; + } + + self.pos = 0; + } + + self.pos += 1; + Some(Ok(self.current_bit)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_baurate_error_correction() { + let input = vec![true, false, true, false]; + + let upsampler = BitUpsampler::from(input.into_iter().map(|x| Ok(x)), 10.0, 4.0); + + let result_data: Result, DspError> = upsampler.collect(); + assert_eq!( + result_data, + Ok(vec![ + true, true, false, false, false, true, true, false, false, false + ]) + ); + } +} diff --git a/src/main.rs b/src/main.rs index 48d5de3..a2421db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,26 @@ #[macro_use] extern crate lazy_static; +extern crate rusty_dsp; + +use rusty_dsp::multiply::MultiplyConst; +use rusty_dsp::sinks::SoundCardSink; +use rusty_dsp::sources::VFOSource; mod baudot; +mod bitgenerator; +mod bitupsampler; +mod symbolgenerator; fn main() { - println!("Hello, world!"); + let data = " kaufen sie jede woche vier gute bequeme pelze xy 1234567890 "; + + let encoder = baudot::Encoder::from(data.to_ascii_uppercase().into_bytes().into_iter()); + let bitgenerator = bitgenerator::BitGenerator::from(encoder); + let bitresampler = bitupsampler::BitUpsampler::from(bitgenerator, 48000.0, 45.5); + let symbolgenerator = symbolgenerator::SymbolGenerator::from(bitresampler, 2125.0, 1955.0); + let source = VFOSource::new(symbolgenerator, 48000.0); + let mult = MultiplyConst::from(source, 0.3); + let mut sink = SoundCardSink::from(mult, 48000); + + sink.start().unwrap(); } diff --git a/src/symbolgenerator.rs b/src/symbolgenerator.rs new file mode 100644 index 0000000..25b46dd --- /dev/null +++ b/src/symbolgenerator.rs @@ -0,0 +1,55 @@ +extern crate rusty_dsp; + +use rusty_dsp::error::DspError; + +pub struct SymbolGenerator<'a> { + space_freq: f32, + mark_freq: f32, + iterator: Box> + 'a>, +} + +impl<'a> SymbolGenerator<'a> { + pub fn from(iterator: I, space_freq: f32, mark_freq: f32) -> SymbolGenerator<'a> + where + I: Iterator> + 'a, + { + SymbolGenerator { + space_freq: space_freq, + mark_freq: mark_freq, + iterator: Box::new(iterator), + } + } +} + +impl<'a> Iterator for SymbolGenerator<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + match self.iterator.next() { + Some(Ok(b)) => { + if b { + Some(Ok(self.mark_freq)) + } else { + Some(Ok(self.space_freq)) + } + } + None => None, + Some(Err(e)) => Some(Err(e)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_symbolgenerator() { + let input = vec![true, false, true]; + + let generator = SymbolGenerator::from(input.into_iter().map(|x| Ok(x)), 23000.0, 42000.0); + + let result_data: Result, DspError> = generator.collect(); + assert_eq!(result_data, Ok(vec![42000.0, 23000.0, 42000.0])); + } +}