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.