//----------------------------------------------------------------------------

//Класс обработки данных

//----------------------- Используемые ресурсы: ------------------------------

//Класс TProcess выполняет обработку данных для одного канала измерителя.
//Для получения данных используется класс TAdc. Метод Execute() должен
//вызываться в основном цикле. Если при очередном вызове данные АЦП готовы,
//то производится их обработка и метод возвращает 1. Если данные не готовы,
//метод возвращает 0. Обработка ведется в двух клок-доменах: на частоте
//дискретизации 96 кГц (TS_MS domain) и после децимации на частоте
//96000/256 = 375 Гц (TD_MS domain).

//Обработка начинается в домене 96 кГц (TS_MS domain).

//Сначала удаляется постоянная составляющая входного сигнала.
//Для этого используется DC Removal Filter, состоящий из последовательно
//включенного дифференециатора и интегратора с утечкой (Leaky Integrator).
//Используется коррекция ошибки реквантования. Входные 12-разрядные данные
//фильтра DCRF_in поступают с массива АЦП. Накопление производится в
//32-разрядном аккумуляторе DCRF_acc. Частота среза фильтра задается
//коэффициентом DCRF_A и выбрана равной 5 Гц. Фильтр имеет ноль в точке
//z = 1 и полюс в точке z = 0.999673. Выходные данные фильтра DCRF_out_1
//тоже 12-разрядные.

//Следующее звено обработки - цифровой двухполупериодный детектор.
//Производится вычисление абсолютного значения числа DCRF_out_1.
//Выходной код детектора DET_out 11-разрядный.

//Далее следуют два IIR-фильтра, которые формируют время интеграции для
//индикации среднего (или квазипикового) значения (отображается столбиками),
//и пикового значения (отображается одиночным горящим сегментом - точкой).
//Накопление производится в 32-разрядных аккумуляторах Integ1_acc и Integ2_acc.
//Выходной код фильтра находится в старшем слове аккумулятора.
//Фильтры работают только при нарастании сигнала.
//Постоянные времени определяются коэффициентами Integ1_a и Integ2_a.
//Значения коэффициентов вычисляются по формуле Integ_a = 65536/96000/tau.
//Чтобы получить tau = 0 (True Peak), надо задать Integ_a = 65536.
//Время интеграции задается с помощью параметров Integ1_t и Integ2_t.
//Оно больше tau в INTEG_TTAU = 4.0 раз (из-за требования достижения уровня
//-2 дБ при Sin сигнале).
//Время интеграции задается в диапазоне 0..2700.0 мс с дискретностью 0.1 мс.
//Integ_a = INTEG_K (27306) / Integ_t (0..27000)
//INTEG_K = INTEG_TTAU (4) * TS_MS (1/96) * SCALE16 (65536) * INT_RES (10)

//Дальнейшая обработка ведется в домене 96000/256 = 375 Гц (TD_MS domain).

//Сначала делается масштабирование сигнала, которое необходимо для выравнивания
//показаний столбиков и точек на стационарном сигнале при разных постоянных
//времени интеграции или обратного хода.
//Выходной 11-разрядный код предыдущих фильтров содержится в старших словах
//аккумуляторов Integ1_acc и Integ2_acc. Он умножается на коэффициенты
//Scale1_a и Scale2_a, затем делится на 4096. На выходе получается максимальный
//код 10000, именно такой диапазон используется в таблице перекодировки.
//После умножения на коэффициенты код ограничивается на уровне LEV_LIM,
//который устанавливается примерно на 10% выше выбранного максимального кода.
//При максимальных значениях коэффициентов Scale1_a и Scale2_a усиление
//равно 32 (примерно +30 дБ).
//Устанавливать коэффициент передачи меньше единицы смысла не имеет,
//поскольку тогда даже при максимальном коде АЦП не будет достигатья
//уровень сигнала, достаточный для зажигания всех сегментов.
//Усиление задается с помощью параметров Scale1_f и Scale2_f.
//Диапазон задания 0..32.000 с дискретностью 0.001.
//Scale_a = SCALE_K (81960) * Scale_f (0..32000) / SCALE12 (4096)
//SCALE_K = LEV_MAX (10000) * SCALE12 (4096) / LEV_RES (1000) *
//SCALE12 (4096) / DET_MAX (2047)

