Program SIGMA1A; // by Hannes.Tammet@ut.e

CONTENT:
Parameters for simulator and timer
GLOBAL CONSTANTS
GLOBAL VARIABLES
init_console
clrscr
init_timer
wintertime
mobility_a
ADCsimulator
show_ADC_error
init_USB
ADC
set_controls
startposition
failurestop
read_simulator
read_cal
read_inverter
read_ini
read_exmeteo
meteo
show_info
failurepause
check_power
voltages
test
internal procedures in test
instruction
controls_and_channels
showcontrols
writechannels
ADC_statistics
switchto
body of procedure test
check_media
win
wreal
show_keys
show_frame
show_scan
show_cycle
make_fileheaders
diurnalname
save_cycle
save_day
save_diagramtables
check_key
make_fractions
make_inverter
adsorptioncorrection
save_scan
scan
interquartile_mean
meandistributions
correct_pulses
correct_transfer
estimate_noise
process_cycle
newperiod
diurnalsave
measurement
show_welcome
MAIN
==================================================
Program SIGMA1A; // by Hannes.Tammet@ut.ee
{$R SIGMA1A.res}
{$APPTYPE CONSOLE}
{$MaxStackSize 33554432} //stack up to 32 MB
USES SysUtils, DateUtils, Windows, Math, CBW, IdGlobal,
     Console; // with acknowledgements to Rudolph Velthuis

CONST title = 'SIGMA1A version 20110214';
  developer = false; // true includes Z in main menu, A and B in test menu

{A simple control program for the Symmetric Inclined Grid Mobility Analyzer,
 where the BSMA-style decade-to-eight fraction schemes and user interface
 are conserved. More flexible interface may be available in future versions.

 All voltage polarities are physically commutable in SIGMA. The terminology
 below expexts the normal configuration, where the left part (close to the
 control board) of the analyzer collects the positive ions and:
    - the repelling electrodes are positive,
    - the attractive grid is negative,
    - the prefilter and the closed gate electrodes are negative.
    - the inner plate of the electrometric filter is negative.
 The right part of the analyzer collects the negative ions and all polarities
 are opposite.

 Output is saved into the subfolders SCANDETAILS, DAYS, MONTHS, and DIATABLES
 of the folder, where SIGMA1A.EXE is located as tab-separated raw data files
 _S1Ayymmdd.*, diurnal files S1Ayymmdd.*, and monthly files S1Ayymm00.*, where
 * marks the extension. The extension (usually xl) can be selected in the
 control file SIGMA1A.INI. Additionally can be saved the diagram table files.
 The structure of the data is similar as in case of BSMA, most of the
 differences occur in the last columns of technical diagnostic. See
 the manual for the detailed explanations.

 A line of a file of processed data begins with a 8-number heading:
    yymmdd hhmm day_of_year temperature humidity pressure noise+ noise-
 where yymmdd hhmm marks the center of the time period. The heading is followed
 by 10+10 values of dN/dlogD and 16+16 values of dN/dlogZ expressed in cm-3.

 At the end of the file is an appendix consisting of 18 values of diagnostic
 and integral parameters (see more explanation in the manual):
   supplyvolt +filtervolt -filtervolt +batteryvolt -batteryvolt
   +elektrometerbias -elektrometerbias pre% tau asym
   N+ N- n+ n- Z+ Z- ovl&sc regime
 There are 78 values in a line (BSMA dataline included 64 values).

 Limits of distribution fractions follow the "decade to eight" scheme.
 11 limits of 10 size distribution fractions are:
     0.422, 0.562, 0.75, 1.00, 1.33, 1.78, 2.37, 3.16, 4.22, 5.62, 7.50 nm
 17 limits of 16 mobility distribution fractions are:
     0.0316, 0.0422, 0.0562, 0.0750, 0.100, 0.133. 0.178, 0.237,
     0.316, 0.422, 0.562, 0.75, 1.00, 1.33, 1.78, 2.37, 3.16 cm2V-1s-1.

 Concentration of particles less than 0.42 nm is negligible, thus the first
 size fraction 0.422-0.562 nm can be considered as a fraction of 0-0.562 nm.
 The last mobility fraction is usually empty in the nature. It controls the
 broadening of the transfer function.

 Times of measuring operations and delays are expressed in milliseconds
 in the program.

 NB! The active folder must consist of a calibration file SIGMA1A.CAL,
     a control file SIGMA1A.INI and 2 subfolders MONTHS and DAYS.
     Additionally, it can consist of subfolders DIATABLES and SCANDETAILS,
     and specific control files SIGMA1A_exmeteo.txt and SIGMA1A_simulator.txt.

 NB: CBW32.dll should be available in the computer even when USB1608
     is not installed}

//* Parameters for simulator and timer
VAR simulator : boolean = false; // = fileexixts (SIGMA1A_simulator.txt)
    simufactor : real = 1; // acceleration of the simulated clock
    simushift : real = 0; // simulator time shift
    spectrumstyle : integer; // 0 = flat dn/dlogZ, 1 = like natural
    simunoisesigma : real; // noise sigma expressed in ADC counts
    simuoutlierprobability : real;
    simuoutliersigma : real; // noise sigma expressed in ADC counts
    timer_zero : int64;
    timer_constant : real;
    timer_start : real;

//* GLOBAL CONSTANTS
CONST
  voltageminutes = 15; // period of checking the voltage
  zeroperiod = 3; // for alternating the open and closed gate regimes
  standardsupplyvolt = 23.5; // conventional value for flowrate reduction
  minsupplyvolt = 21.6; // considering possible use of car batteries
  maxsupplyvolt = 26.5;
  maxasymmetry = 50; // promille
  minfilter = 450; // volt
  maxfilter = 550;
  minbattery = 200;
  maxbattery = 300;
  maxbias = 5; // mV
  maxoverload = 3; // allowed number of overload events during one scan
  maxHVzero = 100; // allowed ADC2 and ADC3 absolute zero level
  n_dia = 10;
  dia_decade = 8;
  lowest_dia = 0.4216965; // nm
  n_mob = 16; // mobility frame is determined in read_ini
  max_mobility = 2.5; // highest physical mobility
  scanmax = 300; // how many scans hold in the scanbuffer
  cyclelimit = 1450; // maximum+ of cycles in a day
  fandelay = 10000; // ms
  vectorlength = 199;
  margin1 = n_mob + 12; // for show_frame
  margin2 = margin1 + n_dia + 3; // for show_frame
  failureminutes = 10; // length of the failure pause in measurement

//* GLOBAL VARIABLES
VAR
// parameters of mobility frame (used only for display)
  mob_decade : integer;
  lowest_mob : real;
// adjustable coefficients, explanations in SIGMA1A.CAL
  voltagefactor, // for voltage-mobility conversion
  concentrationfactor_pos, // for dn/dlogZ, neglecting inlet losses
  concentrationfactor_neg, // for dn/dlogZ, neglecting inlet losses
  standardadsorption, // for Z = 1 cm2V-1cm-1, 0 C, 1013 mb
  filtermobility, // critical mobility for filters
  c_supplyvolt, // supplyvoltage / ADC_counts
  c_filtervolt, // filter voltage / ADC_counts
  c_batteryvolt, // electrometer battery voltage / ADC_counts
  c_bias, // electrometer inlet mV at 1 ADC
  c_pressurea,
  c_pressureb,
  c_temperaturea,
  c_temperatureb,
  c_humiditya,
  c_humidityb,
  delaytime : real; // ms
  chargingtime, // ms
  timeout : integer; // ms
  c_inv_n2,
  c_inv_n1,
  c_inv_p1,
  c_inv_p2,
  z_limit: real; // parameters for extra inverter, see make_inverter
//NB: some calibration constants are included into correct_high_mob_spread

// CONTROL PARAMETERS
// explanations in SIGMA1A.INI
  cboard,
  clusterregime,
  cycleminutes,
  extracorrect,
  showfractions : integer;
  timezone : real;
  extrapath,
  extension : string; // for output file
  initialcontrols : string [4]; // initial setting of output controls
  ini_version,
  cal_version : string [8]; // used in header
// controls, 0 = off or open, 1 = on or closed
  fan, // 1
  HV, // 2
  extern_A, // 4
  extern_B, // 8
  gate_pos, // 16
  gate_neg, // 32
  relay_pos, // 64
  relay_neg, //128
  regime : integer; // 0 = zero, 1 = ions
  externalmeteo,
  noiseregime : boolean;

// GLOBAL VARIABLES FOR MEASUREMENT PROCEDURES
TYPE
  vector = array [0..vectorlength] of real; // for statistics
  t_scanline = array [0..87] of real; // 0) windowstime 1..87) scandetails
  t_cycleline = array [0..78] of real; // 0) windowstime, 1..78) standard data
VAR ln_grid, mob_grid, central_mob : array [0..35] of real; // fine fractions
    inverter : array [1..35, 1..35] of real; // for enhance_resolution
    mob_ref: array [1..n_mob] of real; // ascending order
    dia_lo, dia_ref, dia_hi : array [0..n_dia] of real; // ascending order
       // NB: ascending of dia follows descending of corresponding diamob
    number_of_scan, // will increase until exit measurement
    scan_in_cycle : integer; // will set to zero every cycle
    scanline,
    scanmult : t_scanline; // multiplicator for scandetails
    scanbuffer : array [0..scanmax] of t_scanline; // a ring buffer
    scanpoint : integer;
    lastpoint : array [-2..3] of integer; // last scanpoint for a subperiod
    cycledec : array [1..78] of integer; // how many decimals in output
    cycleline : t_cycleline;
    cyclebuffer : array [0..cyclelimit] of t_cycleline; // a linear buffer
    cyclepoint : integer;
    n_cycle : integer; // how many performed (max = cyclelimit)
   // control variables
    run,
    days_available, // shows availability of the folder for data save
    months_available,
    diatables_available,
    scandetails_available,
    extra_available,
    internal_save, // shows the keyboard request by the user
    external_save,
    diatable_save,
    scan_save : boolean;
    mark : integer; // 0..9, set by keyboard
    failuremessage : string;
    dataheader,
    calibrationheader,
    calibrationstring : string;
    tau : real = 3800; // RC time constant and its empiric initial value;
    volt0pos : integer = 0; // HV zero level, ADC counts
    volt0neg : integer = 0; // HV zero level, ADC counts
   // sensor values
    celsius, // temperature
    humidity, // %
    pressure, // mb
    supplyvolt, // V
    filtervolt_pos, // V
    filtervolt_neg, // V negative voltages inverted to positive numbers
    batteryvolt_pos, // V
    batteryvolt_neg, // V
    bias_pos, // electrometer inlet bias, mV
    bias_neg : real;

procedure init_console (colors, // blackonwhite = 240 , whiteonblack = 15,
                buffercolumns, // yellowonblue = 30, redonblack = 12
                screencolumns,
                  bufferlines,
                  screenlines : integer); // black
   var outh : thandle;
             crd : coord;
             rct : small_rect;
          bc, sc,
          bl, sl : word;
               s : string;
               i : integer;
   begin
   outh := GetStdHandle(STD_OUTPUT_HANDLE);
   crd := GetLargestConsoleWindowSize(outh);
   if screencolumns > crd.X then sc := crd.X else sc := screencolumns;
   if screenlines > crd.Y then sl := crd.Y else sl := screenlines;
   if buffercolumns < sc then bc := sc else bc := buffercolumns;
   if bufferlines < sl then bl := sl else bl := bufferlines;
   crd.x := bc; crd.y := bl;
   SetConsoleScreenBufferSize(outh, crd);
   rct.left := 0; rct.right := sc - 1;
   rct.top := 0; rct.bottom := sl - 1;
   SetConsoleWindowInfo (outh, true, rct);
   SetConsoleTextAttribute (outh, colors);
   setconsoletitle (title);
   s := '';
   for i := 1 to bc do s := s + ' ';
   for i := 1 to bl do write (s);
   end;

Procedure clrscr1;
   var i : integer;
   begin
   for i := 1 to 250 do write (' ');
   gotoxy (1, 1);
   end;

procedure init_timer;
   var f : int64;
   begin
      QueryPerformanceFrequency (f);
      timer_constant := 1000 / f;
      if simulator then timer_constant := simufactor * timer_constant;
   end;

 procedure start_timer; begin QueryPerformanceCounter (timer_zero) end;

 function timer_milliseconds : real;
   var k : int64;
   begin QueryPerformanceCounter (k);
   result := timer_constant * (k - timer_zero) end;

 procedure pause (ms : real);
   var a, b : int64;
   begin
   QueryPerformanceCounter (a);
   repeat QueryPerformanceCounter (b);
   until (timer_constant * (b - a)) > ms;
   end;

function wintertime : real;
   begin
   if simulator
   then wintertime := timer_start + simufactor * (now - timer_start) + simushift
   else wintertime := now + timezonebias + timezone / 24
   end;

