Sign in

    MoonSpectrum

    MoonBit-native scientific signal processing toolkit with FFT, spectra, windows, convolution, FIR filters, and CLI analysis.

    signal-processing
    fft
    spectrum
    dsp
    science
    Download zip
    Author
    Version
    0.2.2
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    56

    Dependencies

    #MoonSpectrum

    CI

    MoonSpectrum 是一个面向 MoonBit 生态的科学信号处理基础库。它提供复数运算、DFT/FFT、频谱分析、窗函数、卷积、基础 FIR 滤波和命令行分析工具,目标是补齐 MoonBit 在传感器数据、实验数据、振动信号、周期信号和教学验证中的基础算法能力。

    本项目不做音频播放、采集或设备后端,也不依赖 native FFI。当前版本已覆盖 STFT、periodogram/Welch PSD、IIR 滤波、重采样、跨通道相关分析和频谱特征;后续再扩展流式输入、时间戳管线和 WebAssembly 可视化。

    #Why

    MoonBit 已经有一些音频、图形、矩阵和工程工具包,但通用科学信号处理方向还缺少一个小而稳的基础库。MoonSpectrum 的定位是:

    • 给 MoonBit 项目提供可复用的 FFT、频谱、窗函数、卷积和基础滤波能力。
    • 给 OSC2026 评审提供可直接运行的测试、示例数据和 CLI。
    • 给后续生态扩展留下清楚边界,而不是把音频引擎、数据框、可视化和控制系统混在一起。

    #Installation

    MoonSpectrum is a MoonBit module. The repository is the source distribution; the published module metadata and dependency version are recorded in moon.mod.

    #From the repository

    Install the current MoonBit toolchain from the official installer, then clone the repository and refresh its pinned dependency:

    # Windows PowerShell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser irm https://cli.moonbitlang.com/install/powershell.ps1 | iex git clone https://github.com/chgttyyr/MoonSpectrum.git cd MoonSpectrum moon update moon check --target all # For Windows native validation, install MSYS2 UCRT64 GCC and add # C:\\msys64\\ucrt64\\bin to PATH before running the all-backend commands.

    # Linux or macOS curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash git clone https://github.com/chgttyyr/MoonSpectrum.git cd MoonSpectrum moon update moon check --target all

    The competition notice recommended MoonBit 0.10.3. The CLI package keeps the backward-compatible options("is-main": true) declaration, so it works with the committee toolchain and newer official releases. Because newer formatters try to migrate that legacy declaration, CI uses scripts/check_format.py: it runs the official formatter against every .mbt source and structurally checks the intentional legacy package file. CI prints moon version --all so the exact compiler is visible. If starting a new copy instead of using the committed moon.mod, the equivalent dependency command is moon add moonbitlang/x@0.4.45.

    #Features

    • Complex 复数类型:加减乘、缩放、幅值、近似比较。
    • DFT / radix-2 FFT / inverse FFT。
    • 幅度谱、功率谱、频率 bin、主频检测。
    • STFT、periodogram PSD、单边 PSD、线性重采样。
    • 信号生成:正弦波、方波、脉冲、线性 chirp、确定性白噪声。
    • 窗函数:Rectangular、Hann、Hamming、Blackman。
    • 卷积:线性卷积、循环卷积。
    • 基础滤波:移动平均、FIR taps、IIR biquad 低通/高通/带通。
    • CLI:demo、fft、analyze、window、convolve、stft、psd、resample、biquad、stats、correlate、welch、peaks。

    #Quick Start

    moon test moon run cmd/main -- demo moon run cmd/main -- fft examples/sine.csv --sample-rate 8 moon run cmd/main -- analyze examples/sine.csv --sample-rate 8 moon run cmd/main -- window hann 4 moon run cmd/main -- convolve examples/sine.csv examples/kernel.csv

    Typical output:

    {"samples":8,"sample_rate":8,"dominant_bin":2,"dominant_frequency":2,"magnitude":4}

    #API Sketch

    test {
    let signal = sine_wave(length=8, sample_rate=8.0, frequency=2.0)
    let spectrum = fft(real_signal(signal))
    let peak = dominant_peak(spectrum, 8.0)
    inspect(peak.index, content="2")
    }

    The root package exports the public API. CLI code lives in cmd/main and uses the same library functions as users would.

    #Project Layout

    • complex.mbt - complex arithmetic and approximate comparisons.
    • fft.mbt - DFT, FFT, inverse FFT, real signal conversion.
    • analysis.mbt - spectra, frequency bins, peak detection.
    • signals.mbt - test/demo signal generation.
    • windows.mbt - window functions and window application.
    • convolution.mbt - linear and circular convolution.
    • filters.mbt - moving average, FIR filtering, low/high-pass taps.
    • stft.mbt, psd.mbt, resampling.mbt, iir_filters.mbt - extended scientific signal-processing primitives.
    • cmd/main - CLI entry point.
    • examples - small CSV fixtures for smoke tests.
    • docs/fixtures.md - fixture provenance, construction rules, and expected numeric invariants.
    • docs/competition - OSC2026 proposal and acceptance material.

    #Verification

    moon info python scripts/check_format.py moon check --target all --warn-list +73 moon build --target all moon test --target all powershell -ExecutionPolicy Bypass -File scripts\verify_acceptance.ps1 -SkipPublishDryRun

    The extended analysis commands are also covered by deterministic smoke tests:

    moon run cmd/main -- stats examples/offset.csv moon run cmd/main -- correlate examples/dual-tone.csv --max-lag 4 moon run cmd/main -- welch examples/sine.csv --sample-rate 8 --segment-length 8 --overlap 0.5 --window hann moon run cmd/main -- peaks examples/dual-tone.csv --threshold 0.4 --min-distance 2

    MoonSpectrum is published as the Mooncakes module chgttyyr/MoonSpectrum@0.2.2. The moon.mod file is the source of truth for the package metadata shown on Mooncakes.

    #Current Boundaries

    • FFT currently requires power-of-two input length.
    • CSV parser intentionally handles simple single-column numeric fixtures.
    • Time zones, timestamps, streaming IO, and visualization are future work.
    • No native FFI is used in v0.2.2.

    #Roadmap

    • Cross-channel correlation and streaming window processing.
    • Robust CSV/JSON data ingestion and timestamp-aware analysis.
    • Browser/WebAssembly demo for frequency-domain visualization.
    • Additional statistical estimators and calibration helpers.

    #License

    Apache-2.0.

    SpectrumError

    pub(all) suberror SpectrumError {
    EmptySignal
    LengthMismatch
    NonPowerOfTwo(length~ : Int)
    InvalidArgument(message~ : String)
    } derive(Eq, ToJson,
    Debug
    )

    Biquad

    pub(all) struct Biquad {
    b0 : Double
    b1 : Double
    b2 : Double
    a1 : Double
    a2 : Double
    x1 : Double
    x2 : Double
    y1 : Double
    y2 : Double
    } derive(Eq, ToJson,
    Debug
    )

    Biquad::allpass

    fn Biquad::allpass(cutoff_hz : Double, sample_rate : Double, q? : Double) -> Biquad raise SpectrumError

    Biquad::bandpass

    fn Biquad::bandpass(cutoff_hz : Double, sample_rate : Double, q? : Double) -> Biquad raise SpectrumError

    Biquad::filter

    fn Biquad::filter(self : Biquad, signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    Biquad::highpass

    fn Biquad::highpass(cutoff_hz : Double, sample_rate : Double, q? : Double) -> Biquad raise SpectrumError

    Biquad::lowpass

    fn Biquad::lowpass(cutoff_hz : Double, sample_rate : Double, q? : Double) -> Biquad raise SpectrumError

    Biquad::new

    fn Biquad::new(b0 : Double, b1 : Double, b2 : Double, a1 : Double, a2 : Double) -> Biquad

    Biquad::notch

    fn Biquad::notch(cutoff_hz : Double, sample_rate : Double, q? : Double) -> Biquad raise SpectrumError

    Biquad::step

    fn Biquad::step(self : Biquad, x : Double) -> Double

    Complex

    pub(all) struct Complex {
    re : Double
    im : Double
    } derive(Eq, ToJson,
    Debug
    )

    Complex::add

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

    Complex::approx_equal

    fn Complex::approx_equal(self : Complex, other : Complex, eps? : Double) -> Bool

    Complex::magnitude

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

    Complex::mul

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

    Complex::scale

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

    Complex::sub

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

    Complex::zero

    fn Complex::zero() -> Complex

    FrequencyResponse

    pub(all) struct FrequencyResponse {
    frequencies : Array[Double]
    magnitude : Array[Double]
    } derive(Eq, ToJson,
    Debug
    )

    Peak

    pub(all) struct Peak {
    index : Int
    frequency : Double
    magnitude : Double
    } derive(Eq, ToJson,
    Debug
    )

    SignalPeak

    pub(all) struct SignalPeak {
    index : Int
    value : Double
    } derive(Eq, ToJson,
    Debug
    )

    SignalStats

    pub(all) struct SignalStats {
    count : Int
    mean : Double
    variance : Double
    stddev : Double
    rms : Double
    minimum : Double
    maximum : Double
    peak_to_peak : Double
    } derive(Eq, ToJson,
    Debug
    )

    SpectralSummary

    pub(all) struct SpectralSummary {
    centroid : Double
    bandwidth : Double
    flatness : Double
    entropy : Double
    rolloff : Double
    } derive(Eq, ToJson,
    Debug
    )

    SpectrogramResult

    pub(all) struct SpectrogramResult {
    frequencies : Array[Double]
    frame_times : Array[Double]
    magnitudes : Array[Array[Double]]
    } derive(Eq, ToJson,
    Debug
    )

    WelchResult

    pub(all) struct WelchResult {
    frequencies : Array[Double]
    psd : Array[Double]
    } derive(Eq, ToJson,
    Debug
    )

    WindowKind

    pub(all) enum WindowKind {
    Rectangular
    Hann
    Hamming
    Blackman
    } derive(Eq, ToJson,
    Debug
    )

    apply_window

    fn apply_window(signal : ArrayView[Double], weights : ArrayView[Double]) -> Array[Double] raise SpectrumError

    approx_equal

    fn approx_equal(a : Double, b : Double, eps? : Double) -> Bool

    autocorrelation

    fn autocorrelation(signal : ArrayView[Double], max_lag : Int) -> Array[Double] raise SpectrumError

    autocovariance

    fn autocovariance(signal : ArrayView[Double], lag : Int) -> Double raise SpectrumError

    Returns the covariance between a signal and a lagged copy of itself.

    bandpass_fir_taps

    fn bandpass_fir_taps(length~ : Int, low_cutoff_hz~ : Double, high_cutoff_hz~ : Double, sample_rate~ : Double) -> Array[Double] raise SpectrumError

    bandstop_fir_taps

    fn bandstop_fir_taps(length~ : Int, low_cutoff_hz~ : Double, high_cutoff_hz~ : Double, sample_rate~ : Double) -> Array[Double] raise SpectrumError

    baseline_corrected

    fn baseline_corrected(signal : ArrayView[Double]) -> (Double, Array[Double]) raise SpectrumError

    Returns the baseline-corrected signal and its estimated offset.

    biquad_frequency_response

    fn biquad_frequency_response(filter : Biquad, sample_rate~ : Double, fft_length~ : Int) -> FrequencyResponse raise SpectrumError

    Measures the positive-frequency response of a biquad section.

    center

    fn center(signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    chirp

    fn chirp(length~ : Int, sample_rate~ : Double, start_hz~ : Double, end_hz~ : Double, amplitude? : Double) -> Array[Double] raise SpectrumError

    circular_convolve

    fn circular_convolve(signal : ArrayView[Double], kernel : ArrayView[Double]) -> Array[Double] raise SpectrumError

    clip_signal

    fn clip_signal(signal : ArrayView[Double], lower : Double, upper : Double) -> Array[Double] raise SpectrumError

    Clamps every sample to the inclusive interval [lower, upper].

    complex

    fn complex(re~ : Double, im~ : Double) -> Complex

    convolve

    fn convolve(signal : ArrayView[Double], kernel : ArrayView[Double]) -> Array[Double] raise SpectrumError

    correlation_coefficient

    fn correlation_coefficient(left : ArrayView[Double], right : ArrayView[Double]) -> Double raise SpectrumError

    covariance

    fn covariance(signal : ArrayView[Double], other : ArrayView[Double]) -> Double raise SpectrumError

    Returns the population covariance of two equally sized signals.

    Covariance is useful for comparing sensor channels without requiring a frequency-domain representation. The divisor is N, matching the other descriptive statistics in this package.

    crest_factor

    fn crest_factor(signal : ArrayView[Double]) -> Double raise SpectrumError

    cross_correlation

    fn cross_correlation(signal : ArrayView[Double], other : ArrayView[Double], max_lag : Int) -> Array[Double] raise SpectrumError

    detect_outliers

    fn detect_outliers(signal : ArrayView[Double], z_threshold~ : Double) -> Array[Int] raise SpectrumError

    Returns sample indices whose robust z-score exceeds the given magnitude.

    detrend_linear

    fn detrend_linear(signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    dft

    fn dft(signal : ArrayView[Complex]) -> Array[Complex] raise SpectrumError

    differentiate

    fn differentiate(signal : ArrayView[Double], sample_interval : Double) -> Array[Double] raise SpectrumError

    dominant_peak

    fn dominant_peak(spectrum : ArrayView[Complex], sample_rate : Double) -> Peak raise SpectrumError

    dynamic_range_db

    fn dynamic_range_db(signal : ArrayView[Double]) -> Double raise SpectrumError

    envelope_follow

    fn envelope_follow(signal : ArrayView[Double], attack~ : Double, release~ : Double) -> Array[Double] raise SpectrumError

    fft

    fn fft(signal : ArrayView[Complex]) -> Array[Complex] raise SpectrumError

    fft_convolve

    fn fft_convolve(signal : ArrayView[Double], kernel : ArrayView[Double]) -> Array[Double] raise SpectrumError

    find_peaks

    fn find_peaks(signal : ArrayView[Double], threshold? : Double, min_distance? : Int) -> Array[SignalPeak] raise SpectrumError

    fir_filter

    fn fir_filter(signal : ArrayView[Double], taps : ArrayView[Double]) -> Array[Double] raise SpectrumError

    fir_frequency_response

    fn fir_frequency_response(taps : ArrayView[Double], sample_rate~ : Double, fft_length~ : Int) -> FrequencyResponse raise SpectrumError

    Measures the positive-frequency response of an FIR filter by zero-padded FFT.

    fir_impulse_response

    fn fir_impulse_response(taps : ArrayView[Double], response_length : Int) -> Array[Double] raise SpectrumError

    Creates the response of a finite impulse response filter to a unit impulse.

    frame_signal

    fn frame_signal(signal : ArrayView[Double], frame_length~ : Int, hop_size~ : Int) -> Array[Array[Double]] raise SpectrumError

    frequency_bins

    fn frequency_bins(length : Int, sample_rate : Double) -> Array[Double] raise SpectrumError

    full_cross_correlation

    fn full_cross_correlation(signal : ArrayView[Double], other : ArrayView[Double]) -> Array[Double] raise SpectrumError

    highpass_taps

    fn highpass_taps(length~ : Int, cutoff_hz~ : Double, sample_rate~ : Double) -> Array[Double] raise SpectrumError

    ifft

    fn ifft(spectrum : ArrayView[Complex]) -> Array[Complex] raise SpectrumError

    impulse

    fn impulse(length~ : Int, index? : Int, amplitude? : Double) -> Array[Double] raise SpectrumError

    integrate_trapezoid

    fn integrate_trapezoid(signal : ArrayView[Double], sample_interval : Double) -> Double raise SpectrumError

    interquartile_range

    fn interquartile_range(signal : ArrayView[Double]) -> Double raise SpectrumError

    Returns the interquartile range using the package's linear quantile rule.

    lowpass_taps

    fn lowpass_taps(length~ : Int, cutoff_hz~ : Double, sample_rate~ : Double) -> Array[Double] raise SpectrumError

    magnitude_spectrum

    fn magnitude_spectrum(spectrum : ArrayView[Complex]) -> Array[Double]

    mean_absolute_deviation

    fn mean_absolute_deviation(signal : ArrayView[Double]) -> Double raise SpectrumError

    Returns the mean absolute deviation around the arithmetic mean.

    mean_absolute_error

    fn mean_absolute_error(reference : ArrayView[Double], estimate : ArrayView[Double]) -> Double raise SpectrumError

    median

    fn median(signal : ArrayView[Double]) -> Double raise SpectrumError

    moving_average

    fn moving_average(signal : ArrayView[Double], width : Int) -> Array[Double] raise SpectrumError

    moving_rms

    fn moving_rms(signal : ArrayView[Double], width : Int) -> Array[Double] raise SpectrumError

    next_power_of_two

    fn next_power_of_two(value : Int) -> Int raise SpectrumError

    normalize_peak

    fn normalize_peak(signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    normalize_range

    fn normalize_range(signal : ArrayView[Double], lower : Double, upper : Double) -> Array[Double] raise SpectrumError

    normalized_correlation

    fn normalized_correlation(signal : ArrayView[Double], max_lag : Int) -> Array[Double] raise SpectrumError

    overlap_add

    fn overlap_add(frames : ArrayView[Array[Double]], hop_size~ : Int) -> Array[Double] raise SpectrumError

    pad_to_power_of_two

    fn pad_to_power_of_two(signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    periodogram

    fn periodogram(signal : ArrayView[Double], sample_rate : Double, window_kind? : WindowKind) -> Array[Double] raise SpectrumError

    power_spectrum

    fn power_spectrum(spectrum : ArrayView[Complex]) -> Array[Double]

    pulse_train

    fn pulse_train(length~ : Int, period~ : Int, amplitude? : Double) -> Array[Double] raise SpectrumError

    quantile

    fn quantile(signal : ArrayView[Double], probability : Double) -> Double raise SpectrumError

    real_signal

    fn real_signal(values : ArrayView[Double]) -> Array[Complex]

    remove_baseline

    fn remove_baseline(signal : ArrayView[Double], baseline : Double) -> Array[Double] raise SpectrumError

    Subtracts a known sensor baseline without changing the input array.

    resample_linear

    fn resample_linear(signal : ArrayView[Double], target_length : Int) -> Array[Double] raise SpectrumError

    resample_sinc

    fn resample_sinc(signal : ArrayView[Double], target_length : Int) -> Array[Double] raise SpectrumError

    response_peak

    fn response_peak(response : FrequencyResponse) -> Peak raise SpectrumError

    Returns the maximum magnitude and its frequency from a response.

    robust_z_scores

    fn robust_z_scores(signal : ArrayView[Double]) -> Array[Double] raise SpectrumError

    Computes robust z-scores using median and median absolute deviation.

    root_mean_square_error

    fn root_mean_square_error(reference : ArrayView[Double], estimate : ArrayView[Double]) -> Double raise SpectrumError

    sawtooth_wave

    fn sawtooth_wave(length~ : Int, sample_rate~ : Double, frequency~ : Double, amplitude? : Double) -> Array[Double] raise SpectrumError

    scale_signal

    fn scale_signal(signal : ArrayView[Double], input_min~ : Double, input_max~ : Double, output_min~ : Double, output_max~ : Double) -> Array[Double] raise SpectrumError

    Linearly maps sensor values from an input range to an engineering range.

    signal_energy

    fn signal_energy(signal : ArrayView[Double]) -> Double raise SpectrumError

    signal_rms

    fn signal_rms(signal : ArrayView[Double]) -> Double raise SpectrumError

    signal_stats

    fn signal_stats(signal : ArrayView[Double]) -> SignalStats raise SpectrumError

    signal_to_noise_db

    fn signal_to_noise_db(reference : ArrayView[Double], estimate : ArrayView[Double]) -> Double raise SpectrumError

    sine_wave

    fn sine_wave(length~ : Int, sample_rate~ : Double, frequency~ : Double, amplitude? : Double) -> Array[Double] raise SpectrumError

    single_sided_psd

    fn single_sided_psd(two_sided_psd : ArrayView[Double]) -> Array[Double] raise SpectrumError

    spectral_band_energy

    fn spectral_band_energy(magnitudes : ArrayView[Double], sample_rate : Double, low_hz : Double, high_hz : Double) -> Double raise SpectrumError

    spectral_bandwidth

    fn spectral_bandwidth(magnitudes : ArrayView[Double], sample_rate : Double) -> Double raise SpectrumError

    spectral_centroid

    fn spectral_centroid(magnitudes : ArrayView[Double], sample_rate : Double) -> Double raise SpectrumError

    spectral_entropy

    fn spectral_entropy(magnitudes : ArrayView[Double]) -> Double raise SpectrumError

    spectral_flatness

    fn spectral_flatness(magnitudes : ArrayView[Double]) -> Double raise SpectrumError

    spectral_flux

    fn spectral_flux(previous : ArrayView[Double], current : ArrayView[Double]) -> Double raise SpectrumError

    spectral_rolloff

    fn spectral_rolloff(magnitudes : ArrayView[Double], sample_rate : Double, ratio? : Double) -> Double raise SpectrumError

    spectral_slope

    fn spectral_slope(magnitudes : ArrayView[Double]) -> Double raise SpectrumError

    spectral_summary

    fn spectral_summary(spectrum : ArrayView[Complex], sample_rate : Double) -> SpectralSummary raise SpectrumError

    spectrogram

    fn spectrogram(signal : ArrayView[Double], window_kind : WindowKind, window_length~ : Int, hop_size~ : Int, sample_rate~ : Double) -> SpectrogramResult raise SpectrumError

    Computes positive-frequency magnitude frames and their time coordinates.

    spectrogram_dominant_peaks

    fn spectrogram_dominant_peaks(result : SpectrogramResult) -> Array[Peak] raise SpectrumError

    Returns the dominant positive-frequency peak for every spectrogram frame.

    spectrogram_energy

    fn spectrogram_energy(result : SpectrogramResult) -> Array[Double]

    Returns the sum of squared positive-frequency magnitudes for each frame.

    spectrogram_loudest_frame

    fn spectrogram_loudest_frame(result : SpectrogramResult) -> Int raise SpectrumError

    Returns the frame index with the highest total energy.

    square_wave

    fn square_wave(length~ : Int, sample_rate~ : Double, frequency~ : Double, amplitude? : Double) -> Array[Double] raise SpectrumError

    stft

    fn stft(signal : ArrayView[Double], window_kind : WindowKind, window_length : Int, hop_size : Int) -> Array[Array[Complex]] raise SpectrumError

    threshold_crossings

    fn threshold_crossings(signal : ArrayView[Double], threshold : Double) -> Array[Int] raise SpectrumError

    Returns sample indices where a signal crosses a threshold in either direction.

    triangle_wave

    fn triangle_wave(length~ : Int, sample_rate~ : Double, frequency~ : Double, amplitude? : Double) -> Array[Double] raise SpectrumError

    welch_psd

    fn welch_psd(signal : ArrayView[Double], sample_rate~ : Double, segment_length~ : Int, overlap~ : Double, window_kind? : WindowKind) -> WelchResult raise SpectrumError

    white_noise

    fn white_noise(length~ : Int, seed~ : UInt, amplitude? : Double) -> Array[Double] raise SpectrumError

    window

    fn window(kind : WindowKind, length : Int) -> Array[Double] raise SpectrumError

    window_coherent_gain

    fn window_coherent_gain(kind : WindowKind, length : Int) -> Double raise SpectrumError

    window_noise_bandwidth

    fn window_noise_bandwidth(kind : WindowKind, length : Int) -> Double raise SpectrumError

    zero_crossing_indices

    fn zero_crossing_indices(signal : ArrayView[Double]) -> Array[Int] raise SpectrumError

    Returns the indices at which adjacent samples have strictly opposite signs.

    zero_crossing_rate

    fn zero_crossing_rate(signal : ArrayView[Double]) -> Double raise SpectrumError