//Далее следуют два IIR-фильтра, которые формируют время срабатывания.
//Они работают в "медленном" домене.
//Выходной код этих фильтров непосредственно связан с показаниями измерителя.
//Накопление производится в 32-разрядных аккумуляторах Resp1_acc и Resp2_acc.
//Выходной код фильтра находится в старшем слове аккумулятора.
//Фильтры работают только при нарастании сигнала.
//Постоянные времени определяются коэффициентами Resp1_a и Resp2_a.
//Значения коэффициентов вычисляются по формуле A = 65536/375/tau.
//Чтобы получить tau = 0, надо задать A = 65536. В этом случае время
//срабатывания будет равно времени интеграции.
//Время срабатывания задается с помощью параметров Resp1_t и Resp2_t.
//Оно больше tau в RESP_TTAU = 2.3 раз (из-за требования достижения уровня
//-1 дБ при DC сигнале).
//Время срабатывания задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//Resp_a = RESP_K (401954) / Resp_t (0..65000)
//RESP_K = RESP_TTAU (2.3) * TD_MS (256/96) * SCALE16 (65536)

//Когда выходной код фильтра срабатывания достигает входного кода с точностью
//1 дБ, то включается фильтр разрядки, воздействующий на аккумуляторы
//фильтров интеграции Integ1_acc и Integ2_acc. Он работает в "медленном" домене.
//Постоянные времени разрядки определяются коэффициентами Decay1_a и Decay2_a.
//Значения коэффициентов вычисляются по формуле A = 65536/375/tau.
//Чтобы получить tau = 0, надо задать A = 65536.
//Время разрядки задается с помощью параметров Decay1_t и Decay2_t.
//Оно больше tau в DECAY_TTAU = 2.3 раз (из-за требования достижения уровня
//-20 дБ при DC сигнале).
//Время разрядки задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//Decay_a = DECAY_K (401954) / Decay_t (0..65000)
//DECAY_K = DECAY_TTAU (2.3) * TD_MS (256/96) * SCALE16 (65536)

//Показания измерителя могут иметь некоторое время удержания, когда после
//пика сигнала показания остаются неизменными. Это время задается с помощью
//коэффициентов Hold1_a и Hold2_a.
//Значения коэффициентов вычисляются по формуле A = 375*t_hold.
//Если задать нулевое значение коэффициентов, удержания показаний не будет,
//сразу после пика сигнала будет начинаться обратный ход.
//Время удержания задается с помощью параметров Hold1_t и Hold2_t.
//Оно задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//Hold_a = HOLD_K (24576) * Hold_t (0..65000) / SCALE16 (65536)
//HOLD_K  = SCALE16 (65536) / TD_MS (256/96)

//Когда время удержания заканчивается, начинается обратный ход измерителя.
//Фильтр обратного хода воздействует на аккумуляторы фильтров срабатывания
//Resp1_acc и Resp2_acc. Он работает в "медленном" домене. Постоянные времени
//обратного хода определяются коэффициентами Fall1_a и Fall2_a.
//Значения коэффициентов вычисляются по формуле A = 65536/375/tau.
//Чтобы получить tau = 0, надо задать A = 65536.
//Время обратного хода задается с помощью параметров Fall1_t и Fall2_t.
//Оно больше tau в FALL_TTAU = 2.3 раз (из-за требования достижения уровня
//-20 дБ при DC сигнале).
//Время обратного хода задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//Fall_a = FALL_K (401954) / Fall_t (0..65000)
//FALL_K  = FALL_TTAU (2.3) * TD_MS (256/96) * SCALE16 (65536);

