[fpc-pascal] best? safest? fastest?
waldo kitty
wkitty42 at windstream.net
Wed Aug 6 02:08:38 CEST 2014
i suspect this is going to be like the long-standing joke of
cheap, fast, stable: choose two
over the years, i've seen two schools of code for dealing with dates... years,
specifically... one school is string based and the other is math based... both
have their faults and pluses...
eg: string based fault : prepend '19' to single digit year value
math based fault : 2003 - 1900 = 103 (3 is intended result)
so what i have is satellite tracking two line element files... these files
contain an epoch for the numbers contained in the TLE... the epoch is laid out as
YYdoy.frac-doy == 14211.03356282
where
YY : 1 or 2 digit year without century ('62' or ' 3' or '05')
doy : day of year, 1, 2 or 3 digits, jan 1 is day 0 (' 0' or ' 15' or '020')
frac-doy : 8 digit fractional day of the year
what i'm needing to do is to choose a route to make the year portion four digits
so that historical TLEs can be used... the program makes a comparison of the
epoch to determine which of two is newer... the newer one is placed into the
in-memory database for later usage... working with historical TLEs from 1957 to
1999 mandates four digit years be used or historical TLEs will override TLEs
from 2000 to 2056 since 00 thru 56 are less than 57 (1957 first launch)...
previous to any processing, the epoch taken from the TLE in the file is
processed so that leading blanks in the epoch are replaced with zeros... today's
epochs don't really need this as creation and processing methods have evloved
over the years... this is done as a precaution for those times when processing
historical TLEs which have not been converted to having leading zeros instead of
blanks (aka spaces)...
we've launched over 40000 objects and there are possibly hundreds or thousands
of TLEs for each one... at least one site is known to have over 9 million TLEs
in their database... these routines are executed for each and every TLE loaded
as we fill our in-memory database...
finally (yeah i know, but i thought the history and methodology was important)
my questions, as the subject alludes to, are which of the following methods
would you choose? why? would you choose another method? why?
all input is greatly welcomed! as always, thanks for your time and attention! :)
{******************************************************************************}
{******************************************************************************}
// make sure we have the full four digit year for the epoch!
// eg: 14211.03356282 becomes 2014211.03356282 which is 2014-07-30 00:48:19.827
// method 1 (string manipulation)
if (Integer_Value(MyEpochStr,1,2) < 57) then // first
launch in 1957
MyEpochStr := FloatToStrF('20'+MyEpochStr,ffFixed,16,8) // value <
57 so year is 21st century (2000 thru 2056)
else
MyEpochStr := FloatToStrF('19'+MyEpochStr,ffFixed,16,8); // value
is >= 57 so year is 20th century (1957 thru 1999)
MyEpoch := Real_Value(MyEpochStr,1,Length(MyEpochStr)); // convert
to real for mathmatical comparison
{******************************************************************************}
// method 2 (combination string and math??)
if (Integer_Value(MyEpochStr,1,2) < 57) then // first
launch in 1957
MyEpochStr := FloatToStrF(Real_Value(MyEpochStr,1,Length(MyEpochStr)) +
2000000,ffFixed,16,8)
else
MyEpochStr := FloatToStrF(Real_Value(MyEpochStr,1,Length(MyEpochStr)) +
1900000,ffFixed,16,8);
MyEpoch := Real_Value(MyEpochStr,1,Length(MyEpochStr)); // convert
to real for mathmatical comparison
{******************************************************************************}
// method 3 (math manipulation)
MyEpoch := Real_Value(MyEpochStr,1,Length(MyEpochStr)); // convert
to real for mathmatical comparison
if (int(MyEpoch / 1000) < 57) then // first
launch in 1957
MyEpoch := (MyEpoch / 1000 + 2000) * 1000 // value <
57 so year is 21st century (2000 thru 2056)
else
MyEpoch := (MyEpoch / 1000 + 1900) * 1000; // value
is >= 57 so year is 20th century (1957 thru 1999)
MyEpochStr := FloatToStrF(MyEpoch,ffFixed,16,8); // store to
MyEpochStr for later use (16 characters!)
{******************************************************************************}
{******************************************************************************}
support routines for the above:
Function Integer_Value(buffer : string; start, length : integer) : integer;
var
MyResult : integer = 0;
answer : integer - 0;
begin
buffer := Copy(buffer,start,length);
Convert_Blanks(buffer);
if buffer = '' then
buffer := '0';
Val(buffer,answer,MyResult);
if MyResult = 0 then
Integer_Value := answer
else
Integer_Value := 0;
end; // Function Integer_Value
Function Real_Value(buffer : string; start, length : integer) : double;
var
MyResult : integer = 0;
answer : double = 0.0;
begin
buffer := Copy(buffer,start,length);
Convert_Blanks(buffer);
if buffer = '' then
buffer := '0';
Val(buffer,answer,MyResult);
if MyResult = 0 then
Real_Value := answer
else
Real_Value := 0.0;
end; // Function Real_Value
for those interested in it, here's the fractional-doy breakout routine (in
displayEpochString)...
Function getJulianDay_Year(year : integer) : double;
var
dYear : double = 0.0;
a : double = 0.0;
b : double = 0.0;
dResult : double = 0.0;
begin
dYear := year - 1;
A := Math.Floor(dYear / 100);
B := 2 - A + Math.Floor(A / 4);
dResult := Math.Floor(365.25 * dYear) + 1721422.9 + B;
getJulianDay_Year := dResult;
end;
Function getJulianDay_SatEpoch(year : integer; dSatEpochDoY : integer) : double;
var
dResult : double = 0.0;
begin
// this tidy section not needed any more with four digit years in epoch
// //Tidy up the year and put it into the correct century
// year := year mod 100;
// if (year < 57) then // first launch in 1957
// year := year + 2000
// else
// year := year + 1900;
dResult := getJulianDay_Year(year); // julian day of first of
year
dResult := dResult + dSatEpochDoY; // add in jDoY
getJulianDay_SatEpoch := dResult;
end;
Function displayEpochString(SatEpoch : double) : string;
var
test : boolean = False;
DT : TDateTime;
Y : word = 0;
Mo : word = 0;
D : word = 0;
H : word = 0;
Mi : word = 0;
S : word = 0;
MS : word = 0;
tstr : string = '';
MyEpochStr : string = '';
MyJEpoch : double = 0.0;
dWork : double = 0.0;
dWork1 : double = 0.0;
MyEpochDayFrac : double = 0.0;
MyEpochDoY : integer = 0;
MyEpochYear : integer = 0;
begin
tstr := FloatToStr(SatEpoch); // whole epoch number
if pos('.',tstr) < 1 then
tstr := tstr + '.0';
MyEpochStr := AddChar('0',Copy(tstr,1,pos('.',tstr)-1),5);
MyEpochYear := Integer_Value(MyEpochStr,1,4); // epoch year -
characters 1-4 because we adjusted to 4 digit year
MyEpochDoY := Integer_Value(MyEpochStr,5,3); // epoch DoY - characters
5-8 because we adjusted to 4 digit year
MyEpochDayFrac := Frac(Real_Value(tstr,pos('.',tstr),9)); // epoch
fractional day starts with decimal point
MyJEpoch := getJulianDay_SatEpoch(MyEpochYear, MyEpochDoY); // JD for year
and DoY only. time portion calc'd below...
test := TryJulianDateToDateTime(MyJEpoch,DT); // convert julian epoch
to TDateTime record
if test then
begin
DecodeDateTime(DT,Y,Mo,D,H,Mi,S,MS); // pull TDateTime record
apart
dWork := MyEpochDayFrac * 24; // calc the hours from
the fractional day of the epoch
H := trunc(dWork); // store whole hours
dWork1 := Frac(dWork); // get frac hours
dWork := dWork1 * 60; // calc the minutes from
the fractional hours
Mi := trunc(dWork); // store whole minutes
dWork1 := Frac(dWork); // get frac minutes
dWork := dWork1 * 60; // calc the seconds from
the fractional minutes
S := trunc(dWork); // store whole seconds
dWork1 := Frac(dWork); // get frac seconds
dWork := dWork1 * 1000; // calc the milliseconds
from the fractional seconds
MS := trunc(dWork); // store whole
milliseconds - trunc() b/c we don't care about fractional part or rounding at
this level
DT := EncodeDateTime(Y,Mo,D,H,Mi,S,MS); // convert it all back to
TDateTime record
displayEpochString:=FormatDateTime('YYYY-MM-DD hh:nn:ss.zzz',DT); //
send back a nicely formatted DateTime string
end
else
displayEpochString:='XXXX-XX-XX XX:XX:XX.XXX'; // send back all X's
because Julian to DateTime failed
end;
--
NOTE: No off-list assistance is given without prior approval.
Please *keep mailing list traffic on the list* unless
private contact is specifically requested and granted.
More information about the fpc-pascal
mailing list