/////////////////////////////////////////////////////////////////

function mobility_a // cm2 V-1 s-1
        (pressure {mb},
         Celsius,
         massdiameter {nm} : real) : real;
  // See Tammet, H. (1995) Size and mobility of nanometer particles,
  // clusters and ions. J. Aerosol Sci. 26, 459-475.
  // Few corrections are included afterwards
  const // air parameters
    GasMass = 28.96;
    Polarizability = 0.00171;
    VisCon1 = 0.3036;
    VisCon2 = 44;
    VisCon3 = 0.8;
    // nanometer particles expected:
    particledensity = 2.08;
    ParticleCharge = 1;

  function Omega11 (x : real) : real; // *(1,1)*(T*) for (*-4) potential
    var p, q : real; // and elastic-specular collisions
    begin
      if x > 1 then Omega11 := 1 + 0.106 / x + 0.263 / exp ((4/3) * ln (x))
      else begin p := sqrt (x); q := sqrt (p);
        Omega11 := 1.4691 / p - 0.341 / q + 0.181 * x * q + 0.059 end;
    end;

  const a = 1.2; b = 0.5; c = 1; // the slip factor coefficients
        ExtraDistance = 0.115 {nm}; TransitionDiameter = 2.48 {nm};
  var GasDiameter, MeanVelocity, Viscosity, FreePath, DipolEffect,
        DeltaTemperature, CheckMark, ParticleMass, CollisionDistance,
        Kn, Omega, s, x, y, Temperature {K} : real;
  begin
    Temperature := Celsius + 273.15;
    Viscosity {microPa s} := 0.02713 * sqrt (GasMass * Temperature) /
      sqr (VisCon1 * (1 + exp (VisCon3 * ln (VisCon2 / Temperature))));
    MeanVelocity {m/s} := 145.5 * sqrt (Temperature / GasMass);
    FreePath {nm} := (166251 * Viscosity * Temperature) /
                      (GasMass * Pressure * MeanVelocity);
    ParticleMass {amu} := 315.3 * ParticleDensity *
                                  exp (3 * ln (MassDiameter));
    DeltaTemperature := Temperature;
    repeat
      CheckMark := DeltaTemperature;
      GasDiameter {nm} := VisCon1 *
        (1 + exp (VisCon3 * ln (VisCon2 / DeltaTemperature)));
      CollisionDistance {nm} := MassDiameter / 2 + ExtraDistance +
                                 GasDiameter / 2;
      DipolEffect := 8355 * ParticleCharge * Polarizability /
                            sqr (sqr (CollisionDistance));
      DeltaTemperature := Temperature + DipolEffect;
    until abs (CheckMark - DeltaTemperature) < 0.01;
    if ParticleCharge = 0 then Omega := 1
                          else Omega := Omega11 (Temperature / DipolEffect);
    Kn := FreePath / CollisionDistance;
    if Kn < 0.03 {underflow safe} then y := 0 else y := exp (- c / Kn);
                  {NB! erratum in JAS (y := 1) corrected!}
    x := (273.15 / DeltaTemperature) *
         exp (3 * ln (TransitionDiameter / MassDiameter));
    if x > 30 {overflow safe} then s := 1
      else if x > 0.001
      then s := 1 + exp (x) * sqr (x / (exp (x) - 1)) * (2.25 / (a + b) - 1)
      else {underflow safe} s := 1 + (2.25 / (a + b) - 1);
    Mobility_a := 1.602 * ((2.25 / (a + b)) / (Omega + s - 1)) *
                sqrt (1 + GasMass / ParticleMass) *
                (1 + Kn * (a + b * y)) /
                (6 * PI * Viscosity * CollisionDistance);
  end; // of mobility_a

function ADCsimulator (i : integer) : integer;
// A scan should be initiated with start_timer;
var t, x, a, b, c, d : real;
begin
   t := wintertime;
   d := t - trunc (t);
   a := 1000 * (0.5 - sqr (d - 0.42));
   b := 50000 / (0.5 - sqr (d - 0.58));
   c := tau / 1000; // s
   t := timer_milliseconds / 1000; // s
   case i of
      0 : begin // simulated error for positive electrometer
          if random < simuoutlierprobability
          then x := randg (200, simuoutliersigma)
          else x := randg (200, simunoisesigma);
          // simulated signal
          if (regime = 1) and not noiseregime then begin
             if spectrumstyle = 0
             then x := x + 300
             else x := x + a * exp (-0.6 * sqr (t - 3.7 * c))
          end;
          // if random < 0.01 then x := 33333; // overload generator
          end;
      1 : begin // simulated error for negative electrometer
          if random < simuoutlierprobability
          then x := randg (300, simuoutliersigma)
          else x := randg (300, simunoisesigma);
          // simulated signal
          if (regime = 1) and not noiseregime then begin
            if spectrumstyle = 0
            then x := x - 300
            else x := x - a * exp (-1 * sqr (t - 4.1 * c))
                        - b / sqr (sqr (t + 5))
            end;
          // if random < 0.0005 then x := -33333; // overload generator
          end;
      2 : x := 32000 * exp (-t / c); // Volt for + ions
      3 : x := -32000 * exp (-t / c); // Volt for - ions
      4 : x := 11000; // fan voltage
      5 : x := 26000; // pressure
      6 : x := 29000; // temperature
      7 : x := 12000; // humidity
   else x := 0;
   end;
   adcsimulator := round (x);
   pause (4);
end; // of ADCsimulator

procedure show_ADC_error (info : string); // and stop
   begin
   window (1,1,80,1);
   textbackground (white); textcolor (red);
   gotoxy (1, 1); clreol;
   write (' USB1608 fatal error : ', info, ' failure : press ENTER!');
   readln; halt;
   end;

procedure init_USB1608;
   begin
   if cbDConfigPort (cboard, AUXPORT, DIGITALOUT) <> 0
   then Show_ADC_error ('configuration');
   if cbDOut (cboard, AUXPORT, 0) <> 0 then Show_ADC_error ('initial output');
   end;

function ADC (channel : integer) : integer; // -32768 .. 32767 == -5 .. +5 V
   var x : word;
   begin
   if simulator then ADC := adcsimulator (channel)
   else begin
      if cbAin (cboard, channel, 0, x) <> 0 then Show_ADC_error ('input');
      ADC := x - 32768;
   end;
   end;

procedure set_controls (t {pause milliseconds after settings} : real);
  var b : byte;
  begin
  b := fan + 2 * HV + 4 * extern_A + 8 * extern_B + 16 * gate_pos
           + 32 * gate_neg + 64 * relay_pos + 128 * relay_neg;
  if not simulator then begin
     if cbDOut (cboard, AUXPORT, b) <> 0 then Show_ADC_error ('output');
  end;
  pause (t);
  end;

procedure startposition;
  begin
  fan := 0;
  HV := 0;
  extern_A := 0;
  extern_B := 0;
  gate_pos := 0;
  gate_neg := 0;
  relay_pos := 0;
  relay_neg := 0;
  set_controls (0);
  end;

procedure failurestop (prompt : string);
  // used only during initial reading of control files
  begin
    writeln;
    writeln ('Fatal error: ', prompt, ',');
    writeln ('press ENTER to escape!');
    readln; halt;
  end; // of failurestop

procedure read_simulator;
  // if SIGMA1A_simulator.txt does not exist then simulator = false
  // and initial values of parameters remain valid
  var f : text;
      k : integer;
  begin
  simulator := fileexists ('SIGMA1A_simulator.txt'); // 6-line text file
  if not simulator then exit;
  assignfile (f, 'SIGMA1A_simulator.txt');
  {$I-} reset (f); {$I+}
  if IOresult <> 0 then failurestop ('cannot read SIGMA1A_simulator.txt');
  readln (f, simufactor);
  readln (f, k);
  simushift := (60 * (k div 100) + k mod 100) / 1440 - (now - date);
  readln (f, spectrumstyle);
  readln (f, simunoisesigma);
  readln (f, simuoutlierprobability);
  readln (f, simuoutliersigma);
  closefile (f);
  end;

procedure read_cal;
  // read SIGMA1A.CAL and evaluate calibration variables
  var p : integer;
            x : real;
            f : text;
      s, a, b,
     filetime : string;
  begin
  // control values
  voltagefactor := -999;
  concentrationfactor_pos := -999;
  concentrationfactor_neg := -999;
  standardadsorption := -999;
  filtermobility := -999;
  c_supplyvolt := -999;
  c_filtervolt := -999;
  c_batteryvolt := -999;
  c_bias := -999;
  c_pressurea := -999;
  c_pressureb := -999;
  c_temperaturea := -999;
  c_temperatureb := -999;
  c_humiditya := -999;
  c_humidityb := -999;
  delaytime := -999;
  chargingtime := -999;
  timeout := -999;
  c_inv_n2 := 0;
  c_inv_n1 := 0;
  c_inv_p1 := 0;
  c_inv_p2 := 0;
  z_limit := 1;
  writeln (' Reading SIGMA1A.CAL ... ');
  datetimetostring (filetime, 'yyyymmdd',
     filedatetodatetime (fileage ('SIGMA1A.CAL')));
  assignfile (f, 'SIGMA1A.CAL');
  {$I-} reset (f); {$I+}
  if IOresult <> 0 then
    failurestop ('cannot find SIGMA1A.CAL in the active folder');
  readln (f, s); // header
  p := pos ('.', s);
  s := copy (s, p, 999);
  p := pos ('2', s);
  cal_version := copy (s, p, 8);
  if cal_version <> filetime then
     failurestop ('SIGMA1A.CAL header contains a wrong revision date');
  while not eof (f) do begin
    readln (f, s);
    if s = '' then s := ' '; // to allow empty lines
    if s [1] <> ' ' then begin
       // a line that begins with space is considered as a comment
       p := pos ('=', s);
       if p = 0 then failurestop
          ('an assignment line does not consist of = in SIGMA1A.CAL');
       a := copy (s, 1, p-1); // identifier of the parameter
       b := copy (s, p+1, length (s) - p);
       p := pos (' ', b);
       if p > 0 then b := copy (b, 1, p - 1);
       val (b, x, p); // value of the parameter
       if p <> 0 then failurestop ('wrong number presentation in SIGMA1A.CAL');
       if a = 'volt_factor' then voltagefactor := x else
       if a = 'conc_factor_pos' then concentrationfactor_pos := x else
       if a = 'conc_factor_neg' then concentrationfactor_neg := x else
       if a = 'standardadsorption' then standardadsorption := x else
       if a = 'filtermobility' then filtermobility := x else
       if a = 'c_supplyvolt' then c_supplyvolt := x else
       if a = 'c_filtervolt' then c_filtervolt := x else
       if a = 'c_batteryvolt' then c_batteryvolt := x else
       if a = 'c_bias' then c_bias := x else
       if a = 'c_pressurea' then c_pressurea := x else
       if a = 'c_pressureb' then c_pressureb := x else
       if a = 'c_temperaturea' then c_temperaturea := x else
       if a = 'c_temperatureb' then c_temperatureb := x else
       if a = 'c_humiditya' then c_humiditya := x else
       if a = 'c_humidityb' then c_humidityb := x else
       if a = 'delaytime' then delaytime := x else
       if a = 'chargingtime' then chargingtime := round (x) else
       if a = 'timeout' then timeout := round (x) else
       if a = 'c_inv_n2' then c_inv_n2 := x else
       if a = 'c_inv_n1' then c_inv_n1 := x else
       if a = 'c_inv_p1' then c_inv_p1 := x else
       if a = 'c_inv_p2' then c_inv_p2 := x else
       if a = 'z_limit' then z_limit := x else
       failurestop ('wrong keyword in SIGMA1A.CAL: ' + a);
    end; // of s [1] <> ' '
  end; // of while not eof (f)
  closefile (f);
  // check control values
  if standardsupplyvolt = -999 then failurestop
                                  ('standardsupplyvolt not initiated');
  if voltagefactor = -999 then failurestop ('voltage factor not initiated');
  if concentrationfactor_pos = -999 then failurestop
                                  ('concentrationfactor+ not initiated');
  if concentrationfactor_neg = -999 then failurestop
                                  ('concentrationfactor- not initiated');
  if standardadsorption =-999 then failurestop
                                  ('standardadsorption not initiated');
  if filtermobility =-999 then failurestop
                                  ('filtermobility not initiated');
  if c_supplyvolt = -999 then failurestop ('c_supplyvolt not initiated');
  if c_filtervolt = -999 then failurestop ('c_filtervolt not initiated');
  if c_batteryvolt = -999 then failurestop ('c_batteryvolt not initiated');
  if c_bias = -999 then failurestop ('c_bias not initiated');
  if c_pressurea = -999 then failurestop ('c_pressurea not initiated');
  if c_pressureb = -999 then failurestop ('c_pressureb not initiated');
  if c_temperaturea = -999 then failurestop ('c_temperaturea not initiated');
  if c_temperatureb = -999 then failurestop ('c_temperatureb not initiated');
  if c_humiditya = -999 then failurestop ('c_humiditya not initiated');
  if c_humidityb = -999 then failurestop ('c_humidityb not initiated');
  if delaytime = -999 then failurestop
                                   ('delaytime not initiated');
  if chargingtime = -999 then failurestop ('chargingtime not initiated');
  if timeout = -999 then failurestop ('timeout not initiated');
  end; // of Read_cal

