[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