+//! FFT and RDFT implementation.
use std::f32::{self, consts};
use std::ops::{Not, Neg, Add, AddAssign, Sub, SubAssign, Mul, MulAssign};
use std::fmt;
+/// Complex number.
#[repr(C)]
#[derive(Debug,Clone,Copy,PartialEq)]
pub struct FFTComplex {
+ /// Real part of the numner.
pub re: f32,
+ /// Complex part of the number.
pub im: f32,
}
impl FFTComplex {
+ /// Calculates `exp(i * val)`.
pub fn exp(val: f32) -> Self {
FFTComplex { re: val.cos(), im: val.sin() }
}
+ /// Returns `-Im + i * Re`.
pub fn rotate(self) -> Self {
FFTComplex { re: -self.im, im: self.re }
}
+ /// Multiplies complex number by scalar.
pub fn scale(self, scale: f32) -> Self {
FFTComplex { re: self.re * scale, im: self.im * scale }
}
}
}
+/// Complex number with zero value.
pub const FFTC_ZERO: FFTComplex = FFTComplex { re: 0.0, im: 0.0 };
+/// Calculates forward or inverse FFT in the straightforward way.
pub fn generic_fft(data: &mut [FFTComplex], forward: bool) {
let mut tmp = Vec::with_capacity(data.len());
tmp.resize(data.len(), FFTC_ZERO);
let size = 1 << bits;
let mut table = Vec::with_capacity(size);
for _ in 0..4 { table.push(FFTC_ZERO); }
- for b in 3..(bits+1) {
+ for b in 3..=bits {
let qsize = (1 << (b - 2)) as usize;
let base = -consts::PI / ((qsize * 2) as f32);
for k in 0..qsize {
}
}
+/// FFT working context.
pub struct FFT {
perms: Vec<usize>,
swaps: Vec<usize>,
}
impl FFT {
+ /// Calculates Fourier transform.
pub fn do_fft(&mut self, src: &[FFTComplex], dst: &mut [FFTComplex]) {
for k in 0..src.len() { dst[k] = src[self.perms[k]]; }
self.do_fft_core(dst);
}
+ /// Calculates inverse Fourier transform.
pub fn do_ifft(&mut self, src: &[FFTComplex], dst: &mut [FFTComplex]) {
for k in 0..src.len() { dst[k] = src[self.perms[k]]; }
self.do_ifft_core(dst);
}
+ /// Performs inplace FFT.
pub fn do_fft_inplace(&mut self, data: &mut [FFTComplex]) {
for idx in 0..self.swaps.len() {
let nidx = self.swaps[idx];
if idx != nidx {
- let t = data[nidx];
- data[nidx] = data[idx];
- data[idx] = t;
+ data.swap(nidx, idx);
}
}
self.do_fft_core(data);
}
+ /// Performs inplace inverse FFT.
pub fn do_ifft_inplace(&mut self, data: &mut [FFTComplex]) {
for idx in 0..self.swaps.len() {
let nidx = self.swaps[idx];
if idx != nidx {
- let t = data[nidx];
- data[nidx] = data[idx];
- data[idx] = t;
+ data.swap(nidx, idx);
}
}
self.do_ifft_core(data);
}
}
+/// [`FFT`] context creator.
+///
+/// [`FFT`]: ./struct.FFT.html
pub struct FFTBuilder {
}
gen_sr_perms(&mut swaps[3*size/4..], size/4);
}
-fn gen_swaps_for_perm(swaps: &mut Vec<usize>, perms: &Vec<usize>) {
+fn gen_swaps_for_perm(swaps: &mut Vec<usize>, perms: &[usize]) {
let mut idx_arr: Vec<usize> = Vec::with_capacity(perms.len());
for i in 0..perms.len() { idx_arr.push(i); }
let mut run_size = 0;
}
}
}
+ /// Constructs a new `FFT` context.
pub fn new_fft(size: usize, forward: bool) -> FFT {
let mut ffts: Vec<(FFTMode, FFTData)> = Vec::with_capacity(1);
let mut perms: Vec<usize> = Vec::with_capacity(size);
for (mode, _) in ffts.iter().rev() {
mode.permute(&mut perms);
}
- gen_swaps_for_perm(&mut swaps, &perms);
+ gen_swaps_for_perm(&mut swaps, perms.as_slice());
FFT { perms, swaps, ffts }
}
}
+/// RDFT working context.
pub struct RDFT {
table: Vec<FFTComplex>,
fft: FFT,
fwd_fft: bool,
}
-fn crossadd(a: &FFTComplex, b: &FFTComplex) -> FFTComplex {
+fn crossadd(a: FFTComplex, b: FFTComplex) -> FFTComplex {
FFTComplex { re: a.re + b.re, im: a.im - b.im }
}
impl RDFT {
+ /// Calculates RDFT.
pub fn do_rdft(&mut self, src: &[FFTComplex], dst: &mut [FFTComplex]) {
dst.copy_from_slice(src);
self.do_rdft_inplace(dst);
}
+ /// Calculates inplace RDFT.
pub fn do_rdft_inplace(&mut self, buf: &mut [FFTComplex]) {
if !self.fwd {
for n in 0..self.size/2 {
let in0 = buf[n + 1];
let in1 = buf[self.size - n - 1];
- let t0 = crossadd(&in0, &in1);
+ let t0 = crossadd(in0, in1);
let t1 = FFTComplex { re: in1.im + in0.im, im: in1.re - in0.re };
let tab = self.table[n];
let t2 = FFTComplex { re: t1.im * tab.im + t1.re * tab.re, im: t1.im * tab.re - t1.re * tab.im };
let in0 = buf[n + 1];
let in1 = buf[self.size - n - 1];
- let t0 = crossadd(&in0, &in1).scale(0.5);
+ let t0 = crossadd(in0, in1).scale(0.5);
let t1 = FFTComplex { re: in0.im + in1.im, im: in0.re - in1.re };
let t2 = t1 * self.table[n];
- buf[n + 1] = crossadd(&t0, &t2);
- buf[self.size - n - 1] = FFTComplex { re: t0.re - t2.re, im: -(t0.im + t2.im) };
+ buf[n + 1] = crossadd(t0, t2);
+ buf[self.size - n - 1] = FFTComplex { re: t0.re - t2.re, im: -(t0.im + t2.im) };
}
let a = buf[0].re;
let b = buf[0].im;
}
}
+/// [`RDFT`] context creator.
+///
+/// [`RDFT`]: ./struct.FFT.html
pub struct RDFTBuilder {
}
impl RDFTBuilder {
+ /// Constructs a new `RDFT` context.
pub fn new_rdft(size: usize, forward: bool, forward_fft: bool) -> RDFT {
let mut table: Vec<FFTComplex> = Vec::with_capacity(size / 4);
let (base, scale) = if forward { (consts::PI / (size as f32), 0.5) } else { (-consts::PI / (size as f32), 1.0) };