converted everything to float, which is faster apparently

This commit is contained in:
Fluora Eigenwire 2020-03-16 10:36:55 -05:00
parent 91348f73cf
commit 4320e50a7a
7 changed files with 127 additions and 285 deletions

72
Cargo.lock generated
View File

@ -1,88 +1,16 @@
# 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"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crossbeam-channel"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hermit-abi"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lapp"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num_cpus"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "threnodyne"
version = "0.1.0"
dependencies = [
"crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lapp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
"checksum lapp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60bf485afeba9437a275ad29a9383b03f2978450e7feceffb55be8c0dbad9829"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"

View File

@ -8,5 +8,3 @@ edition = "2018"
[dependencies]
lapp = "0.4.0"
crossbeam-channel = "0.4.2"
num_cpus = "1.12.0"

View File

@ -10,7 +10,7 @@ pub fn generate
length: f32, // samples
tail: f32, // proportion
) ->
Vec<i16>
Vec<f32>
{
let freq_delta = (freq_hi - freq_lo) / (length * 2.0);
@ -18,7 +18,7 @@ pub fn generate
let env_bot = length * tail;
let env_top = length * (1.0 - tail);
let mut new_chirp:Vec<i16> = Vec::with_capacity(length.round() as usize);
let mut new_chirp:Vec<f32> = Vec::with_capacity(length.round() as usize);
for i in 0..(length.round() as usize)
{
let t = i as f32;
@ -37,21 +37,16 @@ pub fn generate
);
new_chirp.push(
env *
(
32767.0 *
env *
PI * t *
(
PI * t *
(
freq_lo
+
(t * freq_delta)
)
freq_lo
+
(t * freq_delta)
)
.sin()
)
.round()
as i16
.sin()
);
}

View File

