cossin: don't cancel -1 phase offset

master
Robert Jördens 2021-03-01 13:29:59 +01:00
parent cd289687ea
commit 0d6b297d4e
2 changed files with 22 additions and 17 deletions

View File

@ -26,14 +26,16 @@ fn write_cossin_table() {
const AMPLITUDE: f64 = u16::MAX as f64;
for i in 0..(1 << DEPTH) {
// use midpoint samples to save one entry in the LUT
let phase = (PI / 4. / (1 << DEPTH) as f64) * (i as f64 + 0.5);
// add one bit accuracy to cos due to 0.5 < cos(z) <= 1 for |z| < pi/4
let cos = ((phase.cos() - 0.5) * 2. * AMPLITUDE).round() as u32 - 1;
let sin = (phase.sin() * AMPLITUDE).round() as u32;
if i % 4 == 0 {
write!(file, "\n ").unwrap();
}
// Use midpoint samples to save one entry in the LUT
let (sin, cos) =
(PI / 4. * ((i as f64 + 0.5) / (1 << DEPTH) as f64)).sin_cos();
// Add one bit accuracy to cos due to 0.5 < cos(z) <= 1 for |z| < pi/4
// The -1 LSB is cancelled when unscaling with the biased half amplitude
let cos = ((cos * 2. - 1.) * AMPLITUDE - 1.).round() as u32;
let sin = (sin * AMPLITUDE).round() as u32;
write!(file, " {},", cos + (sin << 16)).unwrap();
}
writeln!(file, "\n];").unwrap();

View File

@ -11,7 +11,7 @@ include!(concat!(env!("OUT_DIR"), "/cossin_table.rs"));
///
/// # Returns
/// The cos and sin values of the provided phase as a `(i32, i32)`
/// tuple. With a 7-bit deep LUT there is 9e-6 max and 6e-8 RMS error
/// tuple. With a 7-bit deep LUT there is 9e-6 max and 4e-6 RMS error
/// in each quadrature over 20 bit phase.
pub fn cossin(phase: i32) -> (i32, i32) {
// Phase bits excluding the three highest MSB
@ -40,11 +40,14 @@ pub fn cossin(phase: i32) -> (i32, i32) {
// The phase values used for the LUT are at midpoint for the truncated phase.
// Interpolate relative to the LUT entry midpoint.
// Also cancel the -1 bias that was conditionally introduced above.
phase -= (1 << (ALIGN_MSB - 1)) - (octant & 1) as i32;
phase -= 1 << (ALIGN_MSB - 1);
// Cancel the -1 bias that was conditionally introduced above.
// This lowers the DC spur from 2e-8 to 2e-10 magnitude.
// phase += (octant & 1) as i32;
// Fixed point pi/4.
const PI4: i32 = (PI / 4. * (1 << 16) as f64) as i32;
const PI4: i32 = (PI / 4. * (1 << 16) as f64) as _;
// No rounding bias necessary here since we keep enough low bits.
let dphi = (phase * PI4) >> 16;
@ -123,8 +126,8 @@ mod tests {
max_err.0 = max_err.0.max(err.0.abs());
max_err.1 = max_err.1.max(err.1.abs());
}
rms_err.0 /= MAX_PHASE;
rms_err.1 /= MAX_PHASE;
rms_err.0 /= (1 << PHASE_DEPTH) as f64;
rms_err.1 /= (1 << PHASE_DEPTH) as f64;
println!("sum: {:.2e} {:.2e}", sum.0, sum.1);
println!("demod: {:.2e} {:.2e}", demod.0, demod.1);
@ -133,18 +136,18 @@ mod tests {
println!("max: {:.2e} {:.2e}", max_err.0, max_err.1);
assert!(sum.0.abs() < 4e-10);
assert!(sum.1.abs() < 4e-10);
assert!(sum.1.abs() < 3e-8);
assert!(demod.0.abs() < 4e-10);
assert!(demod.1.abs() < 4e-10);
assert!(demod.1.abs() < 1e-8);
assert!(sum_err.0.abs() < 4e-10);
assert!(sum_err.1.abs() < 4e-10);
assert!(rms_err.0.sqrt() < 6e-8);
assert!(rms_err.1.sqrt() < 6e-8);
assert!(rms_err.0.sqrt() < 4e-6);
assert!(rms_err.1.sqrt() < 4e-6);
assert!(max_err.0 < 9e-6);
assert!(max_err.1 < 9e-6);
assert!(max_err.0 < 1e-5);
assert!(max_err.1 < 1e-5);
}
}