//Отдельное формирование обратного хода для фильтров срабатывания позволяет
//использовать уменьшенное время разрядки для фильтров интеграции,
//что уменьшает влияние промежутка времени между пиками сигнала на показания
//измерителя. При уменьшении времени интеграции будет несколько уменьшаться
//уровень для стационарного сигнала, что требует корректировки с помощью
//масштабирующих коэффициентов. В простейшем случае время обратного ходя
//для фильтров устанавливают одинаковым.

//Максимальное значение измеренного сигнала может запоминаться и некоторое
//время удерживаться. Для запоминания используются аккумуляторы максимального
//значения Max1_acc и Max2_acc. Время удержания этих значений задается с помощью
//коэффициентов MHold1_a и MHold2_a.
//Значения коэффициентов вычисляются по формуле A = 375*t_hold.
//Если задать нулевое значение коэффициентов, удержания показаний не будет,
//сразу после пика сигнала будет начинаться обратный ход.
//Время удержания задается с помощью параметров MHold1_t и MHold2_t.
//Оно задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//MHold_a = HOLD_K (24576) * Hold_t (0..65000) / SCALE16 (65536)
//HOLD_K  = SCALE16 (65536) / TD_MS (256/96)

//Когда время удержания заканчивается, начинается обратный ход максимальных
//значений. Фильтр обратного хода воздействует на аккумуляторы Max1_acc и
//Max2_acc. Он работает в "медленном" домене. Постоянные времени
//обратного хода определяются коэффициентами MFall1_a и MFall2_a.
//Значения коэффициентов вычисляются по формуле A = 65536/375/tau.
//Чтобы получить tau = 0, надо задать A = 65536.
//Время обратного хода задается с помощью параметров MFall1_t и MFall2_t.
//Оно больше tau в FALL_TTAU = 2.3 раз (из-за требования достижения уровня
//-20 дБ при DC сигнале).
//Время обратного хода задается в диапазоне 0..65000 мс с дискретностью 1 мс.
//MFall_a = FALL_K (401954) / Fall_t (0..65000)
//FALL_K  = FALL_TTAU (2.3) * TD_MS (256/96) * SCALE16 (65536);

//Параллельно с аккумуляторами Max1_acc и Max2_acc максимальное значение
//измеренного сигнала сохраняется в еще одних аккумуляторах: AMax1_acc и
//AMax2_acc. Они используются для накопления статистики. При работе измерителя
//значения в этих аккумуляторах могут только увеличиваться. Обратного хода
//эти аккумуляторы не имеют. В них накапливается максимальное измеренное
//значение. Сброс аккумуляторов AMax1_acc и AMax2_acc производится вручную
//с помощью специальной команды.

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

//Для стандартного квазипикового измерителя задается tau интеграции 1.25 мс
//(время интеграции 5 мс по -2 дБ), tau обратного хода 739 мс (время обратного
//ход 1700 мс до уровня -20 дБ), tau срабатывания 43..87 мс (100..200 мс по
//-1 дБ), время удержания - 0 мс.

//Для пикового измерителя tau интеграции 0 мс, tau срабатывания 0..87 мс
//(0..200 мс по -1 дБ), время удержания порядка 2000 мс, tau обратного хода
//от 0 мс (точка исчезает сразу) до 739 мс (точка плавно спадает за 1700 мс
//до уровня -20 дБ).

//----------------------------------------------------------------------------

#include "main.hpp"
#include "process.hpp"
#include "eeprom.hpp"
#include "eemap.hpp"
#include "presets.hpp"

//----------------------------------------------------------------------------
//---------------------------- Класс TProcess: -------------------------------
//----------------------------------------------------------------------------

//---------------------- Статические переменные: -----------------------------

