#ifndef LTEST_MATH_H
#define LTEST_MATH_H

#include "lmath_config.h"

/** Коды ошибок */
typedef enum {
    LMATH_ERR_INVALID_WINTYPE = -20000, /**< Неверно задан тип окна */
    LMATH_ERR_MEMORY_ALLOC    = -20001, /**< Ошибка выделения памяти */
    LMATH_ERR_INVALID_PARAMS  = -20002, /**< Неверно заданы входные параметры */
    LMATH_ERR_INTERNAL_CALC   = -20003  /**< Ошибка при вычислениях */
} t_lmath_errs;


/** Контекст окна.
    Содержит рассчитанные коэффициенты окна заданного размера и
    различные параметры окна */
typedef struct st_lmath_win_context *t_lmath_window_context;

/** Тип используемого окна */
typedef enum  {
    LMATH_WINTYPE_RECTANGLE = 0, /**< Прямоугольное */
    LMATH_WINTYPE_HANNING  = 1,  /**< Hanning */
    LMATH_WINTYPE_HAMMING  = 2,  /**< Hamming */
    LMATH_WINTYPE_BH_4TERM = 7,  /**< 4-Term Blackman Harris */
    LMATH_WINTYPE_BH_7TERM = 8   /**< 7-Term Blackman Harris */
} t_lmath_wintype;


