extern crate basic_dsp;
extern crate rand;
extern crate num;
pub mod tools;
#[cfg(feature = "slow_test")]
mod slow_test {
use basic_dsp::*;
use basic_dsp::conv_types::*;
use basic_dsp::window_functions::*;
use tools::*;
use std::os::raw::c_void;
#[test]
fn compare_conv_freq_multiplication_for_rc() {
for iteration in 0 .. 3 {
let a = create_data_even(201602211, iteration, 1001, 2000);
let delta = create_delta(201602212, iteration);
let time = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let fun: RaisedCosineFunction<f32> = RaisedCosineFunction::new(0.35);
let freq = time.clone().fft().unwrap();
let points = time.points();
let ratio = create_delta(20160229, iteration).abs() / 10.0; let time_res = time.convolve(&fun as &RealImpulseResponse<f32>, ratio, points).unwrap();
let freq_res = freq.multiply_frequency_response(&fun as &RealFrequencyResponse<f32>, 1.0 / ratio).unwrap();
let ifreq_res = freq_res.ifft().unwrap();
let left = &ifreq_res.data();
let right = &time_res.data();
assert_vector_eq_with_reason_and_tolerance(&left, &right, 0.2, "Results should match independent if done in time or frequency domain");
}
}
#[test]
fn compare_conv_freq_multiplication_for_sinc() {
for iteration in 0 .. 3 {
let a = create_data_even(201602214, iteration, 2001, 4000);
let delta = create_delta(201602215, iteration);
let time = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let fun: SincFunction<f32> = SincFunction::new();
let freq = time.clone().fft().unwrap();
let points = time.points();
let ratio = create_delta(201602216, iteration).abs() / 20.0 + 0.5; let time_res = time.convolve(&fun as &RealImpulseResponse<f32>, ratio, points).unwrap();
let freq_res = freq.multiply_frequency_response(&fun as &RealFrequencyResponse<f32>, 1.0 / ratio).unwrap();
let ifreq_res = freq_res.ifft().unwrap();
let left = &ifreq_res.data();
let right = &time_res.data();
assert_vector_eq_with_reason_and_tolerance(&left, &right, 0.3, "Results should match independent if done in time or frequency domain");
}
}
#[test]
fn compare_optimized_and_non_optimized_conv() {
for iteration in 0 .. 3 {
let offset = 2e-6;
let a = create_data_even(201602217, iteration, 2002, 4000);
let b = create_data_even(201602218, iteration, 50, 202);
let delta = create_delta(201602219, iteration);
let time = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let fun: RaisedCosineFunction<f32> = RaisedCosineFunction::new(0.35);
let ratio = iteration as f32 + 1.0;
let left = time.clone().convolve(&fun as &RealImpulseResponse<f32>, ratio, b.len()).unwrap();
let right = time.convolve(&fun as &RealImpulseResponse<f32>, ratio + offset, b.len()).unwrap();
assert_vector_eq_with_reason_and_tolerance(&left.data(), &right.data(), 0.1, "Results should match independent if done in optimized or non optimized code branch");
}
}
#[test]
fn compare_vector_conv_freq_multiplication() {
for iteration in 0 .. 3 {
let a = create_data_even(201601171, iteration, 502, 1000);
let b = create_data_with_len(201601172, iteration, a.len());
let delta = create_delta(201601173, iteration);
let time1 = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let time2 = ComplexTimeVector32::from_interleaved_with_delta(&b, delta);
let left = time1.clone().convolve_vector(&time2).unwrap();
let freq1 = time1.fft().unwrap();
let freq2 = time2.fft().unwrap();
let freq_res = freq1.multiply_vector(&freq2).unwrap();
let right = freq_res.ifft().unwrap();
assert_vector_eq_with_reason_and_tolerance(
left.data(),
&conv_swap(right.data())[0..left.len()],
0.2,
"Results should match independent if done in time or frequency domain");
}
}
#[test]
fn compare_smaller_vector_conv_with_zero_padded_conv() {
for iteration in 0 .. 3 {
let a = create_data_even(201601174, iteration, 1002, 2000);
let b = create_data_even(201601175, iteration, 50, 202);
let delta = create_delta(201601176, iteration);
let time1 = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let time2 = ComplexTimeVector32::from_interleaved_with_delta(&b, delta);
let left = time1.clone().convolve_vector(&time2).unwrap();
let time2 = ComplexTimeVector32::from_interleaved_with_delta(&conv_zero_pad(&b, time1.len(), true), delta);
let right = time1.convolve_vector(&time2).unwrap();
assert_vector_eq_with_reason_and_tolerance(
left.data(),
right.data(),
0.2,
"Results should match independent if done with a smaller vector or with a zero padded vector of the same size");
}
}
#[test]
fn compare_smaller_vector_conv_with_zero_padded_conv_real() {
for iteration in 0 .. 3 {
let a = create_data_even(201601177, iteration, 1002, 2000);
let b = create_data_even(201601178, iteration, 50, 202);
let delta = create_delta(201601179, iteration);
let time1 = RealTimeVector32::from_array_with_delta(&a, delta);
let time2 = RealTimeVector32::from_array_with_delta(&b, delta);
let left = time1.clone().convolve_vector(&time2).unwrap();
let time2 = RealTimeVector32::from_array_with_delta(&conv_zero_pad(&b, time1.len(), false), delta);
let right = time1.convolve_vector(&time2).unwrap();
assert_vector_eq_with_reason_and_tolerance(
left.data(),
right.data(),
0.2,
"Results should match independent if done with a smaller vector or with a zero padded vector of the same size");
}
}
fn conv_zero_pad(data: &[f32], len: usize, is_complex: bool) -> Vec<f32> {
if is_complex {
let mut result = vec![0.0; len];
let points = len / 2;
let data_points = data.len() / 2;
let diff = points - data_points;
let left = diff - diff / 2;
for i in 0..data.len() {
result[2 * left + i] = data[i];
}
result
}
else {
let mut result = vec![0.0; len];
let data_len = data.len();
let diff = len - data_len;
let left = diff - diff / 2;
for i in 0..data.len() {
result[left + i] = data[i];
}
result
}
}
fn conv_swap(data: &[f32]) -> Vec<f32> {
let len = data.len();
let mut result = vec![0.0; len];
let points = len / 2;
let half = 2 *(points / 2 + 1);
for i in 0..len {
if i < half {
result[i] = data[len - half + i];
}
else if i >= len - half {
result[i] = data[i - half];
}
else {
result[i] = data[i];
}
}
result
}
fn unsym_triag_window() -> ForeignWindowFunction<f32> {
ForeignWindowFunction {
window_data: 0,
window_function: call_triag,
is_symmetric: false
}
}
extern fn call_triag(_arg: *const c_void, i: usize, points: usize) -> f32 {
let triag: &WindowFunction<f32> = &TriangularWindow;
triag.window(i, points)
}
fn unsym_rc_mul() -> ForeignRealConvolutionFunction<f32> {
ForeignRealConvolutionFunction {
conv_data: 0,
conv_function: call_freq_rc,
is_symmetric: false
}
}
extern fn call_freq_rc(_arg: *const c_void, x: f32) -> f32 {
let rc: &RealFrequencyResponse<f32> = &RaisedCosineFunction::new(0.35);
rc.calc(x)
}
#[test]
fn compare_sym_optimized_window_with_normal_version() {
parameterized_vector_test(|iteration, range| {
let a = create_data_even(20160116, iteration, range.start, range.end);
let delta = create_delta(201601161, iteration);
let time = ComplexTimeVector32::from_interleaved_with_delta(&a, delta);
let triag_sym = TriangularWindow;
let triag_unsym = unsym_triag_window();
let result_sym = time.clone().apply_window(&triag_sym).unwrap();
let result_unsym = time.apply_window(&triag_unsym).unwrap();
let left = &result_sym.data();
let right = &result_unsym.data();
assert_vector_eq_with_reason_and_tolerance(&left, &right, 1e-2, "Results should match with or without symmetry optimization");
});
}
#[test]
fn compare_sym_optimized_freq_mul_with_normal_version() {
parameterized_vector_test(|iteration, range| {
let a = create_data_even(201601162, iteration, range.start, range.end);
let delta = create_delta(201601163, iteration);
let freq = ComplexFreqVector32::from_interleaved_with_delta(&a, delta);
let rc_sym: RaisedCosineFunction<f32> = RaisedCosineFunction::new(0.35);
let rc_unsym = unsym_rc_mul();
let ratio = create_delta(201601164, iteration).abs() / 20.0 + 0.5; let result_sym = freq.clone().multiply_frequency_response(&rc_sym as &RealFrequencyResponse<f32>, 1.0 / ratio).unwrap();
let result_unsym = freq.multiply_frequency_response(&rc_unsym as &RealFrequencyResponse<f32>, 1.0 / ratio).unwrap();
let left = &result_sym.data();
let right = &result_unsym.data();
assert_vector_eq_with_reason_and_tolerance(&left, &right, 1e-2, "Results should match with or without symmetry optimization");
});
}
}