moonbit-dsp

    A zero-dependency, pure-MoonBit digital signal processing library: FFT, convolution, FIR/IIR filters, windowing, waveform generation and statistics.

    dsp
    signal-processing
    fft
    digital-filter
    spectrum
    audio
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    15

    No dependencies

    #moonbit-dsp

    moonbit-dsp 是一个零外部依赖、纯 MoonBit 实现的通用数字信号处理(DSP)基础库。

    它提供了一组可独立复用、互相组合的底层能力:复数运算、FFT/DCT 变换(含 DFT 与迭代 radix-2 FFT)、卷积与相关、FIR/IIR 滤波器设计、窗函数、波形生成(确定性白噪声等)、频谱分析与信号统计。所有代码均为纯 MoonBit 编写,不依赖任何第三方包,moon check 零警告、39 个单元测试全部通过(含与 O(N²) DFT 对照的 FFT 一致性验证、Parseval 定理守恒验证、卷积交换律与已知结果对照、滤波器频率响应验证、主导频率检测端到端验证)。

    它是音频、振动、传感器、通信、金融时序等各类信号处理应用可以直接引用的基础构件——这正是其通用性与复用价值所在。


    #为什么做这个库

    • 通用性强:不绑定任何具体业务场景。FFT、卷积、滤波、窗函数、统计是几乎所有信号处理任务的公共底层,可以被音频工具、科学计算、嵌入式数据分析等任何项目复用。
    • 高技术含量:实现了教科书级算法并验证正确性——radix-2 迭代 FFT(比特反转 + 蝶形)、窗口法 FIR 设计(DC 增益归一)、双线性变换 Butterworth IIR 设计、半谱幅值谱/功率谱(含非 DC/Nyquist bin 幅值补偿)。
    • 零依赖、纯 MoonBit:可直接作为他人项目的依赖引入,验证了 MoonBit 标准库表达数值算法与工程代码的能力。
    • 质量可验证:39 项单元测试覆盖数学性质(Parseval 定理、卷积交换律、窗对称性、噪声确定性)与数值对照(FFT vs DFT、滤波器频响、主导频率检测)。


    #快速开始

    #1. 作为依赖引入

    在 moon.mod 中声明依赖(发布后可 moon add 2227057182/moonbit-dsp),然后在 moon.pkg 中导入:

    import { "2227057182/moonbit-dsp", }

    #2. 一段最小示例

    fn main { // 生成 8000 Hz 采样率、频率 100 Hz、时长 0.25 s 的正弦波 let x = @moonbit-dsp.sine_wave(2000, 100.0, 8000.0) // 加 Hann 窗抑制频谱泄漏 let w = @moonbit-dsp.apply_window(x, @moonbit-dsp.WindowType::Hann) // 计算幅值谱,找到主导频率 let (freq, mag) = @moonbit-dsp.dominant_frequency(w, 8000.0) println("dominant frequency: \{freq.to_string()} Hz, magnitude: \{mag.to_string()}") }

    #3. 运行测试

    moon test

    预期输出:Total tests: 39, passed: 39, failed: 0.

    #4. 命令行演示程序

    仓库附带一个零依赖 CLI 演示(cmd/main),走「波形生成 → 可选 FIR 低通 → 可选加窗 → FFT 频谱分析」完整链路:

    # 正弦波频谱分析(打印前 3 个谱峰 + 主导频率 + 统计量) moon run cmd/main -- --wave sine --freq 50 --stats # 白噪声频谱(确定性 LCG,种子固定) moon run cmd/main -- --wave noise --seed 42 --peaks 2 # 锯齿波经过 FIR 低通(归一化截止 0.1)+ Hann 窗后的频谱 moon run cmd/main -- --wave sawtooth --freq 100 --lowpass 0.1 --window hann --peaks 3

    全部选项见 moon run cmd/main -- --help。


    #模块总览

    模块文件提供的功能
    Complexcomplex.mbt复数运算(加/减/乘/除、共轭、模、相位)
    FFTfft.mbt迭代 radix-2 FFT、O(N²) DFT 参考实现、2 的幂对齐、FFT 与 DFT 一致性
    Convconv.mbt线性卷积、互相关、自相关
    Windowwindow.mbt矩形/Hann/Hamming/Blackman 窗、加窗
    Filterfilter.mbt窗函数法 FIR 低通设计、一阶 IIR 低通、二阶 Butterworth IIR 低通(双线性变换)
    Waveformwaveform.mbt正弦/方波/三角波/锯齿波/确定性白噪声(LCG 64)
    Spectrumspectrum.mbt幅值谱/功率谱(半谱 + 幅值补偿)、主导频率、峰值查找、Top-K
    Statsstats.mbt均值、方差、标准差、RMS、能量、最大绝对值、归一化


    #API 说明

    #复数 @moonbit-dsp.Complex

    Complex::new(re : Double, im : Double) -> Complex Complex::zero() / Complex::one() add / sub / mul / div / conj / abs / phase / scale

    用于 FFT 内部运算,也可独立作为复数的通用表示。

    #窗函数 @moonbit-dsp.WindowType

    pub(all) enum WindowType { Rectangular, Hann, Hamming, Blackman } window(WindowType, Int) -> Array[Double] // 生成窗系数 apply_window(Array[Double], WindowType) -> Array[Double] hann_window(Int) / hamming_window(Int) / blackman_window(Int) / rectangular_window(Int)

    #FFT

    fft(Array[Double]) -> Array[Complex] // 迭代 radix-2,长度须为 2 的幂 dft(Array[Double]) -> Array[Complex] // O(N²) 参考实现 is_power_of_two(Int) -> Bool next_power_of_two(Int) -> Int // 对非 2 幂输入自动补零对齐

    #卷积与相关

    convolve(Array[Double], Array[Double]) -> Array[Double] // 线性卷积 correlate(Array[Double], Array[Double]) -> Array[Double] // 互相关 autocorrelate(Array[Double]) -> Array[Double]

    #滤波器

    fir_lowpass_coeffs(cutoff : Double, taps : Int, window : WindowType) -> Array[Double] // 窗口法,DC 增益归一 fir_filter(Array[Double], Array[Double]) -> Array[Double] // 直接卷积滤波 iir_lowpass_1(Array[Double], alpha : Double) -> Array[Double] // 一阶 IIR butterworth_lowpass_2(cutoff : Double) -> IirCoeffs2 // 二阶 Butterworth(双线性变换) iir_filter_2(Array[Double], IirCoeffs2) -> Array[Double] // 直接 II 型实现

    cutoff 均为归一化截止频率(0, 0.5),0.5 对应 Nyquist。

    #波形生成

    sine_wave(n : Int, freq : Double, sample_rate : Double, phase~ : Double) -> Array[Double] square_wave(n : Int, freq : Double, sample_rate : Double, duty~ : Double) -> Array[Double] triangle_wave / sawtooth_wave(签名同上) white_noise(n : Int, seed : UInt64) -> Array[Double] // 64 位 LCG,确定性可复现;seed = 0 映射为 1

    #频谱分析

    magnitude_spectrum(Array[Double]) -> Array[Double] // 半谱;非 DC/Nyquist bin 幅值 ×2 power_spectrum(Array[Double]) -> Array[Double] dominant_frequency(Array[Double], sample_rate : Double) -> (Double, Double) find_peaks(Array[Double], min_height : Double, min_distance : Int) -> Array[(Int, Double)] top_k(Array[Double], k : Int) -> Array[(Int, Double)]

    #统计

    mean / variance(x, sample~ : Bool) / stddev / rms / energy / max_abs / normalize


    #设计说明

    • 零外部依赖:仅使用 moonbitlang/core(math / builtin)。库本体与 CLI 均不引入任何第三方包,便于评审与复用。
    • 确定性:白噪声使用 64 位 LCG 且显式接受种子,同一输入同一输出,方便测试与复现实验。
    • 性能分层:fft 为迭代 radix-2(O(N log N)),dft 作为 O(N²) 参考,测试中以 1e-9 容差验证两者一致;fft_inplace 为私有实现,对非 2 幂输入由 pad_to_power_of_two 自动补零。
    • 工程规范:全程 moon check --deny-warn 零警告(修复了包括 pub 冗余修饰、Array::new 废弃、var 废弃、枚举只读性等语言迁移问题);浮点断言使用自写 assert_close(绝对容差),不做脆弱的精确相等比较。
    • 测试策略:39 项测试分为四类——数学恒等式(Parseval 定理、卷积交换律、窗对称性、DFT/FFT 一致性)、已知数值对照(卷积、复数运算、统计量)、频域特性(FIR DC 增益 = 1、Butterworth 频响衰减、主导频率端到端检出)、随机数性质(确定性、均值 ≈ 0)。


    #目录结构

    moonbit-dsp/ ├── moon.mod # 包配置(Apache-2.0, preferred_target = wasm) ├── moon.pkg # 库包声明 ├── complex.mbt # 复数 ├── fft.mbt # FFT / DFT ├── conv.mbt # 卷积与相关 ├── window.mbt # 窗函数 ├── filter.mbt # FIR / IIR 滤波器 ├── waveform.mbt # 波形生成 ├── spectrum.mbt # 频谱分析 ├── stats.mbt # 统计 ├── moonbit-dsp_test.mbt # 39 项单元测试 └── cmd/main/ # CLI 演示(moon run cmd/main -- ...)


    #路线图(后续可扩展)

    • fft 支持实数输入的对称优化、逆变换(IFFT)
    • 更多窗函数(Bartlett、Kaiser 等)与窗函数特征参数(主瓣宽度、旁瓣衰减)
    • FIR 等纹波(Parks-McClellan)与 IIR 高阶设计、频域滤波
    • 分帧 / 短时傅里叶变换(STFT)、频谱图
    • 更多波形(chirp、指数衰减等)与确定性随机分布(高斯噪声 Box-Muller)


    #License

    Apache-2.0

    Complex

    pub struct Complex {
    re : Double
    im : Double
    }

    A double-precision complex number.

    Used as the unit of the discrete Fourier transform and the fast Fourier transform in this library.

    Complex::abs

    fn Complex::abs(self : Complex) -> Double

    The magnitude (modulus) of self.

    Complex::add

    fn Complex::add(self : Complex, other : Complex) -> Complex

    Adds two complex numbers.

    Complex::conj

    fn Complex::conj(self : Complex) -> Complex

    Complex conjugate of self.

    Complex::div

    fn Complex::div(self : Complex, other : Complex) -> Complex

    Divides self by other.

    Complex::mul

    fn Complex::mul(self : Complex, other : Complex) -> Complex

    Multiplies two complex numbers.

    Complex::new

    fn Complex::new(re : Double, im : Double) -> Complex

    Constructs a complex number from its real and imaginary parts.

    Complex::one

    fn Complex::one() -> Complex

    The complex number 1 + 0i.

    Complex::phase

    fn Complex::phase(self : Complex) -> Double

    The phase (argument) of self in radians, in [-pi, pi].

    Complex::scale

    fn Complex::scale(self : Complex, factor : Double) -> Complex

    Scales both components of self by factor.

    Complex::sub

    fn Complex::sub(self : Complex, other : Complex) -> Complex

    Subtracts other from self.

    Complex::zero

    fn Complex::zero() -> Complex

    The complex number 0 + 0i.

    IirCoeffs2

    pub struct IirCoeffs2 {
    b0 : Double
    b1 : Double
    b2 : Double
    a1 : Double
    a2 : Double
    }

    Coefficients of a second-order IIR filter in direct form II: y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2].

    WindowType

    pub(all) enum WindowType {
    Rectangular
    Hann
    Hamming
    Blackman
    }

    The type of a window function used to taper a finite signal before spectral analysis or FIR filter design.

    alpha_from_cutoff

    fn alpha_from_cutoff(cutoff_norm : Double) -> Double

    Maps a normalized cutoff frequency fc/fs in (0, 0.5) to the alpha parameter of a first-order IIR low-pass filter: alpha = 1 - exp(-2*pi*fc/fs).

    apply_window

    fn apply_window(x : Array[Double], window_type : WindowType) -> Array[Double]

    Applies window_type to x sample by sample and returns the result.

    The input array is not modified.

    autocorrelate

    fn autocorrelate(x : Array[Double]) -> Array[Double]

    Autocorrelation of x.

    Equivalent to correlate(x, x). The zero-lag value is at index x.length() - 1.

    blackman_window

    fn blackman_window(n : Int) -> Array[Double]

    Shortcut for window(Blackman, n).

    butterworth_lowpass_2

    fn butterworth_lowpass_2(cutoff_norm : Double) -> IirCoeffs2

    Designs a second-order Butterworth low-pass filter with the bilinear transform.

    cutoff_norm is the normalized cutoff fc/fs in (0, 0.5).

    convolve

    fn convolve(x : Array[Double], h : Array[Double]) -> Array[Double]

    Computes the full linear convolution of x and h.

    out[k] = sum_j x[j] * h[k-j], with length x.length() + h.length() - 1. The input arrays are not modified.

    correlate

    fn correlate(x : Array[Double], y : Array[Double]) -> Array[Double]

    Cross-correlation of x and y.

    The result has length nx + ny - 1; index ny - 1 corresponds to zero lag. Positive lags (as y shifted right relative to x) are stored at indices greater than ny - 1.

    dft

    fn dft(x : Array[Double]) -> Array[Complex]

    Computes the discrete Fourier transform directly in O(n^2).

    X[k] = sum_{i=0}^{n-1} x[i] * exp(-2*pi*i*k*j/n) with j the imaginary unit. Works for any positive length.

    dominant_frequency

    fn dominant_frequency(x : Array[Double], sample_rate : Double) -> (Double, Double)

    Finds the dominant (strongest non-DC) frequency and its magnitude.

    Parameters:

    • x : the time-domain samples.
    • sample_rate : samples per second.

    Returns (frequency_hz, magnitude) of the peak bin. Returns (0.0, 0.0) when the spectrum has no usable peak.

    energy

    fn energy(x : Array[Double]) -> Double

    Total energy of x: sum(x[i]^2).

    fft

    fn fft(x : Array[Double]) -> Array[Complex]

    Computes the fast Fourier transform with an iterative radix-2 Cooley-Tukey algorithm in O(n log n).

    The length n of x must be a power of two. For other lengths use dft.

    find_peaks

    fn find_peaks(values : Array[Double]) -> Array[Int]

    Returns the indices of local maxima in values.

    An index i with 0 < i < n-1 is a peak when values[i] is strictly greater than both neighbours. Endpoints are never reported.

    fir_filter

    fn fir_filter(x : Array[Double], h : Array[Double]) -> Array[Double]

    Applies the FIR filter with impulse response h to x by full convolution. The output length is x.length() + h.length() - 1.

    fir_lowpass_coeffs

    fn fir_lowpass_coeffs(cutoff_norm : Double, taps : Int, window_type : WindowType) -> Array[Double]

    Designs a low-pass FIR filter with the window method.

    Parameters:

    • cutoff_norm : normalized cutoff frequency in (0, 0.5), i.e. the ratio fc / fs. Frequencies above this are attenuated.
    • taps : number of filter taps (odd values give a symmetric, linear-phase filter).
    • window_type : window used to truncate the ideal impulse response.

    Returns normalized coefficients whose DC gain is exactly 1.

    hamming_window

    fn hamming_window(n : Int) -> Array[Double]

    Shortcut for window(Hamming, n).

    hann_window

    fn hann_window(n : Int) -> Array[Double]

    Shortcut for window(Hann, n).

    iir_filter_2

    fn iir_filter_2(x : Array[Double], c : IirCoeffs2) -> Array[Double]

    Applies a second-order IIR filter in direct form II.

    iir_lowpass_1

    fn iir_lowpass_1(x : Array[Double], alpha : Double) -> Array[Double]

    First-order IIR low-pass filter.

    y[n] = alpha * x[n] + (1 - alpha) * y[n-1]. Use alpha_from_cutoff to pick alpha from a normalized cutoff frequency.

    is_power_of_two

    fn is_power_of_two(n : Int) -> Bool

    Returns true when n is a positive power of two.

    magnitude_spectrum

    fn magnitude_spectrum(x : Array[Double]) -> Array[Double]

    Computes the one-sided magnitude spectrum of x.

    The input is zero-padded to a power of two when necessary and analyzed with the FFT. The result has length n/2 + 1 (bins 0..=n/2), where bin k corresponds to the frequency k * fs / n. Non-DC and non-Nyquist bins are scaled by 2 to fold the energy of the mirrored half.

    max_abs

    fn max_abs(x : Array[Double]) -> Double

    The maximum absolute value of x (0.0 for an empty array).

    mean

    fn mean(x : Array[Double]) -> Double

    Returns the arithmetic mean of x. Returns 0.0 for an empty array.

    next_power_of_two

    fn next_power_of_two(n : Int) -> Int

    Returns the smallest power of two that is at least n.

    next_power_of_two(0) is 1.

    normalize

    fn normalize(x : Array[Double]) -> Array[Double]

    Scales x so that its maximum absolute value becomes 1.0.

    Returns a copy; the input is not modified. An all-zero (or empty) input is returned unchanged.

    pad_to_power_of_two

    fn pad_to_power_of_two(x : Array[Double]) -> Array[Double]

    Zero-pads x to the next power of two and returns the padded copy.

    Padded samples are set to 0.0.

    power_spectrum

    fn power_spectrum(x : Array[Double]) -> Array[Double]

    Computes the one-sided power spectrum (periodogram) of x.

    power[k] = |X[k]|^2 / N with the same one-sided scaling as magnitude_spectrum.

    rectangular_window

    fn rectangular_window(n : Int) -> Array[Double]

    Shortcut for window(Rectangular, n).

    rms

    fn rms(x : Array[Double]) -> Double

    Root-mean-square value of x: sqrt(mean(x^2)).

    sawtooth_wave

    fn sawtooth_wave(n : Int, freq_hz : Double, sample_rate : Double) -> Array[Double]

    Generates a rising sawtooth wave of length n samples, amplitude in [-1, 1].

    sine_wave

    fn sine_wave(n : Int, freq_hz : Double, sample_rate : Double, phase? : Double) -> Array[Double]

    Generates a sine wave of length n samples.

    Parameters:

    • n : number of samples.
    • freq_hz : frequency in hertz.
    • sample_rate : samples per second.
    • phase : initial phase in radians (defaults to 0.0).

    square_wave

    fn square_wave(n : Int, freq_hz : Double, sample_rate : Double, duty? : Double) -> Array[Double]

    Generates a square wave of length n samples with amplitude in [-1, 1] and the given duty cycle (fraction of one period spent at +1, defaults to 0.5).

    stddev

    fn stddev(x : Array[Double], sample? : Bool) -> Double

    Standard deviation of x; the square root of variance.

    top_k

    fn top_k(values : Array[Double], count : Int) -> Array[(Int, Double)]

    Returns the count strongest (index, value) pairs of values, sorted by value in descending order. Indices of equal values are ordered by ascending index.

    triangle_wave

    fn triangle_wave(n : Int, freq_hz : Double, sample_rate : Double) -> Array[Double]

    Generates a triangle wave of length n samples, amplitude in [-1, 1].

    variance

    fn variance(x : Array[Double], sample? : Bool) -> Double

    Variance of x.

    sample~ = false (default) computes the population variance (/n); sample~ = true computes the sample variance (/(n-1)). Returns 0.0 for an empty array.

    white_noise

    fn white_noise(n : Int, seed : UInt64) -> Array[Double]

    Generates n samples of white noise uniformly distributed in [-1, 1].

    The generator is a deterministic 64-bit LCG, so a fixed seed always produces the same sequence (useful for reproducible experiments). seed of 0 is mapped to 1.

    window

    fn window(window_type : WindowType, n : Int) -> Array[Double]

    Generates a symmetric window of length n for the given window_type.

    Parameters:

    • window_type : the kind of window to generate.
    • n : the window length in samples. A length of 1 yields [1.0].

    Returns an array of n weights in [0, 1].