Skip to content

File syn_dds.c

File List > dsp > syn_dds.c

Go to the documentation of this file

#include "syn_dds.h"

#include "../util/syn_random.h"

#include <string.h>

SYN_Status syn_dds_init(SYN_DDS *dds, SYN_DDS_Waveform type, uint32_t freq_hz,
                        uint32_t sample_rate_hz)
{
    if (!dds || sample_rate_hz == 0)
        return SYN_INVALID_PARAM;

    memset(dds, 0, sizeof(*dds));
    dds->type = type;
    dds->amplitude = Q16_ONE;
    dds->offset = 0;
    dds->duty_q16 = 32768U; /* 50% default duty cycle */
    dds->rand_state = 0x12345678U;

    return syn_dds_set_freq(dds, freq_hz, sample_rate_hz);
}

SYN_Status syn_dds_set_freq(SYN_DDS *dds, uint32_t freq_hz, uint32_t sample_rate_hz)
{
    if (!dds || sample_rate_hz == 0)
        return SYN_INVALID_PARAM;

    /* phase_step = (freq_hz * 2^32) / sample_rate_hz */
    uint64_t step = ((uint64_t)freq_hz << 32) / (uint64_t)sample_rate_hz;
    dds->phase_step = (uint32_t)step;

    return SYN_OK;
}

SYN_Status syn_dds_set_gain(SYN_DDS *dds, q16_t amplitude, q16_t offset)
{
    if (!dds)
        return SYN_INVALID_PARAM;

    dds->amplitude = amplitude;
    dds->offset = offset;

    return SYN_OK;
}

SYN_Status syn_dds_set_duty(SYN_DDS *dds, float duty_pct)
{
    if (!dds)
        return SYN_INVALID_PARAM;

    float duty = duty_pct;
    if (duty < 0.0f)
        duty = 0.0f;
    if (duty > 100.0f)
        duty = 100.0f;

    dds->duty_q16 = (uint16_t)((duty / 100.0f) * 65535.0f);

    return SYN_OK;
}

q16_t syn_dds_step(SYN_DDS *dds)
{
    if (!dds)
        return 0;

    q16_t raw_sample = 0;
    uint32_t phase = dds->phase;

    switch (dds->type) {
    case SYN_DDS_SINE: {
        /* Convert 32-bit phase (0..2^32-1) to Q16.16 angle (0 to 2*PI), then wrap to -PI..+PI */
        int64_t norm_phase = (int64_t)(phase >> 16);                   /* 0..65535 */
        q16_t angle = (q16_t)((norm_phase * (int64_t)Q16_2_PI) >> 16); /* 0 to 6.28318 */
        if (angle > Q16_PI) {
            angle -= Q16_2_PI;
        }
        raw_sample = q16_sin(angle);
        break;
    }
    case SYN_DDS_TRIANGLE: {
        /* Phase 0..2^31-1 ramps up, 2^31..2^32-1 ramps down */
        if (phase < 0x80000000U) {
            raw_sample = (q16_t)(((int64_t)phase * 131072LL) >> 31) - Q16_ONE;
        } else {
            uint32_t rem = phase - 0x80000000U;
            raw_sample = Q16_ONE - (q16_t)(((int64_t)rem * 131072LL) >> 31);
        }
        break;
    }
    case SYN_DDS_SAWTOOTH: {
        /* Full ramp from -1.0 to +1.0 */
        raw_sample = (q16_t)(((int64_t)phase * 131072LL) >> 32) - Q16_ONE;
        break;
    }
    case SYN_DDS_SQUARE: {
        /* Compare top 16 bits of phase against duty threshold */
        uint16_t top_phase = (uint16_t)(phase >> 16);
        if (top_phase < dds->duty_q16) {
            raw_sample = Q16_ONE;
        } else {
            raw_sample = -Q16_ONE;
        }
        break;
    }
    case SYN_DDS_NOISE: {
        uint32_t rnd = syn_random_u32();
        int32_t signed_val = (int32_t)(rnd & 0xFFFFU) - 32768;
        raw_sample = (q16_t)(signed_val * 2);
        break;
    }
    default:
        break;
    }

    dds->phase += dds->phase_step;

    /* Apply amplitude gain and DC offset: output = (raw_sample * amplitude) + offset */
    return q16_mul(raw_sample, dds->amplitude) + dds->offset;
}

SYN_Status syn_dds_fill_q16(SYN_DDS *dds, q16_t *buf, size_t count)
{
    if (!dds || !buf)
        return SYN_INVALID_PARAM;

    for (size_t i = 0; i < count; i++) {
        buf[i] = syn_dds_step(dds);
    }

    return SYN_OK;
}

SYN_Status syn_dds_fill_u16(SYN_DDS *dds, uint16_t *buf, size_t count, uint16_t dac_center,
                            uint16_t dac_span)
{
    if (!dds || !buf)
        return SYN_INVALID_PARAM;

    for (size_t i = 0; i < count; i++) {
        q16_t sample = syn_dds_step(dds); /* normalized roughly -1.0 to +1.0 */
        int32_t val = (int32_t)dac_center + (int32_t)Q16_TO_INT((int64_t)sample * dac_span);
        if (val < 0)
            val = 0;
        if (val > 65535)
            val = 65535;
        buf[i] = (uint16_t)val;
    }

    return SYN_OK;
}