initial commit! going to pretend it's presentable
This commit is contained in:
commit
544ccd7e88
|
@ -0,0 +1,4 @@
|
|||
bad
|
||||
target
|
||||
scripts
|
||||
proto
|
|
@ -0,0 +1,57 @@
|
|||
# Fluora's Miscellaneous Digital Signal Toolbox
|
||||
### Fluora
|
||||
### 2019 onwards
|
||||
### Public domain
|
||||
|
||||
---
|
||||
|
||||
### What's this?
|
||||
I've made (and continue to keep making) a bunch of little Rust programs for debugging DSP projects and simulating
|
||||
potential techniques. They all have a few things in common:
|
||||
- They're really bad and have no real error handling.
|
||||
- They operate exclusively on 32-bit big-endian floating-point samples, and don't expect to find headers.
|
||||
- They do about one thing each, usually by reading a file and writing to another, but sometimes by writing back to the same file.
|
||||
- They're very inefficient code, and only intended to be run on small files.
|
||||
- They have very little documentation and I'm really sorry.
|
||||
- None of them depend on anything. There are no crates here.
|
||||
|
||||
### What should I use these for?
|
||||
You shouldn't use it at all. If you want to anyway, I can't stop you, but you should probably find a saner DSP toolbox
|
||||
if you want to actually get work done.
|
||||
|
||||
### How do I compile them all at once?
|
||||
You can run the build.sh script I included, which will try to compile everything in this directory that ends in ".rs", and put
|
||||
all the resulting binaries in ./target/.
|
||||
|
||||
### What do they do?
|
||||
Here's the fast version, covering everything included so far:
|
||||
- `addmix` adds two signals, producing a combined output.
|
||||
- `mulmix` multiplies two signals (like an RF mixer or ring modulator), generating harmonics at the sum and difference of frequencies.
|
||||
- `bomp` is a plotter that generates simple images of signals that you can look at.
|
||||
- `termplot` is another, worse plotter, which does something similar using rows of block characters in the terminal.
|
||||
- `correlate` takes the correlation of two signals (multiply-sum for all possible time offsets).
|
||||
- `correlate_cyclic` does the same, but makes the signals periodic.
|
||||
- `fsinechirp` generates chirps (rising or falling tones) with linear change in frequency.
|
||||
- `tsinechirp` generates chirps with linear change in period.
|
||||
- `squarechirp` generates chirps using square waves.
|
||||
- `normalize` multiplies signals by a constant value such that the maximum value becomes 1.
|
||||
- `rectify` takes the absolute value of a signal, flipping negative values into positive values.
|
||||
- `peakdetect` zeroes out all samples that are not local maxima.
|
||||
- `splitbits` extracts the sign from samples, producing an output whose values are only 1 or -1.
|
||||
- `upsample` increases the sample rate of a signal by an integer factor with linear interpolation.
|
||||
- `gfilter` applies a gaussian low-pass filter.
|
||||
- `sfilter` applies a sinc low-pass filter.
|
||||
- `swindow` applies a sinc low-pass and high-pass filter, forming a rectangular bandpass filter.
|
||||
- `worsen` adds a variable amount of random noise to a signal.
|
||||
- `skew` simulates mismatches in sampling frequency and phase.
|
||||
- `sft` is a naĩve, slow (O(n²)) implementation of a discrete Fourier transform. It's like an FFT, but instead of fast, it's slow.
|
||||
- `waterfall` generates 2D frequency/time spectrum images with repeated Fourier transforms.
|
||||
- `zadoffchu` generates Zadoff-Chu sequences, which are low-autocorrelation functions used in telecom.
|
||||
- `unitvex` generates a sequence of complex unit vectors, using input data to set their angles.
|
||||
- `vecsum` takes two signals and combines them with the Pythagorean theorem, e.g. to produce a power spectrum from a complex DFT.
|
||||
- `dcst` simultaneously takes the discrete cosine transform (DCT) and discrete sine transform (DST) of a signal.
|
||||
- `pad` adds arbitrary amounts of silence before and after a signal.
|
||||
- `integrate` takes the integral of a signal.
|
||||
- `differentiate` takes the derivative of a signal.
|
||||
- `orthosine` generates orthogonal waveforms on an interval, i.e. OFDM carriers.
|
||||
- `echospace` simulates reflections in a 3D volume.
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
if ! [ -d target ]; then
|
||||
mkdir target
|
||||
fi
|
||||
|
||||
for file in src/*.rs; do
|
||||
if [ "$file" = "src/template.rs" ]; then continue; fi
|
||||
echo $file
|
||||
rustc -O $file --out-dir ./target/
|
||||
done
|
|
@ -0,0 +1,105 @@
|
|||
// addmix
|
||||
// combines an arbitrary number of input files together into a single output file, with optional input level control.
|
||||
// CPU demands: low.
|
||||
// Memory requirements: 2x size of longest input file.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() < 3
|
||||
{
|
||||
eprintln!("usage: addmix <output.dat> <input_1.dat>:<level> <input_2.dat>:<level> ...");
|
||||
return;
|
||||
}
|
||||
|
||||
let output_filename = argv[1].clone();
|
||||
let mut output_file = match File::create(&argv[1])
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("addmix: could not open/create specified output file {} for writing: {}", argv[1], reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(file) => file,
|
||||
};
|
||||
let mut input_files:Vec<(File,String,f32)> = Vec::new();
|
||||
for i in 2..argv.len()
|
||||
{
|
||||
let mut new_filename = argv[i].clone();
|
||||
let mut new_level:f32 = 1.0;
|
||||
if let Some((filename,levelstring)) = argv[i].rsplit_once(':')
|
||||
{
|
||||
if let Ok(val) = levelstring.parse::<f32>()
|
||||
{
|
||||
// decide how to handle the filename:level vs just filename syntax by testing to see if what's after the colon
|
||||
// actually looks like a number. if it doesn't, silently assume that the whole thing was supposed to be a colon-containing filename.
|
||||
// if this wasn't the case, and the user meant to type a number, then they should be able to understand what happened from the
|
||||
// "file not found" error message.
|
||||
new_level = val;
|
||||
new_filename = filename.to_string();
|
||||
}
|
||||
}
|
||||
let new_file = match File::open(&new_filename)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("addmix: could not open specified input file {} for reading: {}", new_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(file) => file,
|
||||
};
|
||||
input_files.push((new_file, new_filename, new_level));
|
||||
}
|
||||
|
||||
let mut output_samples:Vec<f32> = Vec::new();
|
||||
|
||||
for (file, filename, level) in input_files.iter_mut()
|
||||
{
|
||||
let mut file_data:Vec<u8> = Vec::new();
|
||||
match file.read_to_end(&mut file_data)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("addmix: could not read from input file {}: {}", filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
let file_length:usize = file_data.len()/4;
|
||||
for _ in 0..file_length.saturating_sub(output_samples.len())
|
||||
{
|
||||
output_samples.push(0.0);
|
||||
}
|
||||
|
||||
for (t,chunk) in file_data.chunks_exact(4).enumerate()
|
||||
{
|
||||
let mut sample_bytes:[u8;4] = [0x00; 4];
|
||||
sample_bytes.copy_from_slice(chunk);
|
||||
|
||||
output_samples[t] += *level * f32::from_be_bytes(sample_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(output_samples.len() * 4);
|
||||
for sample in output_samples.iter()
|
||||
{
|
||||
output_data.extend_from_slice(&sample.to_be_bytes());
|
||||
}
|
||||
match output_file.write_all(&output_data)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("addmix: could not write to output file {}: {}", output_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
// Name: bomp
|
||||
// Description: bitmap plotter utility
|
||||
// Why: generate viewable plots from f32be data files
|
||||
// Processor usage: low
|
||||
// Memory usage: loads all input files into memory at once
|
||||
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
|
||||
static OUTER_PADDING:usize = 16;
|
||||
static INTERPLOT_PADDING:usize = 16;
|
||||
static SIZE_ROUNDOFF:usize = 16;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() < 4
|
||||
{
|
||||
eprintln!("usage: bomp <yscale> <output.bmp> <input.dat> [<optional additional inputs>...]");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
yscale (integer):
|
||||
the vertical scale of each plot, equal to the distance in pixels from zero to the plot's max value.
|
||||
plots are thus 2*yscale+1 pixels high, plus padding.
|
||||
|
||||
output.bmp (bitmap file):
|
||||
a classic microsoft bitmap file to which to write the uncompressed plot image.
|
||||
if you want to use it elsewhere, i recommend converting it via ffmpeg to png.
|
||||
|
||||
inputs.dat (raw big-endian f32 data file):
|
||||
simply a sequence of arbitrarily many f32 values in big-endian byte order.
|
||||
you can specify an arbitrary number of input files, each of which will produce
|
||||
its own parallel plot below the previous one on the resulting image. parallel plots
|
||||
will use the same horizontal scale (always exactly one pixel per data value) and
|
||||
vertical scale (automatically adjusted so that the overall maximum value between all
|
||||
data sets matches the specified yscale value in pixel height).
|
||||
*/
|
||||
|
||||
let yscale = match argv[1].parse::<usize>()
|
||||
{
|
||||
Ok(n) => n,
|
||||
Err(_) =>
|
||||
{
|
||||
eprintln!("bomp: the first command-line argument needs to be an integer (number of vertical pixels per plot.)");
|
||||
exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
let output_filename:String = argv[2].clone();
|
||||
let mut output_file = match File::create(&output_filename)
|
||||
{
|
||||
Ok(file) => file,
|
||||
Err(why) =>
|
||||
{
|
||||
eprintln!("bomp: could not open {} for writing: {}", output_filename, why);
|
||||
exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
let mut input_files:Vec<(File, String)> = Vec::new();
|
||||
for i in 3..argv.len()
|
||||
{
|
||||
let new_filename:String = argv[i].clone();
|
||||
let new_file = match File::open(&argv[i])
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("bomp: failed to open {} for reading: {}", new_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
input_files.push((new_file, new_filename));
|
||||
}
|
||||
|
||||
let mut signals:Vec<Vec<f32>> = Vec::new();
|
||||
for (file,filename) in input_files.iter_mut()
|
||||
{
|
||||
let mut file_buffer:Vec<u8> = Vec::new();
|
||||
match file.read_to_end(&mut file_buffer)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("bomp: could not read from input file {}: {}", filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
let mut new_signal:Vec<f32> = Vec::with_capacity(file_buffer.len()/4);
|
||||
for chunk in file_buffer.chunks_exact(4)
|
||||
{
|
||||
let mut sample_bytes:[u8;4] = [0x00; 4];
|
||||
sample_bytes.copy_from_slice(chunk);
|
||||
new_signal.push(f32::from_be_bytes(sample_bytes));
|
||||
}
|
||||
signals.push(new_signal);
|
||||
}
|
||||
|
||||
let mut abs_max:f32 = 0.0;
|
||||
let mut signals_maxlen:usize = 0;
|
||||
for signal in signals.iter()
|
||||
{
|
||||
signals_maxlen = signals_maxlen.max(signal.len());
|
||||
for point in signal.iter()
|
||||
{
|
||||
abs_max = abs_max.max(point.abs());
|
||||
}
|
||||
}
|
||||
let scale_factor = (yscale as f32) / abs_max;
|
||||
eprintln!("peak: {}", abs_max);
|
||||
|
||||
for signal in signals.iter_mut()
|
||||
{
|
||||
for point in signal.iter_mut()
|
||||
{
|
||||
*point *= scale_factor;
|
||||
}
|
||||
}
|
||||
|
||||
let plot_height:usize = (yscale*2 + 1);
|
||||
let plot_width:usize = signals_maxlen;
|
||||
let signal_count:usize = signals.len();
|
||||
let intersignal_count:usize = signals.len()-1;
|
||||
|
||||
// adjust the padding to make the image size a nice round number.
|
||||
// why? i forgot, honestly. seems pretty pointless.
|
||||
let mut lpad:usize = OUTER_PADDING;
|
||||
let mut rpad:usize = OUTER_PADDING;
|
||||
while (plot_width + lpad + rpad) % SIZE_ROUNDOFF != 0
|
||||
{
|
||||
if lpad <= rpad
|
||||
{
|
||||
lpad += 1;
|
||||
} else {
|
||||
rpad += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tpad:usize = OUTER_PADDING;
|
||||
let mut bpad:usize = OUTER_PADDING;
|
||||
while (plot_height*signal_count + intersignal_count*INTERPLOT_PADDING + tpad + bpad) % SIZE_ROUNDOFF != 0
|
||||
{
|
||||
if tpad <= bpad
|
||||
{
|
||||
tpad += 1;
|
||||
} else {
|
||||
bpad += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let color_count:usize = 256;
|
||||
let header_size:usize = 54 + color_count*4;
|
||||
let image_width:usize = plot_width + lpad + rpad;
|
||||
let image_height:usize = signal_count*plot_height + intersignal_count*INTERPLOT_PADDING + tpad + bpad;
|
||||
let file_size:usize = image_height * image_width + 1078;
|
||||
|
||||
eprintln!("plots: {}", signal_count);
|
||||
eprintln!("wide: {}", image_width);
|
||||
eprintln!("high: {}", image_height);
|
||||
eprintln!("size: {}", file_size);
|
||||
|
||||
let mut header:Vec<u8> = Vec::with_capacity(header_size);
|
||||
header.extend_from_slice(&"BM".as_bytes()); // "signature"
|
||||
header.extend_from_slice(&(file_size as u32).to_le_bytes()); // filesize
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // forbidden field
|
||||
header.extend_from_slice(&(header_size as u32).to_le_bytes()); // data offset
|
||||
|
||||
header.extend_from_slice(&40u32.to_le_bytes()); // infoheader size (constant)
|
||||
header.extend_from_slice(&(image_width as u32).to_le_bytes()); // pixel width
|
||||
header.extend_from_slice(&(image_height as u32).to_le_bytes()); // pixel height
|
||||
header.extend_from_slice(&1u16.to_le_bytes()); // number of planes
|
||||
header.extend_from_slice(&8u16.to_le_bytes()); // bits per pixel
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // compression type (none)
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // compressed image size (not applicable)
|
||||
header.extend_from_slice(&1024u32.to_le_bytes()); // horizontal pixels/meter (arbitrary)
|
||||
header.extend_from_slice(&1024u32.to_le_bytes()); // vertical pixels/meter (arbitrary)
|
||||
header.extend_from_slice(&256u32.to_le_bytes()); // colors used (all of them)
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // number of important colors (what does that even mean)
|
||||
|
||||
assert_eq!(header.len(), 54);
|
||||
|
||||
for i in 0x00..=0xFF
|
||||
{
|
||||
header.push(i); // red
|
||||
header.push(i); // green
|
||||
header.push(i); // blue
|
||||
header.push(0x00); // ?????
|
||||
}
|
||||
|
||||
match output_file.write_all(&header)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("bomp: write error on file {}: {}", output_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
output_file.write_all(&vec![0x00; bpad*image_width]).expect("write error (lower padding)");
|
||||
|
||||
for (signal_number, signal) in signals.iter().enumerate().rev() // reversed because bmps go from bottom to top, for some reason
|
||||
{
|
||||
for y in -(yscale as isize)..=(yscale as isize)
|
||||
{
|
||||
let mut row_buffer:Vec<u8> = Vec::with_capacity(image_width);
|
||||
|
||||
row_buffer.extend_from_slice(&vec![0x00; lpad]);
|
||||
|
||||
for x in 0..signal.len()
|
||||
{
|
||||
if
|
||||
(
|
||||
(
|
||||
signal[x] >= (y as f32)
|
||||
&&
|
||||
y >= 0
|
||||
) || (
|
||||
signal[x] <= (y as f32)
|
||||
&&
|
||||
y <= 0
|
||||
)
|
||||
) {
|
||||
row_buffer.push(0xFF);
|
||||
} else {
|
||||
row_buffer.push(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
row_buffer.extend_from_slice(&vec![0x00; signals_maxlen - signal.len() + rpad]);
|
||||
|
||||
output_file.write_all(&row_buffer).expect("write error (row)");
|
||||
}
|
||||
|
||||
if signal_number > 0
|
||||
{
|
||||
output_file.write_all(&vec![0x00; image_width*INTERPLOT_PADDING]).expect("write error (interplot padding)");
|
||||
}
|
||||
}
|
||||
|
||||
output_file.write_all(&vec![0x00; tpad*image_width]).expect("write error (upper padding)");
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("usage: correlate <input_a.dat> <input_b.dat> <output_forward.dat> <output_reverse.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input_a = File::open(&argv[1]).unwrap();
|
||||
let mut input_b = File::open(&argv[2]).unwrap();
|
||||
let mut output_fwd = File::create(&argv[3]).unwrap();
|
||||
let mut output_rev = File::create(&argv[4]).unwrap();
|
||||
|
||||
let mut data_a:Vec<f32> = Vec::new();
|
||||
let mut data_b:Vec<f32> = Vec::new();
|
||||
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_a.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_a.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_b.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_b.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let (longer, shorter) =
|
||||
(
|
||||
match data_a.len() > data_b.len()
|
||||
{
|
||||
true => (data_a, data_b),
|
||||
false => (data_b, data_a),
|
||||
}
|
||||
);
|
||||
|
||||
let mut output_data_fwd:Vec<f32> = Vec::with_capacity(longer.len()+shorter.len());
|
||||
let mut output_data_rev:Vec<f32> = Vec::with_capacity(longer.len()+shorter.len());
|
||||
for offset in 0..=longer.len()+shorter.len()
|
||||
{
|
||||
let mut fwd_sum:f32 = 0.0;
|
||||
let mut rev_sum:f32 = 0.0;
|
||||
for (fi, ri) in
|
||||
(
|
||||
0..shorter.len()
|
||||
).zip(
|
||||
(0..shorter.len()).rev()
|
||||
) {
|
||||
fwd_sum += shorter[fi] * longer.get(offset+fi-shorter.len()).unwrap_or(&0.0);
|
||||
rev_sum += shorter[ri] * longer.get(offset+fi-shorter.len()).unwrap_or(&0.0);
|
||||
}
|
||||
output_data_fwd.push(fwd_sum);
|
||||
output_data_rev.push(rev_sum);
|
||||
}
|
||||
|
||||
let mut output_buffer_fwd:Vec<u8> = Vec::with_capacity(output_data_fwd.len()*4);
|
||||
for &point in output_data_fwd.iter()
|
||||
{
|
||||
output_buffer_fwd.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
let mut output_buffer_rev:Vec<u8> = Vec::with_capacity(output_data_rev.len()*4);
|
||||
for &point in output_data_rev.iter()
|
||||
{
|
||||
output_buffer_rev.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
output_fwd.write_all(&output_buffer_fwd).unwrap();
|
||||
output_rev.write_all(&output_buffer_rev).unwrap();
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("correlate_cyclic <input_a.dat> <input_b.dat> <output_forward.dat> <output_reverse.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input_a = File::open(&argv[1]).unwrap();
|
||||
let mut input_b = File::open(&argv[2]).unwrap();
|
||||
let mut output_fwd = File::create(&argv[3]).unwrap();
|
||||
let mut output_rev = File::create(&argv[4]).unwrap();
|
||||
|
||||
let mut data_a:Vec<f32> = Vec::new();
|
||||
let mut data_b:Vec<f32> = Vec::new();
|
||||
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_a.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_a.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_b.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_b.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let (longer, shorter) =
|
||||
(
|
||||
match data_a.len() > data_b.len()
|
||||
{
|
||||
true => (data_a, data_b),
|
||||
false => (data_b, data_a),
|
||||
}
|
||||
);
|
||||
|
||||
let mut output_data_fwd:Vec<f32> = Vec::with_capacity(longer.len()*2);
|
||||
let mut output_data_rev:Vec<f32> = Vec::with_capacity(longer.len()*2);
|
||||
for offset in -(shorter.len() as isize)..=(longer.len() as isize)
|
||||
{
|
||||
let mut fwd_sum:f32 = 0.0;
|
||||
let mut rev_sum:f32 = 0.0;
|
||||
for (fi, ri) in
|
||||
(
|
||||
0..(shorter.len() as isize)
|
||||
).zip(
|
||||
(0..(shorter.len() as isize)).rev()
|
||||
) {
|
||||
fwd_sum += shorter[fi as usize] * longer[((offset+fi+(longer.len() as isize)) % (longer.len() as isize)) as usize];
|
||||
rev_sum += shorter[ri as usize] * longer[((offset+fi+(longer.len() as isize)) % (longer.len() as isize)) as usize];
|
||||
}
|
||||
output_data_fwd.push(fwd_sum);
|
||||
output_data_rev.push(rev_sum);
|
||||
}
|
||||
|
||||
let mut output_buffer_fwd:Vec<u8> = Vec::with_capacity(output_data_fwd.len()*4);
|
||||
for &point in output_data_fwd.iter()
|
||||
{
|
||||
output_buffer_fwd.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
let mut output_buffer_rev:Vec<u8> = Vec::with_capacity(output_data_rev.len()*4);
|
||||
for &point in output_data_rev.iter()
|
||||
{
|
||||
output_buffer_rev.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
output_fwd.write_all(&output_buffer_fwd).unwrap();
|
||||
output_rev.write_all(&output_buffer_rev).unwrap();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#[allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: dcst <input.dat> <sine-output.dat> <cos-output.dat");
|
||||
return;
|
||||
}
|
||||
let mut signal_file = File::open(&argv[1]).unwrap();
|
||||
let mut iout_file = File::create(&argv[2]).unwrap();
|
||||
let mut qout_file = File::create(&argv[3]).unwrap();
|
||||
|
||||
let mut signal:Vec<f32> = Vec::new();
|
||||
let mut signal_data:Vec<u8> = Vec::new();
|
||||
signal_file.read_to_end(&mut signal_data).unwrap();
|
||||
for chunk in signal_data.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
signal.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
let size = signal.len();
|
||||
|
||||
let mut iout:Vec<f32> = Vec::with_capacity(size);
|
||||
let mut qout:Vec<f32> = Vec::with_capacity(size);
|
||||
for n in 0..size
|
||||
{
|
||||
let mut new_i:f32 = 0.0;
|
||||
let mut new_q:f32 = 0.0;
|
||||
for t in 0..size
|
||||
{
|
||||
let (ti, tq) = (-1.0*(n as f32)*PI*(t as f32) / (size as f32)).sin_cos();
|
||||
new_i += signal[t] * ti;
|
||||
new_q += signal[t] * tq;
|
||||
}
|
||||
new_i /= (size as f32).sqrt();
|
||||
new_q /= (size as f32).sqrt();
|
||||
iout.push(new_i);
|
||||
qout.push(new_q);
|
||||
}
|
||||
|
||||
let mut iout_data:Vec<u8> = Vec::with_capacity(size*4);
|
||||
let mut qout_data:Vec<u8> = Vec::with_capacity(size*4);
|
||||
for n in 0..size
|
||||
{
|
||||
iout_data.extend_from_slice(&iout[n].to_be_bytes());
|
||||
qout_data.extend_from_slice(&qout[n].to_be_bytes());
|
||||
}
|
||||
|
||||
iout_file.write_all(&iout_data).unwrap();
|
||||
qout_file.write_all(&qout_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: differentiate <input.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input = File::open(&argv[1]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mut output:Vec<f32> = Vec::with_capacity(data.len());
|
||||
for i in 1..data.len()
|
||||
{
|
||||
output.push(data[i] - data[i-1]);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(output.len() * 4);
|
||||
for &point in output.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
/*
|
||||
echospace - rudimentary 3D echo simulator
|
||||
|
||||
accepts a signal, a transmitter position, a receiver position, and a scene matrix,
|
||||
and emits a simulated received signal
|
||||
|
||||
! limitations !
|
||||
- assumes transmitter and receiver to be isotropically radiant/sensitive
|
||||
- does not simulate:
|
||||
-- occlusion (hard)
|
||||
-- secondary reflections (computationally intensive)
|
||||
-- any frequency-dependent effects (needs frequency-domain operations, currently not implemented)
|
||||
-- any nonlinear effects (significance unknown)
|
||||
*/
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn distance
|
||||
(
|
||||
a: &[f32;3],
|
||||
b: &[f32;3],
|
||||
) ->
|
||||
f32
|
||||
{
|
||||
(
|
||||
(a[0] - b[0]).powi(2)
|
||||
+
|
||||
(a[1] - b[1]).powi(2)
|
||||
+
|
||||
(a[2] - b[2]).powi(2)
|
||||
).sqrt()
|
||||
}
|
||||
|
||||
fn skew
|
||||
(
|
||||
input: &[f32],
|
||||
output: &mut [f32],
|
||||
offset: f32,
|
||||
) {
|
||||
let skew_factor:f32 = offset.fract();
|
||||
let output_slice:&mut [f32] = &mut output[0..input.len()+1];
|
||||
for (i_point,o_point) in (input.iter()).zip(output_slice.iter_mut())
|
||||
{
|
||||
*o_point += i_point * (1.0 - skew_factor);
|
||||
*o_point += i_point * skew_factor;
|
||||
}
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 7
|
||||
{
|
||||
eprintln!("usage: echospace <txx>,<txy>,<txz> <rxx>,<rxy>,<rxz> <wave_speed> <input_signal.dat> <scene.3da> <output_signal.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
// argument type description
|
||||
// <txx>,<txy>,<txz> f32,f32,f32 coordinates of the transmitter relative to the scene volume
|
||||
// <rxx>,<rxy>,<rxz> f32,f32,f32 coordinates of the receiver relative to the scene volume
|
||||
// <wavespeed> f32 speed of the wave in the medium, in points (3D scene coordinates) per signal sample
|
||||
// <input_signal.dat> dat file signal emitted by the transmitter
|
||||
// <scene.3da> 3da file 3D array file defining a reflectivity value at every point in the simulated volume (see 3da file definition)
|
||||
// <output_signal.dat> dat file simulated signal detected by the receiver
|
||||
|
||||
let txpos_string:String = argv[1].clone();
|
||||
let rxpos_string:String = argv[2].clone();
|
||||
let wave_speed:f32 = argv[3].parse::<f32>().unwrap();
|
||||
let mut input_file = File::open(&argv[4]).unwrap();
|
||||
let mut scene_file = File::open(&argv[5]).unwrap();
|
||||
let mut output_file = File::create(&argv[6]).unwrap();
|
||||
|
||||
let mut txpos:[f32;3] = [0.0, 0.0, 0.0];
|
||||
let txpos_comps:Vec<&str> = txpos_string.split(',').collect();
|
||||
for n in 0..3
|
||||
{
|
||||
txpos[n] = txpos_comps[n].parse::<f32>().unwrap();
|
||||
}
|
||||
|
||||
let mut rxpos:[f32;3] = [0.0, 0.0, 0.0];
|
||||
let rxpos_comps:Vec<&str> = rxpos_string.split(',').collect();
|
||||
for n in 0..3
|
||||
{
|
||||
rxpos[n] = rxpos_comps[n].parse::<f32>().unwrap();
|
||||
}
|
||||
|
||||
let mut input_data:Vec<u8> = Vec::new();
|
||||
input_file.read_to_end(&mut input_data).unwrap();
|
||||
let mut input_signal:Vec<f32> = Vec::with_capacity(input_data.len()/4);
|
||||
for chunk in input_data.chunks_exact(4)
|
||||
{
|
||||
let mut sample_bytes:[u8;4] = [0x00; 4];
|
||||
sample_bytes.copy_from_slice(chunk);
|
||||
input_signal.push(f32::from_be_bytes(sample_bytes));
|
||||
}
|
||||
|
||||
let mut scene_xsize_bytes:[u8;4] = [0x00; 4];
|
||||
let mut scene_ysize_bytes:[u8;4] = [0x00; 4];
|
||||
let mut scene_zsize_bytes:[u8;4] = [0x00; 4];
|
||||
let mut scene_data:Vec<u8> = Vec::new();
|
||||
scene_file.read_exact(&mut scene_xsize_bytes).unwrap();
|
||||
scene_file.read_exact(&mut scene_ysize_bytes).unwrap();
|
||||
scene_file.read_exact(&mut scene_zsize_bytes).unwrap();
|
||||
scene_file.read_to_end(&mut scene_data).unwrap();
|
||||
let scene_xsize:usize = u32::from_be_bytes(scene_xsize_bytes) as usize;
|
||||
let scene_ysize:usize = u32::from_be_bytes(scene_ysize_bytes) as usize;
|
||||
let scene_zsize:usize = u32::from_be_bytes(scene_zsize_bytes) as usize;
|
||||
let mut scene:Vec<f32> = Vec::with_capacity(scene_data.len()/4);
|
||||
for chunk in scene_data.chunks_exact(4)
|
||||
{
|
||||
let mut point_bytes:[u8;4] = [0x00; 4];
|
||||
point_bytes.copy_from_slice(chunk);
|
||||
scene.push(f32::from_be_bytes(point_bytes));
|
||||
}
|
||||
// scene[x][y][z] => scene[z*scene_ysize*scene_xsize + y*scene_xsize + x]
|
||||
// scene[n] => scene[n % scene_xsize][(n/scene_xsize) % scene_ysize][n/(scene_xsize*scene_ysize)]
|
||||
|
||||
let scene_corners:[[f32;3];8] =
|
||||
[
|
||||
[0.0 , 0.0 , 0.0 ],
|
||||
[scene_xsize as f32, 0.0 , 0.0 ],
|
||||
[0.0 , scene_ysize as f32, 0.0 ],
|
||||
[scene_xsize as f32, scene_ysize as f32, 0.0 ],
|
||||
[0.0 , 0.0 , scene_zsize as f32],
|
||||
[scene_xsize as f32, 0.0 , scene_zsize as f32],
|
||||
[0.0 , scene_ysize as f32, scene_zsize as f32],
|
||||
[scene_xsize as f32, scene_ysize as f32, scene_zsize as f32],
|
||||
];
|
||||
let mut max_flight_dist:f32 = 0.0;
|
||||
for corner in scene_corners.iter()
|
||||
{
|
||||
max_flight_dist = max_flight_dist.max(distance(corner,&txpos) + distance(corner,&rxpos));
|
||||
}
|
||||
let max_flight_time = max_flight_dist*wave_speed;
|
||||
eprintln!("maximum simulated path length: {} points", max_flight_dist.ceil());
|
||||
eprintln!("maximum simulated path time: {} samples", max_flight_time.ceil());
|
||||
|
||||
let mut output_signal:Vec<f32> = vec![0.0; (max_flight_time.floor() as usize) + input_signal.len()+1];
|
||||
|
||||
let mut skewed_input:Vec<f32> = vec![0.0; input_signal.len()+1];
|
||||
|
||||
let tx_rx_flight_dist:f32 = distance(&txpos, &rxpos);
|
||||
let tx_rx_flight_time:f32 = tx_rx_flight_dist*wave_speed;
|
||||
let tx_rx_flight_samples:usize = tx_rx_flight_time.floor() as usize;
|
||||
skew(&input_signal, &mut skewed_input, tx_rx_flight_time.fract());
|
||||
for (i_point, o_point) in
|
||||
(
|
||||
skewed_input.iter()
|
||||
).zip(
|
||||
output_signal
|
||||
[
|
||||
tx_rx_flight_samples
|
||||
..
|
||||
tx_rx_flight_samples + skewed_input.len()
|
||||
].iter_mut()
|
||||
) {
|
||||
*o_point += i_point;
|
||||
}
|
||||
|
||||
for x in 0..scene_xsize {
|
||||
for y in 0..scene_ysize {
|
||||
for z in 0..scene_zsize {
|
||||
let pointpos:[f32;3] = [x as f32, y as f32, z as f32];
|
||||
let flight_dist:f32 = distance(&pointpos, &txpos) + distance(&pointpos, &rxpos);
|
||||
let flight_time:f32 = flight_dist*wave_speed;
|
||||
let attenuation:f32 = tx_rx_flight_dist.powi(2) / flight_dist.powi(2);
|
||||
let sample_skew:f32 = flight_time.fract();
|
||||
let flight_samples:usize = flight_time.floor() as usize;
|
||||
let reflectivity:f32 = scene[z*scene_ysize*scene_xsize + y*scene_xsize + x];
|
||||
|
||||
skew(&input_signal, &mut skewed_input, sample_skew);
|
||||
|
||||
for (i_point, o_point) in
|
||||
(
|
||||
skewed_input.iter()
|
||||
).zip(
|
||||
output_signal
|
||||
[
|
||||
flight_samples
|
||||
..
|
||||
flight_samples + skewed_input.len()
|
||||
].iter_mut()
|
||||
) {
|
||||
*o_point += reflectivity * attenuation * i_point;
|
||||
}
|
||||
}}}
|
||||
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(output_signal.len()*4);
|
||||
for point in output_signal.iter()
|
||||
{
|
||||
output_data.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
// write the output file
|
||||
output_file.write_all(&output_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 6
|
||||
{
|
||||
eprintln!("no. <length> <start> <stop> <sin.dat> <cos.dat>");
|
||||
return;
|
||||
}
|
||||
let length = argv[1].parse::<usize>().unwrap();
|
||||
let start_freq = argv[2].parse::<f32>().unwrap();
|
||||
let stop_freq = argv[3].parse::<f32>().unwrap();
|
||||
let mut sin_file = File::create(&argv[4]).unwrap();
|
||||
let mut cos_file = File::create(&argv[5]).unwrap();
|
||||
|
||||
let delta_freq =
|
||||
(
|
||||
(stop_freq - start_freq)
|
||||
/
|
||||
(
|
||||
2.0
|
||||
*
|
||||
(length as f32)
|
||||
)
|
||||
);
|
||||
let mut sin_buffer:Vec<u8> = Vec::with_capacity(length * 4);
|
||||
let mut cos_buffer:Vec<u8> = Vec::with_capacity(length * 4);
|
||||
for i in 0..length
|
||||
{
|
||||
sin_buffer.extend_from_slice(
|
||||
&(
|
||||
(i as f32) * PI
|
||||
*
|
||||
(start_freq + delta_freq * (i as f32))
|
||||
/
|
||||
(length as f32)
|
||||
)
|
||||
.sin()
|
||||
.to_be_bytes()
|
||||
);
|
||||
cos_buffer.extend_from_slice(
|
||||
&(
|
||||
(i as f32) * PI
|
||||
*
|
||||
(start_freq + delta_freq * (i as f32))
|
||||
/
|
||||
(length as f32)
|
||||
)
|
||||
.cos()
|
||||
.to_be_bytes()
|
||||
);
|
||||
}
|
||||
sin_file.write_all(&sin_buffer).unwrap();
|
||||
cos_file.write_all(&cos_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn gauss
|
||||
(
|
||||
freq: f32,
|
||||
input: f32,
|
||||
) ->
|
||||
f32
|
||||
{
|
||||
let stdev = (2.0 * PI * freq).recip();
|
||||
(
|
||||
-0.5 *
|
||||
(
|
||||
input
|
||||
/
|
||||
stdev
|
||||
)
|
||||
.powi(2)
|
||||
)
|
||||
.exp()
|
||||
/
|
||||
(
|
||||
(2.0 * PI).sqrt()
|
||||
*
|
||||
stdev
|
||||
)
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: gfilter <frequency> <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let filter_frequency =
|
||||
(
|
||||
argv[1]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 2000.0
|
||||
);
|
||||
|
||||
let mut input_file = File::open(&argv[2]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_file.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mut filtered_data:Vec<f32> = Vec::with_capacity(data.len());
|
||||
for shift in 0..data.len()
|
||||
{
|
||||
let mut data_sum:f32 = 0.0;
|
||||
for x in 0..data.len()
|
||||
{
|
||||
data_sum +=
|
||||
(
|
||||
data[x]
|
||||
*
|
||||
gauss(
|
||||
filter_frequency,
|
||||
(x as f32) - (shift as f32)
|
||||
)
|
||||
);
|
||||
}
|
||||
filtered_data.push(data_sum);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(filtered_data.len() * 4);
|
||||
for &point in filtered_data.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: integrate <input.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input = File::open(&argv[1]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mut integrated:Vec<f32> = Vec::with_capacity(data.len());
|
||||
let mut val:f32 = 0.0;
|
||||
for i in 0..data.len()
|
||||
{
|
||||
val += data[i];
|
||||
integrated.push(val);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(integrated.len() * 4);
|
||||
for &point in integrated.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// mulmix
|
||||
// combines an arbitrary number of input files together into a single output file by multiplication, with optional input level control.
|
||||
// behaves like a ring modulator or RF mixer.
|
||||
// CPU demands: low.
|
||||
// Memory requirements: 3x size of longest input file.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() < 4
|
||||
{
|
||||
eprintln!("usage: mulmix <output.dat> <input_1.dat>:<level> <input_2.dat>:<level> ...");
|
||||
return;
|
||||
}
|
||||
|
||||
let output_filename = argv[1].clone();
|
||||
let mut output_file = match File::create(&argv[1])
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("mulmix: could not open/create specified output file {} for writing: {}", argv[1], reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(file) => file,
|
||||
};
|
||||
let mut input_files:Vec<(File,String,f32)> = Vec::new();
|
||||
for i in 2..argv.len()
|
||||
{
|
||||
let mut new_filename = argv[i].clone();
|
||||
let mut new_level:f32 = 1.0;
|
||||
if let Some((filename,levelstring)) = argv[i].rsplit_once(':')
|
||||
{
|
||||
if let Ok(val) = levelstring.parse::<f32>()
|
||||
{
|
||||
// decide how to handle the filename:level vs just filename syntax by testing to see if what's after the colon
|
||||
// actually looks like a number. if it doesn't, silently assume that the whole thing was supposed to be a colon-containing filename.
|
||||
// if this wasn't the case, and the user meant to type a number, then they should be able to understand what happened from the
|
||||
// "file not found" error message.
|
||||
new_level = val;
|
||||
new_filename = filename.to_string();
|
||||
}
|
||||
}
|
||||
let new_file = match File::open(&new_filename)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("mulmix: could not open specified input file {} for reading: {}", new_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(file) => file,
|
||||
};
|
||||
input_files.push((new_file, new_filename, new_level));
|
||||
}
|
||||
|
||||
let mut output_samples:Vec<f32> = Vec::new();
|
||||
|
||||
for (file, filename, level) in input_files.iter_mut()
|
||||
{
|
||||
let mut file_data:Vec<u8> = Vec::new();
|
||||
match file.read_to_end(&mut file_data)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("mulmix: could not read from input file {}: {}", filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
let file_length:usize = file_data.len()/4;
|
||||
for _ in 0..file_length.saturating_sub(output_samples.len())
|
||||
{
|
||||
output_samples.push(1.0);
|
||||
}
|
||||
|
||||
for (t,chunk) in file_data.chunks_exact(4).enumerate()
|
||||
{
|
||||
let mut sample_bytes:[u8;4] = [0x00; 4];
|
||||
sample_bytes.copy_from_slice(chunk);
|
||||
|
||||
output_samples[t] *= *level * f32::from_be_bytes(sample_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(output_samples.len() * 4);
|
||||
for sample in output_samples.iter()
|
||||
{
|
||||
output_data.extend_from_slice(&sample.to_be_bytes());
|
||||
}
|
||||
match output_file.write_all(&output_data)
|
||||
{
|
||||
Err(reason) =>
|
||||
{
|
||||
eprintln!("mulmix: could not write to output file {}: {}", output_filename, reason);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
use std::process::exit;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 2
|
||||
{
|
||||
eprintln!("no. arguments: <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input = File::open(&argv[1]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mut abs_max:f32 = 0.0;
|
||||
for &point in data.iter()
|
||||
{
|
||||
if point.is_normal()
|
||||
{
|
||||
abs_max = abs_max.max(point.abs());
|
||||
}
|
||||
}
|
||||
|
||||
if
|
||||
(
|
||||
abs_max < std::f32::MIN_POSITIVE
|
||||
) {
|
||||
eprintln!("cannot normalize {}! it may be empty, it may contain one or more infinite or NaN values, or it may contain only values that are zero or nearly zero.", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for point in data.iter_mut()
|
||||
{
|
||||
*point /= abs_max;
|
||||
}
|
||||
|
||||
let mut output = File::create(&argv[1]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(data.len() * 4);
|
||||
for &point in data.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("usage: orthosine <wavenumber> <size> <output_i.dat> <output_q.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let wavenumber = argv[1].parse::<f32>().unwrap();
|
||||
let size = argv[2].parse::<usize>().unwrap();
|
||||
|
||||
let mut idata = Vec::with_capacity(size);
|
||||
let mut qdata = Vec::with_capacity(size);
|
||||
|
||||
for t in 0..size
|
||||
{
|
||||
let (i,q) = (wavenumber * PI * (t as f32) / (size as f32)).sin_cos();
|
||||
idata.append(&mut i.to_be_bytes().to_vec());
|
||||
qdata.append(&mut q.to_be_bytes().to_vec());
|
||||
}
|
||||
|
||||
let mut output_i = File::create(&argv[3]).unwrap();
|
||||
let mut output_q = File::create(&argv[4]).unwrap();
|
||||
|
||||
output_i.write_all(&idata).unwrap();
|
||||
output_q.write_all(&qdata).unwrap();
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("usage: pad <pre> <post> <input.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let pre = argv[1].parse::<usize>().unwrap();
|
||||
let post = argv[2].parse::<usize>().unwrap();
|
||||
|
||||
let mut input = File::open(&argv[3]).unwrap();
|
||||
let mut input_data:Vec<u8> = Vec::new();
|
||||
input.read_to_end(&mut input_data).unwrap();
|
||||
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(input_data.len() + pre*4 + post*4);
|
||||
for _ in 0..pre
|
||||
{
|
||||
output_data.extend_from_slice(&0.0f32.to_be_bytes());
|
||||
}
|
||||
output_data.append(&mut input_data);
|
||||
for _ in 0..post
|
||||
{
|
||||
output_data.extend_from_slice(&0.0f32.to_be_bytes());
|
||||
}
|
||||
|
||||
let mut output = File::create(&argv[4]).unwrap();
|
||||
output.write_all(&output_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 2
|
||||
{
|
||||
eprintln!("usage: peakdetect <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input_file = File::open(&argv[1]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_file.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mut filtered_data:Vec<f32> = Vec::with_capacity(data.len());
|
||||
filtered_data.push(0.0);
|
||||
for i in 1..data.len()-1
|
||||
{
|
||||
let mut upper_radius:usize = 1;
|
||||
let mut lower_radius:usize = 1;
|
||||
while i.saturating_add(upper_radius) < (data.len()-1) && data[i] == data[i+upper_radius]
|
||||
{
|
||||
upper_radius += 1;
|
||||
}
|
||||
while i.saturating_sub(lower_radius) > 0 && data[i] == data[i-lower_radius]
|
||||
{
|
||||
lower_radius += 1;
|
||||
}
|
||||
if
|
||||
(
|
||||
(data[i-lower_radius].abs() < data[i].abs() && data[i+upper_radius].abs() < data[i].abs())
|
||||
) {
|
||||
filtered_data.push(data[i]);
|
||||
} else {
|
||||
filtered_data.push(0.0);
|
||||
}
|
||||
}
|
||||
filtered_data.push(0.0);
|
||||
|
||||
let mut output_file = File::create(&argv[1]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(filtered_data.len() * 4);
|
||||
for &point in filtered_data.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::SeekFrom;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 2
|
||||
{
|
||||
eprintln!("no. arguments: <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut target =
|
||||
(
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.truncate(false)
|
||||
.create(false)
|
||||
.create_new(false)
|
||||
.open(&argv[1])
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
for i in 0..
|
||||
{
|
||||
let seek_try = target.seek(SeekFrom::Start(i * 4));
|
||||
if seek_try.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let read_try = target.read_exact(&mut buffer);
|
||||
if read_try.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[0] &= 0b0111_1111;
|
||||
|
||||
target.seek(SeekFrom::Start(i * 4)).unwrap();
|
||||
target.write_all(&buffer).unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn sinc
|
||||
(
|
||||
x: f32,
|
||||
) ->
|
||||
f32
|
||||
{
|
||||
if x.abs() > std::f32::MIN_POSITIVE
|
||||
{
|
||||
(PI*x).sin() / (PI*x)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: sfilter <frequency> <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
frequency (f32):
|
||||
the frequency of the sinc function from 0.0 to 1000.0, where 0.0 is zero and 1000.0 is half the sample rate (the Nyquist limit).
|
||||
*/
|
||||
|
||||
let filter_frequency =
|
||||
(
|
||||
argv[1]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 2000.0 // later on, this will need to be normalized so that 1.0 equals the sample rate, so we divide by 2000 so that an input of 1000 becomes 0.5
|
||||
);
|
||||
|
||||
let mut input_file = File::open(&argv[2]).unwrap();
|
||||
let mut input_buffer:Vec<u8> = Vec::new();
|
||||
input_file.read_to_end(&mut input_buffer).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
for chunk in input_buffer.chunks_exact(4)
|
||||
{
|
||||
let mut new_value:[u8;4] = [0x00;4];
|
||||
new_value.copy_from_slice(chunk);
|
||||
data.push(f32::from_be_bytes(new_value));
|
||||
}
|
||||
|
||||
let mut filtered_data:Vec<f32> = Vec::with_capacity(data.len());
|
||||
for shift in 0..data.len()
|
||||
{
|
||||
let s = shift as f32;
|
||||
|
||||
let mut convol_value:f32 = 0.0;
|
||||
for x in 0..data.len()
|
||||
{
|
||||
let t = x as f32;
|
||||
|
||||
convol_value +=
|
||||
(
|
||||
data[x]
|
||||
*
|
||||
2.0
|
||||
*
|
||||
filter_frequency
|
||||
*
|
||||
sinc(
|
||||
2.0
|
||||
*
|
||||
filter_frequency
|
||||
*
|
||||
(t - s)
|
||||
)
|
||||
);
|
||||
}
|
||||
filtered_data.push(convol_value);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(filtered_data.len() * 4);
|
||||
for &point in filtered_data.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
#[allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("usage: sft <i-input.dat> <q-input.dat> <i-output.dat> <q-output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut iout_file = File::create(&argv[3]).unwrap();
|
||||
let mut qout_file = File::create(&argv[4]).unwrap();
|
||||
|
||||
let mut isig_file = File::open(&argv[1]).unwrap();
|
||||
let mut isig_data:Vec<u8> = Vec::new();
|
||||
isig_file.read_to_end(&mut isig_data).unwrap();
|
||||
let mut isig:Vec<f32> = Vec::new();
|
||||
for chunk in isig_data.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
isig.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
let mut qsig_file = File::open(&argv[2]).unwrap();
|
||||
let mut qsig_data:Vec<u8> = Vec::new();
|
||||
qsig_file.read_to_end(&mut qsig_data).unwrap();
|
||||
let mut qsig:Vec<f32> = Vec::new();
|
||||
for chunk in qsig_data.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
qsig.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
let size = isig.len().min(qsig.len());
|
||||
|
||||
let mut iout:Vec<f32> = Vec::with_capacity(size);
|
||||
let mut qout:Vec<f32> = Vec::with_capacity(size);
|
||||
for n in 0..size
|
||||
{
|
||||
let mut new_i:f32 = 0.0;
|
||||
let mut new_q:f32 = 0.0;
|
||||
for t in 0..size
|
||||
{
|
||||
// (R+Bi)*(C+Di) = (AC-BD)+(AD+BC)i
|
||||
let (tq, ti) = (-2.0*(n as f32)*PI*(t as f32) / (size as f32)).sin_cos();
|
||||
new_i += isig[t]*ti - qsig[t]*tq;
|
||||
new_q += isig[t]*tq + qsig[t]*ti;
|
||||
}
|
||||
new_i /= (size as f32).sqrt();
|
||||
new_q /= (size as f32).sqrt();
|
||||
iout.push(new_i);
|
||||
qout.push(new_q);
|
||||
}
|
||||
|
||||
let mut idata:Vec<u8> = Vec::with_capacity(size*4);
|
||||
let mut qdata:Vec<u8> = Vec::with_capacity(size*4);
|
||||
for n in 0..size
|
||||
{
|
||||
idata.extend_from_slice(&iout[n].to_be_bytes());
|
||||
qdata.extend_from_slice(&qout[n].to_be_bytes());
|
||||
}
|
||||
|
||||
iout_file.write_all(&idata).unwrap();
|
||||
qout_file.write_all(&qdata).unwrap();
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: skew <offset‰> <mismatch‰> <target.dat>");
|
||||
return;
|
||||
}
|
||||
// offset: permil phase offset in transmission from receiver
|
||||
// mismatch: permil frequency error in transmission sample rate from receiver sample rate
|
||||
|
||||
let offset =
|
||||
(
|
||||
argv[1]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 1000.0
|
||||
);
|
||||
let mismatch =
|
||||
(
|
||||
argv[2]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
.max(-999.0) // no closer to 0 than 1 permil
|
||||
/ 1000.0
|
||||
);
|
||||
|
||||
let mut input = File::open(&argv[3]).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let output_length =
|
||||
(
|
||||
(
|
||||
data.len() as f32
|
||||
/
|
||||
(1.0 + mismatch)
|
||||
)
|
||||
as usize
|
||||
);
|
||||
|
||||
let mut result:Vec<f32> = Vec::with_capacity(output_length);
|
||||
for i in 0..output_length
|
||||
{
|
||||
let skew = offset + (i as f32) * mismatch;
|
||||
let shift = skew.floor() as usize;
|
||||
let upper_misalign = skew.fract();
|
||||
let lower_misalign = 1.0 - skew.fract();
|
||||
|
||||
result.push(
|
||||
upper_misalign * data[(i + shift + 1) % data.len()]
|
||||
+
|
||||
lower_misalign * data[(i + shift + 0) % data.len()]
|
||||
);
|
||||
}
|
||||
|
||||
let mut output = File::create(&argv[3]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(data.len() * 4);
|
||||
for &point in result.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: splitbits <input.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
// deal with command-line arguments first, before anything big happens
|
||||
let mut input_file = File::open(&argv[1]).unwrap();
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
|
||||
// read the input file
|
||||
let mut input_data:Vec<u8> = Vec::new();
|
||||
input_file.read_to_end(&mut input_data).unwrap();
|
||||
|
||||
// decode the input data
|
||||
let mut input:Vec<f32> = Vec::with_capacity(input_data.len()/4);
|
||||
for chunk in input_data.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
input.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
// actually do the processing
|
||||
let mut output:Vec<f32> = input;
|
||||
for point in output.iter_mut()
|
||||
{
|
||||
*point /= point.abs();
|
||||
}
|
||||
|
||||
// encode the output data
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(output.len()*4);
|
||||
for point in output.iter()
|
||||
{
|
||||
output_data.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
// write the output file
|
||||
output_file.write_all(&output_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("no. <iterations> <output.dat>");
|
||||
return;
|
||||
}
|
||||
let n = argv[1].parse::<usize>().unwrap();
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
for i in 0..n
|
||||
{
|
||||
for _ in 0..2*i
|
||||
{
|
||||
output_file.write_all(&-1.0f32.to_be_bytes()).unwrap();
|
||||
}
|
||||
for _ in 0..2*i+1
|
||||
{
|
||||
output_file.write_all(&1.0f32.to_be_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn sinc
|
||||
(
|
||||
x: f32,
|
||||
) ->
|
||||
f32
|
||||
{
|
||||
if x.abs() > std::f32::MIN_POSITIVE
|
||||
{
|
||||
(PI*x).sin() / (PI*x)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: swindow <low> <high> <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let freq_lo =
|
||||
(
|
||||
argv[1]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 2000.0
|
||||
);
|
||||
let freq_hi =
|
||||
(
|
||||
argv[2]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 2000.0
|
||||
);
|
||||
|
||||
let mut input_file = File::open(&argv[3]).unwrap();
|
||||
let mut input_buffer:Vec<u8> = Vec::new();
|
||||
input_file.read_to_end(&mut input_buffer).unwrap();
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
for chunk in input_buffer.chunks_exact(4)
|
||||
{
|
||||
let mut new_value:[u8;4] = [0x00;4];
|
||||
new_value.copy_from_slice(chunk);
|
||||
data.push(f32::from_be_bytes(new_value));
|
||||
}
|
||||
|
||||
let mut filtered_data:Vec<f32> = Vec::with_capacity(data.len());
|
||||
for shift in 0..data.len()
|
||||
{
|
||||
let s = shift as f32;
|
||||
|
||||
let mut value_hi:f32 = 0.0;
|
||||
let mut value_lo:f32 = 0.0;
|
||||
for x in 0..data.len()
|
||||
{
|
||||
let t = x as f32;
|
||||
|
||||
value_hi +=
|
||||
(
|
||||
data[x]
|
||||
*
|
||||
(2.0 * freq_hi)
|
||||
*
|
||||
sinc(
|
||||
(2.0 * freq_hi)
|
||||
*
|
||||
(t - s)
|
||||
)
|
||||
);
|
||||
|
||||
value_lo +=
|
||||
(
|
||||
data[x]
|
||||
*
|
||||
(2.0 * freq_lo)
|
||||
*
|
||||
sinc(
|
||||
(2.0 * freq_lo)
|
||||
*
|
||||
(t - s)
|
||||
)
|
||||
);
|
||||
|
||||
filtered_data.push(value_hi - value_lo);
|
||||
}
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[3]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(filtered_data.len() * 4);
|
||||
for &point in filtered_data.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 2
|
||||
{
|
||||
eprintln!("no. <input>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input_file = File::open(&argv[1]).unwrap();
|
||||
let mut file_string = String::new();
|
||||
input_file.read_to_string(&mut file_string).unwrap();
|
||||
|
||||
let mut data:Vec<f32> = Vec::new();
|
||||
|
||||
for nstr in file_string.split(|c| c == '\r' || c == '\n' || c == ' ')
|
||||
{
|
||||
if let Ok(n) = nstr.parse::<f32>()
|
||||
{
|
||||
data.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
let mut max_val = data[0];
|
||||
let mut min_val = data[0];
|
||||
|
||||
for &point in data.iter()
|
||||
{
|
||||
max_val = max_val.max(point);
|
||||
min_val = min_val.min(point);
|
||||
}
|
||||
|
||||
let abs_max = max_val.abs().max(min_val.abs());
|
||||
|
||||
for &unnorm_point in data.iter()
|
||||
{
|
||||
let light_point = (50.0 * unnorm_point.abs() / abs_max).round() as usize;
|
||||
let dark_point = 50 - light_point;
|
||||
if unnorm_point > 0.0
|
||||
{
|
||||
for _ in 0..50
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
//print!("┣");
|
||||
print!("█");
|
||||
for _ in 0..light_point
|
||||
{
|
||||
//print!("━");
|
||||
print!("█");
|
||||
}
|
||||
for _ in 0..dark_point
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
if unnorm_point < 0.0
|
||||
{
|
||||
for _ in 0..dark_point
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
for _ in 0..light_point
|
||||
{
|
||||
//print!("━");
|
||||
print!("█");
|
||||
}
|
||||
//print!("┫");
|
||||
print!("█");
|
||||
for _ in 0..50
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
if unnorm_point == 0.0
|
||||
{
|
||||
for _ in 0..50
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
print!("┃");
|
||||
for _ in 0..50
|
||||
{
|
||||
print!(" ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
const TAU:f32 = std::f32::consts::PI*2.0;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("no. <length> <start> <stop> <output.dat>");
|
||||
return;
|
||||
}
|
||||
let length = argv[1].parse::<usize>().unwrap();
|
||||
let start_freq = 1.0 / argv[2].parse::<f32>().unwrap();
|
||||
let stop_freq = 1.0 / argv[3].parse::<f32>().unwrap();
|
||||
let mut output_file = File::create(&argv[4]).unwrap();
|
||||
|
||||
let delta_freq = (stop_freq-start_freq) / (length as f32);
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(length * 4);
|
||||
for i in 0..length
|
||||
{
|
||||
output_buffer.extend_from_slice(
|
||||
&(
|
||||
(i as f32) * TAU
|
||||
/
|
||||
(start_freq + delta_freq * (i as f32))
|
||||
/
|
||||
(length as f32)
|
||||
)
|
||||
.sin()
|
||||
.to_be_bytes()
|
||||
)
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: unitvex <input.dat> <output_i.dat> <output_q.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut infile = File::open(&argv[1]).unwrap();
|
||||
let mut indata:Vec<u8> = Vec::new();
|
||||
infile.read_to_end(&mut indata).unwrap();
|
||||
|
||||
let mut input:Vec<f32> = Vec::with_capacity(indata.len()/4);
|
||||
for chunk in indata.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
input.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
let mut idata:Vec<u8> = Vec::with_capacity(indata.len());
|
||||
let mut qdata:Vec<u8> = Vec::with_capacity(indata.len());
|
||||
for point in input.iter()
|
||||
{
|
||||
let new_i = (2.0*PI*point).cos();
|
||||
let new_q = (2.0*PI*point).sin();
|
||||
idata.append(&mut new_i.to_be_bytes().to_vec());
|
||||
qdata.append(&mut new_q.to_be_bytes().to_vec());
|
||||
}
|
||||
|
||||
let mut outfile_i = File::create(&argv[2]).unwrap();
|
||||
let mut outfile_q = File::create(&argv[3]).unwrap();
|
||||
outfile_i.write_all(&idata).unwrap();
|
||||
outfile_q.write_all(&qdata).unwrap();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: upsample <factor> <input.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
// deal with command-line arguments first, before anything big happens
|
||||
let factor = argv[1].parse::<usize>().unwrap();
|
||||
let mut input_file = File::open(&argv[2]).unwrap();
|
||||
let mut output_file = File::create(&argv[3]).unwrap();
|
||||
|
||||
// read the input file
|
||||
let mut input_data:Vec<u8> = Vec::new();
|
||||
input_file.read_to_end(&mut input_data).unwrap();
|
||||
|
||||
// decode the input data
|
||||
let mut input:Vec<f32> = Vec::with_capacity(input_data.len()/4);
|
||||
for chunk in input_data.chunks_exact(4)
|
||||
{
|
||||
let mut samplebuffer:[u8;4] = [0x00; 4];
|
||||
samplebuffer.copy_from_slice(chunk);
|
||||
input.push(f32::from_be_bytes(samplebuffer));
|
||||
}
|
||||
|
||||
// actually do the processing
|
||||
let output_size = (input.len()-1)*factor;
|
||||
let mut output:Vec<f32> = Vec::with_capacity(output_size);
|
||||
let ffac = factor as f32;
|
||||
for t in 0..input.len()-1
|
||||
{
|
||||
for iu in 0..factor
|
||||
{
|
||||
let u = iu as f32;
|
||||
output.push(
|
||||
input[t] * (ffac-u)/ffac
|
||||
+
|
||||
input[t+1] * u/ffac
|
||||
);
|
||||
}
|
||||
}
|
||||
output.push(input[input.len()-1]);
|
||||
|
||||
// encode the output data
|
||||
let mut output_data:Vec<u8> = Vec::with_capacity(output.len()*4);
|
||||
for point in output.iter()
|
||||
{
|
||||
output_data.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
|
||||
// write the output file
|
||||
output_file.write_all(&output_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 4
|
||||
{
|
||||
eprintln!("usage: vecsum <input_a.dat> <input_b.dat> <output.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut input_a = File::open(&argv[1]).unwrap();
|
||||
let mut data_a:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_a.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_a.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
let mut input_b = File::open(&argv[2]).unwrap();
|
||||
let mut data_b:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_b.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_b.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
|
||||
let mixed_len =
|
||||
(
|
||||
data_a.len()
|
||||
).max(
|
||||
data_b.len()
|
||||
);
|
||||
|
||||
let mut data_mixed:Vec<f32> = Vec::with_capacity(mixed_len);
|
||||
for i in 0..mixed_len
|
||||
{
|
||||
data_mixed.push(
|
||||
(
|
||||
(*data_a.get(i).unwrap_or(&0.0)).powi(2)
|
||||
+
|
||||
(*data_b.get(i).unwrap_or(&0.0)).powi(2)
|
||||
).sqrt()
|
||||
);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[3]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(mixed_len * 4);
|
||||
for &point in data_mixed.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
#[allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn bmp_header
|
||||
(
|
||||
image_width: usize, image_height: usize
|
||||
) ->
|
||||
Vec<u8>
|
||||
{
|
||||
let file_width = image_width*image_height + 1078;
|
||||
let mut header:Vec<u8> = Vec::with_capacity(1078);
|
||||
header.extend_from_slice(&"BM".as_bytes()); // bimp...
|
||||
header.extend_from_slice(&(file_width as u32).to_le_bytes()); // filewidth
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // forbidden field
|
||||
header.extend_from_slice(&1078u32.to_le_bytes()); // data offset
|
||||
|
||||
header.extend_from_slice(&40u32.to_le_bytes()); // infoheader width
|
||||
header.extend_from_slice(&(image_width as u32).to_le_bytes()); // pixel width
|
||||
header.extend_from_slice(&(image_height as u32).to_le_bytes()); // pixel height
|
||||
header.extend_from_slice(&1u16.to_le_bytes()); // number of planes
|
||||
header.extend_from_slice(&8u16.to_le_bytes()); // bits per pixel
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // compression type
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // compressed image width
|
||||
header.extend_from_slice(&1024u32.to_le_bytes()); // horizontal pixels/meter
|
||||
header.extend_from_slice(&1024u32.to_le_bytes()); // vertical pixels/meter
|
||||
header.extend_from_slice(&256u32.to_le_bytes()); // colors used
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // "number of important colors"
|
||||
|
||||
assert_eq!(header.len(), 54);
|
||||
|
||||
for i in 0x00..=0xFF
|
||||
{
|
||||
header.push(i); // red
|
||||
header.push(i); // green
|
||||
header.push(i); // blue
|
||||
header.push(0x00); // ?????
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 5
|
||||
{
|
||||
eprintln!("usage: waterfall <slice width> <gain> <output.bmp> <input.dat>");
|
||||
return;
|
||||
}
|
||||
let width = argv[1].parse::<usize>().unwrap();
|
||||
let gain = argv[2].parse::<f32>().unwrap();
|
||||
let mut plot_file = File::create(&argv[3]).unwrap();
|
||||
let mut signal_file = File::open(&argv[4]).unwrap();
|
||||
let mut signal_data:Vec<u8> = Vec::new();
|
||||
signal_file.read_to_end(&mut signal_data).unwrap();
|
||||
|
||||
let mut image_output:Vec<Vec<u8>> = Vec::new();
|
||||
let mut global_peak:f32 = 0.0;
|
||||
for data_slice in signal_data.chunks_exact(width*4)
|
||||
{
|
||||
let mut signal:Vec<f32> = Vec::with_capacity(width);
|
||||
for chunk in data_slice.chunks_exact(4)
|
||||
{
|
||||
let mut sample_bytes:[u8;4] = [0x00; 4];
|
||||
sample_bytes.copy_from_slice(chunk);
|
||||
signal.push(f32::from_be_bytes(sample_bytes));
|
||||
}
|
||||
|
||||
let mut image_line:Vec<u8> = Vec::with_capacity(width);
|
||||
for n in 0..width
|
||||
{
|
||||
let mut new_i:f32 = 0.0;
|
||||
let mut new_q:f32 = 0.0;
|
||||
for t in 0..width
|
||||
{
|
||||
let (tq, ti) = (-2.0*(n as f32)*PI*(t as f32) / (width as f32)).sin_cos();
|
||||
new_i += signal[t] * ti;
|
||||
new_q += signal[t] * tq;
|
||||
}
|
||||
new_i /= (width as f32).sqrt();
|
||||
new_q /= (width as f32).sqrt();
|
||||
let new_power:f32 = gain*(new_i.powi(2) + new_q.powi(2)).sqrt();
|
||||
global_peak = global_peak.max(new_power);
|
||||
let new_pixel:u8 = (new_power*255.0).floor().min(255.0).max(0.0) as u8;
|
||||
image_line.push(new_pixel);
|
||||
}
|
||||
image_output.push(image_line);
|
||||
}
|
||||
|
||||
let height = image_output.len();
|
||||
|
||||
eprintln!("width: {}", width);
|
||||
eprintln!("height: {}", height);
|
||||
eprintln!("max value: {}", global_peak);
|
||||
|
||||
let mut image_data:Vec<u8> = Vec::with_capacity(height*width+1078);
|
||||
image_data.extend_from_slice(&bmp_header(width, height));
|
||||
for line in image_output.iter_mut().rev()
|
||||
{
|
||||
image_data.append(line);
|
||||
}
|
||||
plot_file.write_all(&image_data).unwrap();
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
const RAND_DEVICE:&str = "/dev/urandom";
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 3
|
||||
{
|
||||
eprintln!("usage: worsen <SNR‰> <target.dat>");
|
||||
return;
|
||||
}
|
||||
|
||||
let snr =
|
||||
(
|
||||
(
|
||||
argv[1]
|
||||
.parse::<f32>()
|
||||
.unwrap()
|
||||
/ 1000.0
|
||||
)
|
||||
.max(0.0)
|
||||
.min(1.0)
|
||||
);
|
||||
|
||||
let mut input_signal = File::open(&argv[2]).unwrap();
|
||||
let mut data_signal:Vec<f32> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
match input_signal.read_exact(&mut buffer)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => break,
|
||||
};
|
||||
data_signal.push(f32::from_be_bytes(buffer));
|
||||
}
|
||||
let mut input_noise = File::open(&RAND_DEVICE).unwrap();
|
||||
let mut data_noise:Vec<f32> = Vec::new();
|
||||
for _ in 0..data_signal.len()
|
||||
{
|
||||
let mut buffer:[u8;4] = [0x00;4];
|
||||
input_noise.read_exact(&mut buffer).unwrap();
|
||||
data_noise.push(
|
||||
(
|
||||
(i32::from_be_bytes(buffer) as f64)
|
||||
/
|
||||
(i32::max_value() as f64)
|
||||
)
|
||||
as f32
|
||||
);
|
||||
}
|
||||
|
||||
let signal_level = snr;
|
||||
let noise_level = 1.0 - snr;
|
||||
|
||||
let mut data_mixed:Vec<f32> = Vec::with_capacity(data_signal.len());
|
||||
for i in 0..data_signal.len()
|
||||
{
|
||||
data_mixed.push(
|
||||
signal_level * data_signal[i]
|
||||
+
|
||||
noise_level * data_noise[i]
|
||||
);
|
||||
}
|
||||
|
||||
let mut output_file = File::create(&argv[2]).unwrap();
|
||||
let mut output_buffer:Vec<u8> = Vec::with_capacity(data_signal.len() * 4);
|
||||
for &point in data_mixed.iter()
|
||||
{
|
||||
output_buffer.extend_from_slice(&point.to_be_bytes());
|
||||
}
|
||||
output_file.write_all(&output_buffer).unwrap();
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#![allow(unused_parens)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::env::args;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
if argv.len() != 6
|
||||
{
|
||||
eprintln!("usage: zadoffchu <u> <q> <length> <i.dat> <q.dat>");
|
||||
return;
|
||||
}
|
||||
let param_u = argv[1].parse::<usize>().unwrap();
|
||||
let param_q = argv[2].parse::<usize>().unwrap();
|
||||
let param_l = argv[3].parse::<usize>().unwrap();
|
||||
let mut sin_file = File::create(&argv[4]).unwrap();
|
||||
let mut cos_file = File::create(&argv[5]).unwrap();
|
||||
|
||||
let u = param_u as f32;
|
||||
let q = param_q as f32;
|
||||
let l = param_l as f32;
|
||||
let cf = l % 2.0;
|
||||
|
||||
let mut sin_buffer:Vec<u8> = Vec::with_capacity(param_l * 4);
|
||||
let mut cos_buffer:Vec<u8> = Vec::with_capacity(param_l * 4);
|
||||
for i in 0..param_l
|
||||
{
|
||||
let t = i as f32;
|
||||
sin_buffer.extend_from_slice(
|
||||
&(
|
||||
(
|
||||
-1.0*PI*u*t
|
||||
) * (
|
||||
t + cf + 2.0*q
|
||||
) / (
|
||||
l
|
||||
)
|
||||
)
|
||||
.cos()
|
||||
.to_be_bytes()
|
||||
);
|
||||
cos_buffer.extend_from_slice(
|
||||
&(
|
||||
(
|
||||
-1.0*PI*u*t
|
||||
) * (
|
||||
t + cf + 2.0*q
|
||||
) / (
|
||||
l
|
||||
)
|
||||
)
|
||||
.sin()
|
||||
.to_be_bytes()
|
||||
);
|
||||
}
|
||||
sin_file.write_all(&sin_buffer).unwrap();
|
||||
cos_file.write_all(&cos_buffer).unwrap();
|
||||
}
|
Loading…
Reference in New Issue