int32_t  TProcess::Integ1_a; uint16_t TProcess::Integ1_t;
int32_t  TProcess::Decay1_a; uint16_t TProcess::Decay1_t;
int32_t  TProcess::Scale1_a; uint16_t TProcess::Scale1_f;
int32_t  TProcess::Resp1_a;  uint16_t TProcess::Resp1_t;
int32_t  TProcess::Fall1_a;  uint16_t TProcess::Fall1_t;
uint16_t TProcess::Hold1_a;  uint16_t TProcess::Hold1_t;
int32_t  TProcess::MFall1_a; uint16_t TProcess::MFall1_t;
uint16_t TProcess::MHold1_a; uint16_t TProcess::MHold1_t;

int32_t  TProcess::Integ2_a; uint16_t TProcess::Integ2_t;
int32_t  TProcess::Decay2_a; uint16_t TProcess::Decay2_t;
int32_t  TProcess::Scale2_a; uint16_t TProcess::Scale2_f;
int32_t  TProcess::Resp2_a;  uint16_t TProcess::Resp2_t;
int32_t  TProcess::Fall2_a;  uint16_t TProcess::Fall2_t;
uint16_t TProcess::Hold2_a;  uint16_t TProcess::Hold2_t;
int32_t  TProcess::MFall2_a; uint16_t TProcess::MFall2_t;
uint16_t TProcess::MHold2_a; uint16_t TProcess::MHold2_t;

//------------------------------ Конструктор: --------------------------------

TProcess::TProcess(chan_t chan)
{
  Adc = new TAdc(chan, Buff);
  //DC removal filter:
  DCRF_acc = 0;
  DCRF_in_1 = 0;
  DCRF_out_1 = 0;
  ResetFilters();
}

//----------------------------------------------------------------------------
//-------------------------- Выполнение обработки: ---------------------------
//----------------------------------------------------------------------------

