Skip to content

Add wavelet transforms (CWT + DWT) as a sibling crate #4

@sunsided

Description

@sunsided

Goal

Add wavelet-transform support alongside the STFT toolkit, without compromising the focus or identity of ruststft. This is purely additive: no changes to the existing STFT code are required.

Background

Wavelets are a different time-frequency tool from the STFT (fixed window) - they give multi-resolution analysis (variable window per scale). Two distinct transforms, with two distinct engines:

CWT (continuous wavelet transform)

  • Output: a scalogram (scale x time).
  • Mother wavelets: Morlet, Ricker / Mexican-hat, Paul, Gaussian-derivative.
  • Fast implementation is frequency-domain: ifft(fft(signal) * conj(psi_hat(scale * omega))) per scale.
  • Reuses our FFT backend, but Morlet is complex-valued, so it needs a full complex FFT (not realfft's real-to-complex). rustfft is already a transitive dependency via realfft; promote it to a direct dependency for CWT, or mirror the realfft output into a full Hermitian spectrum.
  • New pieces: mother-wavelet definitions (analytic psi_hat in the frequency domain), a scale-set generator (log/linear, voices-per-octave), a Scalogram container, cone-of-influence, and admissibility normalization.

DWT (discrete wavelet transform, Mallat algorithm)

  • Output: approximation + detail coefficients per level.
  • Cascaded FIR filtering (lowpass/highpass QMF) + downsample-by-2, applied recursively.
  • Wavelet families: Haar, Daubechies, Symlets, Coiflets, biorthogonal.
  • Reuses almost nothing from the STFT path - no FFT, it is filter-bank convolution plus decimation.
  • New pieces: filter-tap tables (db1..dbN, sym, coif, bior - sizeable const data for PyWavelets parity), boundary modes (symmetric/periodic/zero/reflect - a superset of our PadMode), level math, a jagged WaveletCoeffs container, the inverse filter bank (upsample + reconstruction filters), and perfect-reconstruction tests.
  • Optional extensions: wavelet packet transform (WPT), and undecimated/stationary transform (SWT/MODWT).

What carries over from ruststft

  • The Sample trait, the no_std + alloc patterns, feature-gating conventions, and CI/tooling.
  • The error type (rename StftError to a crate-wide error, or add variants in the new crate).
  • The "coefficient table + cached metadata" pattern (Window) maps cleanly to a WaveletFilter holding decomposition/reconstruction taps.
  • The FFT-convolution machinery, for the CWT only.

Proposed architecture (the key decision)

Prefer a workspace split over bolting wavelets onto the STFT crate's name:

  1. Extract a small dsp-core crate: Sample, the shared error type, and FFT helpers.
  2. Keep ruststft focused on STFT/ISTFT.
  3. Add a new sibling crate (e.g. ruswt) for wavelets, depending on dsp-core.

This keeps independent versioning and a clean identity. The alternative - rebranding ruststft into a general time-frequency crate with stft + wavelet feature-modules - is awkward under the current name and would mean another breaking rename.

Dependency / backend deltas

  • CWT: a direct rustfft dependency (complex FFT). DWT: none (pure FIR).
  • Filter tables embedded as const arrays (keeps no_std support).
  • Parity targets: PyWavelets (pywt) for the DWT, ssqueezepy / scipy for the CWT (note scipy.signal.cwt is deprecated).

Task checklist

  • Workspace refactor: extract dsp-core (Sample, error, FFT helpers); make ruststft depend on it.
  • New wavelet crate scaffold (features, CI, no_std, forbid(unsafe_code)).
  • CWT: mother wavelets, scale generator, frequency-domain engine, Scalogram, cone-of-influence, normalization.
  • DWT: filter-tap tables (Haar, dbN, symN, coifN, bior), boundary modes, forward/inverse, level math, WaveletCoeffs.
  • Perfect-reconstruction and PyWavelets/ssqueezepy parity tests.
  • Optional: WPT, SWT/MODWT.
  • Docs + examples (scalogram, denoising via DWT thresholding).

Effort

  • CWT: moderate (reuses the FFT backend).
  • DWT: larger (tap tables, boundary handling, exact inverse, broad parity matrix).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is neededkilo-auto-fixAuto-generated label by Kilokilo-triagedAuto-generated label by Kilo

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions