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

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

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

//Класс TProcess выполняет обработку данных для одного канала измерителя.
//Для получения данных используется класс TAdc. Метод Execute() должен
//вызываться в основном цикле. Если при очередном вызове данные АЦП готовы,
//то производится их обработка и метод возвращает 1. Если данные не готовы,
//метод возвращает 0. Обработка ведется в двух клок-доменах: на частоте
//дискретизации 96 кГц (TS_MS domain) и после децимации на частоте
//96000/256 = 375 Гц (TD_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-разрядных аккумуляторах INF1_acc и INF2_acc.
//Фильтры работают только при нарастании сигнала. Постоянные времени
//определяются коэффициентами INF1_a и INF2_a. Коэффициенты можно задавать с
//помощью методов SetINF1_a(A) и SetINF2_a(A). Параметры методов - беззнаковые
//16-разрядные числа A = 65535/96000/tau. Чтобы задать tau = 0 (True Peak),
//надо передать A = 65535.

//Дальнейшая обработка ведется в домене 96000/256 = 375 Гц (TD_MS domain).
//Она начинается с масштабирования сигнала, которое необходимо для выравнивания
//показаний столбиков и точек на стационарном сигнале при разных постоянных
//времени интеграции или обратного хода. Выходной 11-разрядный код
//предыдущих фильтров содержится в старших словах INF1_acc и INF2_acc.
//Он умножается на коэффициенты SCL1_a и SCL2_a. Коэффициенты задаются с
//помощью методов SetSCL1_a(A) и SetSCL2_a(A). Для получения единичного
//коэффициента передачи надо задать A = 0x04FF. При этом диапазон кода после
//масштабирования составит от 0 до 10227. Это номинальный диапазон. С учетом
//некоторого запаса на возможные ошибки округления, максимальный код таблицы
//перекодировки выбран 10000. Максимальное значение коэффициента A = 0x0FFF
//ограничено возможным переполнением и соответствует усилению примерно 3.2.
//Величина усиления для разных значений коэффициента приведена ниже:
//   A = 0x0000 ... 0x04FF ... 0x0FFF
//GAIN =    0.0 ...    1.0 ...    3.2

//Далее следуют два IIR-фильтра, которые формируют время срабатывания.
//Он работает в "медленном" домене. Выходной код этих фильтров непосредственно
//связан с показаниями измерителя. Накопление производится в 32-разрядных
//аккумуляторах RSF1_acc и RSF2_acc. Фильтры работают только при нарастании
//сигнала. Постоянные времени определяются коэффициентами RSF1_a и RSF2_a.
//Коэффициенты можно задавать с помощью методов SetRSF1_a(A) и SetRSF2_a(A).
//Параметры методов - беззнаковые 16-разрядные числа A = 65535/375/tau.
//Чтобы задать tau = 0, надо передать A = 65535. В этом случае время
//срабатывания будет равно времени интеграции.

//Когда выходной код фильтра срабатывания достигает входного кода с точностью
//1 дБ, то включается фильтр обратного хода, воздействующий на аккумуляторы
//фильтров интеграции INF1_acc и INF2_acc. Он работает в "медленном" домене.
//Постоянные времени обратного хода определяются коэффициентами DEF1_a и DEF2_a.
//Коэффициенты можно задавать с помощью методов SetDEF1_a(A) и SetDEF2_a(A).
//Параметры методов - беззнаковые 16-разрядные числа A = 65535/375/tau.
//Чтобы задать tau = 0, надо передать A = 65535.

//Показания измерителя могут иметь некоторое время удержания, когда после
//пика сигнала показания остаются неизменными. Это время задается с помощью
//коэффициентов HLD1_a и HLD2_a. Коэффициенты можно задавать с помощью методов
//SetHLD1_a(A) и SetHLD2_a(A). Задержка задается в единицах периода
//дискретизации домена TD_MS (2.667 мс). Если задать нулевую задержку,
//удержание показаний осуществляться не будет, и сразу после пика сигнала
//будет начинаться обратный ход.

//Когда время удержания заканчивается, начинается обратный ход измерителя.
//Фильтр обратного хода воздействует на аккумуляторы фильтров срабатывания
//RSF1_acc и RSF2_acc. Он работает в "медленном" домене. Постоянные времени
//обратного хода определяются коэффициентами FLF1_a и FLF2_a. Коэффициенты можно
//задавать с помощью методов SetFLF1_a(A) и SetFLF2_a(A). Параметры методов -
//беззнаковые 16-разрядные числа A = 65535/375/tau. Чтобы задать tau = 0,
//надо передать A = 65535.

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

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

//Для стандартного квазипикового измерителя задается 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 дБ).

//Возможен режим работы, когда измеритель пикового уровня использует не
//отдельный канал измерения, а фиксирует максимальные показания среднего уровня.
//Для включения такого режима надо задать время срабатывания SetRSF2_a(65535).
//При этом пиковый канал будет использоваться код аккумулятора RSF1_aсс,
//время срабатывания будет такое же, как у канала среднего уровня.
//Время удержания и время обратного хода можно задавать произвольно.