procedure read_inverter;
  // read SIGMA1A_inverter.txt and evaluate coefficients
  var p : integer;
            x : real;
            f : text;
      s, a, b,
     filetime : string;
  begin
  writeln (' Reading SIGMA1A_inverter.txt ... ');
  datetimetostring (filetime, 'yyyymmdd',
     filedatetodatetime (fileage ('SIGMA1A_inverter.txt')));
  assignfile (f, 'SIGMA1A_inverter.txt');
  {$I-} reset (f); {$I+}
  if IOresult <> 0 then exit;
  readln (f, s); // header
  p := pos ('.', s);
  s := copy (s, p, 999);
  p := pos ('2', s);
  cal_version := copy (s, p, 8);
  if cal_version <> filetime then
     failurestop ('SIGMA1A_inverter.txt header contains a wrong revision date');
  while not eof (f) do begin
    readln (f, s);
    if s = '' then s := ' '; // to allow empty lines
    if s [1] <> ' ' then begin
       // a line that begins with space is considered as a comment
       p := pos ('=', s);
       if p = 0 then failurestop
          ('an assignment line does not consist of = in SIGMA1A_inverter.txt');
       a := copy (s, 1, p-1); // identifier of the parameter
       b := copy (s, p+1, length (s) - p);
       p := pos (' ', b);
       if p > 0 then b := copy (b, 1, p - 1);
       val (b, x, p); // value of the parameter
       if p <> 0 then failurestop
             ('wrong number presentation in SIGMA1A_inverter.txt');
       if a = 'c_inv_n2' then c_inv_n2 := x else
       if a = 'c_inv_n1' then c_inv_n1 := x else
       if a = 'c_inv_p1' then c_inv_p1 := x else
       if a = 'c_inv_p2' then c_inv_p2 := x else
       if a = 'z_limit' then z_limit := x else
       failurestop ('wrong keyword in SIGMA1A_inverter.txt: ' + a);
    end; // of s [1] <> ' '
  end; // of while not eof (f)
  closefile (f);
  end; // of Read_inverter

procedure read_ini;
  // read SIGMA1A.INI and evaluate control variables
  var p : integer;
            x : real;
            f : text;
           OK : boolean;
      s, a, b,
     filetime : string;
  begin
  // control values
  extension := '***';
  extrapath := '***';
  initialcontrols := '****';
  cboard := -999;
  clusterregime := -999;
  extracorrect := -999;
  cycleminutes := -999;
  timezone := -999;
  showfractions := -999;
  write (' Reading SIGMA1A.INI ... ');
  datetimetostring (filetime, 'yyyymmdd',
     filedatetodatetime (fileage ('SIGMA1A.INI')));
  assignfile (f, 'SIGMA1A.INI');
  {$I-} reset (f); {$I+}
  if IOresult <> 0 then
    failurestop ('cannot find SIGMA1A.INI in the active folder');
  readln (f, s); // header
  p := pos ('.', s);
  s := copy (s, p, 999);
  p := pos ('2', s);
  ini_version := copy (s, p, 8);
  if ini_version <> filetime then
     failurestop ('SIGMA1A.INI header contains a wrong revision date');
  while not eof (f) do begin
    readln (f, s);
    if s = '' then s := ' '; // to allow empty lines
    if s [1] <> ' ' then begin
       // a line that begins with space is considered as a comment
       p := pos ('=', s);
       if p = 0 then failurestop
          ('an assignment line does not consist of = in SIGMA1A.INI');
       a := copy (s, 1, p-1);
       b := copy (s, p+1, length (s) - p);
       p := pos (' ', b);
       if p > 0 then b := copy (b, 1, p - 1);
       if a = 'extension' then extension := b else
       if a = 'extrapath' then extrapath := b else
       if a = 'controls' then initialcontrols := b else begin
          val (b, x, p);
          if p <> 0 then
             failurestop ('wrong number presentation in SIGMA1A.INI: ' + b);
if a = 'cboard' then cboard := round (x) else
if a = 'clusterregime' then clusterregime := round (x) else
if a = 'extracorrect' then extracorrect := round (x) else
if a = 'cycleminutes' then cycleminutes := round (x) else
          if a = 'showfractions' then showfractions := round (x) else
          if a = 'timezone' then timezone := x else
          failurestop ('wrong keyword in SIGMA1A.INI: ' + a);
       end; // of a = 'controls'
    end; // of s [1] <> ' '
  end; // of while not eof (f)
  closefile (f);
  // check control values
  if timezone = -999 then failurestop ('timezone not initiated');
  if cboard = -999 then failurestop ('cboard not initiated');
  if not (clusterregime in [0, 1]) then failurestop ('wrong clusterregime');
  if not (extracorrect in [0..3]) then failurestop ('wrong extracorrect');
  if not (cycleminutes in [1..6,10]) then failurestop ('wrong cycleminutes');
  if not (showfractions in [0,1]) then failurestop ('wrong showfractions');
  if extension = '***' then failurestop ('extension not initiated');
  // extrapath can remain equal to '***'
  p := length (extrapath);
  if extrapath [p] = '\' then extrapath := copy (extrapath, 1, p - 1);
  OK := true;
  for p := 1 to 4 do OK := OK and (initialcontrols [p] in ['0'..'1']);
  if not OK then failurestop ('initialcontrols not correctly initiated');
  if clusterregime = 1 then begin // init fraction frame constants
     mob_decade := 16;
     lowest_mob := 0.421696503428582; // cm2V-1s-1
  end
  else begin // full range regime
     mob_decade := 8;
     lowest_mob := 0.0316227766016838; // cm2V-1s-1
  end;
  end; // of read_ini

procedure read_exmeteo;
  // read SIGMA1A_exmeteo.txt and force meteovariables
  var p : integer;
            x : real;
            f : text;
      s, a, b : string;
  begin
  assignfile (f, 'SIGMA1A_exmeteo.txt');
  {$I-} reset (f); {$I+}
  if IOresult <> 0 then exit;
  while not eof (f) do begin
    readln (f, s);
    p := pos ('=', s);
    if p > 0 then begin
       a := uppercase (copy (s, 1, p-1));
       b := copy (s, p+1, length (s) - p);
       p := pos (' ', b);
       if p > 0 then b := copy (b, 1, p - 1);
       val (b, x, p);
       if p = 0 then begin
          if a = 'T' then celsius := x else
          if a = 'P' then pressure := x else
          if a = 'RH' then humidity := x;
       end;
    end;
  end;
  closefile (f);
  end;

procedure meteo;
   begin
   pressure := c_pressurea * adc (5) + c_pressureb;
   celsius := c_temperaturea * adc (6) + c_temperatureb;
   humidity := (c_humiditya * adc (7) + c_humidityb) /
               (1.0546 - 0.00216 * celsius); // see HIH4031 datasheet
   externalmeteo := fileexists ('SIGMA1A_exmeteo.txt');
   if externalmeteo then read_exmeteo;
   end;

procedure show_info (s : string);
   begin
      window (49, 1, 85, 2); writeln;
      write (s : 36);
      textcolor (black);
   end;

procedure failurepause (minutes : integer; info : string);
   var i, x : integer;
       d, t : real;
          s : string;
          f : text;
   begin
   d := date;
   s := 'SIGMA1A_failure.txt';
   {$I-}
   assignfile (f, s);
   if fileexists (s) then append (f) else rewrite (f);
   if IOresult <> 0 then begin
      sleep (3000);
      show_info ('disk failure, no record in failure log!');
      sleep (3000);
   end
   else begin
      datetimetostring (s, 'yy-mm-dd hh:nn', wintertime); write (f, s);
      writeln (f, ') ', info);
      closefile (f);
   end;
   {$I+}
   textcolor (red);
   if (c_supplyvolt * adc (4)) < (minsupplyvolt / 2) then
        show_info ('PAUSE: POWER BLACKOUT')
   else show_info ('PAUSE: see SIGMA1A_failure.txt');
   startposition;
   t := wintertime + minutes / 1440;
   repeat // check XZ during sleep and perform fan cycles
      x := 0;
      if keypressed then begin
         while keypressed do begin
            x := ord (upcase (readkey));
            if x = 0 then readkey;
            if x = ord ('X') then begin
               start_timer;
               repeat until keypressed or (timer_milliseconds > 3000);
               if keypressed then x := 909 + ord (upcase (readkey));
            end;
         end;
         if x = 999 then begin t := wintertime; run := false end;
         while keypressed do readkey;
      end;
      if copy (info, 3, 12) = 'ELECTROMETER' then begin // fan cycles
         if simulator then x := 3000 else x := 30000;
         if fan = 1 then begin fan := 0; set_controls (2 * x) end;
         if fan = 0 then begin fan := 1; set_controls (x) end;
      end; // of fan cycles
   until wintertime > t;
   if date <> d then n_cycle := 0;
   number_of_scan := 0;
   scan_in_cycle := 0;
   scanpoint := 0;
   for i := -2 to 3 do lastpoint [i] := -1;
   if not run then exit;
   show_info ('Fan startup (10 s)');
   fan := 1;
   if simulator then set_controls (1000) else set_controls (fandelay);
   end; // of failurepause

procedure check_power;
   begin
   gate_pos := 0; gate_neg := 0; set_controls (100);
   if (c_supplyvolt * adc (4)) < minsupplyvolt then
   failurepause (failureminutes, 'POWER BLACKOUT');
   end;

procedure voltages (var wrong : string; p : real); // requires 5 s
   // out: filtervolt_pos, filtervolt_neg, batteryvolt_pos, batteryvolt_neg,
   // bias_pos, bias_neg, supplyvolt, volt0pos, volt0neg
   // volt0 is renovated as (1-p)*oldvalue + p*lastmeasurement
   // can be performed when HV has been low during 4 tau at least
   var x, y,
     v1p, v2p,
     v1n, v2n : real;
         s, u : string;
            t : double;
   begin
   if simulator then begin
      supplyvolt := 23;
      filtervolt_pos := 500;
      filtervolt_neg := 500;
      batteryvolt_pos := 240;
      batteryvolt_neg := 240;
      bias_pos := 1;
      bias_neg := 1;
      pressure := 1011;
      celsius := 22.2;
      humidity := 50;
      pause (1000);
      exit;
   end;
   HV := 0; gate_pos := 0; gate_neg := 0; set_controls (200);
   v1p := (adc (2) + adc (2)) / 2; v1n := (adc (3) + adc (3)) / 2; // v1
   t := wintertime;
   x := adc (4); // supply voltage
   supplyvolt := c_supplyvolt * x; str (supplyvolt:0:2, s);
   gate_pos := 1; gate_neg := 0; set_controls (600);
   filtervolt_pos := c_filtervolt * (adc (4) - x);
   gate_pos := 0; gate_neg := 1; set_controls (600);
   filtervolt_neg := c_filtervolt * (x - adc (4));
   gate_pos := 1; gate_neg := 1;
   relay_pos := 0; relay_neg := 0; set_controls (1200);
   x := adc (0); y := adc (1);
   relay_pos := 1; relay_neg := 1; set_controls (1200);
   batteryvolt_pos := c_batteryvolt * (adc (0) - x);
   batteryvolt_neg := c_batteryvolt * (y - adc (1));
   bias_pos := c_bias * x;
   bias_neg := c_bias * y;
   gate_pos := 0; gate_neg := 0;
   relay_pos := 0; relay_neg := 0; set_controls (1400);
   // estimating of voltage zero
   // x = exp (-dt / tau), v1 = a + v0, v2 = x * a + v0
   // v1 - v2 = (1 - x) * a, a = (v1 - v2) / (1 - x)
   // measured v0 = v1 - a = v1 - (v1 - v2) / (1 - x)
   // new = (1 - p) * old + p * measured v0
   // Correction made of 20101007:
   // A reason of zero shift may be leakage to HV circuit. In this case
   // the absolute zero should be used instead of the exponential asymptote.
   // The decision is to use the average v0 / 2 and
   // new := (1 - p) * old + p * measured v0 / 2
   v2p := (adc (2) + adc (2)) / 2; v2n := (adc (3) + adc (3)) / 2; // v2
   x := exp (-24 * 60 * 60 * 1000 * (wintertime - t) / tau);
   y := (v1p - (v1p - v2p) / (1 - x)); // measured v0
   volt0pos := round ((1 - p) * volt0pos + p * y / 2);
   y := (v1n - (v1n - v2n) / (1 - x)); // measured v0
   volt0neg := round ((1 - p) * volt0neg + p * y / 2);
   u := '';
   if (supplyvolt < minsupplyvolt) or (supplyvolt > maxsupplyvolt)
   or (filtervolt_pos < minfilter) or (filtervolt_pos > maxfilter)
   or (filtervolt_neg < minfilter) or (filtervolt_neg > maxfilter)
   or (batteryvolt_pos < minbattery) or (batteryvolt_pos > maxbattery)
   or (batteryvolt_neg < minbattery) or (batteryvolt_neg > maxbattery)
   or (abs (volt0pos) > maxHVzero) or (abs (volt0neg) > maxHVzero)
   or (abs (bias_pos) > maxbias) or (abs (bias_neg) > maxbias) then begin
      str (supplyvolt:0:2, s); u := u + 'supply ' + s;
      str (filtervolt_pos:0:0, s); u := u + ', +-filter ' + s;
      str (filtervolt_neg:0:0, s); u := u + ' ' + s;
      str (batteryvolt_pos:0:0, s); u := u + ', +-battery ' + s;
      str (batteryvolt_neg:0:0, s); u := u + ' ' + s;
      str (bias_pos:0:2, s); u := u + ', +-bias ' + s;
      str (bias_neg:0:2, s); u := u + ' ' + s;
      str (volt0pos, s); u := u + ', +-HVzero ' + s;
      str (volt0neg, s); u := u + ' ' + s;
   end;
   wrong := u;
   end;

