Skip to content

File syn_rc_curve.c

File List > control > syn_rc_curve.c

Go to the documentation of this file

#include "syntropic/control/syn_rc_curve.h"

uint16_t syn_rc_curve_apply(uint16_t input_us, const SYN_RCCurve_Config *config)
{
    if (!config) {
        return input_us;
    }

    if (input_us < 1000U) {
        input_us = 1000U;
    }
    if (input_us > 2000U) {
        input_us = 2000U;
    }

    /* Center offset x in range [-500, +500] us */
    int32_t offset = (int32_t)input_us - 1500;

    /* Apply deadband filtering */
    if (offset > 0) {
        if (offset <= (int32_t)config->deadband_us) {
            offset = 0;
        } else {
            offset -= (int32_t)config->deadband_us;
        }
    } else if (offset < 0) {
        if (-offset <= (int32_t)config->deadband_us) {
            offset = 0;
        } else {
            offset += (int32_t)config->deadband_us;
        }
    }

    if (offset == 0) {
        return 1500U;
    }

    /* Convert offset to normalized Q16.16 in range [-1.0, +1.0] */
    int32_t span = 500 - (int32_t)config->deadband_us;
    if (span <= 0) {
        return 1500U; /* LCOV_EXCL_LINE: Defensive NULL check or invalid parameter fallback */
    }

    q16_t x = Q16_FROM_INT(offset) / span;

    /* Polynomial: y = (1 - expo) * x + expo * x^3 */
    q16_t x_cube = q16_mul(q16_mul(x, x), x);
    q16_t linear_term = q16_mul(Q16_ONE - config->expo, x);
    q16_t cubic_term = q16_mul(config->expo, x_cube);
    q16_t y = linear_term + cubic_term;

    /* Apply dual-rate scaling */
    q16_t y_scaled = q16_mul(y, config->dual_rate);

    /* Convert back to microsecond offset */
    int32_t out_offset = Q16_TO_INT(q16_mul(y_scaled, Q16_FROM_INT(500)));
    int32_t final_us = 1500 + out_offset;

    if (final_us < 1000) {
        final_us = 1000;
    }
    if (final_us > 2000) {
        final_us = 2000;
    }

    return (uint16_t)final_us;
}