//Для полного отключения пикововой индикации надо задать SetSCL2_a(0).

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

#include "main.hpp"
#include "process.hpp"

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

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

int32_t  TProcess::INF1_a;
int32_t  TProcess::INF2_a;
int32_t  TProcess::DEF1_a;
int32_t  TProcess::DEF2_a;
int32_t  TProcess::RSF1_a;
int32_t  TProcess::RSF2_a;
int32_t  TProcess::FLF1_a;
int32_t  TProcess::FLF2_a;
int32_t  TProcess::SCL1_a;
int32_t  TProcess::SCL2_a;
uint16_t TProcess::HLD1_a;
uint16_t TProcess::HLD2_a;

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

TProcess::TProcess(chan_t chan)
{
  Adc = new TAdc(chan, Buff);
  Pin_TEST.Init(OUT_PP_50M, OUT_LO);//TODO
  Dac1.Init(); //TODO
  Dac2.Init(); //TODO
  Chan = chan; //TODO

  //DC removal filter:
  DCRF_acc = 0;
  DCRF_in_1 = 0;
  DCRF_out_1 = 0;
  //Integration filter 1:
  INF1_acc = 0;
  SetINF1_a(INF1_A); //5ms/-2dB
  //Integration filter 2:
  INF2_acc = 0;
  SetINF2_a(INF2_A); //0ms (True Peak)
  //Response filter 1:
  RSF1_acc = 0;
  SetRSF1_a(RSF1_A); //100ms/-1dB
  //Response filter 2:
  RSF2_acc = 0;
  SetRSF2_a(RSF2_A); //100ms/-1dB
  //Hold 1:
  HLD1_acc = 0;
  SetHLD1_a(HLD1_A); //20ms
  //Hold 2:
  HLD2_acc = 0;
  SetHLD2_a(HLD2_A); //2000ms
  //Decay filter 1:
  SetDEF1_a(DEF1_A); //1700ms
  //Decay filter 2:
  SetDEF2_a(DEF2_A); //0ms
  //Scale 1:
  SetSCL1_a(SCL1_A); //1.0
  //Scale 2:
  SetSCL2_a(SCL2_A); //1.0
}

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

bool TProcess::Execute(void)
{
  int16_t Offs = Adc->State();
  if(Offs >= 0)
  {
    Pin_TEST = 1; //TODO
    //------ 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 INF1_d = DET_out - HI_W(INF1_acc);
      if(INF1_d > 0) INF1_acc = INF1_acc + INF1_d * INF1_a;
      //Integration filter (peak):
      int16_t INF2_d = DET_out - HI_W(INF2_acc);
      if(INF2_d > 0) INF2_acc = INF2_acc + INF2_d * INF2_a;
    }
    //------ TD_MS domain (bar) ------
    //Scaling (bar):
    int16_t RSF1_in = HI_W(HI_W(INF1_acc) * SCL1_a);
    //Response filter (bar):
    int16_t RSF1_d = RSF1_in - HI_W(RSF1_acc);
    if(RSF1_d >= 0) { RSF1_acc = RSF1_acc + RSF1_d * RSF1_a; HLD1_acc = 0; }
    //Decay filter (bar):
    if(HI_W(RSF1_acc) > HI_W(RSF1_in * M1DB))
      INF1_acc = INF1_acc - HI_W(INF1_acc) * DEF1_a;
    //Fall filter (bar):
    if(HLD1_acc < HLD1_a) HLD1_acc++;
      else RSF1_acc = RSF1_acc - HI_W(RSF1_acc) * FLF1_a;
    //------ TD_MS domain (peak) ------
    //Scaling (peak):
    int16_t RSF2_in = HI_W(HI_W(INF2_acc) * SCL2_a);
    //Response filter (peak):
    if(RSF2_a > UINT16_MAX) //bar hold mode
    {
      if(RSF2_acc < RSF1_acc) { RSF2_acc = RSF1_acc; HLD2_acc = 0; }
    }
    else //peak mode
    {
      int16_t RSF2_d = RSF2_in - HI_W(RSF2_acc);
      if(RSF2_d >= 0) { RSF2_acc = RSF2_acc + RSF2_d * RSF2_a; HLD2_acc = 0; }
    }
    //Decay filter (peak):
    if(HI_W(RSF2_acc) > HI_W(RSF2_in * M1DB))
      INF2_acc = INF2_acc - HI_W(INF2_acc) * DEF2_a;
    //Fall filter (peak):
    if(HLD2_acc < HLD2_a) HLD2_acc++;
      else RSF2_acc = RSF2_acc - HI_W(RSF2_acc) * FLF2_a;
    if(Chan == CHANL) //TODO
    {
      Dac1 = HI_W(RSF1_acc); //TODO
      Dac2 = HI_W(RSF2_acc); //TODO
    }

    Pin_TEST = 0; //TODO
    return(1);
  }
  return(0);
}

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