@ -6,13 +6,13 @@ use crate::gaussian;
struct SignalInput
{
template_rise: Vec<i64>,
template_fall: Vec<i64>,
norm_rise: i64,
norm_fall: i64,
symbol_size: i64,
buffer: posi::Buffer<i64>,
sum: i64,
template_rise: Vec<f32>,
template_fall: Vec<f32>,
norm_rise: f32,
norm_fall: f32,
symbol_length: f32,
buffer: posi::Buffer<f32>,
sum: f32,
}
impl SignalInput
@ -29,35 +29,11 @@ impl SignalInput
let lower_freq = (center_freq - deviation).max(0.0).min(1.0);
// these have to be reversed, since the input buffer shifts in at index 0 and is thus a mirror image
let template_rise:Vec<i64> =
(
chirp::generate(upper_freq, lower_freq, symbol_length, 0.125)
.into_iter()
.map(|x| x as i64)
.collect()
);
let template_fall:Vec<i64> =
(
chirp::generate(lower_freq, upper_freq, symbol_length, 0.125)
.into_iter()
.map(|x| x as i64)
.collect()
);
let norm_rise =
(
template_rise
.iter()
.map(|&x| x.abs())
.sum()
);
let norm_fall =
(
template_rise
.iter()
.map(|&x| x.abs())
.sum()
);
let template_rise:Vec<f32> = chirp::generate(upper_freq, lower_freq, symbol_length, 0.125);
let template_fall:Vec<f32> = chirp::generate(lower_freq, upper_freq, symbol_length, 0.125);
let norm_rise = template_rise.iter().sum();
let norm_fall = template_fall.iter().sum();
SignalInput
{
@ -65,18 +41,18 @@ impl SignalInput
template_fall: template_fall,
norm_rise: norm_rise,
norm_fall: norm_fall,
symbol_size: symbol_length.round() as i64,
symbol_length: symbol_length,
buffer: posi::Buffer::new(symbol_length.round() as usize),
sum: 0,
sum: 0.0,
}
}
fn ingest
(
&mut self,
sample: i16,
sample: f32,
) {
let new_value = sample as i64;
let new_value = sample;
let old_value = self.buffer.last();
self.sum -= old_value.abs();
@ -89,14 +65,14 @@ impl SignalInput
(
&self,
) ->
(i64, i64)
(f32, f32)
{
let local_signal = self.buffer.to_vec();
let mut rise_value = 0;
let mut fall_value = 0;
let mut rise_value:f32 = 0.0;
let mut fall_value:f32 = 0.0;
if self.sum > 0
if self.sum > 0.0
{
for (&a, &b) in
(
@ -116,19 +92,8 @@ impl SignalInput
fall_value += a * b;
}
rise_value =
(
(32768 * rise_value * self.symbol_size)
/
(self.norm_rise * self.sum)
);
fall_value =
(
(32768 * fall_value * self.symbol_size)
/
(self.norm_fall * self.sum)
);
rise_value /= self.norm_rise * self.sum / self.symbol_length;
fall_value /= self.norm_fall * self.sum / self.symbol_length;
}
return (rise_value, fall_value);
@ -137,9 +102,9 @@ impl SignalInput
struct CorrelFilter
{
kernel: Vec<i64>,
buffer_rise: posi::Buffer<i64>,
buffer_fall: posi::Buffer<i64>,
kernel: Vec<f32>,
buffer_rise: posi::Buffer<f32>,
buffer_fall: posi::Buffer<f32>,
}
impl CorrelFilter
@ -164,24 +129,24 @@ impl CorrelFilter
fn ingest
(
&mut self,
rise: i64,
fall: i64,
rise: f32,
fall: f32,
) {
self.buffer_rise.shift_in(rise);
self.buffer_fall.shift_in(fall);
}
fn hi_process
fn lo_process
(
&self,
) ->
(i64, i64)
(f32, f32)
{
let local_rise = self.buffer_rise.to_vec();
let local_fall = self.buffer_fall.to_vec();
let mut rise_result:i64 = 0;
let mut fall_result:i64 = 0;
let mut rise_result:f32 = 0.0;
let mut fall_result:f32 = 0.0;
for (&a, &b) in
(
@ -201,8 +166,16 @@ impl CorrelFilter
fall_result += a * b;
}
rise_result /= 32768;
fall_result /= 32768;
return (rise_result, fall_result);
}
fn hi_process
(
&self,
) ->
(f32, f32)
{
let (rise_result, fall_result) = self.lo_process();
return
(
@ -210,62 +183,22 @@ impl CorrelFilter
self.buffer_fall.mid() - fall_result,
);
}
fn lo_process
(
&self,
) ->
(i64, i64)
{
let local_rise = self.buffer_rise.to_vec();
let local_fall = self.buffer_fall.to_vec();
let mut rise_result:i64 = 0;
let mut fall_result:i64 = 0;
for (&a, &b) in
(
local_rise.iter()
).zip(
self.kernel.iter()
) {
rise_result += a * b;
}
for (&a, &b) in
(
local_fall.iter()
).zip(
self.kernel.iter()
) {
fall_result += a * b;
}
rise_result /= 32768;
fall_result /= 32768;
return
(
rise_result,
fall_result,
);
}
}
struct CorrelOutput
{
rolling_max_decay: i64,
squelch: i64,
rolling_max_decay: f32,
squelch: f32,
buffer_rise: posi::Buffer<i64>,
rise_sum: i64,
rise_maxima: Vec<i64>,
rise_max_rolling: i64,
buffer_rise: posi::Buffer<f32>,
rise_sum: f32,
rise_maxima: Vec<f32>,
rise_max_rolling: f32,
buffer_fall: posi::Buffer<i64>,
fall_sum: i64,
fall_maxima: Vec<i64>,
fall_max_rolling: i64,
buffer_fall: posi::Buffer<f32>,
fall_sum: f32,
fall_maxima: Vec<f32>,
fall_max_rolling: f32,
}
impl CorrelOutput
@ -278,26 +211,26 @@ impl CorrelOutput
{
CorrelOutput
{
rolling_max_decay: (size as i64) * 16,
squelch: 0,
rolling_max_decay: 1.0 - (0.1 / size as f32),
squelch: 0.0,
buffer_rise: posi::Buffer::new(size),
rise_sum: 0,
rise_sum: 0.0,
rise_maxima: Vec::new(),
rise_max_rolling: 0,
rise_max_rolling: 0.0,
buffer_fall: posi::Buffer::new(size),
fall_sum: 0,
fall_sum: 0.0,
fall_maxima: Vec::new(),
fall_max_rolling: 0,
fall_max_rolling: 0.0,
}
}
fn ingest
(
&mut self,
new_rise: i64,
new_fall: i64,
new_rise: f32,
new_fall: f32,
) {
let old_rise = self.buffer_rise.last();
let old_fall = self.buffer_fall.last();
@ -308,12 +241,12 @@ impl CorrelOutput
self.fall_sum += new_fall.abs();
self.fall_sum -= old_fall.abs();
if let Ok(index) = self.rise_maxima.binary_search(&old_rise)
if let Some(index) = self.rise_maxima.iter().position(|&x| x == old_rise)
{
self.rise_maxima.remove(index);
}
if let Ok(index) = self.fall_maxima.binary_search(&old_fall)
if let Some(index) = self.fall_maxima.iter().position(|&x| x == old_fall)
{
self.fall_maxima.remove(index);
}
@ -340,26 +273,11 @@ impl CorrelOutput
let current_rise = self.buffer_rise.mid();
let current_fall = self.buffer_fall.mid();
self.rise_max_rolling =
(
self.rise_max_rolling
*
(self.rolling_max_decay - 1)
/
self.rolling_max_decay
);
self.rise_max_rolling *= self.rolling_max_decay;
self.fall_max_rolling *= self.rolling_max_decay;
self.fall_max_rolling =
(
self.fall_max_rolling
*
(self.rolling_max_decay - 1)
/
self.rolling_max_decay
);
self.rise_max_rolling = self.rise_max_rolling.max(current_rise).max(current_fall / 2);
self.fall_max_rolling = self.fall_max_rolling.max(current_fall).max(current_rise / 2);
self.rise_max_rolling = self.rise_max_rolling.max(current_rise).max(current_fall / 2.0);
self.fall_max_rolling = self.fall_max_rolling.max(current_fall).max(current_rise / 2.0);
}
fn have_bit
@ -371,8 +289,8 @@ impl CorrelOutput
let current_rise = self.buffer_rise.mid();
let current_fall = self.buffer_fall.mid();
let rise_max = *self.rise_maxima.last().unwrap_or(&0);
let fall_max = *self.fall_maxima.last().unwrap_or(&0);
let rise_max = *self.rise_maxima.last().unwrap_or(&0.0);
let fall_max = *self.fall_maxima.last().unwrap_or(&0.0);
let mut have_rise:bool = false;
let mut have_fall:bool = false;
@ -382,9 +300,9 @@ impl CorrelOutput
&&
current_rise >= rise_max
&&
current_rise >= fall_max * 7 / 8
current_rise >= fall_max * 7.0 / 8.0
&&
current_rise >= self.rise_max_rolling / 2
current_rise >= self.rise_max_rolling / 2.0
) {
have_rise = true;
}
@ -394,9 +312,9 @@ impl CorrelOutput
&&
current_fall >= fall_max
&&
current_fall >= rise_max * 7 / 8
current_fall >= rise_max * 7.0 / 8.0
&&
current_fall >= self.fall_max_rolling / 2
current_fall >= self.fall_max_rolling / 2.0
) {
have_fall = true;
}
@ -495,8 +413,8 @@ impl Demodulator
Demodulator
{
signal_input: SignalInput::new(center_freq, deviation, symbol_length),
correl_lopass: CorrelFilter::new(deviation / 2.0),
correl_hipass: CorrelFilter::new(deviation / 16.0),
correl_lopass: CorrelFilter::new(deviation / 2.0), // these filter frequencies may need adjustment.
correl_hipass: CorrelFilter::new(deviation / 4.0), // we don't really know how to set them Correctly, but they seem fairly easy to game.
correl_output: CorrelOutput::new(symbol_length.round() as usize),
accumulator: ByteAccumulator::new((symbol_length * 17.0 / 16.0).round() as usize),
}
@ -505,7 +423,7 @@ impl Demodulator
pub fn process
(
&mut self,
new_sample: i16,
new_sample: f32,
) ->
Option<u8>
{
@ -529,22 +447,22 @@ impl Demodulator
pub fn set_squelch
(
&mut self,
value: u16,
value: f32,
) {
self.correl_output.squelch = (value / 2) as i64;
self.correl_output.squelch = value;
}
pub fn dump_correl
(
&self,
) ->
(i64, i64)
(f32, f32)
{
(
self.correl_output.buffer_rise.mid(),
self.correl_output.buffer_fall.mid(),
//self.correl_output.rise_max_rolling,
//self.correl_output.fall_max_rolling,
//self.correl_output.buffer_rise.mid(),
//self.correl_output.buffer_fall.mid(),
self.correl_output.rise_max_rolling,
self.correl_output.fall_max_rolling,
)
}
}

