The software can be divided into several parts, from reading, saving, analyzing up to visualization of data in different forms.

All source code is Open Source, free available and can be downloaded below. The source code is licensed under the GNU Lesser General Public License version 3 with linking exception, so you can use it in your own projects.

This software version is using the SPI bus in order to control the ADS1262.

The development of this SPI version was much more complicated than the I²C version for the ADS1115. Several little problems had to be solved, e.g. the SPI mode of the ADS1262 is not 0 but 1. Of course, this information is listed in the datasheet on page 113 out of 133 pages, but you need to find and to recognize that too. This problem caused many headaches and blank despair, as nothing reasonable came out of the ADS1262 until the SPI mode was set to 1. Also the functionality, orders, registers and parameters are much higher than on the ADS1115. Last not least, the electrical coupling of the input channels are different to the ADS1115. The only friend in many hours of labor and measurement was the oscilloscope, which worked without complaining and contradiction, simply displaying the naked truth.

Nevertheless, the ADS1262 is an awesome and outstanding ADC and as soon as you get it running, you will get addicted to it (like a V8 engine 😉 ) and it’s sensitivity.

 

Download

The software can be downloaded here and be copied to any directory. The choosen path of the used directory needs to be adjusted in go.sh.

Reading sensor data

The sensor data from the reading thread is processed in a separate thread by a loop with a frequency of around 4 Hz, so the display is updated ones in 4 seconds. All data collected since the last iteration is analysed, the results added to a chart component and the visible section of the x-axis moved to the right.

For more information take a look at the data sheet:
http://www.ti.com/general/docs/lit/getliterature.tsp?genericPartNumber=ads1262&fileType=pdf.

ADS1262_Unit

The file ads1262_unit.pas contains the source code for controlling the ADS1262. The ADS1262 is represented by the class T_ADS1262, which is defined and implemented in this file.

The status of the object is hidden inside the physical IC, and not represented by member variables in order to prevent redundancy. The status of the ADS1262 can be changed by calling commands and by setting its registers.

Inside the System_Offset_Self_Calibration an initial value is written into the OFCAL register. Normally, this should not be needed, but it can increase your noise-free resolution, if ac auto calibration can not be used, e.g. when measuring dc voltages.The value for the OFCAL register can be user determined by short-circuit the inputs AIN0 and AINCOM with a wire, i.e. both inputs gets the same potential. This way, the measured result now should be zero. Instead of receiving zeros, a noise stream is measured. This noise signal should have a zero mean. Most the time the signal doesn’t have a zero mean without adapting OFCAL. The OFCAL value can be changed until the signal has a zero mean.The ac auto calibration offers a similar functionality for ac signals, which have a zero mean, e.g. the signal of a coil which has an induced voltage.

