Full encoding chain is working

This commit is contained in:
Sebastian 2020-06-24 23:21:12 +02:00
parent 39d4c602f4
commit 3d994125a1
7 changed files with 404 additions and 10 deletions

169
Cargo.lock generated
View File

@ -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"

View File

@ -8,3 +8,4 @@ edition = "2018"
[dependencies]
lazy_static = "*"
rusty-dsp = {path = "../rusty-dsp"}

View File

@ -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<u8>,
iterator: Box<dyn Iterator<Item = u8> + 'a>,
@ -70,7 +74,7 @@ impl<'a> Encoder<'a> {
}
impl<'a> Iterator for Encoder<'a> {
type Item = Result<u8, u8>;
type Item = Result<u8, DspError>;
fn next(&mut self) -> Option<Self::Item> {
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<Vec<u8>, u8> {
pub fn encode(msg: &str) -> Result<Vec<u8>, 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<dyn Iterator<Item = u8> + 'a>,
}
@ -138,7 +145,7 @@ impl<'a> Decoder<'a> {
impl<'a> Iterator for Decoder<'a> {
// Option<Result<u8, u8>> 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<u8, u8>;
type Item = Result<u8, DspError>;
fn next(&mut self) -> Option<Self::Item> {
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<String, u8> {
pub fn decode(msg: &[u8]) -> Result<String, DspError> {
let decoder = Decoder::from(msg.iter().copied());
match decoder.collect() {
Ok(v) => Ok(String::from_utf8(v).unwrap()),

64
src/bitgenerator.rs Normal file
View File

@ -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<dyn Iterator<Item = Result<u8, DspError>> + 'a>,
}
impl<'a> BitGenerator<'a> {
pub fn from<I>(iterator: I) -> BitGenerator<'a>
where
I: Iterator<Item = Result<u8, DspError>> + 'a,
{
BitGenerator {
bit_pos: 0,
current_byte: 0,
iterator: Box::new(iterator),
}
}
}
impl<'a> Iterator for BitGenerator<'a> {
type Item = Result<bool, DspError>;
fn next(&mut self) -> Option<Self::Item> {
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<Vec<bool>, 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
])
);
}
}

74
src/bitupsampler.rs Normal file
View File

@ -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<dyn Iterator<Item = Result<bool, DspError>> + 'a>,
}
impl<'a> BitUpsampler<'a> {
pub fn from<I>(iterator: I, sampling_rate: f64, baud_rate: f64) -> BitUpsampler<'a>
where
I: Iterator<Item = Result<bool, DspError>> + '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<bool, DspError>;
fn next(&mut self) -> Option<Self::Item> {
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<Vec<bool>, DspError> = upsampler.collect();
assert_eq!(
result_data,
Ok(vec![
true, true, false, false, false, true, true, false, false, false
])
);
}
}

View File

@ -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();
}

55
src/symbolgenerator.rs Normal file
View File

@ -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<dyn Iterator<Item = Result<bool, DspError>> + 'a>,
}
impl<'a> SymbolGenerator<'a> {
pub fn from<I>(iterator: I, space_freq: f32, mark_freq: f32) -> SymbolGenerator<'a>
where
I: Iterator<Item = Result<bool, DspError>> + 'a,
{
SymbolGenerator {
space_freq: space_freq,
mark_freq: mark_freq,
iterator: Box::new(iterator),
}
}
}
impl<'a> Iterator for SymbolGenerator<'a> {
type Item = Result<f32, DspError>;
fn next(&mut self) -> Option<Self::Item> {
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<Vec<f32>, DspError> = generator.collect();
assert_eq!(result_data, Ok(vec![42000.0, 23000.0, 42000.0]));
}
}