/*
 * @Date: 2025-07-17 13:13:25
 * @Author: mypx
 * @LastEditors: mypx mypx_coder@163.com
 * @LastEditTime: 2025-08-21 15:46:07
 * @FilePath: et_goertzel.h
 * @Description: 
 * Copyright (c) 2025 by mypx, All Rights Reserved.
 */
#ifndef __ET_GOERTZEL_H__
#define __ET_GOERTZEL_H__
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/**
 * @brief Structure for Goertzel configuration
 */
typedef struct
{
    int    sample_count; /* Total number of samples (N) */
    int    target_bin;   /* Target DFT bin index (k) */
    double dc_offset;    /* DC offset value for ADC signal processing. For 12-bit ADC which is unipolar,
                          * it cannot directly output negative values, so hardware design adds a DC bias
                          * to shift the original AC signal (e.g. -V_ref/2 ~ +V_ref/2) to 0 ~ V_ref range,
                          * corresponding to quantization values 0 ~ 4095:
                          * Bias point 2048 corresponds to 0V (or zero point) of the original signal
                          * Values less than 2048 correspond to negative half-cycle of the original signal
                          * Values greater than 2048 correspond to positive half-cycle of the original signal */
    double coefficient;  /* 2 * cos(2*pi*k/N) */
} et_goertzel_config_t;

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * @brief Initialize the Goertzel algorithm configuration.
 *
 * This function sets up the internal parameters needed by the Goertzel
 * algorithm based on the number of samples and the target frequency bin.
 *
 * @param cfg           Pointer to Goertzel configuration structure.
 * @param sample_count  Total number of input samples (N).
 * @param target_bin    Index of the frequency bin to detect (k).
 * @param dc_offset     DC offset value for ADC signal processing. For 12-bit ADC which is unipolar,
 *                      it cannot directly output negative values, so hardware design adds a DC bias
 *                      to shift the original AC signal (e.g. -V_ref/2 ~ +V_ref/2) to 0 ~ V_ref range,
 *                      corresponding to quantization values 0 ~ 4095:
 *                      Bias point 2048 corresponds to 0V (or zero point) of the original signal
 *                      Values less than 2048 correspond to negative half-cycle of the original signal
 *                      Values greater than 2048 correspond to positive half-cycle of the original signal
 * @return              0 on success, -1 for invalid parameters, -2 if target_bin is out of range [0, sample_count/2]
 */
int et_goertzel_init(et_goertzel_config_t *cfg, int sample_count, int target_bin, double dc_offset);

/**
 * @brief Compute the signal power at the target frequency bin.
 *
 * Uses the Goertzel recurrence to estimate the power in a single DFT bin.
 * This function accepts double precision floating point input data.
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated power at the target frequency bin.
 */
double et_goertzel_compute_power_f64(const et_goertzel_config_t *cfg, const double *data);

/**
 * @brief Compute the signal power at the target frequency bin.
 *
 * Uses the Goertzel recurrence to estimate the power in a single DFT bin.
 * This function accepts single precision floating point input data.
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated power at the target frequency bin.
 */
double et_goertzel_compute_power_f32(const et_goertzel_config_t *cfg, const float *data);

/**
 * @brief Compute the signal power at the target frequency bin.
 *
 * Uses the Goertzel recurrence to estimate the power in a single DFT bin.
 * This function accepts unsigned 16-bit integer input data.
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated power at the target frequency bin.
 */
double et_goertzel_compute_power_u16(const et_goertzel_config_t *cfg, const uint16_t *data);

/**
 * @brief Compute the signal power at the target frequency bin.
 *
 * Uses the Goertzel recurrence to estimate the power in a single DFT bin.
 * This function accepts unsigned 32-bit integer input data.
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated power at the target frequency bin.
 */
double et_goertzel_compute_power_u32(const et_goertzel_config_t *cfg, const uint32_t *data);