#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************//**
    Функция вычисляет параметры окна и его коэффициенты и создает контекст
    окна с этими параметрами для дальнейшего применения.
    Выделенные ресурсы должны быть очищены с помощью lmath_window_context_destroy().

    @param[in] wintype      Тип окна (значение из #t_lmath_wintype)
    @param[in] param        Указатель на структуру с параметрами для расчета
                            окна (резерв)
    @param[in] size         Размер окна (количество коэффициентов окна и соответственно
                            количество точек в сигнале, к которому оно может применяться)
    @param[out] win         Массив, в который будут сохранены рассчитанные коэффициенты
                            окна. Должен быть достаточного размера для сохранения
                            size элементов.
    @param[out] win_params  Функция выделяет данную структуру и сохраняет в нее
                            рассчитанные параметры, если передан ненулевой
                            указатель
    @return                 Код ошибки.
    ***************************************************************************/
t_lmath_window_context lmath_window_context_create(int wintype,  void *param, int size);

/***************************************************************************//**
    Уничтожение ранее созданного с помощью lmath_window_context_create()
    контекста.
    @param[in] ctx     Контекст с параметрами окна
 ******************************************************************************/
void lmath_window_context_destroy(t_lmath_window_context winctx);

/* Функции для получения параметров окна по контексту */
int lmath_window_context_win_size(const t_lmath_window_context winctx);
double lmath_window_context_inherent_gain(const t_lmath_window_context winctx);
double lmath_window_context_coherent_gain(const t_lmath_window_context winctx);
double lmath_window_context_enbw(const t_lmath_window_context winctx);


/***************************************************************************//**
    Функция применяет к сигналу окно, параметры которого заранее посчитаны
    с помощью lmath_window_context_create().
    По сути просто умножает точки сигнала на рассчитанные коэффициенты окна.
    Нормирование амплитуды выходного сигнала при этом не выполняется.

    @param[in] winctx      Контекст с параметрами окна
    @param[in] src         Массив с отсчетами входного сигнала
    @param[out] res        Массив, в который будут сохранен результирующий сигнал.
                           Должен быть достаточного размера для сохранения точек,
                           соответствующих размеру окна
    @return                Код ошибки.
    ***************************************************************************/
int lmath_window_apply(const double *src, const t_lmath_window_context winctx, double *res);
/***************************************************************************//**
    Функция применяет к сигналу окно, , параметры которого заранее посчитаны
    с помощью lmath_window_context_create(),
    и выполняет нормирование амплитуды выходного сигнала

    @param[in] winctx      Контекст с параметрами окна
    @param[in] src         Массив с отсчетами входного сигнала
    @param[out] res        Массив, в который будут сохранен результирующий сигнал.
                           Должен быть достаточного размера для сохранения точек,
                           соответствующих размеру окна
    @return                Код ошибки.
    ***************************************************************************/
int lmath_window_apply_scaled(const double *src, t_lmath_window_context winctx, double *res);




/***************************************************************************//**
    Оценка постоянной и переменной (по RMS) составляющей входного временного
    сигнала. Для сглаживания разрывов на краях сигнала функция использует заранее
    рассчитанное с помощью lmath_window_context_create() окно.

    @param[in]  src         Массив с отсчетами исходного временного сигнала
                            (размер определяется размером окна)
    @param[in]  winctx      Контекст с параметрами окна
    @param[out] tmp_buf     Массив для хранения временных результатов, что позволяет
                            не выделять при каждом выполнение функции память.
                            Должен быть размером равным размеру окна.
                            Входное содержимое не имеет значения и затирается
                            промежуточными данными. Если не используется рассчет
                            AC (ac == NULL) и входной сигнал не нужен после
                            выполнения данной функции, то можно использовать
                            src в качестве временного буфера. Если выполняется
                            и рассчет AC, то в качестве данного буфера обязательно
                            должен быть отдельно созданный массив.
    @param[out] dc          В этой переменной возвращается рассчитанное значение
                            постоянной составляющей. Может быть NULL.
    @param[out] ac          В этой переменной возвращается рассчитанное значение
                            переменной составляющей (RMS). Может быть NULL.
    @return                 Код ошибки
*******************************************************************************/
int lmath_acdc_estimation_with_window(const double *src,
                                      const t_lmath_window_context winctx,
                                      double *tmp_buf,
                                      double *dc, double *ac);


/***************************************************************************//**
    Оценка постоянной и переменной (по RMS) составляющей входного временного
    сигнала. Функция использует окно для сглаживания разрывов на краях сигнала.
    Упращенная версия lmath_acdc_estimation_with_window(), которая не требует
    отдельно создавать окно, но требует вычисление коэффициентов окна при каждом
    вызове функции и не позволяет явно определить окно.

    @param[in]  src     Массив с отсчетами исходного временного сигнала
    @param[in]  size    Количество отсчетов во входном массиве
    @param[out] dc      В этой переменной возвращается рассчитанное значение
                            постоянной составляющей. Может быть NULL.
    @param[out] ac      В этой переменной возвращается рассчитанное значение
                            переменной составляющей. Может быть NULL.
    @return             Код ошибки
*******************************************************************************/
int lmath_acdc_estimation(const double *src, int size, double *dc, double *ac);


#ifdef LMATH_USE_FFTW

/***************************************************************************//**
    Расчет амплитудного и фазового спектра входного сигнала.
    Упрощенная функция, не требующая отдельного создания контекста, но
    при этом не позволяющая вынести затраты на расчет постоянных параметров
    вне основного цикла обработки. Амплитудный спектр возвращается в
    значениях пиковой аплитуды гармоники.

    @param[in] src      Массив с отсчетами исходного временного сигнала
    @param[in] size     Количество отсчетов во входном массиве
    @param[in] dt       Интервал времени между отсчетами входного сигнала в
                          секундах
    @param[in] flags    Резерв
    @param[out] amp     В этот массив сохраняется рассчитанный амплитудный
                          спектр размером size/2 + 1 элементов. Может быть NULL.
    @param[out] pha     В этот массив сохраняется рассчитанный фазовый
                          спектр размером size/2 + 1 элементов. Может быть NULL.
    @param[out] df      В этой переменной возвращается изменение частоты между
                          соседними отсчетами рассчитанных спектров в Гц.
                          Может быть NULL.
    @return             Код ошибки
    ***************************************************************************/
int lmath_amp_pha_spectrum_peak(const double *src, int size, double dt, int flags,
                                double *amp, double *pha, double *df);
/* Аналогично lmath_amp_pha_spectrum_peak(), но возвращает амплитудный спектр в
   rms */
int lmath_amp_pha_spectrum_rms(const double *src, int size, double dt, int flags,
                                double *amp, double *pha, double *df);


/** Непрозрачная указатель на структуру, содержащую необходимую информацию
   для расчета комплексного преобразования Фурье из действительного сигнала */
typedef struct st_lmath_fft_r2c_context *t_lmath_fft_r2c_context;

/***************************************************************************//**
    Создание контекста для расчета преобразования Фурье входного действительного 
    сигнала заданного размера. Рассчитывает все параметры, не зависящие от
    самого входного сигнала.
    Создает также буфер для входного сигнала.
    Данная функция не является потокобезопасной, если не была вызвана функция
    fftw_make_planner_thread_safe() (из FFTW версии 3.3.5 или выше)
    @param[in]  size    Количество точек во входном сигнале к которому будет
                        применяться fft
    @param[in]  flags   Флаги (резерв)
    @return             Созданный контекст или нулевой указатель, если не
                        удалось создать.
 ******************************************************************************/
t_lmath_fft_r2c_context lmath_fft_r2c_context_create(int size, int flags);

/***************************************************************************//**
    Уничтожение ранее созданного с помощью lmath_fft_r2c_context_create()
    контекста.
    @param[in] ctx     Контекст расчета FFT
 ******************************************************************************/
void lmath_fft_r2c_context_destroy(t_lmath_fft_r2c_context ctx);

/***************************************************************************//**
    Получение указателя на буфер для входного сигнала, к которому будет применяться
    преобразование Фурье.
    @param[in] ctx     Контекст расчета FFT
    @return            Указетель на начало буфера
 ******************************************************************************/
double *lmath_fft_r2c_context_in_buf(const t_lmath_fft_r2c_context ctx);
/***************************************************************************//**
    Получение размера буфера в точках для входного сигнала, к которому будет
    применяться преобразование Фурье. Это значение соответствует заданному
    при создании контекста.
    @param[in] ctx     Контекст расчета FFT
    @return            Размер буфера в точках
 ******************************************************************************/
int lmath_fft_r2c_context_in_size(const t_lmath_fft_r2c_context ctx);
/***************************************************************************//**
    Получение размера буфера для результата преобразования Фурье в точках.
    @param[in] ctx     Контекст расчета FFT
    @return            Размер буфера в точках
 ******************************************************************************/
int lmath_fft_r2c_context_fft_size(const t_lmath_fft_r2c_context ctx);
/***************************************************************************//**
    Получение шага по частоте на основе шага по времени, который получится в
    результате расчета преобразования Фурье. Если время задано в секундах, то результат
    будет в Герцах.
    @param[in] ctx     Контекст расчета FFT
    @return            Величина шага по частоте
 ******************************************************************************/
double lmath_fft_r2c_context_df(const t_lmath_fft_r2c_context ctx, double dt);

/***************************************************************************//**
    Расчет амплитудно-фазового спектра на основе ранее созданного контекста
    и входного действительного сигнала, помещенного во входной буфер,
    который можно получить с помощью lmath_fft_r2c_context_in_buf().
    Выполняется нормализация амплитудного спектра. Каждый элемент - амплитуда
    (пиковое значение) соответсвующей гармоники.
    Выходные массивы должны быть достаточного размера для сохранения
    lmath_fft_r2c_context_fft_size() точек.
    В качестве любого массива может быть передан нулевой указатель, если нужно
    рассчитать только амплитудный или только фазовый спектр.

    @param[in] ctx     Контекст расчета FFT
    @param[out] amp     В этот массив сохраняется рассчитанный амплитудный
                          спектр размером lmath_fft_r2c_context_fft_size()
                          элементов.  Может быть NULL.
    @param[out] pha     В этот массив сохраняется рассчитанный фазовый
                          спектр размером lmath_fft_r2c_context_fft_size()
                          элементов. Может быть NULL.
    @return             Код ошибки
 ******************************************************************************/
int lmath_fft_r2c_calc_amp_pha_spectrum_peak(t_lmath_fft_r2c_context ctx, double *amp, double *pha);


/***************************************************************************//**
    Расчет амплитудно-фазового спектра на основе ранее созданного контекста
    и входного действительного сигнала. Аналогичен lmath_fft_r2c_calc_amp_pha_spectrum_peak(),
    но в амплитудном спектре каждый элемент представляет собой RMS соответствующей
    гармоники (постоянная составляющая остается такой же)

    @param[in] ctx     Контекст расчета FFT
    @param[out] amp     В этот массив сохраняется рассчитанный амплитудный
                          спектр RMS сигнала размером lmath_fft_r2c_context_fft_size()
                          элементов.  Может быть NULL.
    @param[out] pha     В этот массив сохраняется рассчитанный фазовый
                          спектр размером lmath_fft_r2c_context_fft_size()
                          элементов. Может быть NULL.
    @return             Код ошибки
 ******************************************************************************/
int lmath_fft_r2c_calc_amp_pha_spectrum_rms(t_lmath_fft_r2c_context ctx, double *amp, double *pha);





/***************************************************************************//**
    Поиск значения частоты с максимальной мощностью и мощности сигнала на этой
    частоте по временному сигналу. Функция внутри себя применяет БПФ и анализирует
    спектр. Если уже рассчитан спектр, то стоит использовать
    lmath_find_peak_freq_spectrum()

    @param[in]  src             Массив с отсчетами исходного временного сигнала
    @param[in]  size            Количество отсчетов во входном массиве
    @param[in]  dt              Интервал времени между отсчетами входного
                                  сигнала в секундах
    @param[in]  search_freq     Если значение больше нуля, то ищется частота в
                                  окрестности указанной, иначе - во всей частотной
                                  области (кроме постоянной составляющей)
    @param[out] peak_freq       В этой переменной возвращается рассчитанное значение
                                  частоты, соответствующей максимальной мощности.
                                  Может быть NULL.
    @param[out] peak_pwr        В этой переменной возвращается рассчитанное значение
                                  мощности сигнала на найденной частоте.
                                  Может быть NULL.
    @return                     Код ошибки
*******************************************************************************/
int lmath_find_peak_freq(const double *src, int size, double dt, double search_freq,
                         double *peak_freq, double *peak_pwr);
#endif

/***************************************************************************//**
    Поиск значения частоты с максимальной мощностью и мощности сигнала на этой
    частоте по амплитудному спектру.

    @param[in]  amp_spectrum    Амплитудный спектр входного сигнала
    @param[in]  size            Количество отсчетов в спектре
    @param[in]  df              Изменение частоты между соседними отсчетами
                                  спектра в Гц
    @param[in]  search_freq     Если значение больше нуля, то ищется частота в
                                  окрестности указанной, иначе - во всей частотной
                                  области (кроме постоянной составляющей)
    @param[in]  winctx          Контекст с харакеристиками примененного перед
                                  вычислением спектра окна.
    @param[out] peak_freq       В этой переменной возвращается рассчитанное значение
                                  частоты, соответствующей максимальной мощности.
                                  Может быть NULL.
    @param[out] peak_pwr        В этой переменной возвращается рассчитанное значение
                                  мощности сигнала на найденной частоте.
                                  Может быть NULL.
    @return                     Код ошибки
*******************************************************************************/
int lmath_find_peak_freq_spectrum(const double *amp_spectrum, int size, double df,
                                  double search_freq, const t_lmath_window_context winctx,
                                  double *peak_freq, double *peak_pwr);


/***************************************************************************//**
    Вычисление спектральных параметров по амплитудному спектру сигнала.

    @param[in]  amp_spectrum    Амплитудный спектр входного сигнала
    @param[in]  size            Количество отсчетов в спектре
    @param[in]  df              Изменение частоты между соседними отсчетами
                                  спектра в Гц
    @param[in]  peak_freq       Частота основного сигнала. Должна соответствовать
                                   отсчету с номером не меньше чем freq_span/2.
    @param[in]  harm_cnt        Количество гармоник (включая основную), используемых
                                    при расчете.
    @param[in]  winctx          Контекст с харакеристиками примененного перед
                                  вычислением спектра окна.
    @param[out] SNR             Рассчитанное значение отношения сигнал/шум.
                                  Может быть NULL.
    @param[out] THD             Рассчитанное значение Total Harmonic Distortion:
                                  сигнал/гармоники.
                                  Может быть NULL.
    @param[out] SINAD           Рассчитанное значение signal-to-noise-and-distortion
                                  ratio: сигнал/(шум+гармоники).
                                  Может быть NULL.
    @param[out] SFDR            Рассчитанное значение spurious free dynamic range:
                                    макс. сигнал/макс. гармонику вне сигнала.
                                  Может быть NULL.
    @param[out] ENOB            Рассчитанное значение эффективный бит.
                                  Может быть NULL.
    @return                     Код ошибки
*******************************************************************************/
int lmath_calc_spectrum_params (const double *amp_spectrum, int size,  double df,
                                double peak_freq, int harm_cnt,
                                const t_lmath_window_context winctx,
                                double *SNR, double *THD, double *SINAD,
                                double *SFDR, double *ENOB);

/***************************************************************************//**
    Вычисление следующего отсчета на выходе FIR-фильтра

    @param[in]  coef            Набор коэффициентов (массив из tap-чисел)
    @param[in, out]  prev_x     Набор предыдущих значений, подаваемых на вход
                                фильтра. Массив из tap-чисел (первый элемент ---
                                самый новый).
                                Для расчета первого значения как правило заполняется
                                нулями. Функция обновляет этот массив и для расчета
                                последующих значений передается массив, полученный
                                при вычислении предыдущего значения.
                                Если используются параллельно несколько фильтров,
                                то для каждого должен использоваться свой
                                массив текущих рассчитанных значений.
    @param[in] ntap             Количество коэффициентов фильтра
    @param[in] new_x            Новое значение на входе фильтра
    @return                     Новое рассчитанное значение на выходе фильтра
*******************************************************************************/
double lmath_proc_fir(const double *coef, double *prev_y, int ntap, double new_x);

#ifdef __cplusplus
}
#endif

#endif // LTEST_MATH_H