View File

@ -6,8 +6,8 @@ pub struct Enmodulator
{
symbol_len: usize,
guard_pad: usize,
symbol_rise: Vec<i16>,
symbol_fall: Vec<i16>,
symbol_rise: Vec<f32>,
symbol_fall: Vec<f32>,
}
impl Enmodulator
@ -23,8 +23,8 @@ impl Enmodulator
let lower_freq = (center_freq - deviation).max(0.0).min(1.0);
let upper_freq = (center_freq + deviation).max(0.0).min(1.0);
let symbol_rise:Vec<i16> = chirp::generate(lower_freq, upper_freq, symbol_len, 0.125);
let symbol_fall:Vec<i16> = chirp::generate(upper_freq, lower_freq, symbol_len, 0.125);
let symbol_rise:Vec<f32> = chirp::generate(lower_freq, upper_freq, symbol_len, 0.125);
let symbol_fall:Vec<f32> = chirp::generate(upper_freq, lower_freq, symbol_len, 0.125);
Enmodulator
{
@ -40,9 +40,9 @@ impl Enmodulator
&self,
byte: u8,
) ->
Vec<i16>
Vec<f32>
{
let mut new_signal:Vec<i16> =
let mut new_signal:Vec<f32> =
(
Vec::with_capacity(
(8 * self.symbol_len)
@ -51,7 +51,7 @@ impl Enmodulator
)
);
new_signal.append(&mut vec![0; self.guard_pad]);
new_signal.append(&mut vec![0.0; self.guard_pad]);
for i in 0..8
{
@ -73,7 +73,7 @@ impl Enmodulator
);
}
new_signal.append(&mut vec![0; self.guard_pad]);
new_signal.append(&mut vec![0.0; self.guard_pad]);
return new_signal;
}