//--------------------- Чтение кода среднего уровня: -------------------------

int16_t TProcess::GetBar(void)
{
  return(HI_W(RSF1_acc));
}

//--------------------- Чтение кода пикового уровня: -------------------------

int16_t TProcess::GetPeak(void)
{
  return(HI_W(RSF2_acc));
}

//----------------------- Установка коэффициентов: ---------------------------

void TProcess::SetINF1_a(uint16_t v) { INF1_a = (int32_t)v + 1; }
void TProcess::SetINF2_a(uint16_t v) { INF2_a = (int32_t)v + 1; }
void TProcess::SetDEF1_a(uint16_t v) { DEF1_a = (int32_t)v + 1; }
void TProcess::SetDEF2_a(uint16_t v) { DEF2_a = (int32_t)v + 1; }
void TProcess::SetRSF1_a(uint16_t v) { RSF1_a = (int32_t)v + 1; }
void TProcess::SetRSF2_a(uint16_t v) { RSF2_a = (int32_t)v + 1; }
void TProcess::SetFLF1_a(uint16_t v) { FLF1_a = (int32_t)v + 1; }
void TProcess::SetFLF2_a(uint16_t v) { FLF2_a = (int32_t)v + 1; }
void TProcess::SetSCL1_a(uint16_t v) { SCL1_a = (int32_t)v * 0x100; }
void TProcess::SetSCL2_a(uint16_t v) { SCL2_a = (int32_t)v * 0x100; }
void TProcess::SetHLD1_a(uint16_t v) { HLD1_a = (int32_t)v; }
void TProcess::SetHLD2_a(uint16_t v) { HLD2_a = (int32_t)v; }

//------------------------ Чтение коэффициентов: -----------------------------

uint16_t TProcess::GetINF1_a(void) { return(INF1_a - 1); }
uint16_t TProcess::GetINF2_a(void) { return(INF2_a - 1); }
uint16_t TProcess::GetDEF1_a(void) { return(DEF1_a - 1); }
uint16_t TProcess::GetDEF2_a(void) { return(DEF2_a - 1); }
uint16_t TProcess::GetRSF1_a(void) { return(RSF1_a - 1); }
uint16_t TProcess::GetRSF2_a(void) { return(RSF2_a - 1); }
uint16_t TProcess::GetFLF1_a(void) { return(FLF1_a - 1); }
uint16_t TProcess::GetFLF2_a(void) { return(FLF2_a - 1); }
uint16_t TProcess::GetSCL1_a(void) { return(SCL1_a / 0x100); }
uint16_t TProcess::GetSCL2_a(void) { return(SCL2_a / 0x100); }
uint16_t TProcess::GetHLD1_a(void) { return(HLD1_a); }
uint16_t TProcess::GetHLD2_a(void) { return(HLD2_a); }

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

//TODO: uint8_t pre - preset number

void TProcess::EERead(void)
{
  SetINF1_a(Eeprom->Read(EE_INF1_A, INF1_A));
  SetINF2_a(Eeprom->Read(EE_INF2_A, INF2_A));
  SetDEF1_a(Eeprom->Read(EE_DEF1_A, DEF1_A));
  SetDEF2_a(Eeprom->Read(EE_DEF2_A, DEF2_A));
  SetRSF1_a(Eeprom->Read(EE_RSF1_A, RSF1_A));
  SetRSF2_a(Eeprom->Read(EE_RSF2_A, RSF2_A));
  SetFLF1_a(Eeprom->Read(EE_FLF1_A, FLF1_A));
  SetFLF2_a(Eeprom->Read(EE_FLF2_A, FLF2_A));
  SetSCL1_a(Eeprom->Read(EE_SCL1_A, SCL1_A));
  SetSCL2_a(Eeprom->Read(EE_SCL2_A, SCL2_A));
  SetHLD1_a(Eeprom->Read(EE_HLD1_A, HLD1_A));
  SetHLD2_a(Eeprom->Read(EE_HLD2_A, HLD2_A));
}

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

//TODO: uint8_t pre - preset number

void TProcess::EESave(void)
{
  Eeprom->Update(EE_INF1_A, GetINF1_a());
  Eeprom->Update(EE_INF2_A, GetINF2_a());
  Eeprom->Update(EE_DEF1_A, GetDEF1_a());
  Eeprom->Update(EE_DEF2_A, GetDEF2_a());
  Eeprom->Update(EE_RSF1_A, GetRSF1_a());
  Eeprom->Update(EE_RSF2_A, GetRSF2_a());
  Eeprom->Update(EE_FLF1_A, GetFLF1_a());
  Eeprom->Update(EE_FLF2_A, GetFLF2_a());
  Eeprom->Update(EE_SCL1_A, GetSCL1_a());
  Eeprom->Update(EE_SCL2_A, GetSCL2_a());
  Eeprom->Update(EE_HLD1_A, GetHLD1_a());
  Eeprom->Update(EE_HLD2_A, GetHLD2_a());
}

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