std1.0.0-beta-7 SafeC docs

Source dsp/cwt.sc

View in the repository

// SafeC Standard Library — CWT implementation (see cwt.h)
#pragma once
#include <std/dsp/cwt.h>
#include <std/dsp/complex_dsp.h>
#include <std/math.h>
#include <std/mem.h>
#include <std/dsp/dsp.h>
#include <std/sync/parallel.h>

#define SAFEC_DSP_PI_CWT 3.14159265358979323846

namespace std {

// The wavelet at scale s depends only on (n - b), so each scale's kernel
// (envelope * cos and * -sin, 2 * halfWidth + 1 taps) is computed once, and
// every output is two SIMD dot products of the signal with it (dsp_dot_f64).
// Rows of (scale, position) run on the thread pool.

struct CwtJob {
    const double*  signal;
    unsigned long  signalLen;
    const double*  scales;
    double* const* kRe;    // per scale: 2 * halfWidth + 1 taps
    double* const* kIm;
    double*        out;    // interleaved complex, numScales x signalLen
};

static unsigned long cwt_half_(double s) { return (unsigned long)(4.0 * s) + 1UL; }

static void cwt_rows_(&CwtJob j, unsigned long lo, unsigned long hi) {
    unsafe {
        unsigned long r = lo;
        while (r < hi) {
            unsigned long si = r / j.signalLen;
            unsigned long b = r % j.signalLen;
            unsigned long hw = cwt_half_(j.scales[si]);
            unsigned long nStart = b >= hw ? b - hw : 0UL;
            unsigned long nEnd = b + hw;
            if (nEnd >= j.signalLen) { nEnd = j.signalLen - 1UL; }
            unsigned long len = nEnd - nStart + 1UL;
            unsigned long t0 = nStart + hw - b;      // tap index of n = nStart
            double re = dsp_dot_f64_n_(&j.signal[nStart], (const double*)&j.kRe[si][t0], len);
            double im = dsp_dot_f64_n_(&j.signal[nStart], (const double*)&j.kIm[si][t0], len);
            j.out[2UL * r] = re;
            j.out[2UL * r + 1UL] = im;
            r = r + 1UL;
        }
    }
}

void cwt_morlet_n_(const &double signal, unsigned long signalLen,
                 const &double scales, unsigned long numScales,
                 double w0, &Complex out) {
    if (signalLen == 0UL || numScales == 0UL) return;
    double normConst = pow_d(SAFEC_DSP_PI_CWT, 0.0 - 0.25);
    unsafe {
        double** kRe = (double**)alloc_raw_(sizeof(double*) * numScales);
        double** kIm = (double**)alloc_raw_(sizeof(double*) * numScales);
        if (kRe == (double**)0 || kIm == (double**)0) return;
        unsigned long si = 0UL;
        while (si < numScales) {
            double s = scales[si];
            unsigned long hw = cwt_half_(s);
            unsigned long taps = 2UL * hw + 1UL;
            double invSqrtS = 1.0 / sqrt_d(s);
            kRe[si] = (double*)alloc_raw_(sizeof(double) * taps);
            kIm[si] = (double*)alloc_raw_(sizeof(double) * taps);
            if (kRe[si] == (double*)0 || kIm[si] == (double*)0) return;
            unsigned long t = 0UL;
            while (t < taps) {
                double tt = ((double)t - (double)hw) / s;
                double env = normConst * exp_d(-0.5 * tt * tt) * invSqrtS;
                kRe[si][t] = env * cos_d(w0 * tt);
                kIm[si][t] = -env * sin_d(w0 * tt);
                t = t + 1UL;
            }
            si = si + 1UL;
        }
        struct CwtJob j;
        j.signal = (const double*)signal;
        j.signalLen = signalLen;
        j.scales = (const double*)scales;
        j.kRe = (double* const*)kRe;
        j.kIm = (double* const*)kIm;
        j.out = (double*)out;
        unsigned long rows = numScales * signalLen;
        if (rows >= 4096UL) parallel_for(rows, 1024UL, cwt_rows_, &j);
        else cwt_rows_(&j, 0UL, rows);
        si = 0UL;
        while (si < numScales) { dealloc(kRe[si]); dealloc(kIm[si]); si = si + 1UL; }
        dealloc(kRe);
        dealloc(kIm);
    }
}

// ── Slice API (the public signatures; the (reference, length) cores above
// are the *_n_ functions) ─────────────────────────────────────────────────────

int cwt_morlet([]const double signal, []const double scales, double w0, []Complex out) {
    if (signal.len == 0UL || scales.len == 0UL || out.len / signal.len < scales.len) return 0;
    unsafe { cwt_morlet_n_((const &double)signal.ptr, signal.len, (const &double)scales.ptr, scales.len, w0, (&Complex)out.ptr); }
    return 1;
}

} // namespace std