View File

@ -4,12 +4,12 @@ pub fn generate
(
freq: f32,
) ->
Vec<i64>
Vec<f32>
{
let width = 1.0 / (PI * freq);
let radius = (width * 9.4).round().max(1.0) as isize;
let radius = (width * 10.0).round().max(1.0) as isize;
let mut output:Vec<i64> = Vec::with_capacity(radius as usize * 2 + 1);
let mut output:Vec<f32> = Vec::with_capacity(radius as usize * 2 + 1);
for t in
(
@ -23,18 +23,13 @@ pub fn generate
) {
output.push(
(
32768.0 *
(
-0.5 * (t / width).powi(2)
)
.exp()
/
(
width * (2.0 * PI).sqrt()
)
-0.5 * (t / width).powi(2)
)
.exp()
/
(
width * (2.0 * PI).sqrt()
)
.round()
as i64
);
}

View File

@ -28,7 +28,7 @@ fn main
Chirps can provide substantial resistance to unfavorable channel conditions, which makes this program \
potentially suitable for transmitting data over channels intended for narrowband voice communication.
Audio input/output is in raw 16-bit little-endian PCM format.
Audio input/output is in 16-bit little-endian integer sample format.
Data input/output is arbitrary binary data, which can be text, file contents, or whatever else.
Usage:
@ -39,7 +39,7 @@ fn main
-b, --bit-rate (default 50.0) - Data rate, in chirps (bits) per second. Be aware that lower values will increase system load.
-f, --center-frequency (default 4000.0) - Center frequency of the signal in Hertz.
-d, --deviation (default 2000.0) - Maximum range traversed above and below the center frequency by the signal.
-t, --squelch (default 4000) - Minimum signal match degree for demodulation, from 0 (accept all) to 65535 (ignore all).
-t, --squelch (default 0.4) - Minimum signal match degree for demodulation, from 0.0 (accept all) to 1.0 (ignore all).
-a, --ascii-only - When demodulating, suppress all output that is not an ASCII character. Possibly useful if searching for text on a noisy channel.
-g, --debug (default 0) - If specified when demodulating, emits every nth correlation value in f32 form to threnodyne_rise.dat and threnodyne_fall.dat.
@ -56,7 +56,7 @@ fn main
let arg_bitrate= args.get_float("bit-rate");
let arg_centerfreq = args.get_float("center-frequency");
let arg_deviation = args.get_float("deviation");
let arg_squelch = args.get_integer("squelch");
let arg_squelch = args.get_float("squelch");
let arg_ascii = args.get_bool("ascii-only");
let arg_debug = args.get_integer("debug");
@ -103,9 +103,9 @@ fn main
exit(1);
}
if arg_squelch < 0 || arg_squelch > 65535
if arg_squelch < 0.0
{
eprintln!("threnodyne: the squelch threshold must be at least zero and no more than 65535 ({} won't do)", arg_squelch);
eprintln!("threnodyne: the squelch threshold must be at least 0.0 ({} won't do)", arg_squelch);
exit(1);
}
@ -118,7 +118,7 @@ fn main
{
let enmodulator = enmodulation::Enmodulator::new(center_freq, deviation, symbol_len);
let mut output_buffer:Vec<u8> = Vec::with_capacity(symbol_size);
let mut output_buffer:Vec<u8> = Vec::with_capacity(symbol_size * 2);
let wall_start = Instant::now();
let mut process_time = Duration::new(0,0);
@ -147,7 +147,12 @@ fn main
for &sample in signal.iter()
{
output_buffer.extend_from_slice(&sample.to_le_bytes());
output_buffer.extend_from_slice(
&(
(sample * 32767.0) as i16
)
.to_le_bytes()
);
}
process_time += iter_time.elapsed();
@ -176,7 +181,7 @@ fn main
{
let mut demodulator = demodulation::Demodulator::new(center_freq, deviation, symbol_len);
demodulator.set_squelch(arg_squelch as u16);
demodulator.set_squelch(arg_squelch);
let wall_start = Instant::now();
let mut process_time = Duration::new(0,0);
@ -244,7 +249,12 @@ fn main
let iter_time = Instant::now();
let sample = i16::from_le_bytes(sample_bytes);
let sample =
(
i16::from_le_bytes(sample_bytes) as f32
/
32767.0
);
let demod_result = demodulator.process(sample);
if let Some((risefile, fallfile)) = &mut debug_files
@ -252,10 +262,8 @@ fn main
if samples_processed % (arg_debug as usize) == 0
{
let (riseval, fallval) = demodulator.dump_correl();
let riseval_f = (riseval as f32) / 32768.0;
let fallval_f = (fallval as f32) / 32768.0;
risefile.write_all(&riseval_f.to_be_bytes()).expect("write failed on rise debug file");
fallfile.write_all(&fallval_f.to_be_bytes()).expect("write failed on fall debug file");
risefile.write_all(&riseval.to_be_bytes()).expect("write failed on rise debug file");
fallfile.write_all(&fallval.to_be_bytes()).expect("write failed on fall debug file");
}
}