bool TProcess::Execute(void)
{
  int16_t Offs = Adc->State();
  if(Offs >= 0)
  {

//------------------------------ TS_MS domain: -------------------------------
//----------------------------------------------------------------------------

    uint16_t *Pnt = Buff + Offs;
    for(int i = 0; i < ADC_DCM; i++)
    {

//---------------------------- DC removal filter: ----------------------------

      int16_t DCRF_in = *Pnt++;
      DCRF_acc = LO_W(DCRF_acc) + (int32_t)DCRF_out_1 * DCRF_A;
      DCRF_out_1 = DCRF_in - DCRF_in_1 + HI_W(DCRF_acc);
      DCRF_in_1 = DCRF_in;

//-------------------------------- Detector: ---------------------------------

      DET_out = ABS(DCRF_out_1);

//------------------------ Integration filter (bar): -------------------------

      int16_t Integ1_d = DET_out - HI_W(Integ1_acc);
      if(Integ1_d > 0) Integ1_acc = Integ1_acc + Integ1_d * Integ1_a;

//----------------------- Integration filter (peak): -------------------------

      int16_t Integ2_d = DET_out - HI_W(Integ2_acc);
      if(Integ2_d > 0) Integ2_acc = Integ2_acc + Integ2_d * Integ2_a;
    }

//--------------------------- TD_MS domain (bar): ----------------------------
//----------------------------------------------------------------------------

//-------------------------------- Scaling: ----------------------------------

    int32_t a1 = HI_W(Integ1_acc) * Scale1_a;
    if(a1 > LEV_LIM) a1 = LEV_LIM;
    int16_t Resp1_in = a1 >> 12;

//---------------------------- Response filter: ------------------------------

    int16_t Resp1_d = Resp1_in - HI_W(Resp1_acc);
    if(Resp1_d >= 0) { Resp1_acc = Resp1_acc + Resp1_d * Resp1_a; Hold1_tmr = 0; }

//------------------------------ Decay filter: -------------------------------

    if(HI_W(Resp1_acc) > HI_W(Resp1_in * M1DB))
      Integ1_acc = Integ1_acc - HI_W(Integ1_acc) * Decay1_a;

//------------------------------ Fall filter: --------------------------------

    if(Hold1_tmr < Hold1_a) Hold1_tmr++;
      else Resp1_acc = Resp1_acc - HI_W(Resp1_acc) * Fall1_a;

//------------------------------- Max filter: --------------------------------

    if(MHold1_a)
    {
      if(Max1_acc < Resp1_acc) { Max1_acc = Resp1_acc; MHold1_tmr = 0; }

//----------------------------- Max fall filter: -----------------------------

      if(MHold1_tmr < MHold1_a) MHold1_tmr++;
        else Max1_acc = Max1_acc - HI_W(Max1_acc) * MFall1_a;
    } else Max1_acc = 0;

//----------------------------- Abs max filter: ------------------------------

    if(AMax1_acc < Resp1_acc) AMax1_acc = Resp1_acc;

//----------------------------- TD_MS domain (peak): -------------------------
//----------------------------------------------------------------------------

//-------------------------------- Scaling: ----------------------------------

    int32_t a2 = HI_W(Integ2_acc) * Scale2_a;
    if(a2 > LEV_LIM) a2 = LEV_LIM;
    int16_t Resp2_in = a2 >> 12;

//---------------------------- Response filter: ------------------------------

    int16_t Resp2_d = Resp2_in - HI_W(Resp2_acc);
    if(Resp2_d >= 0) { Resp2_acc = Resp2_acc + Resp2_d * Resp2_a; Hold2_tmr = 0; }

//------------------------------ Decay filter: -------------------------------

    if(HI_W(Resp2_acc) > HI_W(Resp2_in * M1DB))
      Integ2_acc = Integ2_acc - HI_W(Integ2_acc) * Decay2_a;

//------------------------------ Fall filter: --------------------------------

    if(Hold2_tmr < Hold2_a) Hold2_tmr++;
      else Resp2_acc = Resp2_acc - HI_W(Resp2_acc) * Fall2_a;

//------------------------------- Max filter: --------------------------------

    if(MHold2_a)
    {
      if(Max2_acc < Resp2_acc) { Max2_acc = Resp2_acc; MHold2_tmr = 0; }

//----------------------------- Max fall filter: -----------------------------

      if(MHold2_tmr < MHold2_a) MHold2_tmr++;
        else Max2_acc = Max2_acc - HI_W(Max2_acc) * MFall2_a;
    } else Max2_acc = 0;

//----------------------------- Abs max filter: ------------------------------

    if(AMax2_acc < Resp2_acc) AMax2_acc = Resp2_acc;

    return(1);
  }
  return(0);
}

//----------------------------------------------------------------------------
//------------------------- Интерфейсные методы: -----------------------------
//----------------------------------------------------------------------------

//------------------------- Сброс всех фильтров: -----------------------------

void TProcess::ResetFilters(void)
{
  //bar filters:
  Integ1_acc = 0;
  Resp1_acc  = 0;
  Hold1_tmr  = 0;
  Max1_acc   = 0;
  MHold1_tmr = 0;
  AMax1_acc  = 0;
  //peak filters:
  Integ2_acc = 0;
  Resp2_acc  = 0;
  Hold2_tmr  = 0;
  Max2_acc   = 0;
  MHold2_tmr = 0;
  AMax2_acc  = 0;
}

//--------------------- Сброс абсолютных максимумов: -------------------------

void TProcess::ResetAbsMax(void)
{
  AMax1_acc = 0;
  AMax2_acc = 0;
}

//------------------ Перевод параметров в коэффициенты: ----------------------