/**
 * @brief Calculate the Goertzel bin index for a given frequency.
 *
 * Converts a target frequency in Hertz to the closest DFT bin index.
 * Ensures the bin index stays within [0, N/2].
 *
 * @param target_freq  Desired frequency to detect (Hz).
 * @param sample_rate  Sampling rate of the ADC (Hz).
 * @param num_samples  Total number of samples (N).
 * @return             Nearest bin index k (0 <= k <= N/2).
 *                     Returns -1 for invalid parameters.
 *                     Returns -2 if the target frequency violates the Nyquist theorem.
 */
int et_goertzel_calc_bin(double target_freq, double sample_rate, int num_samples);

/**
 * @brief Compute the phase at the target frequency bin (double precision).
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated phase (radians) at the target bin.
 */
double et_goertzel_compute_phase_f64(const et_goertzel_config_t *cfg, const double *data);

/**
 * @brief Compute the phase at the target frequency bin (single precision).
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated phase (radians) at the target bin.
 */
double et_goertzel_compute_phase_f32(const et_goertzel_config_t *cfg, const float *data);

/**
 * @brief Compute the phase at the target frequency bin (16-bit unsigned int).
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated phase (radians) at the target bin.
 */
double et_goertzel_compute_phase_u16(const et_goertzel_config_t *cfg, const uint16_t *data);

/**
 * @brief Compute the phase at the target frequency bin (32-bit unsigned int).
 *
 * @param cfg   Pointer to initialized Goertzel configuration.
 * @param data  Array of input samples (length = cfg->sample_count).
 * @return      Estimated phase (radians) at the target bin.
 */
double et_goertzel_compute_phase_u32(const et_goertzel_config_t *cfg, const uint32_t *data);

/**
 * @brief Find the dominant (center) frequency using Goertzel scanning.
 *
 * This function scans a frequency range (e.g., 20Hz–100Hz) and returns the frequency
 * with the maximum energy response. Suitable for real-time frequency tracking.
 *
 * @param signal        Input signal array (length = num_samples)
 * @param num_samples   Number of samples in signal
 * @param sample_rate   Sampling rate in Hz
 * @param f_min         Minimum frequency to scan (e.g., 20.0)
 * @param f_max         Maximum frequency to scan (e.g., 100.0)
 * @param f_step        Frequency resolution (e.g., 1.0 Hz)
 * @param dc_offset     DC offset value for ADC signal processing
 * @return              Frequency (Hz) with strongest energy (center freq)
 */
double et_goertzel_find_center_frequency_f64(const double *signal, int num_samples, double sample_rate, double f_min,
                                             double f_max, double f_step, double dc_offset);

/**
 * @brief Find the dominant frequency using single precision float input data.
 *
 * @param signal        Input signal array (length = num_samples)
 * @param num_samples   Number of samples in signal
 * @param sample_rate   Sampling rate in Hz
 * @param f_min         Minimum frequency to scan (Hz)
 * @param f_max         Maximum frequency to scan (Hz)
 * @param f_step        Frequency resolution (Hz)
 * @param dc_offset     DC offset value for ADC signal processing
 * @return             Frequency (Hz) with strongest energy
 */
double et_goertzel_find_center_frequency_f32(const float *signal, int num_samples, double sample_rate, double f_min,
                                             double f_max, double f_step, double dc_offset);

/**
 * @brief Find the dominant frequency using 16-bit unsigned int input data.
 */
double et_goertzel_find_center_frequency_u16(const uint16_t *signal, int num_samples, double sample_rate, double f_min,
                                             double f_max, double f_step, double dc_offset);

/**
 * @brief Find the dominant frequency using 32-bit unsigned int input data.
 */
double et_goertzel_find_center_frequency_u32(const uint32_t *signal, int num_samples, double sample_rate, double f_min,
                                             double f_max, double f_step, double dc_offset);

#ifdef __cplusplus
}
#endif

#endif // __ET_GOERTZEL_H__