procedure test;
// Displays immediately ADC counts
var c : char;
        s : string;
     a, b,
     i, j : integer;
        y : array [0..30, 1..2] of integer;

//* internal procedures in test
procedure instruction; // in test
   begin
   writeln;
   writeln (' Remember test commands:');
   writeln (' 0...7 : toggle control line C_# between 0 and 1');
   writeln (' space bar: display milliseconds, readings of ADC,',
            ' and control settings');
   writeln (' R : repeat the space bar action 20 times during 2 s');
   writeln (' N : statistics of 100 measurements of all channels');
   writeln (' S : show voltages, temperature, pressure, and RH');
   writeln (' T : test scan with saving of the record');
   writeln (' U : test scan relative to saved record');
   writeln (' E : display explanation of controls and channels');
   if developer then
   writeln (' A : developer programmable extra operation A');
   if developer then
   writeln (' B : developer programmable extra operation B');
   writeln (' X : exit the test');
   writeln (' another : display again the instruction above');
   writeln (' Channel signals are displayed in ADC counts (-32768..+32767)');
   writeln (' 1 count is 0.1526 mV (6.1 fA for Ch 0&1 and O.1 V for 2&3');
   end;

procedure controls_and_channels; // in test
   begin
   writeln (' Controls: 0 / 1 ADC counts: ');
   writeln (' C_0 = fan off/on ADC0 = + ion electrometer');
   writeln (' C_1 = HV 6 kV off/on ADC1 = - ion electrometer');
   writeln (' C_2 = ext_dev_A off/on ADC2 = + ion voltage (HV)');
   writeln (' C_3 = ext_dev_B off/on ADC3 = - ion voltage (HV)');
   writeln (' C_4 = + gate open/closed ADC4 = fan&filter voltage');
   writeln (' C_5 = - gate open/closed ADC5 = pressure sensor');
   writeln (' C_6 = + relay off/on ADC6 = temperature sensor');
   writeln (' C_7 = - relay off/on ADC7 = humidity sensor');
   end;

procedure showcontrols; // in test
   begin
   write (fan, HV, extern_A, extern_B,
          gate_pos, gate_neg, relay_pos, relay_neg);
   end;

procedure writechannels (c : integer); // in test
   var h : integer;
   begin
   if c = 1 then writeln (' Time:s ADC0 ADC1 ADC2 ADC3 ',
                          'ADC4 ADC5 ADC6 ADC7 controls');
   write (timer_milliseconds / 1000 :8:2);
   for h := 0 to 7 do write (ADC (h):7);
   write (' '); showcontrols;
   end;