void TProcess::ApplyParams(void)
{
  Integ1_a = Integ1_t? (INTEG_K / Integ1_t) : SCALE16;
  Decay1_a = Decay1_t? (DECAY_K / Decay1_t) : SCALE16;
  Scale1_a = SCALE_K * Scale1_f / SCALE12;
  Resp1_a  = Resp1_t? (RESP_K / Resp1_t) : SCALE16;
  Fall1_a  = Fall1_t? (FALL_K / Fall1_t) : SCALE16;
  Hold1_a  = HOLD_K * Hold1_t / SCALE16;
  MFall1_a = MFall1_t? (FALL_K / MFall1_t) : SCALE16;
  MHold1_a = HOLD_K * MHold1_t / SCALE16;

  Integ2_a = Integ2_t? (INTEG_K / Integ2_t) : SCALE16;
  Decay2_a = Decay2_t? (DECAY_K / Decay2_t) : SCALE16;
  Scale2_a = SCALE_K * Scale2_f / SCALE12;
  Resp2_a  = Resp2_t? (RESP_K / Resp2_t) : SCALE16;
  Fall2_a  = Fall2_t? (FALL_K / Fall2_t) : SCALE16;
  Hold2_a  = HOLD_K * Hold2_t / SCALE16;
  MFall2_a = MFall2_t? (FALL_K / MFall2_t) : SCALE16;
  MHold2_a = HOLD_K * MHold2_t / SCALE16;
}

//--------------------- Чтение параметров из EEPROM: -------------------------

void TProcess::EERead(uint8_t p)
{
  Integ1_t = Eeprom->Read(EE_INTEG1_T, INTEG1_P[p]);
  Decay1_t = Eeprom->Read(EE_DECAY1_T, DECAY1_P[p]);
  Scale1_f = Eeprom->Read(EE_SCALE1_T, SCALE1_P[p]);
  Resp1_t  = Eeprom->Read(EE_RESP1_T,  RESP1_P[p]);
  Fall1_t  = Eeprom->Read(EE_FALL1_T,  FALL1_P[p]);
  Hold1_t  = Eeprom->Read(EE_HOLD1_T,  HOLD1_P[p]);
  MFall1_t = Eeprom->Read(EE_MFALL1_T, MFALL1_P[p]);
  MHold1_t = Eeprom->Read(EE_MHOLD1_T, MHOLD1_P[p]);

  Integ2_t = Eeprom->Read(EE_INTEG2_T, INTEG2_P[p]);
  Decay2_t = Eeprom->Read(EE_DECAY2_T, DECAY2_P[p]);
  Scale2_f = Eeprom->Read(EE_SCALE2_T, SCALE2_P[p]);
  Resp2_t  = Eeprom->Read(EE_RESP2_T,  RESP2_P[p]);
  Fall2_t  = Eeprom->Read(EE_FALL2_T,  FALL2_P[p]);
  Hold2_t  = Eeprom->Read(EE_HOLD2_T,  HOLD2_P[p]);
  MFall2_t = Eeprom->Read(EE_MFALL2_T, MFALL2_P[p]);
  MHold2_t = Eeprom->Read(EE_MHOLD2_T, MHOLD2_P[p]);

  ApplyParams();
}

//------------------- Сохранение параметров в EEPROM: ------------------------

void TProcess::EESave(void)
{
  Eeprom->Write(EE_INTEG1_T, Integ1_t);
  Eeprom->Write(EE_DECAY1_T, Decay1_t);
  Eeprom->Write(EE_SCALE1_T, Scale1_f);
  Eeprom->Write(EE_RESP1_T,  Resp1_t);
  Eeprom->Write(EE_FALL1_T,  Fall1_t);
  Eeprom->Write(EE_HOLD1_T,  Hold1_t);
  Eeprom->Write(EE_MFALL1_T, MFall1_t);
  Eeprom->Write(EE_MHOLD1_T, MHold1_t);

  Eeprom->Write(EE_INTEG2_T, Integ2_t);
  Eeprom->Write(EE_DECAY2_T, Decay2_t);
  Eeprom->Write(EE_SCALE2_T, Scale2_f);
  Eeprom->Write(EE_RESP2_T,  Resp2_t);
  Eeprom->Write(EE_FALL2_T,  Fall2_t);
  Eeprom->Write(EE_HOLD2_T,  Hold2_t);
  Eeprom->Write(EE_MFALL2_T, MFall2_t);
  Eeprom->Write(EE_MHOLD2_T, MHold2_t);
}

//----------------------------------------------------------------------------
