Various methods of data analysis will be presented in this post:

1. velocity,
2. low-pass filter (a special type of averaging with lower latency) of the velocity,
3. RMS (root mean square) value of the velocity,
4. integral (simulation of a heavy ball on a flat surface) of the velocity,
5. daily view of the velocity.

The spectral analysis will be explained in the next post.

## Types of analysis

### Velocity

The calculation of the velocity of the geophone is quite simple, all which is needed is the voltage and the sensitivity of the geophone. For the SM-24 the sensitivity s with a shunt of 1 kΩ is 20.9 V/(m/s) (https://www.egl-geo.com/product/geophone-element/109.html). The equation for the calculation of the velocity in mm/s is:
`v = u / s * 1000.`

### Low-pass filter of the velocity

An example for a simple filter is a low pass filter of the velocity, similary to the arithmetic average, one might know from school. The low pass filter has a lower latency as the arithmetic average, but smoothes the velocity very well, usually better than the arithmetic average. The low pass filter used here is based on the super smoother filter of Dr. John Ehlers (http://www.mesasoftware.com/ehlers_webinars.htm).
```A_1                           := exp (-SQR_2 * PI / 20);
B_1                           := 2 * A_1 * cos (SQR_2 * PI / 20);
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] := (C_1 * M_Data_Y_A [M_Data_A_Index]) + (C_2 * M_Data_LP_Y_A [M_Data_A_Index - 1]) + (C_3 * M_Data_LP_Y_A [M_Data_A_Index - 2]);```

### RMS value of the velocity

The RMS value is the root mean square value of the velocity, which is equivalent to the effective velocity. The effective value of an alternating current for example is the value of a direct current, which would produce the same amount of energy than the alternating current.
The RMS value is always greater or equal to zero.

### Integral of the velocity

The integral of the velocity is equivalent to the route of a heavy ball, which is sitting on a flat surface without any friction and is moving because of the movement of the ground, which the geophone is measuring. There is a systemic problem with the ball, as the surface has no friction and the movement of the ball is not damped: the ball does not return to its resting position by itself. The same problem occurs with the integral of the velocity: the integral value moves away from the zero level and might not return to zero within a specified time. The solution to this problem is a restoring force. The calculation of the integral is very simple as the system is operating with discrete equal distance time slices: The integral is the sum of the velocities. The restoring force is a subtraction of a one eight thousandth part of the low pass of the integral. The value of 1/8,000 was determined empirically, one can also you smaller or larger values.
`Integral [Index] := Integral [Index - 1] + Data [Index] - (LP_Integral [Index - 1] / 8000);`
Finally the low pass value of the integral is displayed, with an low pass span of 800. Again the value of 800 was determined empirically and can be changed according to one’s need.

### Daily view of the velocity

The daily view of the velocity displays the values of the last 23 hours within hourly diagrams. In order to simplify the grahics, only the maximum and the minimum within a timeframe of 2 seconds are displayed.
This way, it is easy to recognise large velocity parts within the last hours.

### Day Data Project

The files main_unit.pas, main_unit.frm and project_defines.inc contain the source code for measuring and analysing the data. Again project_defines.inc offer some project options.

The file go.sh will compile and run the program.

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

#### main_unit.pas

```{ ####################################################################################### }
{ ##                                                                                   ## }
{ ## 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
Classes,
SysUtils,
Forms,
Controls,
Graphics,
Dialogs,
StdCtrls,
ExtCtrls,
ComCtrls,
TAGraph,
TASeries,
TACustomSeries,
TAIntervalSources,
TATools,
Types,
TACustomSource,
Process,
LCLType,
LCLIntf;

Const
{\$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;
SM24_VELOCITY_FACTOR           = 20.9;
INTERNET_SERVER_URL_TXT        = 'www.xxxxx.info';
TRANSFER_FILE_TXT              = 'ncftpput -u ' + INTERNET_SERVER_USERNAME_TXT + ' -p ' + INTERNET_SERVER_PASSWORD_TXT + ' -E ' + INTERNET_SERVER_URL_TXT + ' / ';
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                       = 100;
RANDOM_MAX                     = 4 * 1024 * 1024;

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);

{ ####################################################################################### }
{ ####################################################################################### }
Private
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 LP_Span : Integer Read M_LP_Span Write M_LP_Span;
Constructor Create (F_Suspended : Boolean);
Destructor Destroy; Override;
Procedure Execute; Override;

{ ####################################################################################### }
{ ## 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;
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_X_Axis_Extend_E :       TEdit;
Input_LP_Span_E :             TEdit;
Input_X_Axis_Extend_L :       TLabel;
Input_LP_Span_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;
Save_B :                      TButton;
Input_OFCAL_Value_E :         TEdit;
Input_OFCAL_Value_L :         TLabel;
Main_P :                      TPanel;
Output_1_P :                  TPanel;
Output_2_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;
Input_Sensivity_Bits_E :      TEdit;
Output_NPS_L :                TLabel;
Input_Sensivity_Bits_L :      TLabel;
Reset_B :                     TButton;
Save_Image_E :                TCheckBox;
Save_Image_L :                TLabel;
Show_Integral_CB :            TCheckBox;
Show_LP_Integral_CB :         TCheckBox;
Show_Integral_L :             TLabel;
Show_LP_Integral_L :          TLabel;
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;
Transfer_T :                  TTimer;
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_X_Axis_Extend_EEditingDone (Sender : TObject);
Procedure Reset_BClick (Sender : TObject);
Procedure Save_BClick (Sender : TObject);
Procedure Start_Stop_BClick (Sender : TObject);
Procedure Transfer_TTimer (Sender : TObject);
Protected
M_Data_Start_Index :   Integer;
M_Data_End_Index :     Integer;
M_Data_A_Size :        Integer;
M_Transfer_Process :   TProcess;
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_Transfer_Command :   String;
M_Count :              Int_64;
Procedure Debug_Output (F_Text : String);
Function Calculate_Velocity (F_Data : Int_32) : Double;
Procedure Save_Data (F_Filename : String; F_Panel : TPanel);
Procedure Transfer_File (F_Command : String);
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);
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 }

{ ####################################################################################### }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
{ --------------------------------------------------------------------------------------- }
{\$IFDEF DEBUG_ERROR}
Var
Register_Block : T_Register_Block;
{\$ENDIF}
Inherited Create (F_Suspended);

FreeOnTerminate := FALSE;

{\$IFDEF DEBUG_ERROR}
{\$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;

{ --------------------------------------------------------------------------------------- }
{ Free data                                                                               }
{ --------------------------------------------------------------------------------------- }

Inherited Destroy;

{ --------------------------------------------------------------------------------------- }
{ --------------------------------------------------------------------------------------- }
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;

While (Terminated = FALSE) Do
Begin { While }
{\$IFDEF USE_INTERRUPT}
{\$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;

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 }

{ ####################################################################################### }
{ ## 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 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;

M_Data_Start_Index := 0;
M_Data_End_Index   := 0;

M_Transfer_Process := TProcess.Create (NIL);

{\$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;

Sleep (CLOSE_WAIT_TIME_MSEC);
{\$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);

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

{\$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));
{\$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 }
Try
Except
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 }
Try
Except
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 }
{\$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;
Chart_T.Interval            := Round (StrToFloat (Input_X_Axis_Extend_E.Text) * 1000);
End; { TMain_F.Input_X_Axis_Extend_EEditingDone }

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Save_BClick (Sender : TObject);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Save_BClick }
Case T_Chart_Kind (Main_PC.TabIndex) Of
CURRENT_CHART_CK :
Begin { CURRENT_CHART_CK }
Save_Data (TrimFilename (ExtractFilePath (Application.Exename) + CURRENT_IMAGE_FILE_NAME), Main_P);
End; { CURRENT_CHART_CK }
DAILY_CHART_CK :
Begin { DAILY_CHART_CK }
Save_Data (TrimFilename (ExtractFilePath (Application.Exename) + DAILY_IMAGE_FILE_NAME), Main_P);
End; { DAILY_CHART_CK }
End; { Case }
End; { TMain_F.Save_BClick }

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

Transfer_T.Enabled := FALSE;
Transfer_File (M_Transfer_Command);
End; { TMain_F.Transfer_TTimer }

{#####################################################################################}
{ 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.Save_Data (F_Filename : String; F_Panel : TPanel);
{ --------------------------------------------------------------------------------------- }
Var
B :    TBitmap;
jpgb : TJPEGImage;
P :    TPanel;

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

B        := TBitmap.Create;
jpgb     := TJPEGImage.Create;
P        := F_Panel;
B.Width  := P.Width;
B.Height := P.Height;
Try
P.PaintTo (B.Canvas, 0, 0);
jpgb.Assign (B);
jpgb.CompressionQuality := 90;
jpgb.SaveToFile (F_Filename);
Except
End; { Try }
jpgb.Free;
B.Free;
End; { TMain_F.Save_Data }

{ --------------------------------------------------------------------------------------- }
Procedure TMain_F.Transfer_File (F_Command : String);
{ --------------------------------------------------------------------------------------- }
Begin { TMain_F.Transfer_File }
{\$IFDEF DEBUG_INFORMATION}
Debug_Output ('Begin of: TMain_F.Transfer_File');
{\$ENDIF}

{ Transfer file to Internet server }
{\$PUSH}
{\$WARNINGS OFF}
M_Transfer_Process.CommandLine := F_Command;
{\$POP}
M_Transfer_Process.Execute;
End; { TMain_F.Transfer_File }

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

{\$IFDEF USE_CRITICAL_SECTION}
EnterCriticalSection (Main_F.M_Critical_Section);
Try
{\$ENDIF}
{\$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}

{#####################################################################################}
{ 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;
Data_Time :           TDateTime;
Data_Hour :           Integer;
Data_Minute :         Integer;
Data_Second :         Integer;
RMS :                 Double;
Upper_Peak :          Double;
Lower_Peak :          Double;
Hour_Data_LS :        TLineSeries;
Hour_Position_CL :    TConstantLine;
{\$IFDEF SAVE_IMAGE}
Filename :            String;
{\$ENDIF}

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 }
{\$IFDEF SAVE_IMAGE}
Filename           := TrimFilename (ExtractFilePath (Application.Exename) + CURRENT_IMAGE_FILE_NAME);
{\$ENDIF}
{\$IFDEF TRANSFER_DATA_TO_INTERNET_SERVER}
M_Transfer_Command := TRANSFER_FILE_TXT + TrimFilename (ExtractFilePath (Application.Exename) + CURRENT_IMAGE_FILE_NAME);
{\$ENDIF}

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;

Current_LS.BeginUpdate;
Current_LP_LS.BeginUpdate;
Current_EMA_LS.BeginUpdate;
Current_RMS_LS.BeginUpdate;
Current_Integral_LS.BeginUpdate;
Current_Integral_LP_LS.BeginUpdate;

Current_LS.Clear;
Current_LP_LS.Clear;
Current_EMA_LS.Clear;
Current_RMS_LS.Clear;
Current_Integral_LS.Clear;
Current_Integral_LP_LS.Clear;

{\$ELSE}
M_Data_A_Index_1 := RANDOM_N;
{\$ENDIF}
Zero_Crossings_C := 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;
Begin { then }
Inc (Zero_Crossings_C);
End; { then }

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

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;
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 }

Current_Integral_LP_LS.EndUpdate;
Current_Integral_LS.EndUpdate;
Current_RMS_LS.EndUpdate;
Current_EMA_LS.EndUpdate;
Current_LP_LS.EndUpdate;
Current_LS.EndUpdate;

{\$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 }
{\$IFDEF SAVE_IMAGE}
Filename           := TrimFilename (ExtractFilePath (Application.Exename) + DAILY_IMAGE_FILE_NAME);
{\$ENDIF}
{\$IFDEF TRANSFER_DATA_TO_INTERNET_SERVER}
M_Transfer_Command := TRANSFER_FILE_TXT + TrimFilename (ExtractFilePath (Application.Exename) + DAILY_IMAGE_FILE_NAME);
{\$ENDIF}

{ Calculate peak values }
{\$ELSE}
M_Data_A_Index_1 := RANDOM_N;
{\$ENDIF}
Zero_Crossings_C := 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 }
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 }

{\$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;
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;

If Data_Minute <> M_Old_Data_Minute Then
Begin { then }
{\$IFDEF SAVE_IMAGE}
Save_Data (Filename, Main_P);
{\$ENDIF}

{\$IFDEF TRANSFER_DATA_TO_INTERNET_SERVER}
Transfer_T.Enabled := TRUE;
{\$ENDIF}
End; { then }

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 }

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}