procedure ADC_statistics (n : integer); // in test
   var i, j, x : integer;
         s, ss : array [0..7] of real;
      min, max : array [0..7] of integer;
   begin
   for j := 0 to 7 do begin
      s [j] := 0;
      ss [j] := 0;
      min [j] := 99999;
      max [j] := -99999;
   end;
   for i := 1 to n do for j := 0 to 7 do begin
      x := adc (j);
      s [j] := s [j] + (x / n);
      ss [j] := ss [j] + x * (x / n);
      if x < min [j] then min [j] := x;
      if x > max [j] then max [j] := x;
   end;
   writeln (' Ch min ave max sigma');
   for j := 0 to 7 do writeln (j:3, min [j]:8, s [j]:8:0, max [j]:8,
                                    sqrt (ss [j] - sqr (s [j])):8:1);
   end;

 procedure electrometertransition1; // can be used as the hidden Z-operation
                                   // NB: this version is not simulated
   var i, p : integer;
      z_pos, z_neg,
     a, b, c, d, t,
              v, w : real;
   begin
   startposition; // all controls zero
   writeln (' Zero measurement, wait for 10 seconds');
   if not simulator then sleep (10000);
   z_pos := 0; z_neg := 0;
   for i := 1 to 100 do begin
      z_pos := z_pos + adc (0) / 100;
      z_neg := z_neg + adc (1) / 100;
   end;
   writeln (' Charging, wait for 3 seconds');
   relay_pos := 1; relay_neg := 1; set_controls (3000);
   relay_pos := 0; relay_neg := 0; set_controls (0);
   start_timer;
   writeln (' ms pos neg exp');
   p := 20;
   for i := 1 to 30 do begin
      t := timer_milliseconds;
      writeln (t:6:0,
               adc (0) - z_pos:7:0, adc (1) - z_neg:7:0,
               26000 * exp (-t / delaytime):7:0);
      pause (p);
      if i = 10 then p := 50;
      if i = 20 then p := 200;
   end;
   writeln (' Experiment 2, wait for 20 s');
   pause (5000);
   z_pos := 0; z_neg := 0;
   for i := 1 to 1000 do begin
      z_pos := z_pos + adc (0) / 1000;
      z_neg := z_neg + adc (1) / 1000;
   end;
   relay_pos := 1; relay_neg := 1; set_controls (3000);
   relay_pos := 0; relay_neg := 0; set_controls (0);
   start_timer;
   a := 0; b := 0; c := 0; d := 0;
   for i := 1 to 250 do begin
      t := timer_milliseconds;
      v := adc (0); w := adc (1);
      a := a + (v - z_pos) * t;
      b := b + (v - z_pos);
      c := c + (w - z_neg) * t;
      d := d + (w - z_neg);
   end;
   writeln ( ' zero_pos = ', z_pos:0:0,
            ' zero_neg = ', z_neg:0:0,
            ' t_pos = ', a/b:0:0,
            ' t_neg = ', c/d:0:0);
   end;

 procedure electrometertransition3; // can be used as the hidden Z-operation
                                   // NB: this version is not simulated
   var a, i, n, t : integer;
             x, y : array [1..9999] of integer;
                f : text;
   begin
   startposition; // all controls zero
   writeln (' Zero measurement, wait for 10 seconds');
   if not simulator then sleep (10000);
   a := 0;
   for i := 1 to 100 do a := a + adc (1);
   a := a div 100;
   writeln ('zero = ', a);
   n := 0;
   start_timer;
   repeat n := n + 1;
      t := round (timer_milliseconds);
      x [n] := t;
      y [n] := adc (1) - a;
   until t > 1000;
   extern_a := 1; set_controls (10);
   repeat n := n + 1;
      t := round (timer_milliseconds);
      x [n] := t;
      y [n] := adc (1) - a;
   until t > 2000;
   extern_a := 0; set_controls (0);
   repeat n := n + 1;
      t := round (timer_milliseconds);
      x [n] := t;
      y [n] := adc (1) - a;
   until t > 5000;
   assign (f, 'electrometer.txt'); rewrite (f);
   for i := 1 to n do writeln (f, i, #9, x [i], #9, y[i]);
   close (f);
   writeln ('DONE');
   end;

procedure switchto; // in test
   begin
   set_controls (0);
   write ('Controls: ');
   showcontrols;
   writeln;
   end;

//* body of procedure test
begin
   startposition;
   init_console (30, 0, 85, 0, 50);
   clrscr1;
   writeln; writeln (' SIGMA test'); writeln;
   controls_and_channels;
   instruction;
   start_timer;
   regime := 1;
   noiseregime := false;
   repeat
      writeln;
      write (' Please enter command: ');
      repeat c := readkey until not keypressed;
      c := upcase (c);
      writeln (c);
      case c of
         '0' : begin fan := 1 - fan ; switchto end;
         '1' : begin HV := 1 - HV ; switchto end;
         '2' : begin extern_A := 1 - extern_A ; switchto end;
         '3' : begin extern_B := 1 - extern_B ; switchto end;
         '4' : begin gate_pos := 1 - gate_pos ; switchto end;
         '5' : begin gate_neg := 1 - gate_neg ; switchto end;
         '6' : begin relay_pos := 1 - relay_pos; switchto end;
         '7' : begin relay_neg := 1 - relay_neg; switchto end;
         ' ' : writechannels (1);
         'R' : begin
               start_timer;
               writechannels (1); writeln;
               for i := 1 to 20 do begin
                  pause (100);
                  writechannels (0);
                  writeln;
               end;
               end;
         'N' : begin
               writeln (' Acquiring data, please be patient!');
               ADC_statistics (100);
               end;
         'S' : begin
               write (' ... measuring about 5 sec ...');
               voltages (s, 1);
               if s <> '' then writeln ('Warning, voltages out of range!');
               writeln;
               writeln (' Supply voltage = ', supplyvolt:3:1, ' V');
               writeln (' + Filter voltage = ', filtervolt_pos:3:1, ' V');
               writeln (' - Filter voltage = ', filtervolt_neg:3:1, ' V');
               writeln (' + Battery voltage = ', batteryvolt_pos:3:1, ' V');
               writeln (' - Battery voltage = ', batteryvolt_neg:3:1, ' V');
               writeln (' + Electrometer bias = ', bias_pos:3:1, ' mV');
               writeln (' - Electrometer bias = ', bias_neg:3:1, ' mV');
               meteo;
               writeln (' Temperature = ', celsius:3:1, ' C');
               writeln (' Relative humidity = ', humidity:3:1, ' %');
               writeln (' Atmospheric pressure = ', pressure:3:1, ' mb');
               end;
     'T','U' : begin // test scan
               regime := 1;
               writeln (' ... Charging the capacitor ...');
               HV := 1; set_controls (1000);
               writeln ('Seconds Volt+ E_meter+ Volt- E_meter-');
               HV := 0; set_controls (0);
               start_timer;
               for j := 0 to 25 do begin
                  while trunc (timer_milliseconds / 1000) < j do; // wait
                  a := adc (0); b := adc (1);
                  if c = 'T' then begin y [j, 1] := a; y [j, 2] := b end;
                  if c = 'U' then begin a := a - y [j, 1];
                                        b := b - y [j, 2] end;
                  write (round (timer_milliseconds / 1000):7,
                          adc (2):7, a:7, adc (3):9, b:7);
                  writeln;
               end;
               end;
         'E' : controls_and_channels;
         'A' : if developer then electrometertransition1; // can be modified!
         'B' : if developer then electrometertransition3; // can be modified!
      else if c <> 'X' then instruction;
      end; // of case c
   until c = 'X';
end; // of test

//===================== Measurement service procedures ======================
// are kept outside of measurement for easy debugging

procedure check_media;
   begin
   days_available := directoryexists ('days');
   months_available := directoryexists ('months');
   diatables_available := directoryexists ('diatables');
   scandetails_available := directoryexists ('scandetails');
   if extrapath = '***' then extra_available := false
   else extra_available := directoryexists (extrapath);
   end;

procedure win (w : integer);
   begin
   case w of
      0 : window (0, 0, 0, 0); // full
      1 : window (49, 1, 85, 2); // info
      2 : window (9, 4, 69, 8); // cycle meteo
      3 : window (9, 12, 69, margin1 - 1); // cycle mob spectum
      4 : window (9, margin1 + 3, 69, margin2 - 1); // cycle dia spectum
      5 : window (9, margin2 + 1, 69, margin2 + 3); // cycle NnZ
      6 : window (71, 4, 85, 8); // scan meteo
      7 : window (71, 12, 85, margin1 - 1); // scan mob spectum
      8 : window (71, margin1 + 3, 85, margin2 + 3); // diagnostics
      9 : window (38, margin2 + 5, 61, margin2 + 9); // control keys
     99 : window (10, 10, 20, 20); // test
   end;
   end;

procedure wreal (x : real; c, l, f : integer);
   begin
   textcolor (c);
   write (x:l:f);
   end;

procedure show_keys;
   procedure w (x : string);
      begin
      if x [2] = 'F' then textcolor (red) else textcolor (green);
      write (copy (x, 1, 3));
      textcolor (black);
      writeln (copy (x, 4, 99));
      end;
   begin
   win (9);
   if not (days_available or months_available) then w ('OFF (no access)')
   else if internal_save then w ('ON (Key M turns off)')
   else w ('OFF (Key M turns on) ');
   if not extra_available then w ('OFF (no access) ')
   else if external_save then w ('ON (Key E turns off)')
   else w ('OFF (Key E turns on) ');
   if not diatables_available then w ('OFF (no access) ')
   else if diatable_save then w ('ON (Key D turns off)')
   else w ('OFF (Key D turns on) ');
   if not scandetails_available then w ('OFF (no access) ')
   else if scan_save then w ('ON (Key S turns off)')
   else w ('OFF (Key S turns on) ');
   win (0);
   gotoxy (16 , 2);
   if mark = 0 then write (' ')
               else write ('mark ', mark);
   end;

procedure show_frame;
   var i : integer;
       s : string;
   begin
   win (0);
   textbackground (white);
   textcolor (black);
   clrscr1;
   gotoxy (2, 3); for i := 2 to 84 do write (#205);
   gotoxy (2, 9); for i := 2 to 84 do write (#205);
   gotoxy (2, 11); for i := 2 to 84 do write (#205);
   gotoxy (2, margin1); for i := 2 to 84 do write (#205);
   gotoxy (2, margin1 + 2); for i := 2 to 84 do write (#205);
   gotoxy (2, margin2); for i := 2 to 70 do write (#205);
   gotoxy (2, margin2 + 4); for i := 2 to 84 do write (#205);
   for i := 4 to margin2 + 3 do begin gotoxy (8, i); write (#186) end;
   for i := 4 to margin2 + 3 do begin gotoxy (70, i); write (#186) end;
   gotoxy (8, 3); write (#203);
   gotoxy (70, 3); write (#203);
   gotoxy (8, 9); write (#206);
   gotoxy (70, 9); write (#206);
   gotoxy (8, 11); write (#206);
   gotoxy (70, 11); write (#206);
   gotoxy (8, margin1); write (#206);
   gotoxy (70, margin1); write (#206);
   gotoxy (8, margin1 + 2); write (#206);
   gotoxy (70, margin1 + 2); write (#206);
   gotoxy (8, margin2); write (#206);
   gotoxy (70, margin2); write (#185);
   gotoxy (8, margin2 + 4); write (#202);
   gotoxy (70, margin2 + 4); write (#202);
   datetimetostring (s, 'yyyymmdd', wintertime);
   gotoxy (2, 2); write ('Date ', s);
   if simulator then write ('S');
   gotoxy (2, 4); write ('HH:MM');
   gotoxy (2, 5); write (' T:C');
   gotoxy (2, 6); write ('RH:%');
   gotoxy (2, 7); write (' p:mb');
   gotoxy (2, 8); write ('noise');
   gotoxy (3, 10); write ('MOB');
   gotoxy (10, 10);
   if showfractions = 1 then write ('Mobility fraction concentrations : cm-3')
                        else write ('Distribution function dn/d(logZ) : cm-3');
   for i := 1 to n_mob do begin
      gotoxy (2, 11 + i); write (mob_ref [i]:5:3);
   end;
   gotoxy (74, 10); write ('ADC counts');
   gotoxy (3, margin1 + 1); write ('DIA');
   gotoxy (10, margin1 + 1);
   if showfractions = 1 then write ('Diameter fraction concentrations : cm-3')
   else write ('Distribution function dn/d(logD) : cm-3');
   for i := 1 to n_dia do begin
      gotoxy (2, margin1 + 2 + i); write (dia_ref [i]:5:3);
   end;
   gotoxy (73, margin1 + 1); write ('DIAGNOSTICS');
   gotoxy (2, margin2 + 1); write ('N-prt');
   gotoxy (2, margin2 + 2); write ('n-cls');
   gotoxy (2, margin2 + 3); write ('Z-cls');
   gotoxy (2, margin2 + 5); write ('Save current data into MONTHS&DAYS:');
   gotoxy (2, margin2 + 6); write ('Save diurnal data into Extrafolder:');
   gotoxy (2, margin2 + 7); write ('Save diagram tables into DIATABLES:');
   gotoxy (2, margin2 + 8); write ('Save scan details into SCANDETAILS:');
   gotoxy (62, margin2 + 6); write ('Effect of a key appears');
   gotoxy (62, margin2 + 7); write (' at the end of the scan');
   textcolor (blue);
   gotoxy (65, margin2 + 5); write ('Set mark = Key 0...9');
   textcolor (red);
   gotoxy (69, margin2 + 8); write ('SAVE & EXIT = XZ');
   if noiseregime then begin
      win (0); gotoxy (23, 2); write ('NB: noise recording!');
   end;
   textcolor (black);
   show_keys;
   end;

procedure show_scan;
   var i : integer;
        s : string;
   begin
   textcolor (black);
   win (0); gotoxy (7, 2);
   datetimetostring (s, 'yyyymmdd', scanline [0]); write (s);
   win (6);
   datetimetostring (s, 'hh:nn', scanline [0]); writeln (s:11);
   writeln (scanline [3]:11:2);
   writeln (scanline [4]:11:2);
   writeln (scanline [5]:11:2);
   write (' Scan', scan_in_cycle:3, ' reg ', regime);
   win (7);
   for i := 1 to 16 do begin
      wreal ((scanline [ 5 + 2 * i] + scanline [ 6 + 2 * i]) / 2, red, 7, 0);
      wreal ((scanline [40 + 2 * i] + scanline [41 + 2 * i]) / 2, blue, 7, 0);
      if i < n_mob then writeln;
   end;
   textcolor (black);
   show_keys;
   end;

procedure show_cycle;
   var i, c, a, k : integer;
           sf, sd : real;
                s : string;
   begin
   if showfractions = 1 then sf := 1 / mob_decade else sf := 1;
   if showfractions = 1 then sd := 1 / dia_decade else sd := 1;
   a := n_cycle - 3; // a..n_cycle are last four
   if a < 1 then a := 1; // or less
   win (2);
   for c := a to n_cycle do begin
      textcolor (black);
      str (90000 + cyclebuffer [c, 2]:0:0, s);
      gotoxy (15 * (c - a) + 8, 1);
      write (copy (s, 2, 2), ':', copy (s, 4, 2));
      gotoxy (15 * (c - a) + 3, 2); write (cyclebuffer [c, 4]:10:2);
      gotoxy (15 * (c - a) + 3, 3); write (cyclebuffer [c, 5]:10:2);
      gotoxy (15 * (c - a) + 3, 4); write (cyclebuffer [c, 6]:10:2);
      gotoxy (15 * (c - a) + 2, 5);
      wreal (cyclebuffer [c, 7], red, 6, 0); write ('+');
      wreal (cyclebuffer [c, 8], blue, 6, 0); write ('-');
   end;
   win (3);
   for i := 1 to n_mob do begin
      gotoxy (1, i);
      k := 7;
      for c := a to n_cycle do begin
         wreal (sf * cyclebuffer [c, 28 + i], red, k, 0);
         wreal (sf * cyclebuffer [c, 44 + i], blue, 7, 0);
         k := 8;
      end;
   end;
   win (4);
   for i := 1 to n_dia do begin
      gotoxy (1, i);
      k := 7;
      for c := a to n_cycle do begin
         wreal (sd * cyclebuffer [c, 8 + i], red, k, 0);
         wreal (sd * cyclebuffer [c, 18 + i], blue, 7, 0);
         k := 8;
      end;
   end;
   win (5);
   gotoxy (1, 1);
   k := 7;
   for c := a to n_cycle do begin
      wreal (cyclebuffer [c, 71], red, k, 0);
      wreal (cyclebuffer [c, 72], blue, 7, 0);
      k := 8;
   end;
   gotoxy (1, 2);
   k := 7;
   for c := a to n_cycle do begin
      wreal (cyclebuffer [c, 73], red, k, 0);
      wreal (cyclebuffer [c, 74], blue, 7, 0);
      k := 8;
   end;
   gotoxy (1, 3);
   k := 7;
   for c := a to n_cycle do begin
      wreal (cyclebuffer [c, 75], red, k, 2);
      wreal (cyclebuffer [c, 76], blue, 7, 2);
      k := 8;
   end;
   textcolor (black);
   win (8);
   writeln (' Cycle', n_cycle:8);
   writeln (' Scans', scan_in_cycle:8);
   writeln (' Supply', cyclebuffer [n_cycle, 61]:7:1);
   writeln (' Filter+', cyclebuffer [n_cycle, 62]:6:0);
   writeln (' Filter-', cyclebuffer [n_cycle, 63]:6:0);
   writeln (' Battery+', cyclebuffer [n_cycle, 64]:5:0);
   writeln (' Battery-', cyclebuffer [n_cycle, 65]:5:0);
   writeln (' E-bias+', cyclebuffer [n_cycle, 66]:6:2);
   writeln (' E-bias-', cyclebuffer [n_cycle, 67]:6:2);
   writeln (' Pretime%', cyclebuffer [n_cycle, 68]:5:0);
   writeln (' Tau ', cyclebuffer [n_cycle, 69]:6:0);
   writeln (' Asymmetry', cyclebuffer [n_cycle, 70]:4:0);
   str (volt0neg, s);
   writeln (' HV0+/-', volt0pos:6 - length (s), '/', s);
   a := round (cyclebuffer [n_cycle, 77] - scan_in_cycle) div 100;
   str (a mod 100, s);
   write (' Ovrl+/-', (a div 100):(5 - length (s)), '/', s);
   textcolor (black);
   end;

procedure make_fileheaders;
   var i : integer;
      s, c : string;
   begin
   dataheader := 'YYMMDD' + #9 + 'HHMM' + #9 + 'DAY' + #9 + 'T:C' + #9 +
                 'RH:%' + #9 + 'p:mb' + #9 + 'noise+' + #9 + 'noise-';
   for i := 1 to n_dia do begin
      str (dia_ref [i]:5:3, s);
      dataheader := dataheader + #9 + 'D+' + s;
   end;
   for i := 1 to n_dia do begin
      str (dia_ref [i]:5:3, s);
      dataheader := dataheader + #9 + 'D-' + s;
   end;
   for i := 1 to n_mob do begin
      str (mob_ref [i]:5:(3 - clusterregime), s);
      dataheader := dataheader + #9 + 'Z+' + s;
   end;
   for i := 1 to n_mob do begin
      str (mob_ref [i]:5:3, s);
      dataheader := dataheader + #9 + 'Z-' + s;
   end;
   dataheader := dataheader + #9'supply'#9'filt+'#9'filt-'#9'batt+'#9 +
       'batt-'#9'bias+'#9'bias-'#9'pre%'#9'tau'#9'asym'#9'N+'#9 +
       'N-'#9'n+'#9'n-'#9'Z+'#9'Z-'#9'ovl&sc'#9'regime';
   calibrationheader :=
      'SIGMA1A'#9'CALIBR'#9'V-fctr'#9'C-fctr+'#9'C-fctr-'#9'st-ads'#9 +
      'filter-Z'#9'c-sup-V'#9'c-filt-V'#9'c-bat-V'#9'c-bias'#9 +
      'c-pres-a'#9'c-pres-b'#9'c-temp-a'#9'c-temp-b'#9'c-hum-a'#9 +
      'c-hum-b'#9'delay'#9'charging-t'#9'timeout'#9'c-inv-n2'#9 +
      'c-inv-n1'#9'c-inv-p1'#9'c-inv-p2'#9'z-limit';
   // make calibrationstring
   c := copy (title, 17, 8) + #9 + cal_version;
   str (voltagefactor:0:1, s); c := c + #9 + s;
   str (concentrationfactor_pos:0:3, s); c := c + #9 + s;
   str (concentrationfactor_neg:0:3, s); c := c + #9 + s;
   str (standardadsorption:0:3, s); c := c + #9 + s;
   str (filtermobility:0:3, s); c := c + #9 + s;
   str (c_supplyvolt:0:6, s); c := c + #9 + s;
   str (c_filtervolt:0:6, s); c := c + #9 + s;
   str (c_batteryvolt:0:6, s); c := c + #9 + s;
   str (c_bias:0:6, s); c := c + #9 + s;
   str (c_pressurea:0:6, s); c := c + #9 + s;
   str (c_pressureb:0:1, s); c := c + #9 + s;
   str (c_temperaturea:0:6, s); c := c + #9 + s;
   str (c_temperatureb:0:1, s); c := c + #9 + s;
   str (c_humiditya:0:5, s); c := c + #9 + s;
   str (c_humidityb:0:1, s); c := c + #9 + s;
   str (delaytime:0:0, s); c := c + #9 + s;
   str (chargingtime, s); c := c + #9 + s;
   str (timeout, s); c := c + #9 + s;
   str (c_inv_n2:0:3, s); c := c + #9 + s;
   str (c_inv_n1:0:3, s); c := c + #9 + s;
   str (c_inv_p1:0:3, s); c := c + #9 + s;
   str (c_inv_p2:0:3, s); c := c + #9 + s;
   str (z_limit:0:3, s); c := c + #9 + s;
   calibrationstring := c;
   end;

function diurnalname : string;
   var s : string;
   begin
   str (1000000 + cycleline [1]:0:0, s);
   diurnalname := 'S1A' + copy (s, 2, 6) + '.' + extension;
   end;

procedure save_cycle; // only internal disk
   var i : integer;
       s : string;
       f : text;
     old : boolean;
   begin
   show_info ('saving into diurnal file');
   s := 'days\' + diurnalname;
   assignfile (f, s);
   old := fileexists (s);
   {$I-} if old then append (f) else rewrite (f); {$I+}
   if IOresult <> 0 then begin
      sleep (3000);
      show_info ('failure, no success!');
      sleep (3000);
      exit;
   end;
   if not old then begin // write header
      writeln (f, calibrationheader);
      writeln (f, calibrationstring);
      writeln (f, dataheader);
   end;
   for i := 1 to 77 do write (f, cyclebuffer [n_cycle, i]:0:cycledec [i], #9);
   writeln (f, cyclebuffer [n_cycle, 78]:0:0);
   closefile (f);
   end;

procedure save_day (path : string);
   var s : string;
     i, j : integer;
        f : text;
      old : boolean;
   begin
   if n_cycle < 1 then exit;
   show_info ('saving into the monthly file');
   s := path + '\' + diurnalname;
   i := length (s) - length (extension) - 1;
   s [i - 1] := '0';
   s [i] := '0';
   assignfile (f, s);
   old := fileexists (s);
   {$I-} if old then append (f) else rewrite (f); {$I+}
   if IOresult <> 0 then begin
      sleep (3000);
      show_info ('failure, no success!');
      sleep (3000);
      exit;
   end;
   if not old then begin // write header
      writeln (f, calibrationheader);
      writeln (f, calibrationstring);
      writeln (f, dataheader);
   end;
   for j := 1 to n_cycle do begin
     for i := 1 to 77 do write (f, cyclebuffer [j, i]:0:cycledec [i], #9);
     writeln (f, cyclebuffer [j, 78]:0:0);
   end;
   closefile (f);
   end;

procedure save_diagramtables;
   var table : array [1..20, 0..1440] of real;
           n : integer; // actual end (possible maximum is 1440)

   procedure savetable (filename : string);
     var i, j : integer;
            f : text;
     begin
     {$I-}
     assignfile (f, filename); rewrite (f);
     if IOresult <> 0 then begin
        show_info ('Error 1 saving diagram table');
        sleep (3000);
        exit;
     end;
     for j := 0 to n do begin
        write (f, cycleminutes * j, #9);
        for i := 1 to 20 do begin
           if i = 20 then writeln (f, table [i, j]:0:0)
                     else write (f, table [i, j]:0:0, #09);
           if IOresult <> 0 then begin {SI+}
              show_info ('Error 2 saving diagram table');
              sleep (3000);
              closefile (f);
              exit;
           end;
        end;
     end;
     closefile (f);
     {$I+}
     end; // of savetable in savediagramtables

  var i, j, k, hhmm : integer;
          available : array [0..1440] of boolean;
           datetext : string;

  begin // savediagramtables
    if n_cycle < 1 then exit;
    show_info ('saving of diagram tables');
    n := 1440 div cycleminutes;
    datetext := copy (diurnalname, 4, 6);
    for i := 1 to 20 do for j := 0 to n do table [i, j] := 0;
    for j := 0 to n do available [j] := false;
    for k := 1 to n_cycle do begin
       hhmm := round (cyclebuffer [k, 2]); // 0000..2359
       j := (60 * (hhmm div 100) + hhmm mod 100) div cycleminutes; // 0..1439
       available [j] := true;
       for i := 1 to 20 do table [i, j] := cyclebuffer [k, 8 + i]; // + & -
    end;
    available [n] := available [n - 1];
    for i := 1 to 20 do begin
       table [i, n] := table [i, n - 1];
       for j := n - 1 downto 1 do // couple smoothing
          if available [j] and available [j - 1] then
          table [i, j] := (table [i, j - 1] + table [i, j]) / 2;
    end;
    for i := 1 to 20 do for j := 0 to n do
       if table [i, j] < 0 then table [i, j] := 0;
    if diatables_available then
       savetable ('diatables\d' + datetext + '.xl');
    if extra_available then
       savetable (extrapath + '\d' + datetext + '.xl');
    win (0);
  end; // of savediagramtables

procedure check_key;
   var x : integer;
   begin
   x := 0;
   if keypressed then begin
      while keypressed do begin
         x := ord (upcase (readkey));
         if x = 0 then readkey;
         if x = ord ('X') then begin
            start_timer;
            repeat until keypressed or (timer_milliseconds > 3000);
            if keypressed then x := 909 + ord (upcase (readkey));
         end;
      end;
      case x of
              999 : run := false;
        ord ('M') : internal_save := not internal_save;
        ord ('E') : external_save := not external_save;
        ord ('D') : diatable_save := not diatable_save;
        ord ('S') : scan_save := not scan_save;
           48..57 : mark := x - 48;
      end;
      if extrapath = '***' then external_save := false;
      show_keys;
      while keypressed do readkey;
   end;
   if mark = 9 then extern_A := 1 else extern_A := 0;
   end;

procedure make_fractions;
   // in: n_dia, dia_decade, lowest_dia
   // n_mob, mob_decade, lowest_mob
   //out: dia_lo [1..n_dia], dia_ref [1..n_dia], dia_hi [1..n_dia],
   // mob_ref [1..n_mob]
   var i : integer;
   begin
   for i := 0 to 35 do ln_grid [i] := ((i - 25) / 16) * ln (10);
   for i := 0 to 35 do mob_grid [i] := exp (ln_grid [i]);
   for i := 1 to 35 do central_mob [i] := sqrt (mob_grid [i -1] * mob_grid [i]);
   for i := 0 to n_dia do begin
      dia_lo [i] := lowest_dia * exp (((i - 1) / dia_decade) * ln (10));
      dia_ref [i] := lowest_dia * exp (((i - 0.5) / dia_decade)* ln (10));
      dia_hi [i] := lowest_dia * exp ((i / dia_decade)* ln (10));
   end;
   for i := 1 to n_mob do
      mob_ref [i] := lowest_mob * exp (((i - 0.5) / mob_decade)* ln (10));
   end;

procedure make_inverter;
   var i, j : integer;
       c, s : real;
   begin
   for i := 1 to 35 do begin
      c := central_mob [i] / z_limit; if c > 1 then c := 1;
      s := 0;
      for j := 1 to 35 do begin
         case j - i of
            -2 : inverter [i, j] := c * c_inv_n2;
            -1 : inverter [i, j] := c * c_inv_n1;
             0 : inverter [i, j] := 1;
             1 : inverter [i, j] := c * c_inv_p1;
             2 : inverter [i, j] := c * c_inv_p2;
         else inverter [i, j] := 0
         end;
         s := s + inverter [i, j];
      end;
      for j := 1 to 35 do inverter [i, j] := inverter [i, j] / s;
   end;
   end;

function adsorptioncorrection (mob : real) : real;
   // in pressure, celsius, standardadsorption
   // considering that mobility of real ions does not exceed 2
   // and upper fractions are result of instrumental spread
   var z : real;
   begin
   if mob < 2 then z := mob else z := 2;
      adsorptioncorrection := exp (standardadsorption *
      exp ((7/18) * ln (1 + celsius / 273) +
            (1/6) * ln (pressure / 1013) + (2/3) * ln (Z)));
   end;

procedure save_scan;
   // all the scan results
   var s : string;
       i : integer;
       f : text;
     old : boolean;
   begin
   show_info ('saving of scan details');
   datetimetostring (s, 'yymmdd', scanline [0]);
   s := 'scandetails\' + '_S1A' + s + '.' + extension;
   assignfile (f, s);
   old := fileexists (s);
   {$I-} if old then append (f) else rewrite (f); {$I+}
   if IOresult <> 0 then begin
      sleep (3000);
      show_info ('failure, no success!');
      sleep (3000);
      exit;
   end;
   if not old then begin // write 3 headerlines
      writeln (f, calibrationheader);
      writeln (f, calibrationstring);
      write (f, 'hhmmss'#09'reg'#09'T'#09'RH'#09'p');
      for i := 1 to 35 do write (f, #09, 0.9306 * mob_grid [i]:5:3);
      for i := 1 to 35 do write (f, #09, -0.9306 * mob_grid [i]:5:3);
      writeln (f, #09'supply'#09'filt+'#09'filt-'#09'batt+'#09'batt-'#09'bias+',
                  #09'bias-'#09'wait'#09'tau'#09'asym'#09'over'#09'regime');
   end;
   for i := 1 to 86 do write (f, scanmult [i] * scanline [i]:0:0, #9);
   writeln (f, scanline [87]:0:0);
   closefile (f);
   end;

procedure scan; // regime 0 = gate closed, 1 = open
   // in: standardsupplyvolt, voltagefactor
   // timeout, delaytime
   // out: scanline;
   Const limit = 9999; // how many measurements can be hold
   var i, k,
       n_measurements : integer; // ADC records stored during one scan
       electrometer_pos : array [1..limit] of integer; // ADC
       electrometer_neg : array [1..limit] of integer; // ADC
       voltage, // average of + and - voltages
       deviation, // + voltage from the average
       millisecond : array [1..limit] of real;
       distribution_pos,
       distribution_neg,
       fractiontime : array [0..35] of real;
       overloads_pos, overloads_neg : integer;
       asymmetry : real;
       ADC2, ADC3, lastvoltage : integer; // for physical scan
       // variables fo extract fractions:
       a, n,
       min_fr_n,
       min_fr_i : integer;
       min0, max0,
       min1, max1,
       x, s0, s1, cv : real;
       s, si, sn : string;
       xi : array [0..35] of integer; // fraction voltages : ADC
   begin
   if number_of_scan mod zeroperiod = 0 then regime := 0 else regime := 1;
   // first scan in measurement will done at regime = 0
   number_of_scan := number_of_scan + 1; // this will increase until the exit
   if noiseregime then gate_pos := 1 else gate_pos := 1 - regime;
   gate_neg := gate_pos;
  // physical scan
      overloads_pos := 0;
      overloads_neg := 0;
      meteo;
      lastvoltage := round (voltagefactor * (supplyvolt / standardsupplyvolt) *
              exp (-delaytime / tau) / mob_grid [35]);
      show_info ('charging HV capacitors');
      HV := 1; set_controls (chargingtime);
      HV := 0; set_controls (0);
      show_info ('scanning mob&dia distributions');
      start_timer;
      n_measurements := 0;
      repeat // until the voltage is low
         n_measurements := n_measurements + 1;
         electrometer_pos [n_measurements] := ADC (0);
         ADC2 := ADC (2) - volt0pos; // voltage pos
         millisecond [n_measurements] := round (timer_milliseconds);
         ADC3 := ADC (3) - volt0neg; // voltage neg
         electrometer_neg [n_measurements] := -ADC (1); // inverted to +
         voltage [n_measurements] := (ADC2 - ADC3) / 2; // ADC2 > 0 and ADC3 < 0
         deviation [n_measurements] := (ADC2 + ADC3) / 2;
         if abs (electrometer_pos [n_measurements]) > 32766 then begin
            overloads_pos := overloads_pos + 1;
            textcolor (red); show_info ('+ electrometer overload');
         end;
         if abs (electrometer_neg [n_measurements]) > 32766 then begin
            overloads_neg := overloads_neg + 1;
            textcolor (blue); show_info ('- electrometer overload');
         end;
      until (voltage [n_measurements] < lastvoltage) or
            (millisecond [n_measurements] > timeout);
      if millisecond [n_measurements] > timeout
      then begin failurepause (2 * failureminutes, 'TIMEOUT'); exit end;
      if overloads_pos > maxoverload
      then begin failurepause (failureminutes, '+ ELECTROMETER OVERLOAD');
           exit end;
      if overloads_neg > maxoverload
      then begin failurepause (failureminutes, '- ELECTROMETER OVERLOAD');
           exit end;
     // relaxation time and asymmetry
      i := n_measurements div 4;
      tau := (millisecond [3 * i] - millisecond [i]) /
               ln (voltage [i] / voltage [3 * i]);
      asymmetry := 1000 * deviation [2 * i] / voltage [2 * i]; // halftime
      if abs (asymmetry) > maxasymmetry
      then begin failurepause (failureminutes, 'LARGE ASYMMETRY'); exit end;
      voltage [n_measurements] := 0; // to avoid runout during processing
      check_power;
     // end of physical_scan, results:
      // n_measurements, millisecond, electrometer, voltage, deviation,
      // overloads_pos, overloads_neg, supplyvolt, meteo, tau, asymmetry
     // extract fine fractions in ADC units
      show_info ('processing scan records');
      cv := voltagefactor * (supplyvolt / standardsupplyvolt) *
            exp (-delaytime / tau); // includes delay effect
            // to get Z corresponding to V we should wait until
            // voltage reaches V * exp (-delaytime / tau)
      for i := 0 to 35 do xi [i]:= round (cv / mob_grid [i]); // ADC borders
      min_fr_n := 999; // lowest number of measurements in a fraction
      min_fr_i := 0; // its location
      a := 1;
      while voltage [a] > xi [0] do a := a + 1;
      fractiontime [0] := millisecond [a];
      for i := 1 to 35 do begin // fine fractions
         n := 0; // number of measurements in a fraction
         s0 := 0; s1 := 0; // sum of measurements in a fraction
         min0 := 99999; max0 := -99999; // in a fraction
         min1 := 99999; max1 := -99999; // in a fraction
         while voltage [a] > xi [i] do begin
            a := a + 1;
            n := n + 1;
            x := electrometer_pos [a];
            s0 := s0 + x;
            if x < min0 then min0 := x;
            if x > max0 then max0 := x;
            x := electrometer_neg [a];
            s1 := s1 + x;
            if x < min1 then min1 := x;
            if x > max1 then max1 := x;
         end;
         fractiontime [i] := millisecond [a];
         if n < 1 then distribution_pos [i] := -999999
         else if n < 5 then distribution_pos [i] := s0 / n
         else distribution_pos [i] := (s0 - min0 - max0) / (n - 2);
         if n < 1 then distribution_neg [i] := -999999
         else if n < 5 then distribution_neg [i] := s1 / n
         else distribution_neg [i] := (s1 - min1 - max1) / (n - 2);
         if n < min_fr_n then begin min_fr_i := i; min_fr_n := n end;
      end; // of i
      if min_fr_n < 3 then begin
         str (min_fr_i, si); str (min_fr_n, sn);
         failurepause (failureminutes, 'SHORT FRACTION ' + si + ' n = ' + sn);
         exit;
      end;
  // end of extract fine fractions (distribution_pos/neg) in ADC units
  // compile scanline
   scanline [0] := wintertime - 1 / (simufactor * 8640); // 8640*10 s in a day
      // considering the 10 second shift from the scan center expressed in days
   datetimetostring (s, 'hhnnss', scanline [0]);
   val (s, scanline [1], k);
   scanline [2] := regime;
   scanline [3] := celsius;
   scanline [4] := humidity;
   scanline [5] := pressure;
   for i := 6 to 40 do scanline [i] := distribution_pos [i - 5];
   for i := 41 to 75 do scanline [i] := distribution_neg [i - 40];
   scanline [76] := supplyvolt;
   scanline [77] := filtervolt_pos;
   scanline [78] := filtervolt_neg;
   scanline [79] := batteryvolt_pos;
   scanline [80] := batteryvolt_neg;
   scanline [81] := bias_pos;
   scanline [82] := bias_neg;
   scanline [83] := 100 * fractiontime [1] /
                         (fractiontime [2] - fractiontime [1]);
   scanline [84] := tau;
   scanline [85] := asymmetry;
   scanline [86] := 100 * overloads_pos + overloads_neg;
   scanline [87] := mark + 100 * extracorrect; // regime
   if externalmeteo then scanline [87] := scanline [87] + 10;
   if noiseregime then scanline [87] := scanline [87] + 1000;
   if simulator then scanline [87] := scanline [87] + 10000;
   if clusterregime = 1 then scanline [87] := scanline [87] + 100000;
   check_key;
   show_scan;
   scanpoint := (scanpoint + 1) mod scanmax;
   scanbuffer [scanpoint] := scanline;
   if directoryexists ('scandetails') and scan_save then save_scan;
   end; // of scan

function interquartile_mean (var x : vector) : real;
   // n = x [0] = number of entries in the sample
   // x [1..n] = sample
   var i, n, k, trim, ix : integer;
           min, max, sum : real;
                included : array [1..vectorlength] of boolean;
   begin
   n := round (x [0]); ix := 0;
   for i := 1 to n do included [i] := true; // to include
   for trim := 1 to (n + 1) div 4 do begin // trim sample
      // nothing for n = 1..2, once for 3..6, twice for 7..10 etc
      // interquartiles: 1..2, 1..4, 3..6 etc
      min := 1e33;
      for i := 1 to n do if included [i] and (x [i] < min)
         then begin ix := i; min := x [i] end;
      included [ix] := false;
      max := -1e33;
      for i := 1 to n do if included [i] and (x [i] > max)
         then begin ix := i; max := x [i] end;
      included [ix] := false;
   end;
   sum := 0; k := 0;
   for i := 1 to n do if included [i] then begin
      sum := sum + x [i];
      k := k + 1;
   end;
   result := sum / k;
   end;

procedure meandistributions (j1, j2, // range of parameters
                             i1, i2, // repeated measurements
                                ion : integer; // ion = 0 zero = 1
                            var ave : vector);
   var i, j, k, n : integer;
                x : vector;
   begin // pick data from buffer and compute interquartile mean
   for j := j1 to j2 do begin // all values of the parameter
      n := 0;
      for i := i1 to i2 do begin
         k := i mod scanmax;
         if scanbuffer [k, 2] = ion then begin
            n := n + 1; x [n] := scanbuffer [k, j]
         end;
      end;
      x [0] := n;
      ave [j] := interquartile_mean (x);
   end;
   end;

procedure correct_pulses (var y : vector);
   var j : integer;
       p, q : real;
   begin
   for j := 6 to 40 do begin
      p := y [j]; // pos
      q := y [j + 35]; // neg index := 41 to 75
      if (p < 0) and ((p + q) >= 0) then begin
         y [j] := 0;
         y [j + 35] := p + q;
      end;
      if (q < 0) and ((p + q) >= 0) then begin
         y [j + 35] := 0;
         y [j] := p + q;
      end;
   end;
   end;

procedure correct_transfer (var y : vector);
   var i, j : integer;
          p : real;
   begin
   for i := 1 to 35 do begin
      p := 0;
      for j := 1 to 35 do p := p + inverter [i, j] * y [j + 5]; // pos
      y [i + 5] := p;
      p := 0;
      for j := 1 to 35 do p := p + inverter [i, j] * y [j + 40]; // pos
      y [i + 40] := p;
   end;
   end;

procedure estimate_noise (x : vector; var pos, neg : real);
   // as scattering along zero vector x
   var i : integer;
      sum : real;
   begin
   sum := 0; // positive ions
   for i := 9 to 38 do sum := sum +
   sqr (6 * x [i] - 4 * x [i - 1] - 4 * x [i + 1] + x [i - 2] + x [i + 2]);
   pos := sqrt (sum / 1080); // 1080 = 30 * sqr (6)
   sum := 0; // negative ions
   for i := 44 to 73 do sum := sum +
   sqr (6 * x [i] - 4 * x [i - 1] - 4 * x [i + 1] + x [i - 2] + x [i + 2]);
   neg := sqrt (sum / 1080); // 1080 = 30 * sqr (6)
   end;

procedure process_cycle;
   var i, j, k,
       a1, a2, b1, b2,
          left, right : integer;
           correction,
       sd_pos, sd_neg,
       cf_pos, cf_neg,
     sum, z, dln_grid : real;
                    s : string;
                 y, x : vector;
           ln_diamob : array [0..n_dia] of real;
   begin
   show_info ('processing cycle data');
   // lastpoint is last scanpoint in a period,
   // next period begins with (lastpoint + 1) mod scanmax
   // lastpoints / next to lastpoint [2]
   // -2 -1 0 1 2 V 3
   // ---------------------=========----------------------- scanbuffer
   // | zero | zero | ions | zero | zero |
  // ion period is between next to lastpoint [0] and lastpoint [1]
   a1 := (lastpoint [0] + 1) mod scanmax;
   a2 := lastpoint [1]; if a2 < a1 then a2 := a2 + scanmax;
  // zero is between next to lastpoint [-2] and lastpoint [3]
   b1 := (lastpoint [-2] + 1) mod scanmax;
   b2 := lastpoint [3]; if b2 < b1 then b2 := b2 + scanmax;
   // NB: (i + scanmax) mod scanmax = i mod scanmax
  // calculate plain averages for scan results excl distributions
   for j := 0 to 86 do if not (j in [6..75]) then begin // 0..5 & 76..86
      sum := 0;
      for i := a1 to a2 do sum := sum + scanbuffer [i mod scanmax, j];
      if j = 86 then y [j] := sum // overloads
      else y [j] := sum / (1 + a2 - a1); // variables 1.. 9
   end;
  // fill cycleline with calculated averages
   cycleline [0] := y [0]; // windows time
   datetimetostring (s, 'yymmddhhnn', y [0] + 1 / 5760);
   // 1/4 min shift avoids HHMM fluctuations when y [0] is close to full minute
   val (copy (s, 1, 6), cycleline [1], k); // YYMMDD
   val (copy (s, 7, 4), cycleline [2], k); // HHMM
   cycleline [3] := dayoftheyear (y [0]) + secondoftheday (y [0]) / 86400;
   cycleline [4] := y [3]; // T
   cycleline [5] := y [4]; // RH
   cycleline [6] := y [5]; // p
   for i := 61 to 70 do cycleline [i] := y [i + 15];
   cycleline [77] := 100 * y [86] + scan_in_cycle; // overloads
   cycleline [78] := scanbuffer [a2 mod scanmax, 87]; // last regime
  // cyclelines 0..6 and 61..70, 77..78 are filled
  // calculate average fine distribution y for ions and x for zero
   meandistributions (6, 75, a1, a2, 1, y); // ions
   meandistributions (6, 75, b1, b2, 0, x); // zero
   for j := 6 to 75 do y [j] := y [j] - x [j]; // subtract zero
   if extracorrect mod 2 = 1 then correct_pulses (y);
   if extracorrect > 1 then correct_transfer (y);
  // convert y from ADC to cm-3 incl filter- and adsorptioncorrection
   cf_pos := concentrationfactor_pos / (y [76] / standardsupplyvolt);
   cf_neg := concentrationfactor_neg / (y [76] / standardsupplyvolt);
  // NB: concentrationfactors are presented for dn/dlogZ
   for i := 1 to 35 do begin // fine fractions 6..75
      z := central_mob [i]; if z > max_mobility then z := max_mobility;
      correction := (480 / (batteryvolt_pos + batteryvolt_neg))*
                     filtermobility / z; // collector and gate loss
      if correction < 1 then correction := 1;
      correction := correction * adsorptioncorrection (z);
      y [ 5 + i] := cf_pos * correction * y [ 5 + i];
      y [40 + i] := cf_neg * correction * y [40 + i];
   end;
   estimate_noise (x, sd_pos, sd_neg);
   cycleline [7] := cf_pos * sd_pos;
   cycleline [8] := cf_neg * sd_neg;
  // cyclelines 9..60 and 71..76 are still not filled
  // compile integral parameters: cycleline [71..76] (central_mob [20] = 0.487)
  // NB: y presents dn/dlogZ 16 points per decade
   sum := 0; for i := 7 to 25 do sum := sum + y [i]; // fractions 2..20 +
   cycleline [71] := sum / 16; // N+
   sum := 0; for i := 42 to 60 do sum := sum + y [i]; // fractions 2..20 -
   cycleline [72] := sum / 16; // N-
   sum := 0; for i := 26 to 40 do sum := sum + y [i]; // fractions 21..35 +
   cycleline [73] := sum / 16; // n+
   sum := 0; for i := 61 to 75 do sum := sum + y [i]; // fractions 21..35 -
   cycleline [74] := sum / 16; // n-
   sum := 0;
   for i := 26 to 40 do begin
      z := central_mob [i - 5]; if z > max_mobility then z := max_mobility;
      sum := sum + z * y [i]; // Zn+
   end;
   if cycleline [73] < 10 then cycleline [75] := 0
   else cycleline [75] := sum / (16 * cycleline [73]); // Z+
   sum := 0;
   for i := 61 to 75 do begin
      z := central_mob [i - 40]; if z > max_mobility then z := max_mobility;
      sum := sum + z * y [i]; // Zn-
   end;
   if cycleline [74] < 10 then cycleline [76] := 0
   else cycleline [76] := sum / (16 * cycleline [74]); // Z-
  // compile mobility distribution into cycleline [29..60]
   if clusterregime = 0 then begin // full range
      for i := 29 to 44 do // positive y [7&8] ==> cycleline [29] etc.
      cycleline [i] := (y [2 * i - 51] + y [2 * i - 50]) / 2;
      for i := 45 to 60 do // negative y [42&43] ==> cycleline [45] etc.
      cycleline [i] := (y [2 * i - 48] + y [2 * i - 47]) / 2;
   end
   else begin // clusterregime
      for i := 29 to 44 do // positive y [25] ==> cycleline [29] etc.
      cycleline [i] := y [i - 4];
      for i := 45 to 60 do // negative y [60] ==> cycleline [45] etc.
      cycleline [i] := y [i + 15];
   end;
  // compile diameter distribution into cycleline [9..28]
   ln_diamob [0] := ln_grid [34];
   for i := 1 to n_dia do ln_diamob [i] :=
            ln (mobility_a (scanline [5], scanline [3], dia_hi [i]));
   // Boundaries of size fractions on scale of ln (mobility):
   // dia increases and diamob decreases with i, borders of diafraction [i]
   // are dia_hi [i - 1]..dia_hi [i] or diamob [i]..diamob [i - 1]
   //
   // ln_grid ln_grid ===> ln_grid ln_grid
   // [left-1] [left] [right-1] [right]
   // | finefraction [left] | | finefraction [right] |
   // ----------------===================================-----------------
   // | diafraction [i] |
   // ln_diamob ln_diamob
   // [i] [i - 1]
   // y [i] = 16 * concentration of fine mob-fraction
   // diadistribution = 8 * concentration of dia-fraction
   // it follows diadistribution = sum (y [i]) / 2
   dln_grid := ln (10) / 16;
   for i := 1 to n_dia do begin
      left := 0;
      while ln_grid [left] < ln_diamob [i] do left := left + 1;
      right := 0;
      while ln_grid [right] < ln_diamob [i - 1] do right := right + 1;
      // left and right are grid indices, for y index add 5 or 40
      // calculate sum = diafraction concentration for positive
      if right = left then sum :=
         y [left + 5] * (ln_diamob [i - 1] - ln_diamob [i]) / dln_grid
      else sum :=
         y [left + 5] * (ln_grid [left] - ln_diamob [i]) / dln_grid
       + y [right + 5] * (ln_diamob [i - 1] - ln_grid [right - 1]) / dln_grid;
      if (right - left) > 1 then
         for j := left + 6 to right + 4 do sum := sum + y [j];
      cycleline [i + 8] := sum / 2; // diadistribution +
      // calculate sum = diafraction concentration for negative
      if right = left then sum :=
         y [left + 40] * (ln_diamob [i - 1] - ln_diamob [i]) / dln_grid
      else sum :=
         y [left + 40] * (ln_grid [left] - ln_diamob [i]) / dln_grid
       + y [right + 40] * (ln_diamob [i - 1] - ln_grid [right - 1]) / dln_grid;
      if (right - left) > 1 then
         for j := left + 41 to right + 39 do sum := sum + y [j];
      cycleline [i + 18] := sum / 2; // diadistribution -
   end;
   n_cycle := n_cycle + 1;
   cyclebuffer [n_cycle] := cycleline;
   show_cycle;
   check_media;
   if internal_save and days_available then save_cycle;
   end; // of process_cycle

function newperiod (minutes : integer) : boolean;
   begin newperiod := (minuteoftheday (scanbuffer [(scanpoint + scanmax -1)
                                                  mod scanmax, 0]) div minutes)
   <> (minuteoftheday (scanbuffer [scanpoint, 0]) div minutes);
   end;

procedure diurnalsave;
   begin
   check_media;
   if internal_save and months_available then save_day ('months');
   if external_save and extra_available then save_day (extrapath);
   if diatable_save and (diatables_available or extra_available)
      then save_diagramtables;
   n_cycle := 0;
   end;

procedure measurement;
var i : integer;
    s : string;
begin
   init_console (15, 0, 85, 0, 50);
   for i := 1 to 86 do scanmult [i] := 1;
   for i := 4 to 76 do scanmult [i] := 10;
   scanmult [3] := 100;
   scanmult [81] := 100;
   scanmult [82] := 100;
   for i := 1 to 78 do cycledec [i] := 0;
   cycledec [3] := 4; // day of year
   cycledec [4] := 2; // temperature
   cycledec [5] := 1; // RH
   cycledec [6] := 2; // pressure
   cycledec [61] := 1; // supply
   cycledec [66] := 2; cycledec [67] := 2; // bias
   cycledec [75] := 2; cycledec [76] := 2; // Z
   internal_save := (initialcontrols [1] = '1');
   external_save := (initialcontrols [2] = '1');
   diatable_save := (initialcontrols [3] = '1');
   scan_save := (initialcontrols [4] = '1');
   show_frame;
   show_info ('Warming up about 30 s');
   startposition;
   fan := 1; HV := 1;
   if simulator then set_controls (1000) else set_controls (fandelay);
   HV := 0;
   if simulator then set_controls (1000) else set_controls (20000);
   show_info ('Checking voltages and meteo');
   meteo;
   repeat voltages (s, 0.5);
      if s <> '' then failurepause (failureminutes, s);
   until s = '';
   check_media;
   run := true;
   mark := 0;
   number_of_scan := 0;
   scan_in_cycle := 0;
   n_cycle := 0;
   scanpoint := 0;
   for i := -2 to 3 do lastpoint [i] := -1;
   repeat scan;
      scan_in_cycle := scan_in_cycle + 1;
      if newperiod (voltageminutes) then repeat voltages (s, 0.2);
         if s <> '' then failurepause (failureminutes, s);
      until s = '';
      if newperiod (cycleminutes) then begin
         for i := -2 to 2 do lastpoint [i] := lastpoint [i + 1];
         lastpoint [3] := (scanpoint + scanmax - 1) mod scanmax;
         if lastpoint [-2] >= 0 then begin
            process_cycle;
            scan_in_cycle := 0;
            if trunc (scanbuffer [lastpoint [2], 0]) <>
               trunc (scanbuffer [lastpoint [1], 0])
            then diurnalsave;
         end;
      end;
   until not run;
   startposition;
   if lastpoint [-2] >= 0 then diurnalsave;
   win (0);
   clrscr1;
   while keypressed do readkey;
end; // of measurement

procedure show_welcome;
   var s : string;
   begin
   textbackground (blue);
   textcolor (yellow);
   clrscr1;
   writeln;
   writeln (' Welcome to SIGMA control and logging program SIGMA1A.exe');
   writeln;
   writeln (' Requirements for the computer:');
   writeln (' running under Windows XP,');
   writeln (' no time waisting background processes,');
   writeln (' computer clock adjusted to the proper time,');
   writeln (' free space on disk for writing the results,');
   writeln (' SIGMA connected to the computer USB port,');
   writeln;
   write (' Local winter time (press ENTER to refresh): ');
   datetimetostring (s, 'yy-mm-dd hh:nn:ss', wintertime); writeln (s);
   writeln (' if incorrect then correct computer time and/or time zone!');
   writeln;
   writeln (' Keywords and corresponding tasks are:');
   writeln (' T - Test operations,');
   writeln (' M - Measure charged particles and clusters,');
   writeln (' Noise - Noise test ',
                    '(measurement with permanenetly closed inlet gate),');
   if developer then writeln (' Z - user-programmable Z_test,');
   writeln (' X - eXit the program.');
   write (' Please write a keyword and press ENTER! ');
   end;

 procedure Z_test; // used when debugging
   // var m, k, p, i : integer;
   begin
   init_console (30, 0, 85, 0, 50);
   clrscr1;
   writeln; writeln (' Done, press ENTER!');
   readln;
   end;

//===========================================================================

VAR taskselector : string;
               s : string;
               c : char;
               v : real;
            i, j : integer;

//* MAIN
BEGIN
try
c_inv_n2 := 0; // five default values for the inverter
c_inv_n1 := 0;
c_inv_p1 := 0;
c_inv_p2 := 0;
z_limit := 1;
buffersize.x := 85; buffersize.y := 50;
init_console (30, 0, 85, 0, 50); gotoxy (2, 2);
timer_start := now;
read_simulator;
read_cal;
read_ini;
if extracorrect > 1 then begin
   if fileexists ('SIGMA1A_inverter.txt') then read_inverter
   else failurestop ('extracorrect > 1, but SIGMA1A_inverter.txt not found');
end;
init_timer;
if simulator then randomize else init_USB1608;
make_fractions;
make_inverter;
make_fileheaders;
startposition;
writeln;
repeat
   v := c_supplyvolt * adc (4);
   if v < minsupplyvolt then begin
      writeln (' Voltage = ', v:0:1, ', please turn SIGMA power on!');
      pause (5000);
   end;
until v > minsupplyvolt;
check_media;
writeln; writeln (' Checking voltages and meteo ... (about 20 s)');
pause (15000);
meteo;
repeat
   writeln (' ...please wait...');
   voltages (s, 1);
   if s <> '' then begin
      writeln;
      writeln (' Voltage out of range, check values:');
      writeln (' ', s);
      writeln (' SIGMA should be repaired, test regime only is available now.');
      write (' Press T to continue or another key to escape: ');
      repeat c := readkey until not keypressed;
      c := upcase (c); writeln (c);
      if c = 'T' then test;
      startposition;
      halt;
   end;
   startposition;
   show_welcome;
   readln (taskselector);
   if taskselector <> '' then taskselector [1] := upcase (taskselector [1]);
   if taskselector = 'T' then test;
   if taskselector = 'M' then begin noiseregime := false; measurement end;
   if taskselector = 'Noise' then begin noiseregime := true; measurement end;
   if (taskselector = 'Z') and developer then Z_test;
until taskselector = 'X';
startposition;
except end;

{=======================================================================
SIGMA1A.CAL 20110214
 Includes calibration coefficients for SIGMA No. 2 with orifice 4
 Any line beginning with a space is a comment.
 An assignment should be written without spaces.
 A space after the assignment starts a comment until the end of the line.
 Initial text of SIGMA1A.CAL is attached to the end of SIGMA1A.DPR
volt_factor=900 for voltage-mobility conversion
conc_factor_pos=6.1 for ADC-dn/dlogZ conversion
conc_factor_neg=6.3 for ADC-dn/dlogZ conversion
standardadsorption=0.049 for Z = 1 cm2V-1cm-1, 0 C, 1013 mb
filtermobility=0.03 cm2V-1cm-1, effective value for corrections
c_supplyvolt=0.002185 supplyvoltage / ADC_counts
c_filtervolt=-0.089 filter voltage / ADC_counts
c_batteryvolt=0.0093 electrometer battery voltage / ADC_counts
c_bias=0.003052 electrometer inlet mV at 1 ADC, 0.1526 / 50
c_pressurea=0.03368 pressure (mb) = c_pressurea * ADCcounts + c_pressureb
c_pressureb=120
c_temperaturea=0.0108 Celsius = c_temperaturea * ADCcounts + c_temperatureb
c_temperatureb=-291 factory value is -273
c_humiditya=0.007 humidity (%) = c_humiditya * ADCcounts + c_humidityb
c_humidityb=-48 required correction RH = RH (1.0546 - 0.0216 T:C)
delaytime=380 ms, incl. airflow + electrometer - HV signal
chargingtime=1500 ms
timeout=30000 ms
========================================================================
SIGMA1A.INI 20110214
 Includes custom controls.
 Any line beginning with a space is a comment.
 An assignment should be written without spaces.
 A space after the assignment starts a comment until the end of the line.
 Initial text of SIGMA1A.INI is attached into the end of SIGMA1A.DPR
cboard=0 defined during the USB1608 initialization
clusterregime=0 0 = dn/dlogZ full range 8 fr/dec
                    1 = dn/dlogZ only clusters 16 fr/dec
extracorrect=1 0 = no, 1 = dust pulses, 2 = transfer, 3 = both
cycleminutes=5 1, 2, 3, 4, 5, 6, or 10, standard value is 5
timezone=2 for time presentation
showfractions=1 0 = show dn/dlog, 1 - show fraction n (save still dn/dlog)
extension=xl extension of the output file name, usually txt or xl
extrapath=*** for extracopy of midnight tables, *** means no extracopy
controls=1011 four symbols 0 or 1 setting initial values of saving
                    controls: internal, external, diatable, scandetails
 extrapath can be written with or without final \
 a sample: extrapath=J:\SIGMA

========================================================================}

END.