#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "fixed-point")]
use fixed::{types::*, FixedI32, FixedI64};
use core::fmt;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed32<const FRAC: u32> {
raw: i32,
}
impl<const FRAC: u32> Fixed32<FRAC> {
pub const ZERO: Self = Self { raw: 0 };
pub const ONE: Self = Self { raw: 1 << FRAC };
pub const MIN: Self = Self { raw: i32::MIN };
pub const MAX: Self = Self { raw: i32::MAX };
#[inline]
pub const fn from_raw(raw: i32) -> Self {
Self { raw }
}
#[inline]
pub const fn to_raw(self) -> i32 {
self.raw
}
#[inline]
pub const fn from_int(value: i32) -> Self {
Self { raw: value << FRAC }
}
#[inline]
pub const fn to_int(self) -> i32 {
self.raw >> FRAC
}
#[inline]
pub fn from_float(value: f32) -> Self {
let scale = (1u64 << FRAC) as f32;
Self {
raw: (value * scale) as i32,
}
}
#[inline]
pub fn to_float(self) -> f32 {
let scale = (1u64 << FRAC) as f32;
self.raw as f32 / scale
}
#[inline]
pub const fn abs(self) -> Self {
Self {
raw: self.raw.abs(),
}
}
pub fn sqrt(self) -> Self {
if self.raw <= 0 {
return Self::ZERO;
}
let scaled = (self.raw as u64) << FRAC;
let bits = 64 - scaled.leading_zeros();
let mut result: u64 = 1u64 << ((bits + 1) / 2);
for _ in 0..32 {
if result == 0 {
break;
}
let next = (result + scaled / result) >> 1;
if next >= result {
break; }
result = next;
}
Self { raw: result as i32 }
}
#[inline]
pub const fn saturating_add(self, rhs: Self) -> Self {
Self {
raw: self.raw.saturating_add(rhs.raw),
}
}
#[inline]
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self {
raw: self.raw.saturating_sub(rhs.raw),
}
}
#[inline]
pub fn saturating_mul(self, rhs: Self) -> Self {
let result = (self.raw as i64 * rhs.raw as i64) >> FRAC;
Self {
raw: result.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
}
}
}
impl<const FRAC: u32> Add for Fixed32<FRAC> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
raw: self.raw + rhs.raw,
}
}
}
impl<const FRAC: u32> AddAssign for Fixed32<FRAC> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.raw += rhs.raw;
}
}
impl<const FRAC: u32> Sub for Fixed32<FRAC> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
raw: self.raw - rhs.raw,
}
}
}
impl<const FRAC: u32> SubAssign for Fixed32<FRAC> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.raw -= rhs.raw;
}
}
impl<const FRAC: u32> Mul for Fixed32<FRAC> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
let result = (self.raw as i64 * rhs.raw as i64) >> FRAC;
Self { raw: result as i32 }
}
}
impl<const FRAC: u32> MulAssign for Fixed32<FRAC> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const FRAC: u32> Div for Fixed32<FRAC> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
let result = ((self.raw as i64) << FRAC) / rhs.raw as i64;
Self { raw: result as i32 }
}
}
impl<const FRAC: u32> DivAssign for Fixed32<FRAC> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<const FRAC: u32> Neg for Fixed32<FRAC> {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self { raw: -self.raw }
}
}
impl<const FRAC: u32> Default for Fixed32<FRAC> {
fn default() -> Self {
Self::ZERO
}
}
impl<const FRAC: u32> fmt::Display for Fixed32<FRAC> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_float())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed64<const FRAC: u32> {
raw: i64,
}
impl<const FRAC: u32> Fixed64<FRAC> {
pub const ZERO: Self = Self { raw: 0 };
pub const ONE: Self = Self { raw: 1 << FRAC };
#[inline]
pub const fn from_raw(raw: i64) -> Self {
Self { raw }
}
#[inline]
pub const fn to_raw(self) -> i64 {
self.raw
}
#[inline]
pub const fn from_int(value: i64) -> Self {
Self { raw: value << FRAC }
}
#[inline]
pub const fn to_int(self) -> i64 {
self.raw >> FRAC
}
#[inline]
pub fn from_float(value: f32) -> Self {
let scale = (1u64 << FRAC) as f32;
Self {
raw: (value * scale) as i64,
}
}
#[inline]
pub fn from_double(value: f64) -> Self {
let scale = (1u64 << FRAC) as f64;
Self {
raw: (value * scale) as i64,
}
}
#[inline]
pub fn to_float(self) -> f32 {
let scale = (1u64 << FRAC) as f32;
self.raw as f32 / scale
}
#[inline]
pub fn to_double(self) -> f64 {
let scale = (1u64 << FRAC) as f64;
self.raw as f64 / scale
}
}
impl<const FRAC: u32> Add for Fixed64<FRAC> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
raw: self.raw + rhs.raw,
}
}
}
impl<const FRAC: u32> Sub for Fixed64<FRAC> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
raw: self.raw - rhs.raw,
}
}
}
impl<const FRAC: u32> Mul for Fixed64<FRAC> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
let result = (self.raw as i128 * rhs.raw as i128) >> FRAC;
Self { raw: result as i64 }
}
}
impl<const FRAC: u32> Div for Fixed64<FRAC> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
let result = ((self.raw as i128) << FRAC) / rhs.raw as i128;
Self { raw: result as i64 }
}
}
impl<const FRAC: u32> Default for Fixed64<FRAC> {
fn default() -> Self {
Self::ZERO
}
}
impl<const FRAC: u32> fmt::Display for Fixed64<FRAC> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_double())
}
}
pub mod types {
use super::*;
pub type Q16 = Fixed32<16>;
pub type Q24 = Fixed32<24>;
pub type Q8 = Fixed32<8>;
pub type Q32 = Fixed64<32>;
}
pub mod math {
use super::*;
pub fn sin<const FRAC: u32>(x: Fixed32<FRAC>) -> Fixed32<FRAC> {
let pi = Fixed32::<FRAC>::from_float(core::f32::consts::PI);
let two_pi = Fixed32::<FRAC>::from_float(2.0 * core::f32::consts::PI);
let mut angle = x;
while angle > pi {
angle -= two_pi;
}
while angle < -pi {
angle += two_pi;
}
let x2 = angle * angle;
let x3 = x2 * angle;
let x5 = x3 * x2;
let x7 = x5 * x2;
let term1 = angle;
let term2 = x3 / Fixed32::<FRAC>::from_int(6); let term3 = x5 / Fixed32::<FRAC>::from_int(120); let term4 = x7 / Fixed32::<FRAC>::from_int(5040);
term1 - term2 + term3 - term4
}
pub fn cos<const FRAC: u32>(x: Fixed32<FRAC>) -> Fixed32<FRAC> {
let half_pi = Fixed32::<FRAC>::from_float(core::f32::consts::FRAC_PI_2);
sin(x + half_pi)
}
pub fn exp<const FRAC: u32>(x: Fixed32<FRAC>) -> Fixed32<FRAC> {
let mut result = Fixed32::<FRAC>::ONE;
let mut term = Fixed32::<FRAC>::ONE;
for n in 1..10 {
term = term * x / Fixed32::<FRAC>::from_int(n);
result += term;
if term.abs().to_raw().abs() < 4 {
break;
}
}
result
}
pub fn ln<const FRAC: u32>(x: Fixed32<FRAC>) -> Fixed32<FRAC> {
if x.to_raw() <= 0 {
return Fixed32::<FRAC>::MIN;
}
let one = Fixed32::<FRAC>::ONE;
let numerator = x - one;
let denominator = x + one;
let y = numerator / denominator;
let y2 = y * y;
let term1 = y;
let term2 = (y * y2) / Fixed32::<FRAC>::from_int(3);
let term3 = (y * y2 * y2) / Fixed32::<FRAC>::from_int(5);
Fixed32::<FRAC>::from_int(2) * (term1 + term2 + term3)
}
}
pub mod signal {
use super::*;
pub struct FirFilter<const FRAC: u32, const N: usize> {
coeffs: [Fixed32<FRAC>; N],
buffer: [Fixed32<FRAC>; N],
index: usize,
}
impl<const FRAC: u32, const N: usize> FirFilter<FRAC, N> {
pub fn new(coeffs: &[Fixed32<FRAC>; N]) -> Self {
Self {
coeffs: *coeffs,
buffer: [Fixed32::<FRAC>::ZERO; N],
index: 0,
}
}
pub fn process(&mut self, input: Fixed32<FRAC>) -> Fixed32<FRAC> {
self.buffer[self.index] = input;
self.index = (self.index + 1) % N;
let mut output = Fixed32::<FRAC>::ZERO;
for i in 0..N {
let buf_idx = (self.index + N - 1 - i) % N;
output += self.coeffs[i] * self.buffer[buf_idx];
}
output
}
pub fn reset(&mut self) {
self.buffer = [Fixed32::<FRAC>::ZERO; N];
self.index = 0;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed32_basic() {
let a = Fixed32::<16>::from_float(core::f32::consts::PI);
let b = Fixed32::<16>::from_float(2.0);
let sum = a + b;
assert!((sum.to_float() - (core::f32::consts::PI + 2.0)).abs() < 0.01);
let product = a * b;
assert!((product.to_float() - core::f32::consts::TAU).abs() < 0.01);
}
#[test]
fn test_fixed32_sqrt() {
let x = Fixed32::<16>::from_float(4.0);
let sqrt_x = x.sqrt();
assert!((sqrt_x.to_float() - 2.0).abs() < 0.1);
}
#[test]
fn test_fixed64_precision() {
let a = Fixed64::<32>::from_double(std::f64::consts::PI);
let b = Fixed64::<32>::from_double(2.0);
let product = a * b;
assert!((product.to_double() - std::f64::consts::TAU).abs() < 1e-9);
}
#[test]
fn test_math_functions() {
use math::*;
let x = Fixed32::<16>::from_float(0.0);
let sin_x = sin(x);
assert!((sin_x.to_float() - 0.0).abs() < 0.01);
let half_pi = Fixed32::<16>::from_float(core::f32::consts::FRAC_PI_2);
let sin_half_pi = sin(half_pi);
assert!((sin_half_pi.to_float() - 1.0).abs() < 0.1);
}
#[test]
fn test_fir_filter() {
use signal::*;
let coeffs = [Fixed32::<16>::from_float(0.2); 5];
let mut filter = FirFilter::new(&coeffs);
let input = Fixed32::<16>::from_float(1.0);
let output = filter.process(input);
assert!(output.to_float() > 0.0);
}
}