{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## ADS1262_Unit                                                                      ## }
{ ##                                                                                   ## }
{ ## Library for ADS1262                                                               ## }
{ ## Based on:                                                                         ## }
{ ## - Timm Thaler: http://wiki.lazarus.freepascal.org/index.php?title=                ## }
{ ##        Raspberry_Pi_-_SPI/de&oldid=113577,                                        ## }
{ ## - Professor Plate: http://www.netzmafia.de/skripten/hardware/RasPi/RasPi_SPI.html,## }
{ ## - Blake Felt library: https://github.com/Molorius/ADS126X and                     ## }
{ ## - ProtoCentral library: https://github.com/Protocentral/ProtoCentral_ads1262.     ## }
{ ## - pascalio library: https://github.com/SAmeis/pascalio                            ## }
{ ##                                                                                   ## }
{ ## Copyright (C) 2018-2019  : Dr. Jürgen Abel                                        ## }
{ ## Email                    : juergen@mve.info                                       ## }
{ ## Internet                 : www.seismometer.info                                   ## }
{ ##                                                                                   ## }
{ ## This program is free software: you can redistribute it and/or modify              ## }
{ ## it under the terms of the GNU Lesser General Public License as published by       ## }
{ ## the Free Software Foundation, either version 3 of the License, or                 ## }
{ ## (at your option) any later version with the following modification:               ## }
{ ##                                                                                   ## }
{ ## As a special exception, the copyright holders of this library give you            ## }
{ ## permission to link this library with independent modules to produce an            ## }
{ ## executable, regardless of the license terms of these independent modules, and     ## }
{ ## to copy and distribute the resulting executable under terms of your choice,       ## }
{ ## provided that you also meet, for each linked independent module, the terms        ## }
{ ## and conditions of the license of that module. An independent module is a          ## }
{ ## module which is not derived from or based on this library. If you modify          ## }
{ ## this library, you may extend this exception to your version of the library,       ## }
{ ## but you are not obligated to do so. If you do not wish to do so, delete this      ## }
{ ## exception statement from your version.                                            ## }
{ ##                                                                                   ## }
{ ## This program is distributed in the hope that it will be useful,                   ## }
{ ## but WITHOUT ANY WARRANTY; without even the implied warranty of                    ## }
{ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                     ## }
{ ## GNU General Public License for more details.                                      ## }
{ ##                                                                                   ## }
{ ## You should have received a copy of the GNU Lesser General Public License          ## }
{ ## COPYING.LGPL.txt along with this program.                                         ## }
{ ## If not, see https://www.gnu.org/licenses/                                         ## }
{ ##                                                                                   ## }
{ ####################################################################################### }

Unit ADS1262_Unit;

{$mode objfpc}{$H+}

{$INCLUDE project_defines.inc}

Interface

Uses
  CThreads,
  BaseUnix,
  Classes;

Const
  ADS1262_REGISTER_N = $15;

Type
  { ####################################################################################### }
  { ## Number types                                                                      ## }
  { ####################################################################################### }
  Int_8     = Int8;
  P_Int_8   = ^Int_8;
  U_Int_8   = UInt8;
  P_U_Int_8 = ^U_Int_8;
  Char      = Int_8;
  P_Char    = ^Char;
  U_Char    = U_Int_8;
  P_U_Char  = ^U_Char;

  Int_16     = Int16;
  P_Int_16   = ^Int_16;
  U_Int_16   = UInt16;
  P_U_Int_16 = ^U_Int_16;

  Int_32     = Int32;
  P_Int_32   = ^Int_32;
  U_Int_32   = UInt32;
  P_U_Int_32 = ^U_Int_32;

  Int_64     = Int64;
  P_Int_64   = ^Int_64;
  U_Int_64   = UInt64;
  P_U_Int_64 = ^U_Int_64;

  Bool   = Bytebool;
  P_Bool = ^Bool;

  T_GPIO_Direction            = (GPIO_DIRECTION_IN, GPIO_DIRECTION_OUT);
  T_GPIO_Interrupt_Mode_Types = (GPIO_INTERRUPT_RISING, GPIO_INTERRUPT_FALLING);
  T_GPIO_Interrupt_Mode       = Set Of T_GPIO_Interrupt_Mode_Types;

  T_Buffer_A = Array Of U_Int_8;

  T_SPI_Transfer_R = Record
      tx_buf :        U_Int_64;
      rx_buf :        U_Int_64;
      len :           U_Int_32;
      speed_hz :      U_Int_32;
      delay_usecs :   U_Int_16;
      bits_per_word : U_Int_8;
      cs_change :     U_Int_8;
      pad :           U_Int_32;
  End; { T_SPI_Transfer_R }

  T_Register_Block = Array [0 .. ADS1262_REGISTER_N - 1] Of U_Int_8;

Const
  { ####################################################################################### }
  { ## GPIO data                                                                         ## }
  { ####################################################################################### }
  GPIO_LINUX_BASE_DIR             = '/sys/class/gpio/';
  GPIO_LINUX_GPIOPIN_DIR          = GPIO_LINUX_BASE_DIR + 'gpio%d/';
  GPIO_LINUX_EXPORT_FILE          = GPIO_LINUX_BASE_DIR + 'export';
  GPIO_LINUX_UNEXPORT_FILE        = GPIO_LINUX_BASE_DIR + 'unexport';
  GPIO_DRDY_PIN                   = 25;
  GPIO_WAIT_TIME_MS_EXPORT        = 100;
  GPIO_WAIT_TIME_MS_COMMAND       = 10;
  GPIO_TIMEOUT_INTERRUPTMODE_MS   = 100;
  GPIO_TIMEOUT_INTERRUPT_INFINITE = -1;
  GPIO_INTERRUPTMODE_MS           = 100;

  { ####################################################################################### }
  { ## SPI general data                                                                  ## }
  { ####################################################################################### }
  SPI_IOC_DEVICE               = '/dev/spidev0.0';
  SPI_IOC_WAIT_TIME_MS_OPEN    = 100;
  SPI_IOC_WAIT_TIME_MS_COMMAND = 10;

  { ####################################################################################### }
  { ## SPI request commands                                                              ## }
  { ####################################################################################### }
  SPI_IOC_MODE_READ  = $80;
  SPI_IOC_MODE_WRITE = $40;
  SPI_IOC_MODE_CTRL  = $6B;

  SPI_IOC_RD_MODE : U_Int_32          = (SPI_IOC_MODE_READ shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($01);       { $80016B01; }
  SPI_IOC_WR_MODE : U_Int_32          = (SPI_IOC_MODE_WRITE shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($01);      { $40016B01; }
  SPI_IOC_RD_BITS_PER_WORD : U_Int_32 = (SPI_IOC_MODE_READ shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($03);       { $80016B03; }
  SPI_IOC_WR_BITS_PER_WORD : U_Int_32 = (SPI_IOC_MODE_WRITE shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($03);      { $40016B03; }
  SPI_IOC_RD_LSB_FIRST : U_Int_32     = (SPI_IOC_MODE_READ shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($02);       { $80016B02; }
  SPI_IOC_WR_LSB_FIRST : U_Int_32     = (SPI_IOC_MODE_WRITE shl $18) or ($01 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($02);      { $40016B02; }
  SPI_IOC_RD_MAX_SPEED_HZ : U_Int_32  = (SPI_IOC_MODE_READ shl $18) or ($04 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($04);       { $80046B04; }
  SPI_IOC_WR_MAX_SPEED_HZ : U_Int_32  = (SPI_IOC_MODE_WRITE shl $18) or ($04 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($04);      { $40046B04; }
  SPI_IOC_WR_TRANSFER : U_Int_32      = (SPI_IOC_MODE_WRITE shl $18) or ($20 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($00);      { $40206B00; }
  SPI_IOC_RD_MODE32 : U_Int_32        = (SPI_IOC_MODE_READ shl $18) or ($04 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($05);       { $80046B05; }
  SPI_IOC_WR_MODE32 : U_Int_32        = (SPI_IOC_MODE_WRITE shl $18) or ($04 shl $10) or (SPI_IOC_MODE_CTRL shl $08) or ($05);      { $40046B05; }

  { ####################################################################################### }
  { ## SPI modes                                                                         ## }
  { ####################################################################################### }
  SPI_IOC_CPHA      = $01;  { Clock phase }
  SPI_IOC_CPOL      = $02;  { Clock polarity }
  SPI_IOC_CS_HIGH   = $04;  { Chip Select active high }
  SPI_IOC_LSB_FIRST = $08;  { Least significant bit first }
  SPI_IOC_3WIRE     = $10;  { SI/SO signals shared }
  SPI_IOC_LOOP      = $20;  { Loopback }
  SPI_IOC_NO_CS     = $40;  { no Chip Select }
  SPI_IOC_READY     = $80;  { Slave pulls low to pause }

  SPI_IOC_MODE_0 = (0 * SPI_IOC_CPOL) or (0 * SPI_IOC_CPHA);
  { Clock idle low, data is clocked in on rising edge, output data change on falling edge }
  SPI_IOC_MODE_1 = (0 * SPI_IOC_CPOL) or (1 * SPI_IOC_CPHA);
  { Clock idle low, data is clocked in on falling edge, output data change on rising edge }
  SPI_IOC_MODE_2 = (1 * SPI_IOC_CPOL) or (0 * SPI_IOC_CPHA);
  { Clock idle high, data is clocked in on falling edge, output data change on rising edge }
  SPI_IOC_MODE_3 = (1 * SPI_IOC_CPOL) or (1 * SPI_IOC_CPHA);
  { Clock idle high, data is clocked in on rising, edge output data change on falling edge }

  { ####################################################################################### }
  { ## SPI settings                                                                      ## }
  { ####################################################################################### }
  SPI_IOC_DEFAULT_MODE          = SPI_IOC_MODE_1;
  SPI_IOC_DEFAULT_BITS_PER_WORD = 8;
  SPI_IOC_DEFAULT_LSB_FIRST     = FALSE;
  SPI_IOC_DEFAULT_SPEED         = 8 * 1000 * 1000;

  { ####################################################################################### }
  { ## ADS1262 registers                                                                 ## }
  { ####################################################################################### }
  ADS1262_REGISTER_ID        = $00;
  ADS1262_REGISTER_POWER     = $01;
  ADS1262_REGISTER_INTERFACE = $02;
  ADS1262_REGISTER_MODE0     = $03;
  ADS1262_REGISTER_MODE1     = $04;
  ADS1262_REGISTER_MODE2     = $05;
  ADS1262_REGISTER_INPMUX    = $06;
  ADS1262_REGISTER_OFCAL0    = $07;
  ADS1262_REGISTER_OFCAL1    = $08;
  ADS1262_REGISTER_OFCAL2    = $09;
  ADS1262_REGISTER_FSCAL0    = $0A;
  ADS1262_REGISTER_FSCAL1    = $0B;
  ADS1262_REGISTER_FSCAL2    = $0C;
  ADS1262_REGISTER_IDACMUX   = $0D;
  ADS1262_REGISTER_IDACMAG   = $0E;
  ADS1262_REGISTER_REFMUX    = $0F;
  ADS1262_REGISTER_TDACP     = $10;
  ADS1262_REGISTER_TDACN     = $11;
  ADS1262_REGISTER_GPIOCON   = $12;
  ADS1262_REGISTER_GPIODIR   = $13;
  ADS1262_REGISTER_GPIODAT   = $14;

  { ####################################################################################### }
  { ## ADS1262 commands                                                                  ## }
  { ####################################################################################### }
  ADS1262_COMMAND_NOP     = $00;
  ADS1262_COMMAND_RESET   = $06;
  ADS1262_COMMAND_START1  = $08;
  ADS1262_COMMAND_STOP1   = $0A;
  ADS1262_COMMAND_RDATA1  = $12;
  ADS1262_COMMAND_SYOCAL1 = $16;
  ADS1262_COMMAND_SYGCAL1 = $17;
  ADS1262_COMMAND_SFOCAL1 = $19;
  ADS1262_COMMAND_RREG    = $20;
  ADS1262_COMMAND_WREG    = $40;

  { ####################################################################################### }
  { ## ADS1262 POWER bits                                                                ## }
  { ####################################################################################### }
  ADS1262_POWER_RESERVED_MASK     = $EC;
  ADS1262_POWER_RESET_MASK        = $10;
  ADS1262_POWER_VBIAS_MASK        = $02;
  ADS1262_POWER_INTREF_MASK       = $01;
  ADS1262_POWER_RESET_HAS_OCCURED = $10;
  ADS1262_POWER_VBIAS_ENABLED     = $02;
  ADS1262_POWER_VBIAS_DISABLED    = $00;
  ADS1262_POWER_INTREF_ENABLED    = $01;
  ADS1262_POWER_INTREF_DISABLED   = $00;

  { ####################################################################################### }
  { ## ADS1262 MODE0 bits                                                                ## }
  { ####################################################################################### }
  ADS1262_MODE0_REFREV_MASK                                    = $80;
  ADS1262_MODE0_RUNMODE_MASK                                   = $40;
  ADS1262_MODE0_CHOP_MASK                                      = $30;
  ADS1262_MODE0_DELAY_MASK                                     = $0F;
  ADS1262_MODE0_REFREV_NORMAL_POLARITY                         = $00;
  ADS1262_MODE0_RUNMODE_CONTINUOUS_CONVERSION                  = $00;
  ADS1262_MODE0_RUNMODE_PULSE_CONVERSION                       = $40;
  ADS1262_MODE0_CHOP_INPUT_CHOP_AND_IDAC_ROTATION_DISABLED     = $00;
  ADS1262_MODE0_CHOP_INPUT_CHOP_ENABLED_IDAC_ROTATION_DISABLED = $10;
  ADS1262_MODE0_DELAY_NO_DELAY                                 = $00;

  { ####################################################################################### }
  { ## ADS1262 MODE1 bits                                                                ## }
  { ####################################################################################### }
  ADS1262_MODE1_FILTER_MASK       = $E0;
  ADS1262_MODE1_SBADC_MASK        = $10;
  ADS1262_MODE1_SBPOL_MASK        = $08;
  ADS1262_MODE1_SBMAG_MASK        = $07;
  ADS1262_MODE1_FILTER_SINC1_MODE = $00;
  ADS1262_MODE1_FILTER_SINC2_MODE = $20;
  ADS1262_MODE1_FILTER_SINC3_MODE = $40;
  ADS1262_MODE1_FILTER_SINC4_MODE = $60;
  ADS1262_MODE1_FILTER_FIR_MODE   = $80;

  { ####################################################################################### }
  { ## ADS1262 MODE2 bits                                                                ## }
  { ####################################################################################### }
  ADS1262_MODE2_BYPASS_MASK         = $80;
  ADS1262_MODE2_GAIN_MASK           = $70;
  ADS1262_MODE2_DR_MASK             = $0F;
  ADS1262_MODE2_BYPASS_PGA_BYPASSED = $80;
  ADS1262_MODE2_BYPASS_PGA_ENABLED  = $00;
  ADS1262_MODE2_GAIN_01             = $00;  { Gain  1 : ±2.500 V }
  ADS1262_MODE2_GAIN_02             = $10;  { Gain  2 : ±1.250 V }
  ADS1262_MODE2_GAIN_04             = $20;  { Gain  4 : ±0.625 V }
  ADS1262_MODE2_GAIN_08             = $30;  { Gain  8 : ±0.312 V }
  ADS1262_MODE2_GAIN_16             = $40;  { Gain 16 : ±0.156 V }
  ADS1262_MODE2_GAIN_32             = $50;  { Gain 32 : ±0.078 V }
  ADS1262_MODE2_GAIN_01_LIMIT       = 2.5;
  ADS1262_MODE2_GAIN_02_LIMIT       = ADS1262_MODE2_GAIN_01_LIMIT / 2;
  ADS1262_MODE2_GAIN_04_LIMIT       = ADS1262_MODE2_GAIN_01_LIMIT / 4;
  ADS1262_MODE2_GAIN_08_LIMIT       = ADS1262_MODE2_GAIN_01_LIMIT / 8;
  ADS1262_MODE2_GAIN_16_LIMIT       = ADS1262_MODE2_GAIN_01_LIMIT / 16;
  ADS1262_MODE2_GAIN_32_LIMIT       = ADS1262_MODE2_GAIN_01_LIMIT / 32;
  ADS1262_MODE2_DR_2_5_SPS          = $00;  { Data rate : 2.5 SPS }
  ADS1262_MODE2_DR_5_SPS            = $01;  { Data rate : 5 SPS }
  ADS1262_MODE2_DR_10_SPS           = $02;  { Data rate : 10 SPS }
  ADS1262_MODE2_DR_16_6_SPS         = $03;  { Data rate : 16.6 SPS }
  ADS1262_MODE2_DR_20_SPS           = $04;  { Data rate : 20 SPS (old default) }
  ADS1262_MODE2_DR_50_SPS           = $05;  { Data rate : 50 SPS }
  ADS1262_MODE2_DR_60_SPS           = $06;  { Data rate : 60 SPS }
  ADS1262_MODE2_DR_100_SPS          = $07;  { Data rate : 100 SPS }
  ADS1262_MODE2_DR_400_SPS          = $08;  { Data rate : 400 SPS }
  ADS1262_MODE2_DR_1200_SPS         = $09;  { Data rate : 1,200 SPS }
  ADS1262_MODE2_DR_2400_SPS         = $0A;  { Data rate : 2,400 SPS }
  ADS1262_MODE2_DR_4800_SPS         = $0B;  { Data rate : 4,800 SPS }
  ADS1262_MODE2_DR_7200_SPS         = $0C;  { Data rate : 7,200 SPS }
  ADS1262_MODE2_DR_14400_SPS        = $0D;  { Data rate : 14,400 SPS }
  ADS1262_MODE2_DR_19200_SPS        = $0E;  { Data rate : 19,200 SPS }
  ADS1262_MODE2_DR_38400_SPS        = $0F;  { Data rate : 38,400 SPS }

  { ####################################################################################### }
  { ## ADS1262 INPMUX bits                                                               ## }
  { ####################################################################################### }
  ADS1262_INPMUX_MUXP_MASK       = $F0;
  ADS1262_INPMUX_MUXN_MASK       = $0F;
  ADS1262_INPMUX_MUXP_AIN0       = $00;  { Default }
  ADS1262_INPMUX_MUXP_AIN1       = $10;
  ADS1262_INPMUX_MUXP_AIN2       = $20;
  ADS1262_INPMUX_MUXP_AIN3       = $30;
  ADS1262_INPMUX_MUXP_AIN4       = $40;
  ADS1262_INPMUX_MUXP_AIN5       = $50;
  ADS1262_INPMUX_MUXP_AIN6       = $60;
  ADS1262_INPMUX_MUXP_AIN7       = $70;
  ADS1262_INPMUX_MUXP_AIN8       = $80;
  ADS1262_INPMUX_MUXP_AIN9       = $90;
  ADS1262_INPMUX_MUXP_AINCOM     = $A0;
  ADS1262_INPMUX_MUXP_TEMP_M_POS = $B0;
  ADS1262_INPMUX_MUXP_APS_M_POS  = $C0;
  ADS1262_INPMUX_MUXP_DPS_M_POS  = $D0;
  ADS1262_INPMUX_MUXP_TDAC_POS   = $E0;
  ADS1262_INPMUX_MUXP_FLOAT      = $F0;
  ADS1262_INPMUX_MUXN_AIN0       = $00;
  ADS1262_INPMUX_MUXN_AIN1       = $01;  { Old Default }
  ADS1262_INPMUX_MUXN_AIN2       = $02;
  ADS1262_INPMUX_MUXN_AIN3       = $03;
  ADS1262_INPMUX_MUXN_AIN4       = $04;
  ADS1262_INPMUX_MUXN_AIN5       = $05;
  ADS1262_INPMUX_MUXN_AIN6       = $06;
  ADS1262_INPMUX_MUXN_AIN7       = $07;
  ADS1262_INPMUX_MUXN_AIN8       = $08;
  ADS1262_INPMUX_MUXN_AIN9       = $09;
  ADS1262_INPMUX_MUXN_AINCOM     = $0A;  { New Default }
  ADS1262_INPMUX_MUXN_TEMP_M_POS = $0B;
  ADS1262_INPMUX_MUXN_APS_M_POS  = $0C;
  ADS1262_INPMUX_MUXN_DPS_M_POS  = $0D;
  ADS1262_INPMUX_MUXN_TDAC_POS   = $0E;
  ADS1262_INPMUX_MUXN_FLOAT      = $0F;

  { ####################################################################################### }
  { ## ADS1262 initial data                                                              ## }
  { ####################################################################################### }
  ADS1262_REGISTER_OFCAL_LENGTH                = 3;
  ADS1262_WAIT_TIME_MS_COMMAND                 = 100;
  ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_BEFORE = 1000;
  ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER  = 500;
  ADS1262_WAIT_TIME_MS_READ_REGISTER           = 0;
  ADS1262_WAIT_TIME_MS_WRITE_REGISTER          = 10;
  ADS1262_WAIT_TIME_MS_READ_ADC1               = 0;
  ADS1262_CRC_MAGIC_BYTE                       = $9B;
  ADS1262_DATA_EMA_SPAN                        = 4000;
  ADS1262_DATA_EMA_FACTOR                      = 2 / (ADS1262_DATA_EMA_SPAN + 1);
  ADS1262_INPMUX_MUX_P                         = ADS1262_INPMUX_MUXP_AIN0;
  ADS1262_INPMUX_MUX_N                         = ADS1262_INPMUX_MUXN_AINCOM;
  ADS1262_VBIAS_MODE                           = ADS1262_POWER_VBIAS_DISABLED; 
  ADS1262_PGA_MODE                             = ADS1262_MODE2_BYPASS_PGA_ENABLED; // ADS1262_MODE2_BYPASS_PGA_ENABLED; // ADS1262_MODE2_BYPASS_PGA_BYPASSED;
  ADS1262_DATA_RATE                            = ADS1262_MODE2_DR_400_SPS;
  ADS1262_FILTER_MODE                          = ADS1262_MODE1_FILTER_SINC4_MODE;
  ADS1262_CHOP_MODE                            = ADS1262_MODE0_CHOP_INPUT_CHOP_AND_IDAC_ROTATION_DISABLED; // ADS1262_MODE0_CHOP_INPUT_CHOP_ENABLED_IDAC_ROTATION_DISABLED; // ADS1262_MODE0_CHOP_INPUT_CHOP_AND_IDAC_ROTATION_DISABLED;
  ADS1262_GAIN                                 = ADS1262_MODE2_GAIN_32;
  ADS1262_INITIAL_OFCAL                        = $FFFD00; { 24 bit value : $000000 }
  ADS1262_INITIAL_SENSIVITY                    = 32;

Type
  { ####################################################################################### }
  { ## T_GPIO_Pin                                                                        ## }
  { ####################################################################################### }
  T_GPIO_Pin = Class(TObject)
  Protected
      M_GPIO_Pin : U_Int_32;
      Procedure GPIO_Write_File (Const F_Filename : String; Const F_Output_Buffer_A : T_Buffer_A; F_Length : U_Int_32);
      Function GPIO_Read_File (Const F_Filename : String; F_Length : U_Int_32) : String;
      Procedure Set_Export (F_Export_F : Boolean);
      Function Get_Direction () : T_GPIO_Direction;
      Procedure Set_Direction (F_GPIO_Direction : T_GPIO_Direction);
      Function Get_Value () : Boolean;
      Procedure Set_Value (F_Value : Boolean);
      Function Get_Interrupt_Mode () : T_GPIO_Interrupt_Mode;
      Procedure Set_Interrupt_Mode (F_GPIO_Interrupt_Mode : T_GPIO_Interrupt_Mode);
  Public
      Constructor Create (F_GPIO_Pin : U_Int_32);
      Destructor Destroy; Override;
      Function Wait_For_Interrupt (F_Timeout : Integer) : Boolean;
  Published
      Property Direction : T_GPIO_Direction Read Get_Direction Write Set_Direction;
      Property Value : Boolean Read Get_Value Write Set_Value;
      Property Interrupt_Mode : T_GPIO_Interrupt_Mode Read Get_Interrupt_Mode Write Set_Interrupt_Mode;
  End; { T_SPI_DEVICE }

  { ####################################################################################### }
  { ## T_SPI_Device                                                                      ## }
  { ####################################################################################### }
  T_SPI_Device = Class(TObject)
  Protected
      M_SPI_File_Handle :   Int_32;
      M_SPI_Mode :          U_Int_8;
      M_SPI_Bits_per_Word : U_Int_8;
      M_SPI_LSB_First :     Boolean;
      M_SPI_Max_Speed :     U_Int_32;
      Function Get_SPI_Mode : U_Int_8;
      Procedure Set_SPI_Mode (F_Mode : U_Int_8);
      Function Get_SPI_Bits_per_Word : U_Int_8;
      Procedure Set_SPI_Bits_per_Word (F_Bits_per_Word : U_Int_8);
      Function Get_SPI_LSB_First : Boolean;
      Procedure Set_SPI_LSB_First (F_LSB_First : Boolean);
      Function Get_SPI_Max_Speed : U_Int_32;
      Procedure Set_SPI_Max_Speed (F_Max_Speed : U_Int_32);
      Function SPI_Write_Read_Buffer (Var F_Output_Buffer_A : T_Buffer_A; Var F_Input_Buffer_A : T_Buffer_A; F_Length : U_Int_8) : Int_32;
  Public
      Constructor Create ();
      Destructor Destroy; Override;
  Published
      Property SPI_Mode : U_Int_8 Read Get_SPI_Mode Write Set_SPI_Mode;
      Property SPI_Bits_per_Word : U_Int_8 Read Get_SPI_Bits_per_Word Write Set_SPI_Bits_per_Word;
      Property SPI_LSB_First : Boolean Read Get_SPI_LSB_First Write Set_SPI_LSB_First;
      Property SPI_Max_Speed : U_Int_32 Read Get_SPI_Max_Speed Write Set_SPI_Max_Speed;
  End; { T_SPI_Device }


  { ####################################################################################### }
  { ## T_ADS1262                                                                         ## }
  { ####################################################################################### }
  T_ADS1262 = Class(T_SPI_Device)
  Protected
      M_Count :         UInt64;
      M_Bit_N :         Integer;
      M_EMA_Data :      Int_32;
      M_DRDY_Pin :      T_GPIO_Pin;
      M_Voltage_Limit : Double;
      M_Sensivity :     Int_32;
  Public
      Constructor Create ();
      Destructor Destroy (); Override;
      Procedure Reset_and_Init ();
      Procedure Execute_Command (F_Command : U_Int_8; F_Wait_Time_msec : Int_32 = ADS1262_WAIT_TIME_MS_COMMAND);
      Function Read_Register (F_Register_Adress : U_Int_8) : U_Int_8;
      Procedure Write_Register (F_Register_Adress : U_Int_8; F_Data : U_Int_8);
      Procedure System_Offset_Self_Calibration ();
      Procedure Set_Continuous_Conversion ();
      Procedure Set_Pulse_Conversion ();
      Procedure Start_Conversion ();
      Procedure Stop_Conversion ();
      Procedure Set_POWER (F_VBIAS_Mode : U_Int_8; F_INTREF_Mode : U_Int_8);
      Function Get_INPMUX () : U_Int_8;
      Procedure Set_INPMUX (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
      Procedure Set_INPMUX_Without_Wait (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
      Procedure Set_PGA (F_Bypass_Mode : U_Int_8; F_Gain_Mode : U_Int_8);
      Procedure Set_Data_Rate (F_Rate_Mode : U_Int_8);
      Procedure Set_Filter_Mode (F_Filter_Mode : U_Int_8);
      Procedure Set_Chop_Mode (F_Chop_Mode : U_Int_8);
      Procedure Set_OFCAL (F_OFCAL : Int_32);
      Function Read_ADC1_Data (Var F_Status : U_Int_8; Var F_CRC : U_Int_8) : Int_32;
      Function Read_ADC1_Data : Int_32;
      Procedure Read_All_Registers (Var F_Register_Block : T_Register_Block);
      Function Calculate_Voltage (F_Data : Int_32) : Double;
      Function Get_Voltage () : Double;
      Function Calculate_Sensivity_Value (F_Value : Int_32; F_Sensivity : Int_32) : Int_32;
  Published
      Property DRDY_Pin : T_GPIO_Pin Read M_DRDY_Pin;
      Property ADC1_Data : Int_32 Read Read_ADC1_Data;
      Property Voltage : Double Read Get_Voltage;
      Property EMA_Data : Int_32 Read M_EMA_Data;
      Property Sensivity : Int_32 Read M_Sensivity Write M_Sensivity;
  End; { T_ADS1262 }


Implementation

Uses
  SysUtils,
  Dialogs;


{ ####################################################################################### }
{ ## T_GPIO_Pin                                                                        ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Constructor T_GPIO_Pin.Create (F_GPIO_Pin : U_Int_32);
  { Initialization of the object                                                            }
  { --------------------------------------------------------------------------------------- }
Begin { T_GPIO_Pin.Create }
  Inherited Create ();

  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Create');
  {$ENDIF}

  M_GPIO_Pin := F_GPIO_Pin;
  Set_Export (TRUE);
End; { T_GPIO_Pin.Create }


{ --------------------------------------------------------------------------------------- }
Destructor T_GPIO_Pin.Destroy ();
  { Free data                                                                               }
  { --------------------------------------------------------------------------------------- }
Begin { T_GPIO_Pin.Destroy }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Destroy');
  {$ENDIF}

  Set_Export (FALSE);

  Inherited Destroy;
End; { T_GPIO_Pin.Destroy }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.GPIO_Write_File (Const F_Filename : String; Const F_Output_Buffer_A : T_Buffer_A; F_Length : U_Int_32);
{ Write data to file                                                                      }
{ --------------------------------------------------------------------------------------- }
Var
  GPIO_File_Handle : Int_32;

Begin { T_GPIO_Pin.GPIO_Write_File }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.GPIO_Write_File');
  {$ENDIF}

  GPIO_File_Handle := FpOpen (F_Filename, O_WRONLY);
  If GPIO_File_Handle < 0 Then
    Begin { then }
      ShowMessage ('Error T_GPIO_Pin.GPIO_Write_File:' + IntToStr (GPIO_File_Handle));
      Exit;
    End; { then }

  FpWrite (GPIO_File_Handle, F_Output_Buffer_A [0], F_Length);

  FpClose (GPIO_File_Handle);
End; { T_GPIO_Pin.GPIO_Write_File }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.GPIO_Read_File (Const F_Filename : String; F_Length : U_Int_32) : String;
  { Read data from file                                                                     }
  { --------------------------------------------------------------------------------------- }
Var
  GPIO_File_Handle : Int_32;
  Read_N :           U_Int_32;

Begin { T_GPIO_Pin.GPIO_Read_File }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.GPIO_Read_File');
  {$ENDIF}

  Result := '';

  If F_Length <= 0 Then
    Begin { then }
      Exit;
    End; { then }

  SetLength (Result, F_Length);

  GPIO_File_Handle := FpOpen (F_Filename, O_RDONLY);
  If GPIO_File_Handle < 0 Then
    Begin { then }
      ShowMessage ('Error T_GPIO_Pin.GPIO_Read_File:' + IntToStr (GPIO_File_Handle));
      Exit;
    End; { then }

  Read_N := FpRead (GPIO_File_Handle, Result [1], F_Length);

  FpClose (GPIO_File_Handle);

  SetLength (Result, Read_N);
  Result := Trim (Result);
End; { T_GPIO_Pin.GPIO_Read_File }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.Set_Export (F_Export_F : Boolean);
{ Set export                                                                              }
{ --------------------------------------------------------------------------------------- }
Var
  S : String;

Begin { T_GPIO_Pin.Set_Export }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Set_Export');
  {$ENDIF}

  S := IntToStr (M_GPIO_Pin);

  If F_Export_F = TRUE Then
    Begin { then }
      GPIO_Write_File (GPIO_LINUX_EXPORT_FILE, @S [1], Length (S));
    End { then }
  Else
    Begin { else  }
      GPIO_Write_File (GPIO_LINUX_UNEXPORT_FILE, @S [1], Length (S));
    End; { else  }

  Sleep (GPIO_WAIT_TIME_MS_EXPORT);
End; { T_GPIO_Pin.Set_Export }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Get_Direction () : T_GPIO_Direction;
  { Get direction of pin                                                                    }
  { --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;

Begin { T_GPIO_Pin.Get_Direction }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Get_Direction');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'direction', [M_GPIO_Pin]);
  S        := GPIO_Read_File (Format_S, 3);
  Case S Of
      'in' :
        Begin { 'in' }
          Result := GPIO_DIRECTION_IN;
        End; { 'in' }
      'out' :
        Begin { 'out' }
          Result := GPIO_DIRECTION_OUT;
        End; { 'out' }
      Else
        Begin { else }
          Result := GPIO_DIRECTION_IN;
        End;   { else }
    End;       { Case }
End; { T_GPIO_Pin.Get_Direction }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.Set_Direction (F_GPIO_Direction : T_GPIO_Direction);
{ Get direction of pin                                                                    }
{ --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;

Begin { T_GPIO_Pin.Set_Direction }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Set_Direction');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'direction', [M_GPIO_Pin]);
  Case F_GPIO_Direction Of
      GPIO_DIRECTION_IN :
        Begin { GPIO_DIRECTION_IN }
          S := 'in';
        End; { GPIO_DIRECTION_IN }
      GPIO_DIRECTION_OUT :
        Begin { GPIO_DIRECTION_OUT }
          S := 'out';
        End; { GPIO_DIRECTION_OUT }
      Else
        Begin { else }
          S := 'in';
        End;   { else }
    End;       { Case }

  GPIO_Write_File (Format_S, @S [1], Length (S));

  Sleep (GPIO_WAIT_TIME_MS_COMMAND);
End; { T_GPIO_Pin.Set_Direction }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Get_Value () : Boolean;
  { Get value of pin                                                                        }
  { --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;

Begin { T_GPIO_Pin.Get_Value }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Get_Value');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'value', [M_GPIO_Pin]);
  S        := GPIO_Read_File (Format_S, 1);
  Case S Of
      '0' :
        Begin { '0' }
          Result := FALSE;
        End; { '0' }
      '1' :
        Begin { '1' }
          Result := TRUE;
        End; { '1' }
      Else
        Begin { else }
          Result := FALSE;
        End;   { else }
    End;       { Case }
End; { T_GPIO_Pin.Get_Value }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.Set_Value (F_Value : Boolean);
{ Get value of pin                                                                        }
{ --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;

Begin { T_GPIO_Pin.Set_Value }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Set_Value');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'value', [M_GPIO_Pin]);
  If F_Value = TRUE Then
    Begin { then }
      S := '1';
    End { then }
  Else
    Begin { else  }
      S := '0';
    End; { else  }
  GPIO_Write_File (Format_S, @S [1], Length (S));
End; { T_GPIO_Pin.Set_Value }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Get_Interrupt_Mode () : T_GPIO_Interrupt_Mode;
  { Get interrupt mode                                                                      }
  { --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;

Begin { T_GPIO_Pin.Get_Interrupt_Mode }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Get_Interrupt_Mode');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'edge', [M_GPIO_Pin]);
  S        := GPIO_Read_File (Format_S, 7);
  Case S Of
      'none' :
        Begin { 'none' }
          Result := [];
        End; { 'none' }
      'rising' :
        Begin { 'rising' }
          Result := [GPIO_INTERRUPT_RISING];
        End; { 'rising' }
      'falling' :
        Begin { 'falling' }
          Result := [GPIO_INTERRUPT_FALLING];
        End; { 'falling' }
      'both' :
        Begin { 'both' }
          Result := [GPIO_INTERRUPT_RISING, GPIO_INTERRUPT_FALLING];
        End; { 'both' }
      Else
        Begin { else }
          Result := [];
        End;   { else }
    End;       { Case }
End; { T_GPIO_Pin.Get_Interrupt_Mode }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.Set_Interrupt_Mode (F_GPIO_Interrupt_Mode : T_GPIO_Interrupt_Mode);
{ Set interrupt mode                                                                      }
{ --------------------------------------------------------------------------------------- }
Var
  Format_S : String;
  S :        String;
  I :        Integer;

Begin { T_GPIO_Pin.Set_Interrupt_Mode }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Set_Interrupt_Mode');
  {$ENDIF}

  Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'edge', [M_GPIO_Pin]);
  I        := 0;
  S        := 'none';
  If F_GPIO_Interrupt_Mode = [] Then
    Begin { then }
      S := 'none';
    End { then }
  Else
    Begin { else  }
      If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_RISING] Then
        Begin { then }
          S := 'rising';
        End { then }
      Else
        Begin { else  }
          If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_FALLING] Then
            Begin { then }
              S := 'falling';
            End { then }
          Else
            Begin { else  }
              If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_RISING, GPIO_INTERRUPT_FALLING] Then
                Begin { then }
                  S := 'both';
                End; { then }
            End; { else  }
        End; { else  }
    End; { else  }

  I := 0;
  Repeat
      GPIO_Write_File (Format_S, @S [1], Length (S));
      Sleep (1);
      Inc (I);
  Until (Interrupt_Mode = F_GPIO_Interrupt_Mode) or (I > GPIO_TIMEOUT_INTERRUPTMODE_MS);

  If I > GPIO_TIMEOUT_INTERRUPTMODE_MS Then
    Begin { then }
      ShowMessage ('Timeout in Set_Interrupt_Mode: ' + S);
      Exit;
    End; { then }

  Sleep (GPIO_WAIT_TIME_MS_COMMAND);
End; { T_GPIO_Pin.Set_Interrupt_Mode }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Wait_For_Interrupt (F_Timeout : Integer) : Boolean;
  { Wait for next interrupt or timeout                                                      }
  { --------------------------------------------------------------------------------------- }
Var
  Format_S :            String;
  GPIO_File_Handle :    Int_32;
  File_Descriptor_Set : Array[0..0] Of PollFd;
  File_Descriptor_C :   Int_32;
  Dummy_Content :       Array[0..1] Of Char;

Begin { T_GPIO_Pin.Wait_For_Interrupt }
  {$IFDEF DEBUG_GPIO_OUTPUT}
  WriteLn ('Begin of: T_GPIO_Pin.Wait_For_Interrupt');
  {$ENDIF}

  Result := FALSE;

  If Get_Interrupt_Mode () = [] Then
    Begin { then }
      ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: No interrupt mode set.');
      Exit;
    End; { then }

  Format_S         := Format (GPIO_LINUX_GPIOPIN_DIR + 'value', [M_GPIO_Pin]);
  GPIO_File_Handle := FpOpen (Format_S, O_RDONLY or O_NONBLOCK);
  If GPIO_File_Handle < 0 Then
    Begin { then }
      ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: Error T_GPIO_Pin.Wait_For_Interrupt:' + IntToStr (GPIO_File_Handle));
      Exit;
    End; { then }

  FpRead (GPIO_File_Handle, Dummy_Content [0], Length (Dummy_Content));

  FillByte (File_Descriptor_Set [0], SizeOf (File_Descriptor_Set), 0);
  File_Descriptor_Set[0].fd     := GPIO_File_Handle;
  File_Descriptor_Set[0].events := POLLPRI;

  File_Descriptor_C := FpPoll (@File_Descriptor_Set [0], 1, F_Timeout);
  If File_Descriptor_C < 0 Then
    Begin { then }
      ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: Interrupt failed.');
      FpClose (GPIO_File_Handle);
      Exit;
    End { then }
  Else
    Begin { else  }
      If File_Descriptor_C = 0 Then
        Begin { then }
          { Timeout occured }
          FpClose (GPIO_File_Handle);
          Exit;
        End; { then }
    End; { else  }

  If (File_Descriptor_Set [0].REvents and POLLPRI) <> 0 Then
    Begin { then }
      { Interrupt occured }
      FpClose (GPIO_File_Handle);
      Result := TRUE;
      Exit;
    End; { then }

  FpClose (GPIO_File_Handle);
  Result := TRUE;
End; { T_GPIO_Pin.Wait_For_Interrupt }


{ ####################################################################################### }
{ ## T_SPI_Device                                                                      ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Constructor T_SPI_Device.Create ();
  { Initialization of the object                                                            }
  { --------------------------------------------------------------------------------------- }
Begin { T_SPI_Device.Create }
  Inherited Create ();

  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Create');
  {$ENDIF}

  M_SPI_File_Handle := FpOpen (SPI_IOC_DEVICE, O_RDWR);
  If M_SPI_File_Handle < 0 Then
    Begin { then }
      ShowMessage ('Error open SPI bus:' + IntToStr (M_SPI_File_Handle));
      Exit;
    End; { then }

  Sleep (SPI_IOC_WAIT_TIME_MS_OPEN);

  SPI_Mode          := SPI_IOC_DEFAULT_MODE;
  SPI_Bits_per_Word := SPI_IOC_DEFAULT_BITS_PER_WORD;
  SPI_LSB_First     := SPI_IOC_DEFAULT_LSB_FIRST;
  SPI_Max_Speed     := SPI_IOC_DEFAULT_SPEED;
End; { T_SPI_Device.Create }


{ --------------------------------------------------------------------------------------- }
Destructor T_SPI_Device.Destroy ();
  { Free data                                                                               }
  { --------------------------------------------------------------------------------------- }
Begin { T_SPI_Device.Destroy }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Destroy');
  {$ENDIF}

  FpClose (M_SPI_File_Handle);

  Inherited Destroy;
End; { T_SPI_Device.Destroy }


{ --------------------------------------------------------------------------------------- }
Function T_SPI_Device.Get_SPI_Mode () : U_Int_8;
  { Get SPI mode                                                                            }
  { --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Get_SPI_Mode }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Get_SPI_Mode');
  {$ENDIF}

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_RD_MODE, @Result);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error read SPI mode: ' + IntToStr (Return_Value));
    End; { then }

  M_SPI_Mode := Result;
End; { T_SPI_Device.Get_SPI_Mode }


{ --------------------------------------------------------------------------------------- }
Procedure T_SPI_Device.Set_SPI_Mode (F_Mode : U_Int_8);
{ Set SPI mode                                                                            }
{ --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Set_SPI_Mode }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Set_SPI_Mode');
  {$ENDIF}

  M_SPI_Mode := F_Mode;

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_WR_MODE, @F_Mode);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error set SPI mode: ' + IntToStr (Return_Value));
    End; { then }

  Sleep (SPI_IOC_WAIT_TIME_MS_COMMAND);
End; { T_SPI_Device.Set_SPI_Mode }


{ --------------------------------------------------------------------------------------- }
Function T_SPI_Device.Get_SPI_Bits_per_Word : U_Int_8;
  { Get bits per word                                                                       }
  { --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Get_SPI_Bits_per_Word }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Get_SPI_Bits_per_Word');
  {$ENDIF}

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_RD_BITS_PER_WORD, @Result);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error read bits per word: ' + IntToStr (Return_Value));
    End; { then }

  M_SPI_Bits_per_Word := Result;
End; { T_SPI_Device.Get_SPI_Bits_per_Word }


{ --------------------------------------------------------------------------------------- }
Procedure T_SPI_Device.Set_SPI_Bits_per_Word (F_Bits_per_Word : U_Int_8);
{ Set bits per word                                                                       }
{ --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Set_SPI_Bits_per_Word }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Set_SPI_Bits_per_Word');
  {$ENDIF}

  M_SPI_Bits_per_Word := F_Bits_per_Word;

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_WR_BITS_PER_WORD, @F_Bits_per_Word);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error set bits per word: ' + IntToStr (Return_Value));
    End; { then }

  Sleep (SPI_IOC_WAIT_TIME_MS_COMMAND);
End; { T_SPI_Device.Set_SPI_Bits_per_Word }


{ --------------------------------------------------------------------------------------- }
Function T_SPI_Device.Get_SPI_LSB_First : Boolean;
  { Get bits per word                                                                       }
  { --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Get_SPI_LSB_First }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Get_SPI_LSB_First');
  {$ENDIF}

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_RD_LSB_FIRST, @Result);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error read LSB_first: ' + IntToStr (Return_Value));
    End; { then }

  M_SPI_LSB_First := Result;
End; { T_SPI_Device.Get_SPI_LSB_First }


{ --------------------------------------------------------------------------------------- }
Procedure T_SPI_Device.Set_SPI_LSB_First (F_LSB_First : Boolean);
{ Set LSB_First; F_LSB_First=$00 or F_LSB_First=$FF                                       }
{ --------------------------------------------------------------------------------------- }
Var
  Value :        U_Int_16;
  Return_Value : Int_32;

Begin { T_SPI_Device.Set_SPI_LSB_First }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Set_SPI_LSB_First');
  {$ENDIF}

  M_SPI_LSB_First := F_LSB_First;

  If F_LSB_First = TRUE Then
    Begin { then }
      Value := $0001;
    End { then }
  Else
    Begin { else  }
      Value := $0000;
    End; { else  }

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_WR_LSB_FIRST, @Value);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error set LSB_first: ' + IntToStr (Return_Value));
    End; { then }

  Sleep (SPI_IOC_WAIT_TIME_MS_COMMAND);
End; { T_SPI_Device.Set_SPI_LSB_First }


{ --------------------------------------------------------------------------------------- }
Function T_SPI_Device.Get_SPI_Max_Speed : U_Int_32;
  { Get bits per word                                                                       }
  { --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Get_SPI_Max_Speed }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Get_SPI_Max_Speed');
  {$ENDIF}

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_RD_MAX_SPEED_HZ, @Result);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error read max speed: ' + IntToStr (Return_Value));
    End; { then }

  M_SPI_Max_Speed := Result;
End; { T_SPI_Device.Get_SPI_Max_Speed }


{ --------------------------------------------------------------------------------------- }
Procedure T_SPI_Device.Set_SPI_Max_Speed (F_Max_Speed : U_Int_32);
{ Set bits per word                                                                       }
{ --------------------------------------------------------------------------------------- }
Var
  Return_Value : Int_32;

Begin { T_SPI_Device.Set_SPI_Max_Speed }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.Set_SPI_Max_Speed');
  {$ENDIF}

  M_SPI_Max_Speed := F_Max_Speed;

  Return_Value := FpIOCtl (M_SPI_File_Handle, SPI_IOC_WR_MAX_SPEED_HZ, @F_Max_Speed);

  If Return_Value < 0 Then
    Begin { then }
      ShowMessage ('Error set max speed: ' + IntToStr (Return_Value));
    End; { then }

  Sleep (SPI_IOC_WAIT_TIME_MS_COMMAND);
End; { T_SPI_Device.Set_SPI_Max_Speed }


{ --------------------------------------------------------------------------------------- }
Function T_SPI_Device.SPI_Write_Read_Buffer (Var F_Output_Buffer_A : T_Buffer_A; Var F_Input_Buffer_A : T_Buffer_A; F_Length : U_Int_8) : Int_32;
  { Write and read buffer                                                                   }
  { --------------------------------------------------------------------------------------- }
Var
  Transfer_R : T_SPI_Transfer_R;

Begin { T_SPI_Device.SPI_Write_Read_Buffer }
  {$IFDEF DEBUG_SPI_OUTPUT}
  WriteLn ('Begin of  : T_SPI_Device.SPI_Write_Read_Buffer');
  {$ENDIF}

  If F_Length <= 0 Then
    Begin { then }
      ShowMessage ('Error T_SPI_Device.SPI_Write_Read_Buffer: F_Length <= 0');
      Exit;
    End; { then }

  {$WARNINGS OFF}
  Transfer_R.tx_buf        := U_Int_64 (@(F_Output_Buffer_A [0]));
  Transfer_R.rx_buf        := U_Int_64 (@(F_Input_Buffer_A [0]));
  {$WARNINGS ON}
  Transfer_R.len           := F_Length;
  Transfer_R.speed_hz      := SPI_IOC_DEFAULT_SPEED;
  Transfer_R.delay_usecs   := 0;
  Transfer_R.bits_per_word := SPI_IOC_DEFAULT_BITS_PER_WORD;
  Transfer_R.cs_change     := 0;
  Transfer_R.pad           := 0;

  Result := FpIOCtl (M_SPI_File_Handle, SPI_IOC_WR_TRANSFER, @Transfer_R);

  If Result < 0 Then
    Begin { then }
      ShowMessage ('Error write and read buffer: ' + IntToStr (Result));
    End;   { then }
End;       { T_SPI_Device.SPI_Write_Read_Buffer }


{ ####################################################################################### }
{ ## T_ADS1262                                                                         ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Constructor T_ADS1262.Create ();
  { Initialization of the object                                                            }
  { --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Create }
  Inherited Create ();

  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Create');
  {$ENDIF}

  M_DRDY_Pin                := T_GPIO_Pin.Create (GPIO_DRDY_PIN);
  M_DRDY_Pin.Direction      := GPIO_DIRECTION_IN;
  M_DRDY_Pin.Interrupt_Mode := [GPIO_INTERRUPT_FALLING];

  M_Count := 0;

  M_Voltage_Limit := ADS1262_MODE2_GAIN_01_LIMIT;

  M_Sensivity := ADS1262_INITIAL_SENSIVITY;

  Reset_and_Init ();
End; { T_ADS1262.Create }


{ --------------------------------------------------------------------------------------- }
Destructor T_ADS1262.Destroy ();
  { Free data                                                                               }
  { --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Destroy }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Destroy');
  {$ENDIF}

  M_DRDY_Pin.Free;

  Execute_Command (ADS1262_COMMAND_RESET);

  Inherited Destroy ();
End; { T_ADS1262.Destroy }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Reset_and_Init ();
{ Init ADC                                                                                }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Block : T_Register_Block;

Begin { T_ADS1262.Reset_and_Init }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Init');
  {$ENDIF}

  M_EMA_Data  := 0;

  Execute_Command (ADS1262_COMMAND_RESET);

  Read_All_Registers (Register_Block);

  Stop_Conversion ();
  Set_POWER (ADS1262_VBIAS_MODE, ADS1262_POWER_INTREF_ENABLED);
  Set_INPMUX (ADS1262_INPMUX_MUX_P, ADS1262_INPMUX_MUX_N);
  Set_PGA (ADS1262_PGA_MODE, ADS1262_GAIN);
  Set_Data_Rate (ADS1262_DATA_RATE);
  Set_Filter_Mode (ADS1262_FILTER_MODE);
  Set_Chop_Mode (ADS1262_CHOP_MODE);
  System_Offset_Self_Calibration ();

  Read_All_Registers (Register_Block);
End; { T_ADS1262.Reset_and_Init }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Execute_Command (F_Command : U_Int_8; F_Wait_Time_msec : Int_32 = ADS1262_WAIT_TIME_MS_COMMAND);
{ Execute a one byte command                                                              }
{ --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;

Begin { T_ADS1262.Execute_Command }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Execute_Command');
  {$ENDIF}

  Length := 1;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := F_Command;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);

  If F_Wait_Time_msec > 0 Then
    Begin { then }
      Sleep (F_Wait_Time_msec);
    End; { then }
End;     { T_ADS1262.Execute_Command }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Read_Register (F_Register_Adress : U_Int_8) : U_Int_8;
  { Read data from register                                                                 }
  { --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;

Begin { T_ADS1262.Read_Register }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Read_Register');
  {$ENDIF}

  Length := 3;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := ADS1262_COMMAND_RREG or F_Register_Adress;
  Output_Buffer_A[1] := $00;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);

  Result := Input_Buffer_A [2];

  {$IF ADS1262_WAIT_TIME_MS_READ_REGISTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_READ_REGISTER);
  {$ENDIF}
End; { T_ADS1262.Read_Register }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Write_Register (F_Register_Adress : U_Int_8; F_Data : U_Int_8);
{ Write data into register                                                                }
{ --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;

Begin { T_ADS1262.Write_Register }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Write_Register');
  {$ENDIF}

  Length := 3;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := ADS1262_COMMAND_WREG or F_Register_Adress;
  Output_Buffer_A[1] := $00;
  Output_Buffer_A[2] := F_Data;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);

  {$IF ADS1262_WAIT_TIME_MS_WRITE_REGISTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_WRITE_REGISTER);
  {$ENDIF}
End; { T_ADS1262.Write_Register }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.System_Offset_Self_Calibration ();
{ ADC1 self offset calibration SFOCAL1                                                    }
{ --------------------------------------------------------------------------------------- }
Var
  Old_INPMUX : U_Int_8;

Begin { T_ADS1262.System_Offset_Self_Calibration }
  {$IF DEFINED (DEBUG_ADS1262_OUTPUT) or DEFINED (DEBUG_OFFSET_SELF_CALIBRATION)}
  WriteLn ('Begin of    : T_ADS1262.System_Offset_Self_Calibration');
  {$ENDIF}

  { Save old values of INPMUX }
  Old_INPMUX := Get_INPMUX ();

  { Float all inputs }
  Set_INPMUX (ADS1262_INPMUX_MUXP_FLOAT, ADS1262_INPMUX_MUXN_FLOAT);

  {$IF ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_BEFORE > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_BEFORE);
  {$ENDIF}

  { ADC1 self offset calibration SFOCAL1 }
  Execute_Command (ADS1262_COMMAND_SFOCAL1);

  {$IF ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER);
  {$ENDIF}

  { Set an initial offset, this values must be adopted on your own ADS1262 and your wiring. }
  { Normally, this should not be needed, but it can increase your noise-free resolution, if no ac auto calibration is used. }
  Set_OFCAL (ADS1262_INITIAL_OFCAL);

  {$IF ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER);
  {$ENDIF}

  Set_OFCAL (ADS1262_INITIAL_OFCAL);

  {$IF ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_SELF_CALIBRATION_AFTER);
  {$ENDIF}

  { Reset inputs }
  Set_INPMUX ((Old_INPMUX and $F0), (Old_INPMUX and $0F));
End; { T_ADS1262.System_Offset_Self_Calibration }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_Continuous_Conversion ();
{ Start continuous conversion                                                             }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_Continuous_Conversion }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_Continuous_Conversion');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE0) and (ADS1262_MODE0_REFREV_MASK or ADS1262_MODE0_CHOP_MASK or ADS1262_MODE0_DELAY_MASK);

  { Set continuous conversion }
  Register_Value := Register_Value or ADS1262_MODE0_RUNMODE_CONTINUOUS_CONVERSION;

  Write_Register (ADS1262_REGISTER_MODE0, Register_Value);
End; { T_ADS1262.Set_Continuous_Conversion }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_Pulse_Conversion ();
{ Start pulse conversion                                                                  }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_Pulse_Conversion }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_Pulse_Conversion');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE0) and (ADS1262_MODE0_REFREV_MASK or ADS1262_MODE0_CHOP_MASK or ADS1262_MODE0_DELAY_MASK);

  { Set pulse conversion }
  Register_Value := Register_Value or ADS1262_MODE0_RUNMODE_PULSE_CONVERSION;

  Write_Register (ADS1262_REGISTER_MODE0, Register_Value);
End; { T_ADS1262.Set_Pulse_Conversion }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Start_Conversion ();
{ Start conversion                                                                        }
{ --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Start_Conversion }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Start_Conversion');
  {$ENDIF}

  { Start continuous conversion }
  Execute_Command (ADS1262_COMMAND_START1, 0);
End; { T_ADS1262.Start_Conversion }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Stop_Conversion ();
{ Stop conversion                                                                         }
{ --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Stop_Conversion }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Stop_Conversion');
  {$ENDIF}

  { Stop continuous conversion }
  Execute_Command (ADS1262_COMMAND_STOP1, 0);
End; { T_ADS1262.Stop_Conversion }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_POWER (F_VBIAS_Mode : U_Int_8; F_INTREF_Mode : U_Int_8);
{ Set POWER mode                                                                          }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_POWER }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_POWER');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_POWER) and (ADS1262_POWER_RESET_MASK or ADS1262_POWER_RESERVED_MASK);

  { Set VBIAS and INTREF }
  Register_Value := Register_Value or F_VBIAS_Mode or F_INTREF_Mode;

  Write_Register (ADS1262_REGISTER_POWER, Register_Value);
End; { T_ADS1262.Set_POWER }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Get_INPMUX () : U_Int_8;
  { Get INPMUX                                                                              }
  { --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Get_INPMUX }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Get_INPMUX');
  {$ENDIF}

  { Get INPMUX }
  Result := Read_Register (ADS1262_REGISTER_INPMUX);
End; { T_ADS1262.Get_INPMUX }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_INPMUX (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
{ Set INPMUX                                                                              }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_INPMUX }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_INPMUX');
  {$ENDIF}

  { Set INPMUX }
  Register_Value := F_MUXP or F_MUXN;

  Write_Register (ADS1262_REGISTER_INPMUX, Register_Value);
End; { T_ADS1262.Set_INPMUX }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_INPMUX_Without_Wait (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
{ Set INPMUX                                                                              }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value :  U_Int_8;
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;

Begin { T_ADS1262.Set_INPMUX_Without_Wait }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_INPMUX_Without_Wait');
  {$ENDIF}

  { Set INPMUX }
  Register_Value := F_MUXP or F_MUXN;

  Length := 3;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := ADS1262_COMMAND_WREG or ADS1262_REGISTER_INPMUX;
  Output_Buffer_A[1] := $00;
  Output_Buffer_A[2] := Register_Value;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);
End; { T_ADS1262.Set_INPMUX_Without_Wait }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_PGA (F_Bypass_Mode : U_Int_8; F_Gain_Mode : U_Int_8);
{ Set PGA mode                                                                            }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_PGA }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_PGA');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE2) and (ADS1262_MODE2_DR_MASK);

  { Set BYPASS and GAIN }
  Register_Value := Register_Value or F_Bypass_Mode or F_Gain_Mode;

  Write_Register (ADS1262_REGISTER_MODE2, Register_Value);

  M_Voltage_Limit := ADS1262_MODE2_GAIN_01_LIMIT;
  Case F_Gain_Mode Of
      ADS1262_MODE2_GAIN_01 :
        Begin { ADS1262_MODE2_GAIN_01 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_01_LIMIT;
        End; { ADS1262_MODE2_GAIN_01 }
      ADS1262_MODE2_GAIN_02 :
        Begin { ADS1262_MODE2_GAIN_02 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_02_LIMIT;
        End; { ADS1262_MODE2_GAIN_02 }
      ADS1262_MODE2_GAIN_04 :
        Begin { ADS1262_MODE2_GAIN_04 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_04_LIMIT;
        End; { ADS1262_MODE2_GAIN_04 }
      ADS1262_MODE2_GAIN_08 :
        Begin { ADS1262_MODE2_GAIN_08 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_08_LIMIT;
        End; { ADS1262_MODE2_GAIN_08 }
      ADS1262_MODE2_GAIN_16 :
        Begin { ADS1262_MODE2_GAIN_16 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_16_LIMIT;
        End; { ADS1262_MODE2_GAIN_16 }
      ADS1262_MODE2_GAIN_32 :
        Begin { ADS1262_MODE2_GAIN_32 }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_32_LIMIT;
        End; { ADS1262_MODE2_GAIN_32 }
      Else
        Begin { else }
          M_Voltage_Limit := ADS1262_MODE2_GAIN_01_LIMIT;
        End; { else }
    End; { Case }
End; { T_ADS1262.Set_PGA }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_Data_Rate (F_Rate_Mode : U_Int_8);
{ Set data rate                                                                           }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_Data_Rate }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_Data_Rate');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE2) and (ADS1262_MODE2_BYPASS_MASK or ADS1262_MODE2_GAIN_MASK);

  { Set data rate }
  Register_Value := Register_Value or F_Rate_Mode;

  Write_Register (ADS1262_REGISTER_MODE2, Register_Value);
End; { T_ADS1262.Set_Data_Rate }

{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_Filter_Mode (F_Filter_Mode : U_Int_8);
{ Set filter mode                                                                         }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_Filter_Mode }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_Filter_Mode');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE1) and (ADS1262_MODE1_SBADC_MASK or ADS1262_MODE1_SBPOL_MASK or ADS1262_MODE1_SBMAG_MASK);

  { Set filter mode }
  Register_Value := Register_Value or F_Filter_Mode;

  Write_Register (ADS1262_REGISTER_MODE1, Register_Value);
End; { T_ADS1262.Set_Filter_Mode }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_Chop_Mode (F_Chop_Mode : U_Int_8);
{ Set chop mode                                                                           }
{ --------------------------------------------------------------------------------------- }
Var
  Register_Value : U_Int_8;

Begin { T_ADS1262.Set_Chop_Mode }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_Chop_Mode');
  {$ENDIF}

  { Mask the rest of the old register bits not involved }
  Register_Value := Read_Register (ADS1262_REGISTER_MODE0) and (ADS1262_MODE0_REFREV_MASK or ADS1262_MODE0_RUNMODE_MASK or ADS1262_MODE0_DELAY_MASK);

  { Set chope mode }
  Register_Value := Register_Value or F_Chop_Mode;

  Write_Register (ADS1262_REGISTER_MODE0, Register_Value);
End; { T_ADS1262.Set_Chop_Mode }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Set_OFCAL (F_OFCAL : Int_32);
{ Set offset calibration registers                                                        }
{ --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;

Begin { T_ADS1262.Set_OFCAL }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Set_OFCAL');
  {$ENDIF}

  { Set offset calibration registers }
  Length := ADS1262_REGISTER_OFCAL_LENGTH + 2;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := ADS1262_COMMAND_WREG or ADS1262_REGISTER_OFCAL0;
  Output_Buffer_A[1] := ADS1262_REGISTER_OFCAL_LENGTH - 1;
  Output_Buffer_A[2] := F_OFCAL and $FF;
  Output_Buffer_A[3] := (F_OFCAL shr $08) and $FF;
  Output_Buffer_A[4] := (F_OFCAL shr $10) and $FF;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);
End; { T_ADS1262.Set_OFCAL }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Read_ADC1_Data (Var F_Status : U_Int_8; Var F_CRC : U_Int_8) : Int_32;
  { Read ADC1 data                                                                          }
  { --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;
  CRC_Calculated :  U_Int_8;

Begin { T_ADS1262.Read_ADC1_Data }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Read_ADC1_Data');
  {$ENDIF}

  { Check for recalibration }
  Inc (M_Count);

  { Read ADC1 data }
  Length := 7;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  Output_Buffer_A[0] := ADS1262_COMMAND_RDATA1;
  Output_Buffer_A[1] := $00;
  Output_Buffer_A[2] := $00;
  Output_Buffer_A[3] := $00;
  Output_Buffer_A[4] := $00;
  Output_Buffer_A[5] := $00;
  Output_Buffer_A[6] := $00;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);

  F_Status := Input_Buffer_A [1];

  Result := (Input_Buffer_A [2] shl $18) or (Input_Buffer_A [3] shl $10) or (Input_Buffer_A [4] shl $08) or (Input_Buffer_A [5]);

  F_CRC          := Input_Buffer_A [6];
  CRC_Calculated := (Input_Buffer_A [2] + Input_Buffer_A [3] + Input_Buffer_A [4] + Input_Buffer_A [5] + ADS1262_CRC_MAGIC_BYTE) and $FF;
  If CRC_Calculated <> F_CRC Then
    Begin { then }
      WriteLn ('CRC error : ' + IntToHex (F_CRC, 2) + ' - ' + IntToHex (CRC_Calculated, 2));
    End; { then }

  M_EMA_Data := Round ((ADS1262_DATA_EMA_FACTOR * Result) + ((1 - ADS1262_DATA_EMA_FACTOR) * M_EMA_Data));

  {$IFDEF SELF_CALIBRATION_ON}
  Result := Result - M_EMA_Data;
  {$ENDIF}

  {$IFDEF DEBUG_ADC1_DATA}
  WriteLn ('Count : ', IntToHex (M_Count, 8), ' - ADC1 data : ', IntToHex (Result, 8), ' - V : ',
      FormatFloat (' #,##0.00000000;-#,##0.00000000; #,##0.00000000', Double (Result) * 2.5 / $7FFFFFFF), ' - Bits : ',
  {$ENDIF}

  {$IF ADS1262_WAIT_TIME_MS_READ_ADC1 > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_READ_ADC1);
  {$ENDIF}
End; { T_ADS1262.Read_ADC1_Data }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Read_ADC1_Data : Int_32;
  { Read ADC1 data                                                                          }
  { --------------------------------------------------------------------------------------- }
Var
  Status : U_Int_8;
  CRC :    U_Int_8;

Begin { T_ADS1262.Read_ADC1_Data }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Read_ADC1_Data');
  {$ENDIF}

  { Check for recalibration }
  Result := Read_ADC1_Data (Status, CRC);
End; { T_ADS1262.Read_ADC1_Data }


{ --------------------------------------------------------------------------------------- }
Procedure T_ADS1262.Read_All_Registers (Var F_Register_Block : T_Register_Block);
{ Read all registers into register array                                                  }
{ --------------------------------------------------------------------------------------- }
Var
  Length :          U_Int_8;
  Output_Buffer_A : T_Buffer_A;
  Input_Buffer_A :  T_Buffer_A;
  {$IF DEFINED (DEBUG_ADS1262_OUTPUT) or DEFINED (DEBUG_ADC1_DATA)}
  I :               Integer;
  S :               String;
  {$ENDIF}

Begin { T_ADS1262.Read_All_Registers }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Read_All_Registers');
  {$ENDIF}

  { Read ADC1 data }
  Length := ADS1262_REGISTER_N + 2;
  SetLength (Output_Buffer_A, Length);
  SetLength (Input_Buffer_A, Length);

  FillByte (Output_Buffer_A [0], Length, 0);
  FillByte (Input_Buffer_A [0], Length, 0);

  Output_Buffer_A[0] := ADS1262_COMMAND_RREG or ADS1262_REGISTER_ID;
  Output_Buffer_A[1] := ADS1262_REGISTER_N - 1;

  SPI_Write_Read_Buffer (Output_Buffer_A, Input_Buffer_A, Length);

  Move (Input_Buffer_A [2], F_Register_Block [0], ADS1262_REGISTER_N);

  {$IF DEFINED (DEBUG_ADS1262_OUTPUT) or DEFINED (DEBUG_ADC1_DATA)}
  S := '';
  For I := 0 To ADS1262_REGISTER_N - 1 Do
    Begin { For }
      S := S + IntToHex (F_Register_Block [I], 2) + ' ';
    End; { For }

  WriteLn ('Registerblock: ' + S);
  {$ENDIF}

  {$IF ADS1262_WAIT_TIME_MS_READ_REGISTER > 0 }
  Sleep (ADS1262_WAIT_TIME_MS_READ_REGISTER);
  {$ENDIF}
End; { T_ADS1262.Read_All_Registers }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Calculate_Voltage (F_Data : Int_32) : Double;
  { Calculate voltage                                                                       }
  { --------------------------------------------------------------------------------------- }

Begin { T_ADS1262.Calculate_Voltage }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Calculate_Voltage');
  {$ENDIF}

  { Calculate voltage }
  Result := Calculate_Sensivity_Value (F_Data, M_Sensivity) * M_Voltage_Limit / $7FFFFFFF;
End; { T_ADS1262.Calculate_Voltage }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Get_Voltage () : Double;
  { Read voltage from ADC                                                                   }
  { --------------------------------------------------------------------------------------- }

Begin { T_ADS1262.Get_Voltage }
  {$IFDEF DEBUG_ADS1262_OUTPUT}
  WriteLn ('Begin of    : T_ADS1262.Get_Voltage');
  {$ENDIF}

  { Calculate voltage }
  Result := Calculate_Voltage (Read_ADC1_Data);
End; { T_ADS1262.Get_Voltage }


{ --------------------------------------------------------------------------------------- }
Function T_ADS1262.Calculate_Sensivity_Value (F_Value : Int_32; F_Sensivity : Int_32) : Int_32;
  { --------------------------------------------------------------------------------------- }
Var
  Mask : Int_32;

Begin { T_ADS1262.Calculate_Sensivity_Value }
  If F_Value >= 0 Then
    Begin { then }
      If F_Sensivity < 32 Then
        Begin { then }
          Mask   := not (($01 shl (32 - F_Sensivity)) - 1);
          Result := F_Value and Mask;
        End { then }
      Else
        Begin { else  }
          Result := F_Value;
        End; { else  }
    End { then }
  Else
    Begin { else  }
      If F_Sensivity < 32 Then
        Begin { then }
          Mask   := not (($01 shl (32 - F_Sensivity)) - 1);
          Result := -(-F_Value and Mask);
        End { then }
      Else
        Begin { else  }
          Result := F_Value;
        End; { else  }
    End; { else  }
End; { T_ADS1262.Calculate_Sensivity_Value }


End.

Master Control Program (MCP) with main form and control loop

The master control program rules the complete process. The sensor data is read in an own thread and processed in the main form of the program. The results are displayd on a chart and updated frequenty all 4 seconds.

Main_Unit

The files main_unit.pas and main_unit.frm contain the source code for the main application form. The form contains a chart, which displays the results. This unit controls two threads: one thread for reading the sensor data, represented by the class T_Timer_Thread, and a second thread, which is the main thread of the program represented by class TMain_F, for displaying the data at the chart.

T_Timer_Thread is a thread class for reading the sensor data from the ADS1262. The sensor data is read with interrupt support in order not to stress the CPU. All data is saved in a ring buffer. The frequency of the loop is around 400 Hz.

TMain_F is the class for the main form of the application. The form contains a chart, which displays the sensor data.

{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## Main_Unit                                                                         ## }
{ ##                                                                                   ## }
{ ## Main form                                                                         ## }
{ ##                                                                                   ## }
{ ## Copyright (C) 2018-2020  : Dr. Jürgen Abel                                        ## }
{ ## Email                    : juergen@juergen-abel.info                              ## }
{ ## Internet                 : www.seismometer.info                                   ## }
{ ##                                                                                   ## }
{ ## This program is free software: you can redistribute it and/or modify              ## }
{ ## it under the terms of the GNU Lesser General Public License as published by       ## }
{ ## the Free Software Foundation, either version 3 of the License, or                 ## }
{ ## (at your option) any later version with the following modification:               ## }
{ ##                                                                                   ## }
{ ## As a special exception, the copyright holders of this library give you            ## }
{ ## permission to link this library with independent modules to produce an            ## }
{ ## executable, regardless of the license terms of these independent modules, and     ## }
{ ## to copy and distribute the resulting executable under terms of your choice,       ## }
{ ## provided that you also meet, for each linked independent module, the terms        ## }
{ ## and conditions of the license of that module. An independent module is a          ## }
{ ## module which is not derived from or based on this library. If you modify          ## }
{ ## this library, you may extend this exception to your version of the library,       ## }
{ ## but you are not obligated to do so. If you do not wish to do so, delete this      ## }
{ ## exception statement from your version.                                            ## }
{ ##                                                                                   ## }
{ ## This program is distributed in the hope that it will be useful,                   ## }
{ ## but WITHOUT ANY WARRANTY; without even the implied warranty of                    ## }
{ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                     ## }
{ ## GNU General Public License for more details.                                      ## }
{ ##                                                                                   ## }
{ ## You should have received a copy of the GNU Lesser General Public License          ## }
{ ## COPYING.LGPL.txt along with this program.                                         ## }
{ ## If not, see https://www.gnu.org/licenses/                                         ## }
{ ##                                                                                   ## }
{ ####################################################################################### }


Unit Main_Unit;

{$mode objfpc}{$H+}

{$INCLUDE project_defines.inc}

Interface

Uses
  CThreads,
  Classes,
  SysUtils,
  Forms,
  Controls,
  Graphics,
  Dialogs,
  StdCtrls,
  ExtCtrls,
  ComCtrls,
  TAGraph,
  TASeries,
  ADS1262_Unit,
  TACustomSeries,
  TAIntervalSources,
  TATools,
  Types,
  TACustomSource,
  TASources,
  TAChartExtentLink,
  TAFuncSeries,
  Process,
  LCLType,
  LCLIntf,
  SpinEx,
  TALegend,
  TADrawUtils;

Const
  ADS1262_MODE2_GAIN_01_LIMIT    = 2.5;
  ADS1262_MODE2_GAIN_02_LIMIT    = ADS1262_MODE2_GAIN_01_LIMIT / 2;
  ADS1262_MODE2_GAIN_04_LIMIT    = ADS1262_MODE2_GAIN_01_LIMIT / 4;
  ADS1262_MODE2_GAIN_08_LIMIT    = ADS1262_MODE2_GAIN_01_LIMIT / 8;
  ADS1262_MODE2_GAIN_16_LIMIT    = ADS1262_MODE2_GAIN_01_LIMIT / 16;
  ADS1262_MODE2_GAIN_32_LIMIT    = ADS1262_MODE2_GAIN_01_LIMIT / 32;
  ADS1262_SAMPLE_RATE            = 400;
  ADS1262_INTERRUPT_TIMEOUT_MSEC = 1000;
  {$IFDEF USE_INTERRUPT}
  {$ELSE}
  DATA_INTERVAL_MS               = 2;
  {$ENDIF}
  LP_SPAN_INIT                   = 20;
  LP_SLOW_SPAN                   = 4000;
  LP_SLOW_SETTLE_FACTOR          = 4;
  LP_INTEGRAL_SPAN               = 800;
  LP_INTEGRAL_CORRECTION_DIVIDER = 8000;
  DATA_ARRAY_SIZE                = 1024 * 1024;
  DATA_CHART_EXTEND_Y            = 0.02;
  DATA_CHART_EXTEND_Y_MAX        = 2.5;
  DATA_CHART_LABEL_SIZE          = 120;
  DATA_CHART_UNIT_FORMAT         = '%0:.9g mm/s';
  TIME_UNIT_FORMAT               = '%0:.9g mm/s';
  VALUES_PER_HOUR                = 60 * 60;
  RMS_COLOR                      = $2222AA;
  RMS_BAR_WIDTH_PERCENT          = 100;
  DATA_COLOR                     = $FF4444;
  DATA_PIN_WIDTH                 = 3;
  POSITION_COLOR                 = $66EE66;
  POSITION_WIDTH                 = 11;
  CURRENT_IMAGE_FILE_NAME        = 'images/current_image.jpg';
  DAILY_IMAGE_FILE_NAME          = 'images/daily_image.jpg';
  HOUR_CHART_N                   = 24;
  HOUR_CHART_FONT_SIZE           = 6;
  STATION_NAME                   = 'SEISMOMETER_NORF_GERMANY';
  CURRENT_TIME_FORMAT_00_S       = 'Live Data  -  Station Name: ' + STATION_NAME + '  -  Date: ';
  CURRENT_TIME_FORMAT_01_S       = '  -  Time: ';
  SQR_2                          = sqrt (2);
  BIT_N_RANGE                    = 100;
  VOLTAGE_LIMIT                  = ADS1262_MODE2_GAIN_32_LIMIT;
  SM24_VELOCITY_FACTOR           = 20.9;
  DELAY_SHOW_TIME_MSEC           = 2000;
  HALT_TIME_MSEC                 = 60000;
  FORM_LEFT                      = 10;
  FORM_TOP                       = 10;
  FORM_WIDTH                     = 1920;
  FORM_HEIGHT                    = 1038;
  CLOSE_WAIT_TIME_MSEC           = 1000;
  RANDOM_N                       = 400;
  RANDOM_MAX                     = 4 * 1024 * 1024;
  SPECTRUM_MAX_FREQUENCY         = 100;
  SPECTRUM_Y_RESOLUTION          = 100;
  SPECTRUM_DATETIME_OFFSET       = 43800;
  SPECTRUM_SAMPLE_N              = 400;
  SPECTRUM_SAMPLE_RATE           = 400;
  SPECTRUM_X_ARRAY_SIZE          = SPECTRUM_SAMPLE_N;
  SPECTRUM_MAXIMUM_SPAN          = 4;
  SPECTRUM_CHART_UNIT_FORMAT     = '%0:.9g Hz';

Type
  T_Data_X_A                 = Array [0 .. DATA_ARRAY_SIZE - 1] Of TDateTime;
  T_Data_Y_A                 = Array [0 .. DATA_ARRAY_SIZE - 1] Of Int_32;
  T_Hour_Chart_A             = Array [0 .. HOUR_CHART_N - 1] Of TChart;
  T_Hour_RMS_BS_A            = Array [0 .. HOUR_CHART_N - 1] Of TBarSeries;
  T_Hour_Data_LS_A           = Array [0 .. HOUR_CHART_N - 1] Of TLineSeries;
  T_Hour_Position_CL_A       = Array [0 .. HOUR_CHART_N - 1] Of TConstantLine;
  T_Chart_Kind               = (CURRENT_CHART_CK, DAILY_CHART_CK);
  T_Spectrum_A               = Array [0 .. SPECTRUM_X_ARRAY_SIZE - 1, 0 .. SPECTRUM_Y_RESOLUTION] Of Double;
  T_Spectrum_Maximum_A       = Array [0 .. SPECTRUM_X_ARRAY_SIZE - 1] Of Double;
  T_Spectrum_Input_A         = Array Of Double;
  T_Spectrum_Window_Funktion = (TWF_None, TWF_von_Hahn, TWF_Hamming, TWF_Blackman, TWF_KaiserBessel, TWF_FlatTop);

  { ####################################################################################### }
  { ## T_Timer_Thread                                                                    ## }
  { ####################################################################################### }
  T_Timer_Thread = Class(TThread)
  Private
      M_ADS1262 :              T_ADS1262;
      M_Data_X_A :             T_Data_X_A;
      M_Data_Y_A :             T_Data_Y_A;
      M_Data_LP_Y_A :          T_Data_Y_A;
      M_Data_Integral_Y_A :    T_Data_Y_A;
      M_Data_LP_Integral_Y_A : T_Data_Y_A;
      M_Data_A_Index :         Integer;
      M_Count :                Int_64;
      M_LP_Span :              Integer;
  Public
      Property Data_X_A : T_Data_X_A Read M_Data_X_A;
      Property Data_Y_A : T_Data_Y_A Read M_Data_Y_A;
      Property Data_LP_Y_A : T_Data_Y_A Read M_Data_LP_Y_A;
      Property Data_Integral_Y_A : T_Data_Y_A Read M_Data_Integral_Y_A;
      Property Data_LP_Integral_Y_A : T_Data_Y_A Read M_Data_LP_Integral_Y_A;
      Property Data_A_Index : Integer Read M_Data_A_Index;
      Property Count : Int_64 Read M_Count;
      Property ADS1262 : T_ADS1262 Read M_ADS1262;
      Property LP_Span : Integer Read M_LP_Span Write M_LP_Span;
      Constructor Create (F_Suspended : Boolean);
      Destructor Destroy; Override;
      Procedure Execute; Override;
  End; { T_Timer_Thread }

  { ####################################################################################### }
  { ## TMain_F                                                                           ## }
  { ####################################################################################### }

  { TMain_F }

  TMain_F = Class(TForm)
      Chart_00_11_P :               TPanel;
      Chart_12_23_P :               TPanel;
      Chart_TS :                    TChartToolset;
      Chart_TS_ZoomDragTool :       TZoomDragTool;
      Chart_TS_DataPointHintTool :  TDataPointHintTool;
      Chart_TS_PanDragTool :        TPanDragTool;
      Chart_TS_ZoomMouseWheelTool : TZoomMouseWheelTool;
      Close_B :                     TButton;
      Current_Chart_C :             TChart;
      Current_Integral_LP_LS :      TLineSeries;
      Current_Integral_LS :         TLineSeries;
      Current_CL :                  TConstantLine;
      Current_EMA_LS :              TLineSeries;
      Current_LP_LS :               TLineSeries;
      Current_LS :                  TLineSeries;
      Current_RMS_LS :              TLineSeries;
      Daily_Chart_P :               TPanel;
      Data_Splitter :               TSplitter;
      Hour_00_11_P :                TPanel;
      Hour_0_C :                    TChart;
      Hour_0_Data_LS :              TLineSeries;
      Hour_0_Position_CL :          TConstantLine;
      Hour_0_RMS_BS :               TBarSeries;
      Hour_10_C :                   TChart;
      Hour_10_Data_LS :             TLineSeries;
      Hour_10_Position_CL :         TConstantLine;
      Hour_10_RMS_BS :              TBarSeries;
      Hour_11_C :                   TChart;
      Hour_11_Data_LS :             TLineSeries;
      Hour_11_Position_CL :         TConstantLine;
      Hour_11_RMS_BS :              TBarSeries;
      Hour_12_23_P :                TPanel;
      Hour_12_C :                   TChart;
      Hour_12_Data_LS :             TLineSeries;
      Hour_12_Position_CL :         TConstantLine;
      Hour_12_RMS_BS :              TBarSeries;
      Hour_13_C :                   TChart;
      Hour_13_Data_LS :             TLineSeries;
      Hour_13_Position_CL :         TConstantLine;
      Hour_13_RMS_BS :              TBarSeries;
      Hour_14_C :                   TChart;
      Hour_14_Data_LS :             TLineSeries;
      Hour_14_Position_CL :         TConstantLine;
      Hour_14_RMS_BS :              TBarSeries;
      Hour_15_C :                   TChart;
      Hour_15_Data_LS :             TLineSeries;
      Hour_15_Position_CL :         TConstantLine;
      Hour_15_RMS_BS :              TBarSeries;
      Hour_16_C :                   TChart;
      Hour_16_Data_LS :             TLineSeries;
      Hour_16_Position_CL :         TConstantLine;
      Hour_16_RMS_BS :              TBarSeries;
      Hour_17_C :                   TChart;
      Hour_17_Data_LS :             TLineSeries;
      Hour_17_Position_CL :         TConstantLine;
      Hour_17_RMS_BS :              TBarSeries;
      Hour_18_C :                   TChart;
      Hour_18_Data_LS :             TLineSeries;
      Hour_18_Position_CL :         TConstantLine;
      Hour_18_RMS_BS :              TBarSeries;
      Hour_19_C :                   TChart;
      Hour_19_Data_LS :             TLineSeries;
      Hour_19_Position_CL :         TConstantLine;
      Hour_19_RMS_BS :              TBarSeries;
      Hour_1_C :                    TChart;
      Hour_1_Data_LS :              TLineSeries;
      Hour_1_Position_CL :          TConstantLine;
      Hour_1_RMS_BS :               TBarSeries;
      Hour_20_C :                   TChart;
      Hour_20_Data_LS :             TLineSeries;
      Hour_20_Position_CL :         TConstantLine;
      Hour_20_RMS_BS :              TBarSeries;
      Hour_21_C :                   TChart;
      Hour_21_Data_LS :             TLineSeries;
      Hour_21_Position_CL :         TConstantLine;
      Hour_21_RMS_BS :              TBarSeries;
      Hour_22_C :                   TChart;
      Hour_22_Data_LS :             TLineSeries;
      Hour_22_Position_CL :         TConstantLine;
      Hour_22_RMS_BS :              TBarSeries;
      Hour_23_C :                   TChart;
      Hour_23_Data_LS :             TLineSeries;
      Hour_23_Position_CL :         TConstantLine;
      Hour_23_RMS_BS :              TBarSeries;
      Hour_2_C :                    TChart;
      Hour_2_Data_LS :              TLineSeries;
      Hour_2_Position_CL :          TConstantLine;
      Hour_2_RMS_BS :               TBarSeries;
      Hour_3_C :                    TChart;
      Hour_3_Data_LS :              TLineSeries;
      Hour_3_Position_CL :          TConstantLine;
      Hour_3_RMS_BS :               TBarSeries;
      Hour_4_C :                    TChart;
      Hour_4_Data_LS :              TLineSeries;
      Hour_4_Position_CL :          TConstantLine;
      Hour_4_RMS_BS :               TBarSeries;
      Hour_5_C :                    TChart;
      Hour_5_Data_LS :              TLineSeries;
      Hour_5_Position_CL :          TConstantLine;
      Hour_5_RMS_BS :               TBarSeries;
      Hour_6_C :                    TChart;
      Hour_6_Data_LS :              TLineSeries;
      Hour_6_Position_CL :          TConstantLine;
      Hour_6_RMS_BS :               TBarSeries;
      Hour_7_C :                    TChart;
      Hour_7_Data_LS :              TLineSeries;
      Hour_7_Position_CL :          TConstantLine;
      Hour_7_RMS_BS :               TBarSeries;
      Hour_8_C :                    TChart;
      Hour_8_Data_LS :              TLineSeries;
      Hour_8_Position_CL :          TConstantLine;
      Hour_8_RMS_BS :               TBarSeries;
      Hour_9_C :                    TChart;
      Hour_9_Data_LS :              TLineSeries;
      Hour_9_Position_CL :          TConstantLine;
      Hour_9_RMS_BS :               TBarSeries;
      Input_LP_Span_E :             TEdit;
      Input_LP_Span_L :             TLabel;
      Input_OFCAL_Value_E :         TEdit;
      Input_OFCAL_Value_L :         TLabel;
      Input_Sensivity_Bits_E :      TEdit;
      Input_Sensivity_Bits_L :      TLabel;
      Input_Window_Function_L :  TLabel;
      Input_Window_Function_SE : TSpinEditEx;
      Input_X_Axis_Extend_E :       TEdit;
      Input_Timer_Sec_E :           TEdit;
      Input_X_Axis_Extend_L :       TLabel;
      Input_Timer_Sec_L :           TLabel;
      Label_0 :                     TLabel;
      Label_1 :                     TLabel;
      Label_10 :                    TLabel;
      Label_11 :                    TLabel;
      Label_12 :                    TLabel;
      Label_13 :                    TLabel;
      Label_14 :                    TLabel;
      Label_15 :                    TLabel;
      Label_16 :                    TLabel;
      Label_17 :                    TLabel;
      Label_18 :                    TLabel;
      Label_19 :                    TLabel;
      Label_2 :                     TLabel;
      Label_20 :                    TLabel;
      Label_21 :                    TLabel;
      Label_22 :                    TLabel;
      Label_23 :                    TLabel;
      Label_3 :                     TLabel;
      Label_4 :                     TLabel;
      Label_5 :                     TLabel;
      Label_6 :                     TLabel;
      Label_7 :                     TLabel;
      Label_8 :                     TLabel;
      Label_9 :                     TLabel;
      Maint_00_11_P :               TPanel;
      Maint_12_23_P :               TPanel;
      Main_PC :                     TPageControl;
      Margin_00_11_P :              TPanel;
      Margin_12_23_P :              TPanel;
      Current_Chart_P :             TPanel;
      Output_1_P :                  TPanel;
      Red_Yellow_White_CS :         TListChartSource;
      Main_P :                      TPanel;
      Output_2_P :                  TPanel;
      Output_3_P :                  TPanel;
      Output_Bits_E :               TEdit;
      Output_Bits_L :               TLabel;
      Output_Counter_E :            TEdit;
      Output_Counter_L :            TLabel;
      Output_Freq_E :               TEdit;
      Output_Freq_L :               TLabel;
      Output_NPS_E :                TEdit;
      Output_NPS_L :                TLabel;
      Reset_B :                     TButton;
      Show_Integral_CB :            TCheckBox;
      Show_LP_Integral_CB :         TCheckBox;
      Show_Integral_L :             TLabel;
      Show_LP_Integral_L :          TLabel;
      Show_Spektrum_CB :            TCheckBox;
      Show_Spektrum_L :             TLabel;
      Spectrum_CEL :                TChartExtentLink;
      Spectrum_Chart_C :            TChart;
      Spectrum_CMS :                TColorMapSeries;
      Status_L :                    TLabel;
      Status_P :                    TPanel;
      Show_Current_Data_CB :        TCheckBox;
      Show_Data_CB :                TCheckBox;
      Show_EMA_CB :                 TCheckBox;
      Show_RMS_CB :                 TCheckBox;
      Show_EMA_L :                  TLabel;
      Show_RMS_L :                  TLabel;
      Show_LP_Data_CB :             TCheckBox;
      Show_Current_Data_L :         TLabel;
      Show_Data_L :                 TLabel;
      Show_LP_Data_L :              TLabel;
      Start_Stop_B :                TButton;
      Current_Chart_TS :            TTabSheet;
      Daily_Chart_TS :              TTabSheet;
      Daily_Chart_0_P :             TPanel;
      Time_CS :                     TDateTimeIntervalChartSource;
      Output_0_P :                  TPanel;
      Chart_T :                     TTimer;
      Procedure Chart_TS_DataPointHintToolHint (ATool : TDataPointHintTool; Const APoint : TPoint; Var AHint : String);
      Procedure Chart_TTimer (Sender : TObject);
      Procedure Close_BClick (Sender : TObject);
      Procedure FormCreate (Sender : TObject);
      Procedure FormDestroy (Sender : TObject);
      Procedure FormShow (Sender : TObject);
      Procedure Input_LP_Span_EEditingDone (Sender : TObject);
      Procedure Input_OFCAL_Value_EEditingDone (Sender : TObject);
      Procedure Input_Sensivity_Bits_EEditingDone (Sender : TObject);
      Procedure Input_Timer_Sec_EEditingDone (Sender : TObject);
      Procedure Input_X_Axis_Extend_EEditingDone (Sender : TObject);
      Procedure Reset_BClick (Sender : TObject);
      Procedure Spectrum_CMSCalculate (Const AX, AY : Double; out AZ : Double);
      Procedure Start_Stop_BClick (Sender : TObject);
  Protected
      M_Data_Start_Index :        Integer;
      M_Data_End_Index :          Integer;
      M_Data_A_Size :             Integer;
      M_Transfer_Process :        TProcess;
      M_Timer_Thread :            T_Timer_Thread;
      M_Data_A_Index_0 :          Integer;
      M_Data_A_Index_1 :          Integer;
      M_Start_Time :              TDateTime;
      M_End_Time :                TDateTime;
      M_Hour_Chart_A :            T_Hour_Chart_A;
      M_Hour_RMS_BS_A :           T_Hour_RMS_BS_A;
      M_Hour_Data_LS_A :          T_Hour_Data_LS_A;
      M_Hour_Position_CL_A :      T_Hour_Position_CL_A;
      M_Old_Data_Time :           TDateTime;
      M_Old_Data_Hour :           Integer;
      M_Old_Data_Minute :         Integer;
      M_Old_Data_Second :         Integer;
      M_Last_Saved_Minute :       Integer;
      M_Count :                   Int_64;
      M_Spectrum_A :              T_Spectrum_A;
      M_Spectrum_Maximum_A :      T_Spectrum_Maximum_A;
      M_Period_Range :            Integer;
      M_Frequency :               Double;
      M_Window_Size :             Integer;
      M_Windowing_Function :      T_Spectrum_Window_Funktion;
      M_Sensivity :               Integer;
      M_Goertzel_Window_Divider : Integer;
      M_DateTime_Start_Offset :   Integer;
      Procedure Debug_Output (F_Text : String);
      Function Calculate_Velocity (F_Data : Int_32) : Double;
      Procedure Read_Array_Data (F_Index : Integer; Var F_X : TDateTime; Var F_Y : Int_32; Var F_LP_Y : Int_32);
      Procedure Output_Current_Time;
      Procedure Output_Current_Data (F_Measurement_N : Integer; F_Zero_Crossings_N : Integer; F_End_Time : TDateTime);
      Procedure Clear_Hour_Data_LS (F_Hour_Data_LS : TLineSeries);
      Procedure Clear_Hour_RMS_BS (F_Hour_RMS_BS : TBarSeries);
      Function Get_Time_Index (F_Time_Value : TDateTime) : Integer;
      Function Get_Spectrum (F_X : TDateTime; F_Y : Integer) : Double;
      Procedure Set_Spectrum (F_X : TDateTime; F_Y : Integer; F_Value : Double);
      Procedure Set_Amplitude (F_X : Integer; F_Y : Integer; F_Value : Double);
      Procedure Set_Phi (F_X : Integer; F_Y : Integer; F_Value : Double);
      Procedure Calculate_Goertzel_DFT (F_End_Index : Integer; F_End_Time : TDateTime);
  Public
      {$IFDEF USE_CRITICAL_SECTION}
      M_Critical_Section : TCriticalSection;
      {$ENDIF}
      Property Count : Int_64 Read M_Count;
  End; { TMain_F }

Var
  Main_F : TMain_F;

Implementation

{$R *.frm}

Uses
  Math,
  DateUtils,
  LazFileUtils,
  TAChartUtils;


{#####################################################################################}
{ Utilities                                                                           }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Function Validate_Array_Index (F_Index : Integer; F_Array_Size : Integer) : Integer;
  { Validate index into array                                                               }
  { --------------------------------------------------------------------------------------- }
Begin { Validate_Array_Index }
  If F_Index >= 0 Then
    Begin { then }
      Result := F_Index mod F_Array_Size;
    End { then }
  Else
    Begin { else  }
      Result := ((F_Index mod F_Array_Size) + F_Array_Size) mod F_Array_Size;
    End; { else  }
End; { Validate_Array_Index }


{ ####################################################################################### }
{ ## T_Timer_Thread                                                                    ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Constructor T_Timer_Thread.Create (F_Suspended : Boolean);
  { Create timer thread                                                                     }
  { --------------------------------------------------------------------------------------- }
{$IFDEF DEBUG_ERROR}
Var
  Register_Block : T_Register_Block;
{$ENDIF}
Begin { T_Timer_Thread.Create }
  Inherited Create (F_Suspended);

  FreeOnTerminate := FALSE;

  M_ADS1262 := T_ADS1262.Create ();
  M_ADS1262.Set_Continuous_Conversion ();
  M_ADS1262.Start_Conversion ();
  Sleep (ADS1262_WAIT_TIME_MS_COMMAND);

  {$IFDEF DEBUG_ERROR}
  M_ADS1262.Read_All_Registers (Register_Block);
  {$ENDIF}

  FillByte (M_Data_X_A [0], DATA_ARRAY_SIZE * SizeOf (TDateTime), $00);
  FillByte (M_Data_Y_A [0], DATA_ARRAY_SIZE * SizeOf (Int_32), $00);
  FillByte (M_Data_LP_Y_A [0], DATA_ARRAY_SIZE * SizeOf (Int_32), $00);
  FillByte (M_Data_Integral_Y_A [0], DATA_ARRAY_SIZE * SizeOf (Int_32), $00);
  FillByte (M_Data_LP_Integral_Y_A [0], DATA_ARRAY_SIZE * SizeOf (Int_32), $00);

  M_Data_A_Index := 0;
  M_Count        := 0;
  M_LP_Span      := LP_SPAN_INIT;
End; { T_Timer_Thread.Create }


{ --------------------------------------------------------------------------------------- }
Destructor T_Timer_Thread.Destroy;
  { Free data                                                                               }
  { --------------------------------------------------------------------------------------- }
Begin { T_Timer_Thread.Destroy }
  M_ADS1262.Free;

  Inherited Destroy;
End; { T_Timer_Thread.Destroy }


{ --------------------------------------------------------------------------------------- }
Procedure T_Timer_Thread.Execute;
{ Execute thread                                                                          }
{ --------------------------------------------------------------------------------------- }
Var
  A_1 :             Double;
  B_1 :             Double;
  C_1 :             Double;
  C_2 :             Double;
  C_3 :             Double;
  A_1_LP_Integral : Double;
  B_1_LP_Integral : Double;
  C_1_LP_Integral : Double;
  C_2_LP_Integral : Double;
  C_3_LP_Integral : Double;

Begin { T_Timer_Thread.Execute }
  While (Terminated = FALSE) Do
    Begin { While }
      {$IFDEF USE_INTERRUPT}
      M_ADS1262.DRDY_Pin.Wait_For_Interrupt (ADS1262_INTERRUPT_TIMEOUT_MSEC);
      {$ELSE}
      Sleep (DATA_INTERVAL_MS);
      {$ENDIF}

      {$IFDEF USE_CRITICAL_SECTION}
      EnterCriticalSection (Main_F.M_Critical_Section);
        Try
      {$ENDIF}
          M_Data_X_A[M_Data_A_Index] := Now;
          M_Data_Y_A[M_Data_A_Index] := M_ADS1262.ADC1_Data;

          A_1                           := exp (-SQR_2 * PI / Round (M_LP_Span));
          B_1                           := 2 * A_1 * cos (SQR_2 * PI / Round (M_LP_Span));
          C_2                           := B_1;
          C_3                           := -A_1 * A_1;
          C_1                           := 1 - C_2 - C_3;
          M_Data_LP_Y_A[M_Data_A_Index] := Round ((C_1 * Round (M_Data_Y_A [M_Data_A_Index])) + (C_2 * Round (M_Data_LP_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)])) + (C_3 * Round (M_Data_LP_Y_A [Validate_Array_Index (M_Data_A_Index - 2, DATA_ARRAY_SIZE)])));

          A_1_LP_Integral := exp (-SQR_2 * PI / Round (LP_INTEGRAL_SPAN));
          B_1_LP_Integral := 2 * A_1_LP_Integral * cos (SQR_2 * PI / Round (LP_INTEGRAL_SPAN));
          C_2_LP_Integral := B_1_LP_Integral;
          C_3_LP_Integral := -A_1_LP_Integral * A_1_LP_Integral;
          C_1_LP_Integral := 1 - C_2_LP_Integral - C_3_LP_Integral;

          If M_Count > LP_SLOW_SETTLE_FACTOR * LP_SLOW_SPAN Then
            Begin { then }
              //              M_Data_Integral_Y_A[M_Data_A_Index]    := M_Data_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)] + M_Data_Y_A [M_Data_A_Index] - M_Data_LP_Y_A[M_Data_A_Index] - Round (M_Data_LP_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)] / LP_INTEGRAL_CORRECTION_DIVIDER);



              M_Data_Integral_Y_A[M_Data_A_Index] := M_Data_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)] + M_Data_Y_A [M_Data_A_Index] - Round (M_Data_LP_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)] / LP_INTEGRAL_CORRECTION_DIVIDER);




              M_Data_LP_Integral_Y_A[M_Data_A_Index] := Round ((C_1_LP_Integral * Round (M_Data_Integral_Y_A [M_Data_A_Index])) + (C_2_LP_Integral * Round (M_Data_LP_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 1, DATA_ARRAY_SIZE)])) + (C_3_LP_Integral * Round (M_Data_LP_Integral_Y_A [Validate_Array_Index (M_Data_A_Index - 2, DATA_ARRAY_SIZE)])));
            End { then }
          Else
            Begin { else  }
              M_Data_Integral_Y_A[M_Data_A_Index]    := 0;
              M_Data_LP_Integral_Y_A[M_Data_A_Index] := 0;
            End; { else  }

          Inc (M_Count);
          M_Data_A_Index := Validate_Array_Index (M_Data_A_Index + 1, DATA_ARRAY_SIZE);
      {$IFDEF USE_CRITICAL_SECTION}
        Finally
          LeaveCriticalSection (Main_F.M_Critical_Section);
        End;
      {$ENDIF}
    End; { While }
End; { T_Timer_Thread.Execute }


{ ####################################################################################### }
{ ## TMain_F                                                                           ## }
{ ####################################################################################### }

{#####################################################################################}
{ Build form                                                                          }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.FormCreate (Sender : TObject);
{ Create main from                                                                        }
{ --------------------------------------------------------------------------------------- }
Var
  I :                Integer;
  Hour_Chart :       TChart;
  Hour_RMS_BS :      TBarSeries;
  Hour_Data_LS :     TLineSeries;
  Hour_Position_CL : TConstantLine;

Begin { TMain_F.FormCreate }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.FormCreate');
  {$ENDIF}

  { Create CriticalSection }
  {$IFDEF USE_CRITICAL_SECTION}
  InitializeCriticalSection (M_Critical_Section);
  {$ENDIF}

  { Initialize arrays }
  FillByte (M_Spectrum_A [0, 0], SPECTRUM_X_ARRAY_SIZE * SizeOf (Double) * (SPECTRUM_Y_RESOLUTION + 1), $00);
  FillByte (M_Spectrum_Maximum_A [0], SPECTRUM_X_ARRAY_SIZE * SizeOf (Double), $00);

  { Initialize daily chart }
  M_Hour_Chart_A[0]  := Hour_0_C;
  M_Hour_Chart_A[1]  := Hour_1_C;
  M_Hour_Chart_A[2]  := Hour_2_C;
  M_Hour_Chart_A[3]  := Hour_3_C;
  M_Hour_Chart_A[4]  := Hour_4_C;
  M_Hour_Chart_A[5]  := Hour_5_C;
  M_Hour_Chart_A[6]  := Hour_6_C;
  M_Hour_Chart_A[7]  := Hour_7_C;
  M_Hour_Chart_A[8]  := Hour_8_C;
  M_Hour_Chart_A[9]  := Hour_9_C;
  M_Hour_Chart_A[10] := Hour_10_C;
  M_Hour_Chart_A[11] := Hour_11_C;
  M_Hour_Chart_A[12] := Hour_12_C;
  M_Hour_Chart_A[13] := Hour_13_C;
  M_Hour_Chart_A[14] := Hour_14_C;
  M_Hour_Chart_A[15] := Hour_15_C;
  M_Hour_Chart_A[16] := Hour_16_C;
  M_Hour_Chart_A[17] := Hour_17_C;
  M_Hour_Chart_A[18] := Hour_18_C;
  M_Hour_Chart_A[19] := Hour_19_C;
  M_Hour_Chart_A[20] := Hour_20_C;
  M_Hour_Chart_A[21] := Hour_21_C;
  M_Hour_Chart_A[22] := Hour_22_C;
  M_Hour_Chart_A[23] := Hour_23_C;

  M_Hour_RMS_BS_A[0]  := Hour_0_RMS_BS;
  M_Hour_RMS_BS_A[1]  := Hour_1_RMS_BS;
  M_Hour_RMS_BS_A[2]  := Hour_2_RMS_BS;
  M_Hour_RMS_BS_A[3]  := Hour_3_RMS_BS;
  M_Hour_RMS_BS_A[4]  := Hour_4_RMS_BS;
  M_Hour_RMS_BS_A[5]  := Hour_5_RMS_BS;
  M_Hour_RMS_BS_A[6]  := Hour_6_RMS_BS;
  M_Hour_RMS_BS_A[7]  := Hour_7_RMS_BS;
  M_Hour_RMS_BS_A[8]  := Hour_8_RMS_BS;
  M_Hour_RMS_BS_A[9]  := Hour_9_RMS_BS;
  M_Hour_RMS_BS_A[10] := Hour_10_RMS_BS;
  M_Hour_RMS_BS_A[11] := Hour_11_RMS_BS;
  M_Hour_RMS_BS_A[12] := Hour_12_RMS_BS;
  M_Hour_RMS_BS_A[13] := Hour_13_RMS_BS;
  M_Hour_RMS_BS_A[14] := Hour_14_RMS_BS;
  M_Hour_RMS_BS_A[15] := Hour_15_RMS_BS;
  M_Hour_RMS_BS_A[16] := Hour_16_RMS_BS;
  M_Hour_RMS_BS_A[17] := Hour_17_RMS_BS;
  M_Hour_RMS_BS_A[18] := Hour_18_RMS_BS;
  M_Hour_RMS_BS_A[19] := Hour_19_RMS_BS;
  M_Hour_RMS_BS_A[20] := Hour_20_RMS_BS;
  M_Hour_RMS_BS_A[21] := Hour_21_RMS_BS;
  M_Hour_RMS_BS_A[22] := Hour_22_RMS_BS;
  M_Hour_RMS_BS_A[23] := Hour_23_RMS_BS;

  M_Hour_Data_LS_A[0]  := Hour_0_Data_LS;
  M_Hour_Data_LS_A[1]  := Hour_1_Data_LS;
  M_Hour_Data_LS_A[2]  := Hour_2_Data_LS;
  M_Hour_Data_LS_A[3]  := Hour_3_Data_LS;
  M_Hour_Data_LS_A[4]  := Hour_4_Data_LS;
  M_Hour_Data_LS_A[5]  := Hour_5_Data_LS;
  M_Hour_Data_LS_A[6]  := Hour_6_Data_LS;
  M_Hour_Data_LS_A[7]  := Hour_7_Data_LS;
  M_Hour_Data_LS_A[8]  := Hour_8_Data_LS;
  M_Hour_Data_LS_A[9]  := Hour_9_Data_LS;
  M_Hour_Data_LS_A[10] := Hour_10_Data_LS;
  M_Hour_Data_LS_A[11] := Hour_11_Data_LS;
  M_Hour_Data_LS_A[12] := Hour_12_Data_LS;
  M_Hour_Data_LS_A[13] := Hour_13_Data_LS;
  M_Hour_Data_LS_A[14] := Hour_14_Data_LS;
  M_Hour_Data_LS_A[15] := Hour_15_Data_LS;
  M_Hour_Data_LS_A[16] := Hour_16_Data_LS;
  M_Hour_Data_LS_A[17] := Hour_17_Data_LS;
  M_Hour_Data_LS_A[18] := Hour_18_Data_LS;
  M_Hour_Data_LS_A[19] := Hour_19_Data_LS;
  M_Hour_Data_LS_A[20] := Hour_20_Data_LS;
  M_Hour_Data_LS_A[21] := Hour_21_Data_LS;
  M_Hour_Data_LS_A[22] := Hour_22_Data_LS;
  M_Hour_Data_LS_A[23] := Hour_23_Data_LS;

  M_Hour_Position_CL_A[0]  := Hour_0_Position_CL;
  M_Hour_Position_CL_A[1]  := Hour_1_Position_CL;
  M_Hour_Position_CL_A[2]  := Hour_2_Position_CL;
  M_Hour_Position_CL_A[3]  := Hour_3_Position_CL;
  M_Hour_Position_CL_A[4]  := Hour_4_Position_CL;
  M_Hour_Position_CL_A[5]  := Hour_5_Position_CL;
  M_Hour_Position_CL_A[6]  := Hour_6_Position_CL;
  M_Hour_Position_CL_A[7]  := Hour_7_Position_CL;
  M_Hour_Position_CL_A[8]  := Hour_8_Position_CL;
  M_Hour_Position_CL_A[9]  := Hour_9_Position_CL;
  M_Hour_Position_CL_A[10] := Hour_10_Position_CL;
  M_Hour_Position_CL_A[11] := Hour_11_Position_CL;
  M_Hour_Position_CL_A[12] := Hour_12_Position_CL;
  M_Hour_Position_CL_A[13] := Hour_13_Position_CL;
  M_Hour_Position_CL_A[14] := Hour_14_Position_CL;
  M_Hour_Position_CL_A[15] := Hour_15_Position_CL;
  M_Hour_Position_CL_A[16] := Hour_16_Position_CL;
  M_Hour_Position_CL_A[17] := Hour_17_Position_CL;
  M_Hour_Position_CL_A[18] := Hour_18_Position_CL;
  M_Hour_Position_CL_A[19] := Hour_19_Position_CL;
  M_Hour_Position_CL_A[20] := Hour_20_Position_CL;
  M_Hour_Position_CL_A[21] := Hour_21_Position_CL;
  M_Hour_Position_CL_A[22] := Hour_22_Position_CL;
  M_Hour_Position_CL_A[23] := Hour_23_Position_CL;

  For I := 0 To HOUR_CHART_N - 1 Do
    Begin { For }
      Hour_Chart       := M_Hour_Chart_A [I];
      Hour_RMS_BS      := M_Hour_RMS_BS_A [I];
      Hour_Data_LS     := M_Hour_Data_LS_A [I];
      Hour_Position_CL := M_Hour_Position_CL_A [I];

      With Hour_Chart Do
        Begin { With }
          Extent.XMax    := 0;
          Extent.XMin    := 60;
          Extent.UseXMax := TRUE;
          Extent.UseXMin := TRUE;

          ExtentSizeLimit.XMax    := 0;
          ExtentSizeLimit.XMin    := 60;
          ExtentSizeLimit.UseXMax := TRUE;
          ExtentSizeLimit.UseXMin := TRUE;

          Extent.YMax    := DATA_CHART_EXTEND_Y;
          Extent.YMin    := -DATA_CHART_EXTEND_Y;
          Extent.UseYMax := TRUE;
          Extent.UseYMin := TRUE;

          ExtentSizeLimit.YMax    := DATA_CHART_EXTEND_Y;
          ExtentSizeLimit.YMin    := -DATA_CHART_EXTEND_Y;
          ExtentSizeLimit.UseYMax := TRUE;
          ExtentSizeLimit.UseYMin := TRUE;

          LeftAxis.Marks.LabelFont.Size   := HOUR_CHART_FONT_SIZE;
          BottomAxis.Marks.LabelFont.Size := HOUR_CHART_FONT_SIZE;
        End; { With }

      Clear_Hour_RMS_BS (Hour_RMS_BS);

      Clear_Hour_Data_LS (Hour_Data_LS);

      With Hour_Position_CL Do
        Begin { With }
          Active      := FALSE;
          SeriesColor := POSITION_COLOR;
          Pen.Width   := POSITION_WIDTH;
          Position    := 0;
        End; { With }
    End; { For }

  M_Old_Data_Time     := Now;
  M_Old_Data_Hour     := 0;
  M_Old_Data_Minute   := 0;
  M_Old_Data_Second   := 0;
  M_Last_Saved_Minute := 0;

  { Position main form }
  Left   := FORM_LEFT;
  Top    := FORM_TOP;
  Width  := FORM_WIDTH;
  Height := FORM_HEIGHT;

  { Initialize current chart }
  Current_LS.Clear;
  Current_LP_LS.Clear;
  Current_EMA_LS.Clear;
  Current_RMS_LS.Clear;
  Current_Integral_LS.Clear;
  Current_Integral_LP_LS.Clear;

  Current_Chart_C.Extent.UseXMin          := TRUE;
  Current_Chart_C.Extent.UseXMax          := TRUE;
  Current_Chart_C.Extent.UseYMin          := TRUE;
  Current_Chart_C.Extent.UseYMax          := TRUE;
  Current_Chart_C.ExtentSizeLimit.YMax    := DATA_CHART_EXTEND_Y_MAX * 2;
  Current_Chart_C.ExtentSizeLimit.YMin    := -DATA_CHART_EXTEND_Y_MAX * 2;
  Current_Chart_C.ExtentSizeLimit.UseYMax := TRUE;
  Current_Chart_C.ExtentSizeLimit.UseYMin := TRUE;

  Current_Chart_C.AxisList.Axes[0].LabelSize    := DATA_CHART_LABEL_SIZE;
  Current_Chart_C.AxisList.Axes[0].Marks.Format := DATA_CHART_UNIT_FORMAT;

  Spectrum_CMS.Extent.UseYMin := TRUE;
  Spectrum_CMS.Extent.UseYMax := TRUE;
  Spectrum_CMS.Extent.YMax    := SPECTRUM_MAX_FREQUENCY;
  Spectrum_CMS.Extent.YMin    := 0;
  Spectrum_CMS.OnCalculate    := NIL;

  Spectrum_Chart_C.AxisList.Axes[0].LabelSize    := DATA_CHART_LABEL_SIZE;
  Spectrum_Chart_C.AxisList.Axes[0].Marks.Format := SPECTRUM_CHART_UNIT_FORMAT;

  M_Data_Start_Index := 0;
  M_Data_End_Index   := 0;

  M_Transfer_Process := TProcess.Create (NIL);

  {$IFDEF USE_TIMER_THREAD}
  M_Timer_Thread := T_Timer_Thread.Create (TRUE);
  M_Timer_Thread.ADS1262.Set_OFCAL (StrToInt (Input_OFCAL_Value_E.Text));
  {$ELSE}
  M_Count := 0;
  {$ENDIF}

  Reset_BClick (Sender);
End; { TMain_F.FormCreate }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.FormDestroy (Sender : TObject);
{ Free data                                                                               }
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.FormDestroy }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.FormDestroy');
  {$ENDIF}

  Chart_T.Enabled := FALSE;

  M_Transfer_Process.Free;

  {$IFDEF USE_TIMER_THREAD}
  M_Timer_Thread.Terminate;
  Sleep (CLOSE_WAIT_TIME_MSEC);
  M_Timer_Thread.Free;
  {$ENDIF}

  { Free CriticalSection }
  {$IFDEF USE_CRITICAL_SECTION}
  DeleteCriticalSection (M_Critical_Section);
  {$ENDIF}
End; { TMain_F.FormDestroy }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.FormShow (Sender : TObject);
{ Show main form                                                                          }
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.FormShow }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.FormShow');
  {$ENDIF}

  Input_X_Axis_Extend_EEditingDone (NIL);
  Input_Timer_Sec_EEditingDone (NIL);


  M_Data_A_Index_0 := 0;
  M_Data_A_Index_1 := 0;
  M_Start_Time     := Now;

  {$IFDEF USE_TIMER_THREAD}
  M_Timer_Thread.Start;
  {$ENDIF}

  { Display data }
  Chart_T.Enabled := TRUE;
End; { TMain_F.FormShow }


{#####################################################################################}
{ Buttons and edit components                                                         }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Output_Current_Data (F_Measurement_N : Integer; F_Zero_Crossings_N : Integer; F_End_Time : TDateTime);
{ --------------------------------------------------------------------------------------- }
Var
  I :         Integer;
  Bit_N_Max : Double;

Begin { TMain_F.Output_Current_Data }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Output_Current_Data');
  {$ENDIF}

  Bit_N_Max := 0;
  For I := 0 To BIT_N_RANGE - 1 Do
    Begin { For }
      //      Bit_N_Max := Max (Bit_N_Max, Log2 (Max (Abs (M_Timer_Thread.M_Data_Y_A [Validate_Array_Index (M_Data_A_Size - 1 - I, DATA_ARRAY_SIZE)]), 1)));
    End; { For }

  Output_NPS_E.Text     := FormatFloat ('#,##0.0', Double (F_Measurement_N) / (Double (MilliSecondSpan (M_Start_Time, F_End_Time)) / 1000));
  {$IFDEF USE_TIMER_THREAD}
  Output_Counter_E.Text := FormatFloat ('#,##0', M_Timer_Thread.Count);
  {$ELSE}
  Output_Counter_E.Text := FormatFloat ('#,##0', M_Count);
  {$ENDIF}
  Output_Bits_E.Text    := FormatFloat ('#,##0.0', Bit_N_Max);
  Output_Freq_E.Text    := FormatFloat ('#,##0.0', F_Zero_Crossings_N / 2 / Double (MilliSecondSpan (M_Start_Time, F_End_Time)) * 1000);
  M_Start_Time          := F_End_Time;
End; { TMain_F.Output_Current_Data }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Output_Current_Time;
{ --------------------------------------------------------------------------------------- }
Var
  Time_Value : TDateTime;

Begin { TMain_F.Output_Current_Time }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Output_Current_Time');
  {$ENDIF}

  Time_Value       := Now;
  Status_L.Caption := CURRENT_TIME_FORMAT_00_S + FormatDateTime ('DD.MM.YYYY', Time_Value) + CURRENT_TIME_FORMAT_01_S + FormatDateTime ('hh:nn:ss', Time_Value);

  Repaint;
  Application.ProcessMessages;
End; { TMain_F.Output_Current_Time }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Close_BClick (Sender : TObject);
{ Close button pressed                                                                    }
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Close_BClick }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Close_BClick');
  {$ENDIF}

  Chart_T.Enabled := FALSE;
  Sleep (CLOSE_WAIT_TIME_MSEC);

  Close;
End; { TMain_F.Close_BClick }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Start_Stop_BClick (Sender : TObject);
{ Start/Stop button pressed                                                               }
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Start_Stop_BClick }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Start_Stop_BClick');
  {$ENDIF}

  Chart_T.Enabled := not Chart_T.Enabled;
End; { TMain_F.Start_Stop_BClick }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Reset_BClick (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Reset_BClick }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Reset_BClick');
  {$ENDIF}

  Current_Chart_C.Extent.XMin := Now - (StrToInt (Input_X_Axis_Extend_E.Text) / 24 / 60 / 60);
  Current_Chart_C.Extent.XMax := Now;
  Current_Chart_C.Extent.YMax := DATA_CHART_EXTEND_Y;
  Current_Chart_C.Extent.YMin := -DATA_CHART_EXTEND_Y;
End; { TMain_F.Reset_BClick }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Input_Sensivity_Bits_EEditingDone (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Input_Sensivity_Bits_EEditingDone }
  {$IFDEF USE_TIMER_THREAD}
    Try
      M_Timer_Thread.ADS1262.Sensivity := StrToInt (Input_Sensivity_Bits_E.Text);
    Except
      M_Timer_Thread.ADS1262.Sensivity := ADS1262_INITIAL_SENSIVITY;
    End; { Try }
  {$ENDIF}
End; { TMain_F.Input_Sensivity_Bits_EEditingDone }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Input_OFCAL_Value_EEditingDone (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Input_OFCAL_Value_EEditingDone }
  {$IFDEF USE_TIMER_THREAD}
    Try
      M_Timer_Thread.ADS1262.Set_OFCAL (StrToInt (Input_OFCAL_Value_E.Text));
    Except
      M_Timer_Thread.ADS1262.Set_OFCAL (ADS1262_INITIAL_OFCAL);
    End; { Try }
  {$ENDIF}
End; { TMain_F.Input_OFCAL_Value_EEditingDone }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Input_LP_Span_EEditingDone (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Input_LP_Span_EEditingDone }
  {$IFDEF USE_TIMER_THREAD}
  M_Timer_Thread.LP_Span := StrToInt (Input_LP_Span_E.Text);
  {$ENDIF}
End; { TMain_F.Input_LP_Span_EEditingDone }

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Input_X_Axis_Extend_EEditingDone (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Input_X_Axis_Extend_EEditingDone }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Input_X_Axis_Extend_EEditingDone');
  {$ENDIF}

  Current_Chart_C.Extent.XMin := Now - (StrToInt (Input_X_Axis_Extend_E.Text) / 24 / 60 / 60);
  Current_Chart_C.Extent.XMax := Now;
End; { TMain_F.Input_X_Axis_Extend_EEditingDone }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Input_Timer_Sec_EEditingDone (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Input_Timer_Sec_EEditingDone }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Input_Timer_Sec_EEditingDone');
  {$ENDIF}

  Chart_T.Interval := Round (StrToFloat (Input_Timer_Sec_E.Text) * 1000);
End; { TMain_F.Input_Timer_Sec_EEditingDone }


{#####################################################################################}
{ Utilities                                                                           }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Debug_Output (F_Text : String);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Debug_Output }
  {$IFDEF DEBUG_INFORMATION}
  WriteLn (FormatDateTime ('DD.MM.YYYY - hh:nn:ss - ', Now) + F_Text);
  {$ENDIF}
End; { TMain_F.Debug_Output }


{ --------------------------------------------------------------------------------------- }
Function TMain_F.Calculate_Velocity (F_Data : Int_32) : Double;
  { Calculate voltage from raw data                                                         }
  { --------------------------------------------------------------------------------------- }
Begin { TMain_F.Calculate_Velocity }
  Result := F_Data * VOLTAGE_LIMIT / $7FFFFFFF / SM24_VELOCITY_FACTOR * 1000;
End; { TMain_F.Calculate_Velocity }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Read_Array_Data (F_Index : Integer; Var F_X : TDateTime; Var F_Y : Int_32; Var F_LP_Y : Int_32);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Read_Array_Data }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Read_Array_Data');
  {$ENDIF}

  {$IFDEF USE_CRITICAL_SECTION}
  EnterCriticalSection (Main_F.M_Critical_Section);
    Try
  {$ENDIF}
  {$IFDEF USE_TIMER_THREAD}
      F_X    := M_Timer_Thread.Data_X_A [F_Index];
      F_Y    := M_Timer_Thread.Data_Y_A [F_Index];
      F_LP_Y := M_Timer_Thread.Data_LP_Y_A [F_Index];
  {$ELSE}
      F_X    := Now;
      F_Y    := Random (RANDOM_MAX);
      F_LP_Y := Random (RANDOM_MAX);
  {$ENDIF}
  {$IFDEF USE_CRITICAL_SECTION}
    Finally
      LeaveCriticalSection (Main_F.M_Critical_Section);
    End;
  {$ENDIF}
End; { TMain_F.Read_Array_Data }


{#####################################################################################}
{ Chart                                                                               }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Clear_Hour_Data_LS (F_Hour_Data_LS : TLineSeries);
{ --------------------------------------------------------------------------------------- }
Var
  J : Integer;

Begin { TMain_F.Clear_Hour_Data_LS }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Clear_Hour_Data_LS');
  {$ENDIF}

  With F_Hour_Data_LS Do
    Begin { With }
      Clear;
      SeriesColor   := DATA_COLOR;
      LinePen.Width := DATA_PIN_WIDTH;
      For J := 0 To VALUES_PER_HOUR - 1 Do
        Begin { For }
          AddXY (Round (J) / 60, 0);
          AddXY (Round (J) / 60, 0);
        End; { For }
    End; { With }
End; { TMain_F.Clear_Hour_Data_LS }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Clear_Hour_RMS_BS (F_Hour_RMS_BS : TBarSeries);
{ --------------------------------------------------------------------------------------- }
Var
  J : Integer;

Begin { TMain_F.Clear_Hour_RMS_BS }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Clear_Hour_RMS_BS');
  {$ENDIF}

  With F_Hour_RMS_BS Do
    Begin { With }
      Clear;
      SeriesColor     := RMS_COLOR;
      BarWidthPercent := RMS_BAR_WIDTH_PERCENT;
      For J := 0 To VALUES_PER_HOUR - 1 Do
        Begin { For }
          AddXY (Round (J) / 60, 0.0);
        End; { For }
    End; { With }
End; { TMain_F.Clear_Hour_RMS_BS }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Chart_TS_DataPointHintToolHint (ATool : TDataPointHintTool; Const APoint : TPoint; Var AHint : String);
{ --------------------------------------------------------------------------------------- }
Const
  SPACE_START = '         ';

Var
  Time_Value : TDateTime;
  Voltage :    Double;

Begin { TMain_F.Chart_TS_DataPointHintToolHint }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Chart_TS_DataPointHintToolHint');
  {$ENDIF}

    Try
      Time_Value := TChartSeries (ATool.Series).Source.Item [ATool.PointIndex]^.X;
      Voltage    := TChartSeries (ATool.Series).Source.Item [ATool.PointIndex]^.Y;
      AHint      := SPACE_START + 'X : ' + FormatDateTime ('hh:nn:ss.zzz', Time_Value) + #13 + Format (SPACE_START + 'Y : %.8f', [Voltage]);
    Except
      AHint := '';
    End; { Try }
End; { TMain_F.Chart_TS_DataPointHintToolHint }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Chart_TTimer (Sender : TObject);
{ Repaint chart                                                                           }
{ --------------------------------------------------------------------------------------- }
Var
  Index :               Integer;
  Time_Value :          TDateTime;
  Voltage :             Double;
  Old_Voltage :         Double;
  Integral_Voltage :    Double;
  LP_Integral_Voltage : Double;
  Zero_Crossings_C :    Integer;
  Current_Extend :      TDoubleRect;
  Extend_Dif :          Double;
  J :                   Integer;
  Start_Index :         Integer;
  End_Index :           Integer;
  V :                   Double;
  LP_Voltage :          Double;
  Additional_Index :    Integer;
  Data_Time :           TDateTime;
  Data_Hour :           Integer;
  Data_Minute :         Integer;
  Data_Second :         Integer;
  Old_Data_Second :     Integer;
  RMS :                 Double;
  Upper_Peak :          Double;
  Lower_Peak :          Double;
  Hour_Data_LS :        TLineSeries;
  Hour_Position_CL :    TConstantLine;

Begin { TMain_F.Chart_TTimer }
  {$IFDEF DEBUG_INFORMATION}
  Debug_Output ('Begin of: TMain_F.Chart_TTimer');
  {$ENDIF}

  Case T_Chart_Kind (Main_PC.TabIndex) Of
      CURRENT_CHART_CK :
        Begin { CURRENT_CHART_CK }
          Current_LS.Active             := Show_Data_CB.Checked;
          Current_LP_LS.Active          := Show_LP_Data_CB.Checked;
          Current_EMA_LS.Active         := Show_EMA_CB.Checked;
          Current_RMS_LS.Active         := Show_RMS_CB.Checked;
          Current_Integral_LS.Active    := Show_Integral_CB.Checked;
          Current_Integral_LP_LS.Active := Show_LP_Integral_CB.Checked;
          Spectrum_CMS.Active           := Show_Spektrum_CB.Checked;

          Current_LS.BeginUpdate;
          Current_LP_LS.BeginUpdate;
          Current_EMA_LS.BeginUpdate;
          Current_RMS_LS.BeginUpdate;
          Current_Integral_LS.BeginUpdate;
          Current_Integral_LP_LS.BeginUpdate;
          Spectrum_CMS.OnCalculate := NIL;

          {$IFDEF CLEAR_SERIES}
          Current_LS.Clear;
          Current_LP_LS.Clear;
          Current_EMA_LS.Clear;
          Current_RMS_LS.Clear;
          Current_Integral_LS.Clear;
          Current_Integral_LP_LS.Clear;
          {$ENDIF}

          {$IFDEF USE_TIMER_THREAD}
          M_Data_A_Index_1 := M_Timer_Thread.Data_A_Index;
          {$ELSE}
          M_Data_A_Index_1 := RANDOM_N;
          {$ENDIF}
          Zero_Crossings_C := 0;
          Additional_Index := 0;
          Old_Data_Second  := 0;

          If M_Data_A_Index_1 < M_Data_A_Index_0 Then
            Begin { then }
              Start_Index := M_Data_A_Index_0;
              End_Index   := DATA_ARRAY_SIZE - 1;
              Voltage     := 0;
              Old_Voltage := Voltage;
              Index       := Start_Index;
              While Index <= End_Index Do
                Begin { While }
                  {$IFDEF USE_TIMER_THREAD}
                  Time_Value          := M_Timer_Thread.Data_X_A [Index];
                  Voltage             := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Index]);
                  LP_Voltage          := Calculate_Velocity (M_Timer_Thread.Data_LP_Y_A [Index]);
                  Integral_Voltage    := Calculate_Velocity (M_Timer_Thread.Data_Integral_Y_A [Index]);
                  LP_Integral_Voltage := Calculate_Velocity (M_Timer_Thread.Data_LP_Integral_Y_A [Index]);
                  Current_LS.AddXY (Time_Value, Voltage);
                  Current_LP_LS.AddXY (Time_Value, LP_Voltage);
                  Current_Integral_LS.AddXY (Time_Value, Integral_Voltage);
                  Current_Integral_LP_LS.AddXY (Time_Value, LP_Integral_Voltage);
                  Current_EMA_LS.AddXY (Time_Value, Calculate_Velocity (M_Timer_Thread.ADS1262.EMA_Data));
                  {$ELSE}
                  Time_Value          := M_Old_Data_Time + ((Now - M_Old_Data_Time) / End_Index * Index);
                  Voltage             := Calculate_Velocity (Random (RANDOM_MAX));
                  LP_Voltage          := Calculate_Velocity (Random (RANDOM_MAX));
                  Integral_Voltage    := Calculate_Velocity (Random (RANDOM_MAX));
                  LP_Integral_Voltage := Calculate_Velocity (Random (RANDOM_MAX));
                  Current_LS.AddXY (Time_Value, Voltage);
                  Current_LP_LS.AddXY (Time_Value, LP_Voltage);
                  Current_Integral_LS.AddXY (Time_Value, Integral_Voltage);
                  Current_Integral_LP_LS.AddXY (Time_Value, LP_Integral_Voltage);
                  Current_EMA_LS.AddXY (Time_Value, Calculate_Velocity (Random (RANDOM_MAX)));
                  Inc (M_Count);
                  {$ENDIF}

                  { Calculate RMS }
                  RMS := 0;
                  For J := 0 To StrToInt (Input_LP_Span_E.Text) - 1 Do
                    Begin { For }
                      {$IFDEF USE_TIMER_THREAD}
                      V   := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Validate_Array_Index (Index - J, DATA_ARRAY_SIZE)]);
                      {$ELSE}
                      V   := Calculate_Velocity (Random (RANDOM_MAX));
                      {$ENDIF}
                      RMS := RMS + Sqr (V);
                    End; { For }
                  RMS := Sqrt (RMS / (StrToInt (Input_LP_Span_E.Text)));
                  Current_RMS_LS.AddXY (Time_Value, RMS);

                  { Calculate spectrum }
                  Data_Second := SecondOf (Time_Value);
                  If Data_Second <> Old_Data_Second Then
                    Begin { then }
                      Calculate_Goertzel_DFT (Index, Time_Value);
                      Old_Data_Second := Data_Second;
                    End; { then }

                  If (Index > Start_Index) and ((Old_Voltage < 0) and (Voltage > 0) or (Old_Voltage > 0) and (Voltage < 0)) Then
                    Begin { then }
                      Inc (Zero_Crossings_C);
                    End; { then }

                  Old_Voltage := Voltage;
                  Index       := Validate_Array_Index (Index + 1, DATA_ARRAY_SIZE);
                End; { While }

              Additional_Index := End_Index - Start_Index;
              M_Data_A_Index_0 := 0;
            End; { then }

          Start_Index := M_Data_A_Index_0;
          End_Index   := M_Data_A_Index_1 - 1;
          Voltage     := 0;
          Old_Voltage := Voltage;
          Index       := Start_Index;
          While Index <= End_Index Do
            Begin { While }
              {$IFDEF USE_TIMER_THREAD}
              Time_Value          := M_Timer_Thread.Data_X_A [Index];
              Voltage             := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Index]);
              LP_Voltage          := Calculate_Velocity (M_Timer_Thread.Data_LP_Y_A [Index]);
              Integral_Voltage    := Calculate_Velocity (M_Timer_Thread.Data_Integral_Y_A [Index]);
              LP_Integral_Voltage := Calculate_Velocity (M_Timer_Thread.Data_LP_Integral_Y_A [Index]);
              Current_LS.AddXY (Time_Value, Voltage);
              Current_LP_LS.AddXY (Time_Value, LP_Voltage);
              Current_Integral_LS.AddXY (Time_Value, Integral_Voltage);
              Current_Integral_LP_LS.AddXY (Time_Value, LP_Integral_Voltage);
              Current_EMA_LS.AddXY (Time_Value, Calculate_Velocity (M_Timer_Thread.ADS1262.EMA_Data));
              {$ELSE}
              Time_Value          := M_Old_Data_Time + ((Now - M_Old_Data_Time) / End_Index * Index);
              Voltage             := Calculate_Velocity (Random (RANDOM_MAX));
              LP_Voltage          := Calculate_Velocity (Random (RANDOM_MAX));
              Integral_Voltage    := Calculate_Velocity (Random (RANDOM_MAX));
              LP_Integral_Voltage := Calculate_Velocity (Random (RANDOM_MAX));
              Current_LS.AddXY (Time_Value, Voltage);
              Current_LP_LS.AddXY (Time_Value, LP_Voltage);
              Current_Integral_LS.AddXY (Time_Value, Integral_Voltage);
              Current_Integral_LP_LS.AddXY (Time_Value, LP_Integral_Voltage);
              Current_EMA_LS.AddXY (Time_Value, Calculate_Velocity (Random (RANDOM_MAX)));
              Inc (M_Count);
              {$ENDIF}

              { Calculate RMS }
              RMS := 0;
              For J := 0 To StrToInt (Input_LP_Span_E.Text) - 1 Do
                Begin { For }
                  {$IFDEF USE_TIMER_THREAD}
                  V   := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Validate_Array_Index (Index - J, DATA_ARRAY_SIZE)]);
                  {$ELSE}
                  V   := Calculate_Velocity (Random (RANDOM_MAX));
                  {$ENDIF}
                  RMS := RMS + Sqr (V);
                End; { For }
              RMS := Sqrt (RMS / (StrToInt (Input_LP_Span_E.Text)));
              Current_RMS_LS.AddXY (Time_Value, RMS);

              { Calculate spectrum }
              Data_Second := SecondOf (Time_Value);
              If Data_Second <> Old_Data_Second Then
                Begin { then }
                  Calculate_Goertzel_DFT (Index, Time_Value);
                  Old_Data_Second := Data_Second;
                End; { then }

              If (Index > Start_Index) and ((Old_Voltage < 0) and (Voltage > 0) or (Old_Voltage > 0) and (Voltage < 0)) Then
                Begin { then }
                  Inc (Zero_Crossings_C);
                End; { then }

              Old_Voltage := Voltage;
              Index       := Validate_Array_Index (Index + 1, DATA_ARRAY_SIZE);
            End; { While }

          If Show_Current_Data_CB.Checked = TRUE Then
            Begin { then }
              Current_Extend                := Current_Chart_C.LogicalExtent;
              Extend_Dif                    := Current_Extend.b.X - Current_Extend.a.X;
              Current_Extend.b.X            := Now;
              Current_Extend.a.X            := Current_Extend.b.X - Extend_Dif;
              Current_Chart_C.LogicalExtent := Current_Extend;

              Current_Extend                := Current_Chart_C.LogicalExtent;
              Current_Chart_C.LogicalExtent := Current_Extend;
            End; { then }

          Spectrum_CMS.OnCalculate := @Main_F.Spectrum_CMSCalculate;
          Current_Integral_LP_LS.EndUpdate;
          Current_Integral_LS.EndUpdate;
          Current_RMS_LS.EndUpdate;
          Current_EMA_LS.EndUpdate;
          Current_LP_LS.EndUpdate;
          Current_LS.EndUpdate;

          {$IFDEF USE_TIMER_THREAD}
          Time_Value  := M_Timer_Thread.Data_X_A [End_Index];
          {$ELSE}
          Time_Value  := Now;
          {$ENDIF}
          Data_Time   := Time_Value;
          Data_Hour   := HourOf (Time_Value);
          Data_Minute := MinuteOf (Time_Value);
          Data_Second := SecondOf (Time_Value);
        End; { CURRENT_CHART_CK }
      DAILY_CHART_CK :
        Begin { DAILY_CHART_CK }

          { Calculate peak values }
          {$IFDEF USE_TIMER_THREAD}
          M_Data_A_Index_1 := M_Timer_Thread.Data_A_Index;
          {$ELSE}
          M_Data_A_Index_1 := RANDOM_N;
          {$ENDIF}
          Zero_Crossings_C := 0;
          Additional_Index := 0;
          Upper_Peak       := 0;
          Lower_Peak       := 0;

          If M_Data_A_Index_1 < M_Data_A_Index_0 Then
            Begin { then }
              Start_Index := M_Data_A_Index_0;
              End_Index   := DATA_ARRAY_SIZE - 1;
              Voltage     := 0;
              Old_Voltage := Voltage;
              Index       := Start_Index;
              While Index <= End_Index Do
                Begin { While }
                  {$IFDEF USE_TIMER_THREAD}
                  Voltage := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Index]);
                  {$ELSE}
                  Voltage := Calculate_Velocity (Random (RANDOM_MAX));
                  Inc (M_Count);
                  {$ENDIF}

                  Upper_Peak := Max (Upper_Peak, Voltage);
                  Lower_Peak := Min (Lower_Peak, Voltage);

                  If (Index > Start_Index) and ((Old_Voltage < 0) and (Voltage > 0) or (Old_Voltage > 0) and (Voltage < 0)) Then
                    Begin { then }
                      Inc (Zero_Crossings_C);
                    End; { then }

                  Old_Voltage := Voltage;
                  Index       := Validate_Array_Index (Index + 1, DATA_ARRAY_SIZE);
                End; { While }
              Additional_Index := End_Index - Start_Index;
              M_Data_A_Index_0 := 0;
            End; { then }

          Start_Index := M_Data_A_Index_0;
          End_Index   := M_Data_A_Index_1 - 1;
          Voltage     := 0;
          Old_Voltage := Voltage;
          Index       := Start_Index;
          While Index <= End_Index Do
            Begin { While }
              {$IFDEF USE_TIMER_THREAD}
              Voltage := Calculate_Velocity (M_Timer_Thread.Data_Y_A [Index]);
              {$ELSE}
              Voltage := Calculate_Velocity (Random (RANDOM_MAX));
              Inc (M_Count);
              {$ENDIF}

              Upper_Peak := Max (Upper_Peak, Voltage);
              Lower_Peak := Min (Lower_Peak, Voltage);

              If (Index > Start_Index) and ((Old_Voltage < 0) and (Voltage > 0) or (Old_Voltage > 0) and (Voltage < 0)) Then
                Begin { then }
                  Inc (Zero_Crossings_C);
                End; { then }

              Old_Voltage := Voltage;
              Index       := Validate_Array_Index (Index + 1, DATA_ARRAY_SIZE);
            End; { While }

          {$IFDEF USE_TIMER_THREAD}
          Time_Value  := M_Timer_Thread.Data_X_A [End_Index];
          {$ELSE}
          Time_Value  := Now;
          {$ENDIF}
          Data_Time   := Time_Value;
          Data_Hour   := HourOf (Time_Value);
          Data_Minute := MinuteOf (Time_Value);
          Data_Second := SecondOf (Time_Value);

          Index            := (Data_Minute * 60) + Data_Second;
          Hour_Data_LS     := M_Hour_Data_LS_A [Data_Hour];
          Hour_Position_CL := M_Hour_Position_CL_A [Data_Hour];

          M_Hour_Position_CL_A[M_Old_Data_Hour].Active := FALSE;
          Hour_Position_CL.Position                    := Index / 60;
          Hour_Position_CL.Active                      := TRUE;

          { Output peak values }
          Hour_Data_LS.BeginUpdate;

          { Clear data if new hour starts }
          If Data_Hour <> M_Old_Data_Hour Then
            Begin { then }
              Clear_Hour_Data_LS (Hour_Data_LS);
            End; { then }

          Hour_Data_LS.ListSource.Item[Index shl 1]^.Y       := Upper_Peak;
          Hour_Data_LS.ListSource.Item[(Index shl 1) + 1]^.Y := Lower_Peak;

          Hour_Data_LS.EndUpdate;
        End; { DAILY_CHART_CK }
    End; { Case }

  M_Data_A_Size    := (End_Index - Start_Index) + Additional_Index;
  {$IFDEF USE_TIMER_THREAD}
  M_Data_A_Index_0 := M_Data_A_Index_1;
  {$ELSE}
  M_Data_A_Index_0 := 0;
  {$ENDIF}

  Output_Current_Data (M_Data_A_Size, Zero_Crossings_C, Time_Value);

  Output_Current_Time;

  M_Old_Data_Time   := Data_Time;
  M_Old_Data_Hour   := Data_Hour;
  M_Old_Data_Minute := Data_Minute;
  M_Old_Data_Second := Data_Second;
End; { TMain_F.Chart_TTimer }


{#####################################################################################}
{ Spectral chart                                                                      }
{#####################################################################################}

{-------------------------------------------------------------------------------------}
Function TMain_F.Get_Time_Index (F_Time_Value : TDateTime) : Integer;
  { Get number of seconds of time value                                                 }
  {-------------------------------------------------------------------------------------}
Begin { TMain_F.Get_Time_Index }
  Result := Validate_Array_Index (Round (Double (F_Time_Value - SPECTRUM_DATETIME_OFFSET) * 24 * 60 * 60) - M_DateTime_Start_Offset, SPECTRUM_X_ARRAY_SIZE);
End; { TMain_F.Get_Time_Index }


{-------------------------------------------------------------------------------------}
Procedure TMain_F.Spectrum_CMSCalculate (Const AX, AY : Double; out AZ : Double);
{ Calculate spectrum chart                                                            }
{-------------------------------------------------------------------------------------}
Begin { TMain_F.Spectrum_CMSCalculate }
  AZ := Get_Spectrum (AX, Round (AY / SPECTRUM_MAX_FREQUENCY * SPECTRUM_Y_RESOLUTION)) * 100;
End; { TMain_F.Spectrum_CMSCalculate }


{-------------------------------------------------------------------------------------}
Function TMain_F.Get_Spectrum (F_X : TDateTime; F_Y : Integer) : Double;
  { Get spectrum value                                                                  }
  {-------------------------------------------------------------------------------------}
Var
  X : Integer;

Begin { TMain_F.Get_Spectrum }
  X   := Get_Time_Index (F_X);
  //  X   := Max (0, X);
  //  X   := Min (SPECTRUM_X_ARRAY_SIZE - 1, X);
  F_Y := Max (0, F_Y);
  F_Y := Min (SPECTRUM_Y_RESOLUTION, F_Y);

  Result := M_Spectrum_A [X, F_Y];
End; { TMain_F.Get_Spectrum }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Set_Spectrum (F_X : TDateTime; F_Y : Integer; F_Value : Double);
{ --------------------------------------------------------------------------------------- }
Var
  X : Integer;

Begin { TMain_F.Set_Spectrum }
  X   := Get_Time_Index (F_X);
  //  X   := Max (0, X);
  //  X   := Min (SPECTRUM_X_ARRAY_SIZE - 1, X);
  F_Y := Max (0, F_Y);
  F_Y := Min (SPECTRUM_Y_RESOLUTION, F_Y);

  M_Spectrum_A[X, F_Y] := F_Value;


End; { TMain_F.Set_Spectrum }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Set_Amplitude (F_X : Integer; F_Y : Integer; F_Value : Double);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Set_Amplitude }
End; { TMain_F.Set_Amplitude }


{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Set_Phi (F_X : Integer; F_Y : Integer; F_Value : Double);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Set_Phi }
End; { TMain_F.Set_Phi }


{#####################################################################################}
{ Spectral analysis                                                                   }
{#####################################################################################}

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Calculate_Goertzel_DFT (F_End_Index : Integer; F_End_Time : TDateTime);
{ --------------------------------------------------------------------------------------- }

  Function Windowing_Filter (F_Input : Double; F_C : Integer; F_Window_Size : Integer; F_Windowing_Function : T_Spectrum_Window_Funktion) : Double;
  Begin { Windowing_Filter }
      {---------------------------------------------}
      { Calculates a value using a windowing filter }
      {---------------------------------------------}
      F_C := F_C - (F_Window_Size div 2);

      Case F_Windowing_Function Of
          TWF_None :
            Begin { TWF_None }
              Result := F_Input;
            End; { TWF_None }
          TWF_von_Hahn :
            Begin { TWF_von_Hahn }
              Result := F_Input * (0.5 * (1 - cos ((F_C * 2 * Pi) / (F_Window_Size - 1))));
            End; { TWF_von_Hahn }
          TWF_Hamming :
            Begin { TWF_Hamming }
              Result := F_Input * (0.54 - 0.46 * cos ((F_C * 2 * Pi) / (F_Window_Size - 1)));
            End; { TWF_Hamming }
          TWF_Blackman :
            Begin { TWF_Blackman }
              Result := F_Input * (0.42 - 0.5 * cos ((F_C * 2 * Pi) / (F_Window_Size - 1)) + 0.08 * cos ((F_C * 4 * Pi) / (F_Window_Size - 1)));
            End; { TWF_Blackman }
          TWF_KaiserBessel :
            Begin { TWF_KaiserBessel }
              Result := F_Input * (0.4021 - 0.4986 * cos ((F_C * 2 * Pi) / (F_Window_Size - 1)) + 0.0981 * cos ((F_C * 4 * Pi) / (F_Window_Size - 1)) - 0.0012 * cos ((F_C * 6 * Pi) / (F_Window_Size - 1)));
            End; { TWF_KaiserBessel }
          TWF_FlatTop :
            Begin { TWF_FlatTop }
              Result := F_Input * (0.2155 - 0.4159 * cos ((F_C * 2 * Pi) / (F_Window_Size - 1)) + 0.2780 * cos ((F_C * 4 * Pi) / (F_Window_Size - 1)) - 0.0836 * cos ((F_C * 6 * Pi) / (F_Window_Size - 1)) + 0.0070 * cos ((F_C * 8 * Pi) / (F_Window_Size - 1)));
            End; { TWF_FlatTop }
        End; { Case }
  End; { Windowing_Filter }


  {-------------------------------------------------------------------------------------}
  Function Calculate_Goertzel_DFT_Amplitude (F_Sample_N : Integer; F_Target_Frequency : Integer; F_Sampling_Rate : Integer; F_Input_A : T_Spectrum_Input_A; F_Windowing_Function : T_Spectrum_Window_Funktion) : Double;
      { Calculate Goertzel DFT for one freqency                                             }
      {-------------------------------------------------------------------------------------}
  Var
      I :               Integer;
      K :               Integer;
      floatnumSamples : Double;
      omega :           Double;
      V_Sine :          Double;
      V_Cosine :        Double;
      coeff :           Double;
      q0 :              Double;
      q1 :              Double;
      q2 :              Double;
      magnitude :       Double;
      V_Real :          Double;
      V_Imag :          Double;
      scalingFactor :   Double;
      V :               Double;

  Begin { Calculate_Goertzel_DFT_Amplitude }
      scalingFactor := F_Sample_N / 2.0;

      floatnumSamples := F_Sample_N;
      k               := Trunc (0.5 + ((floatnumSamples * F_Target_Frequency) / F_Sampling_Rate));
      omega           := (2.0 * Pi * k) / floatnumSamples;
      V_Sine          := sin (omega);
      V_Cosine        := cos (omega);
      coeff           := 2.0 * V_Cosine;
      q0              := 0;
      q1              := 0;
      q2              := 0;

      For I := 0 To F_Sample_N - 1 Do
        Begin { For }
          q2 := q1;
          q1 := q0;
          V  := Windowing_Filter (F_Input_A [i], I, F_Sample_N, F_Windowing_Function);
          q0 := (coeff * q1) - q2 + V;
        End; { For }

      V_Real := (q0 - (q1 * V_Cosine)) / scalingFactor;
      V_Imag := (-q1 * V_Sine) / scalingFactor;

      magnitude := sqrt (sqr (V_Real) + sqr (V_Imag));
      Result    := magnitude;
  End; { Calculate_Goertzel_DFT_Amplitude }

Var
  V :             Double;
  I :             Integer;
  X :             Integer;
  Test_A :        T_Spectrum_Input_A;
  Amplitude_A :   Array [0 .. SPECTRUM_Y_RESOLUTION] Of Double;
  Max_Amplitude : Double;

Begin { TMain_F.Calculate_Goertzel_DFT }
  SetLength (Test_A, SPECTRUM_SAMPLE_N);
  For I := 0 To Length (Test_A) - 1 Do
    Begin { For }
      {$IFDEF USE_TIMER_THREAD}
      Test_A[I] := M_Timer_Thread.Data_Y_A [Validate_Array_Index (F_End_Index - SPECTRUM_SAMPLE_N + I + 1, DATA_ARRAY_SIZE)];
      {$ELSE}
      Test_A[I] := Random (RANDOM_MAX);
      {$ENDIF}
    End; { For }

  Amplitude_A[0] := 0;
  For I := 1 To SPECTRUM_Y_RESOLUTION Do
    Begin { For }
      V              := Calculate_Goertzel_DFT_Amplitude (SPECTRUM_SAMPLE_N, Round (SPECTRUM_MAX_FREQUENCY * I / SPECTRUM_Y_RESOLUTION), SPECTRUM_SAMPLE_RATE, Test_A, T_Spectrum_Window_Funktion (Input_Window_Function_SE.Value));
      Amplitude_A[I] := V;
    End; { For }

  Max_Amplitude := 0;
  For I := 0 To SPECTRUM_Y_RESOLUTION Do
    Begin { For }
      V             := Amplitude_A [I];
      //      Max_Amplitude := Max_Amplitude + V;
      Max_Amplitude := Max (Max_Amplitude, V);
    End; { For }
  If Max_Amplitude = 0 Then
    Begin { then }
      Max_Amplitude := 1;
    End; { then }
  M_Spectrum_Maximum_A[Get_Time_Index (F_End_Time)] := Max_Amplitude;

  Max_Amplitude := 0;
  X             := Get_Time_Index (F_End_Time);
  For I := 0 To SPECTRUM_MAXIMUM_SPAN - 1 Do
    Begin { For }
      V             := M_Spectrum_Maximum_A [X];
      Max_Amplitude := Max (Max_Amplitude, V);
      X             := Validate_Array_Index (X - 1, SPECTRUM_X_ARRAY_SIZE);
    End; { For }

  For I := 0 To SPECTRUM_Y_RESOLUTION Do
    Begin { For }
      Amplitude_A[I] := Amplitude_A [I] / Max_Amplitude;
    End; { For }

  For I := 0 To SPECTRUM_Y_RESOLUTION Do
    Begin { For }
      V := Amplitude_A [I];
      Set_Spectrum (F_End_Time, I, V);
    End; { For }
End; { TMain_F.Calculate_Goertzel_DFT }


End.

project_defines.inc

{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## Includes                                                                          ## }
{ ##                                                                                   ## }
{ ## Copyright (C) 2018-2019  : Dr. Jürgen Abel                                        ## }
{ ## Email                    : juergen@juergen-abel.info                              ## }
{ ## Internet                 : www.seismometer.info                                   ## }
{ ##                                                                                   ## }
{ ####################################################################################### }


{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## Global defines                                                                    ## }
{ ##                                                                                   ## }
{ ####################################################################################### }

{$PACKRECORDS C}

{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## Following Defines can be enabled or disabled                                      ## }
{ ##                                                                                   ## }
{ ####################################################################################### }

{   $DEFINE DEBUG_GPIO_OUTPUT}
{   $DEFINE DEBUG_SPI_OUTPUT}
{   $DEFINE DEBUG_ADS1262_OUTPUT}
{   $DEFINE DEBUG_MAIN_OUTPUT}
{   $DEFINE DEBUG_ERROR}
{   $DEFINE DEBUG_ADC1_DATA}
{   $DEFINE DEBUG_INFORMATION}
{$DEFINE SELF_CALIBRATION_ON}
{$DEFINE USE_INTERRUPT}
{   $DEFINE SAVE_IMAGE}
{   $DEFINE TRANSFER_DATA_TO_INTERNET_SERVER}
{   $DEFINE USE_CRITICAL_SECTION}
{$DEFINE USE_TIMER_THREAD}
{   $DEFINE CLEAR_SERIES}