/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StAstro.pas
ViewVC logotype

Annotation of /dao/DelphiScanner/Components/tpsystools_4.04/source/StAstro.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 52624 byte(s)
Added tpsystools component
1 torben 2671 // Upgraded to Delphi 2009: Sebastian Zierer
2    
3     (* ***** BEGIN LICENSE BLOCK *****
4     * Version: MPL 1.1
5     *
6     * The contents of this file are subject to the Mozilla Public License Version
7     * 1.1 (the "License"); you may not use this file except in compliance with
8     * the License. You may obtain a copy of the License at
9     * http://www.mozilla.org/MPL/
10     *
11     * Software distributed under the License is distributed on an "AS IS" basis,
12     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13     * for the specific language governing rights and limitations under the
14     * License.
15     *
16     * The Original Code is TurboPower SysTools
17     *
18     * The Initial Developer of the Original Code is
19     * TurboPower Software
20     *
21     * Portions created by the Initial Developer are Copyright (C) 1996-2002
22     * the Initial Developer. All Rights Reserved.
23     *
24     * Contributor(s):
25     *
26     * ***** END LICENSE BLOCK ***** *)
27    
28     {*********************************************************}
29     {* SysTools: StAstro.pas 4.04 *}
30     {*********************************************************}
31     {* SysTools: Astronomical Routines *}
32     {*********************************************************}
33    
34     {$I StDefine.inc}
35    
36     { ************************************************************** }
37     { Sources: }
38     { 1. Astronomical Algorithms, Jean Meeus, Willmann-Bell, 1991. }
39     { }
40     { 2. Planetary and Lunar Coordinates (1984-2000), U.S. Govt, }
41     { 1983. }
42     { }
43     { 3. Supplement to the American Ephemeris and Nautical Almanac,}
44     { U.S. Govt, 1964. }
45     { }
46     { 4. MPO96 source files, Brian D. Warner, 1995. }
47     { }
48     { ************************************************************** }
49    
50     unit StAstro;
51    
52     interface
53    
54     uses
55     Windows, SysUtils,
56     StConst, StBase, StDate, StStrS, StDateSt, StMath;
57    
58     type
59     TStTwilight = (ttCivil, ttNautical, ttAstronomical);
60    
61     TStRiseSetRec = record
62     ORise : TStTime;
63     OSet : TStTime;
64     end;
65    
66     TStPosRec = record
67     RA,
68     DC : Double;
69     end;
70     TStPosRecArray = array[1..3] of TStPosRec;
71    
72     TStSunXYZRec = record
73     SunX,
74     SunY,
75     SunZ,
76     RV,
77     SLong,
78     SLat : Double;
79     end;
80    
81     TStLunarRecord = record
82     T : array[0..1] of TStDateTimeRec;
83     end;
84    
85     TStPhaseRecord = packed record
86     NMDate,
87     FQDate,
88     FMDate,
89     LQDate : Double;
90     end;
91     TStPhaseArray = array[0..13] of TStPhaseRecord;
92    
93     TStDLSDateRec = record
94     Starts : TStDate;
95     Ends : TStDate;
96     end;
97    
98     TStMoonPosRec = record
99     RA,
100     DC,
101     Phase,
102     Dia,
103     Plx,
104     Elong : Double;
105     end;
106    
107    
108     const
109     radcor = 57.29577951308232; {number of degrees in a radian}
110     StdDate = 2451545.0; {Ast. Julian Date for J2000 Epoch}
111     OB2000 = 0.409092804; {J2000 obliquity of the ecliptic (radians)}
112    
113    
114     {Sun procedures/functions}
115     function AmountOfSunlight(LD : TStDate; Longitude, Latitude : Double) : TStTime;
116     {-compute the hours, min, sec of sunlight on a given date}
117     function FixedRiseSet(LD : TStDate; RA, DC, Longitude, Latitude : Double) : TStRiseSetRec;
118     {-compute the rise and set time for a fixed object, e.g., a star}
119     function SunPos(UT : TStDateTimeRec) : TStPosRec;
120     {-compute the J2000 RA/Declination of the Sun}
121     function SunPosPrim(UT : TStDateTimeRec) : TStSunXYZRec;
122     {-compute the J2000 geocentric rectangular & ecliptic coordinates of the sun}
123     function SunRiseSet(LD : TStDate; Longitude, Latitude : Double) : TStRiseSetRec;
124     {-compute the Sun rise or set time}
125     function Twilight(LD : TStDate; Longitude, Latitude : Double; TwiType : TStTwilight) : TStRiseSetRec;
126     {-compute the beginning and end of twilight (civil, nautical, or astron.)}
127    
128     {Lunar procedures/functions}
129     function LunarPhase(UT : TStDateTimeRec) : Double;
130     {-compute the phase of the moon}
131     function MoonPos(UT : TStDateTimeRec) : TStMoonPosRec;
132     {-compute the J2000 RA/Declination of the moon}
133     function MoonRiseSet(LD : TStDate; Longitude, Latitude : Double) : TStRiseSetRec;
134     {-compute the Moon rise and set time}
135     function FirstQuarter(D : TStDate) : TStLunarRecord;
136     {-compute date/time of FirstQuarter(s)}
137     function FullMoon(D : TStDate) : TStLunarRecord;
138     {-compute the date/time of FullMoon(s)}
139     function LastQuarter(D : TStDate) : TStLunarRecord;
140     {-compute the date/time of LastQuarter(s)}
141     function NewMoon(D : TStDate) : TStLunarRecord;
142     {-compute the date/time of NewMoon(s)}
143     function NextFirstQuarter(D : TStDate) : TStDateTimeRec;
144     {-compute the date/time of the next closest FirstQuarter}
145     function NextFullMoon(D : TStDate) : TStDateTimeRec;
146     {-compute the date/time of the next closest FullMoon}
147     function NextLastQuarter(D : TStDate) : TStDateTimeRec;
148     {-compute the date/time of the next closest LastQuarter}
149     function NextNewMoon(D : TStDate) : TStDateTimeRec;
150     {-compute the date/time of the next closest NewMoon}
151     function PrevFirstQuarter(D : TStDate) : TStDateTimeRec;
152     {-compute the date/time of the prev closest FirstQuarter}
153     function PrevFullMoon(D : TStDate) : TStDateTimeRec;
154     {-compute the date/time of the prev closest FullMoon}
155     function PrevLastQuarter(D : TStDate) : TStDateTimeRec;
156     {-compute the date/time of the prev closest LastQuarter}
157     function PrevNewMoon(D : TStDate) : TStDateTimeRec;
158     {-compute the date/time of the prev closest NewMoon}
159    
160     {Calendar procedures/functions}
161     function SiderealTime(UT : TStDateTimeRec) : Double;
162     {-compute Sidereal Time at Greenwich}
163     function Solstice(Y, Epoch : Integer; Summer : Boolean) : TStDateTimeRec;
164     {-compute the date/time of the summer or winter solstice}
165     function Equinox(Y, Epoch : Integer; Vernal : Boolean) : TStDateTimeRec;
166     {-compute the date/time of the vernal/autumnal equinox}
167     function Easter(Y, Epoch : Integer) : TStDate;
168     {-compute the date of Easter (astronmical calendar)}
169    
170     {Astronomical Julian Date Conversions}
171     function DateTimeToAJD(D : TDateTime) : Double;
172    
173     {Conversion routines}
174     function HoursMin(RA : Double) : String;
175     {-convert RA to hh:mm:ss string}
176     function DegsMin(DC : Double) : String;
177     {-convert Declination to +/-dd:mm:ss string}
178     function AJDToDateTime(D : Double) : TDateTime;
179    
180    
181     implementation
182    
183     var
184     AJDOffset : Double;
185    
186     function CheckDate(UT : TStDateTimeRec) : Boolean;
187     begin
188     with UT do begin
189     if (D < MinDate) or (D > MaxDate) or
190     (T < 0) or (T > MaxTime) then
191     Result := False
192     else
193     Result := True;
194     end;
195     end;
196    
197     function CheckYear(Y, Epoch : Integer) : Integer;
198     begin
199     if Y < 100 then begin
200     if Y >= (Epoch mod 100) then
201     Result := ((Epoch div 100) * 100) + Y
202     else
203     Result := ((Epoch div 100) * 100) + 100 + Y;
204     end else
205     Result := Y;
206     end;
207    
208     function SiderealTime(UT : TStDateTimeRec) : Double;
209     {-compute Sidereal Time at Greenwich in degrees}
210     var
211     T,
212     JD : Double;
213     begin
214     if not CheckDate(UT) then begin
215     Result := -1;
216     Exit;
217     end;
218    
219     JD := AstJulianDate(UT.D) + UT.T/86400;
220    
221     T := (JD - 2451545.0) / 36525.0;
222    
223     Result := 280.46061837
224     + 360.98564736629 * (JD - 2451545.0)
225     + 0.000387933 * sqr(T)
226     - (sqr(T) * T / 38710000);
227     Result := Frac(Result/360.0) * 360.0;
228     if Result < 0 then
229     Result := 360 + Result;
230     end;
231    
232     function SunPosPrim(UT : TStDateTimeRec) : TStSunXYZRec;
233     {-compute J2000 XYZ coordinates of the Sun}
234     var
235     JD,
236     T0,
237     A,
238     L,
239     B,
240     X,Y,Z : Double;
241    
242     begin
243     if not CheckDate(UT) then begin
244     with Result do begin
245     SunX := -99;
246     SunY := -99;
247     SunZ := -99;
248     RV := -99;
249     SLong := -99;
250     SLat := -99;
251     end;
252     Exit;
253     end;
254    
255     JD := AstJulianDate(UT.D) + UT.T/86400;
256     T0 := (JD - StdDate) / 365250;
257    
258     {solar longitude}
259     L := 175347046
260     + 3341656 * cos(4.6692568 + 6283.07585*T0)
261     + 34894 * cos(4.6261000 + 12566.1517*T0)
262     + 3497 * cos(2.7441000 + 5753.3849*T0)
263     + 3418 * cos(2.8289000 + 3.5231*T0)
264     + 3136 * cos(3.6277000 + 77713.7715*T0)
265     + 2676 * cos(4.4181000 + 7860.4194*T0)
266     + 2343 * cos(6.1352000 + 3930.2097*T0)
267     + 1324 * cos(0.7425000 + 11506.7698*T0)
268     + 1273 * cos(2.0371000 + 529.6910*T0)
269     + 1199 * cos(1.1096000 + 1577.3435*T0)
270     + 990 * cos(5.2330000 + 5884.9270*T0)
271     + 902 * cos(2.0450000 + 26.1490*T0)
272     + 857 * cos(3.5080000 + 398.149*T0)
273     + 780 * cos(1.1790000 + 5223.694*T0)
274     + 753 * cos(2.5330000 + 5507.553*T0)
275     + 505 * cos(4.5830000 + 18849.228*T0)
276     + 492 * cos(4.2050000 + 775.523*T0)
277     + 357 * cos(2.9200000 + 0.067*T0)
278     + 317 * cos(5.8490000 + 11790.626*T0)
279     + 284 * cos(1.8990000 + 796.298*T0)
280     + 271 * cos(0.3150000 + 10977.079*T0)
281     + 243 * cos(0.3450000 + 5486.778*T0)
282     + 206 * cos(4.8060000 + 2544.314*T0)
283     + 205 * cos(1.8690000 + 5573.143*T0)
284     + 202 * cos(2.4580000 + 6069.777*T0)
285     + 156 * cos(0.8330000 + 213.299*T0)
286     + 132 * cos(3.4110000 + 2942.463*T0)
287     + 126 * cos(1.0830000 + 20.775*T0)
288     + 115 * cos(0.6450000 + 0.980*T0)
289     + 103 * cos(0.6360000 + 4694.003*T0)
290     + 102 * cos(0.9760000 + 15720.839*T0)
291     + 102 * cos(4.2670000 + 7.114*T0)
292     + 99 * cos(6.2100000 + 2146.170*T0)
293     + 98 * cos(0.6800000 + 155.420*T0)
294     + 86 * cos(5.9800000 +161000.690*T0)
295     + 85 * cos(1.3000000 + 6275.960*T0)
296     + 85 * cos(3.6700000 + 71430.700*T0)
297     + 80 * cos(1.8100000 + 17260.150*T0);
298    
299     A := 628307584999.0
300     + 206059 * cos(2.678235 + 6283.07585*T0)
301     + 4303 * cos(2.635100 + 12566.1517*T0)
302     + 425 * cos(1.590000 + 3.523*T0)
303     + 119 * cos(5.796000 + 26.298*T0)
304     + 109 * cos(2.966000 + 1577.344*T0)
305     + 93 * cos(2.590000 + 18849.23*T0)
306     + 72 * cos(1.140000 + 529.69*T0)
307     + 68 * cos(1.870000 + 398.15*T0)
308     + 67 * cos(4.410000 + 5507.55*T0)
309     + 59 * cos(2.890000 + 5223.69*T0)
310     + 56 * cos(2.170000 + 155.42*T0)
311     + 45 * cos(0.400000 + 796.30*T0)
312     + 36 * cos(0.470000 + 775.52*T0)
313     + 29 * cos(2.650000 + 7.11*T0)
314     + 21 * cos(5.340000 + 0.98*T0)
315     + 19 * cos(1.850000 + 5486.78*T0)
316     + 19 * cos(4.970000 + 213.30*T0)
317     + 17 * cos(2.990000 + 6275.96*T0)
318     + 16 * cos(0.030000 + 2544.31*T0);
319     L := L + (A * T0);
320    
321     A := 8722 * cos(1.0725 + 6283.0758*T0)
322     + 991 * cos(3.1416)
323     + 295 * cos(0.437 + 12566.1520*T0)
324     + 27 * cos(0.050 + 3.52*T0)
325     + 16 * cos(5.190 + 26.30*T0)
326     + 16 * cos(3.69 + 155.42*T0)
327     + 9 * cos(0.30 + 18849.23*T0)
328     + 9 * cos(2.06 + 77713.77*T0);
329     L := L + (A * sqr(T0));
330    
331     A := 289 * cos(5.842 + 6283.076*T0)
332     + 21 * cos(6.05 + 12566.15*T0)
333     + 3 * cos(5.20 + 155.42*T0)
334     + 3 * cos(3.14);
335     L := L + (A * sqr(T0) * T0);
336     L := L / 1.0E+8;
337    
338    
339     {solar latitude}
340     B := 280 * cos(3.199 + 84334.662*T0)
341     + 102 * cos(5.422 + 5507.553*T0)
342     + 80 * cos(3.88 + 5223.69*T0)
343     + 44 * cos(3.70 + 2352.87*T0)
344     + 32 * cos(4.00 + 1577.34*T0);
345     B := B / 1.0E+8;
346    
347     A := 227778 * cos(3.413766 + 6283.07585*T0)
348     + 3806 * cos(3.3706 + 12566.1517*T0)
349     + 3620
350     + 72 * cos(3.33 + 18849.23*T0)
351     + 8 * cos(3.89 + 5507.55*T0)
352     + 8 * cos(1.79 + 5223.69*T0)
353     + 6 * cos(5.20 + 2352.87*T0);
354     B := B + (A * T0 / 1.0E+8);
355    
356     A := 9721 * cos(5.1519 + 6283.07585*T0)
357     + 233 * cos(3.1416)
358     + 134 * cos(0.644 + 12566.152*T0)
359     + 7 * cos(1.07 + 18849.23*T0);
360     B := B + (A * sqr(T0) / 1.0E+8);
361    
362     A := 276 * cos(0.595 + 6283.076*T0)
363     + 17 * cos(3.14)
364     + 4 * cos(0.12 + 12566.15*T0);
365     B := B + (A * sqr(T0) * T0 / 1.0E+8);
366    
367    
368     {solar radius vector (astronomical units)}
369     Result.RV := 100013989
370     + 1670700 * cos(3.0984635 + 6283.07585*T0)
371     + 13956 * cos(3.05525 + 12566.15170*T0)
372     + 3084 * cos(5.1985 + 77713.7715*T0)
373     + 1628 * cos(1.1739 + 5753.3849*T0)
374     + 1576 * cos(2.8649 + 7860.4194*T0)
375     + 925 * cos(5.453 + 11506.770*T0)
376     + 542 * cos(4.564 + 3930.210*T0)
377     + 472 * cos(3.661 + 5884.927*T0)
378     + 346 * cos(0.964 + 5507.553*T0)
379     + 329 * cos(5.900 + 5223.694*T0)
380     + 307 * cos(0.299 + 5573.143*T0)
381     + 243 * cos(4.273 + 11790.629*T0)
382     + 212 * cos(5.847 + 1577.344*T0)
383     + 186 * cos(5.022 + 10977.079*T0)
384     + 175 * cos(3.012 + 18849.228*T0)
385     + 110 * cos(5.055 + 5486.778*T0)
386     + 98 * cos(0.89 + 6069.78*T0)
387     + 86 * cos(5.69 + 15720.84*T0)
388     + 86 * cos(1.27 +161000.69*T0)
389     + 65 * cos(0.27 + 17260.15*T0)
390     + 63 * cos(0.92 + 529.69*T0)
391     + 57 * cos(2.01 + 83996.85*T0)
392     + 56 * cos(5.24 + 71430.70*T0)
393     + 49 * cos(3.25 + 2544.31*T0)
394     + 47 * cos(2.58 + 775.52*T0)
395     + 45 * cos(5.54 + 9437.76*T0)
396     + 43 * cos(6.01 + 6275.96*T0)
397     + 39 * cos(5.36 + 4694.00*T0)
398     + 38 * cos(2.39 + 8827.39*T0)
399     + 37 * cos(0.83 + 19651.05*T0)
400     + 37 * cos(4.90 + 12139.55*T0)
401     + 36 * cos(1.67 + 12036.46*T0)
402     + 35 * cos(1.84 + 2942.46*T0)
403     + 33 * cos(0.24 + 7084.90*T0)
404     + 32 * cos(0.18 + 5088.63*T0)
405     + 32 * cos(1.78 + 398.15*T0)
406     + 28 * cos(1.21 + 6286.60*T0)
407     + 28 * cos(1.90 + 6279.55*T0)
408     + 26 * cos(4.59 + 10447.39*T0);
409     Result.RV := Result.RV / 1.0E+8;
410    
411     A := 103019 * cos(1.107490 + 6283.075850*T0)
412     + 1721 * cos(1.0644 + 12566.1517*T0)
413     + 702 * cos(3.142)
414     + 32 * cos(1.02 + 18849.23*T0)
415     + 31 * cos(2.84 + 5507.55*T0)
416     + 25 * cos(1.32 + 5223.69*T0)
417     + 18 * cos(1.42 + 1577.34*T0)
418     + 10 * cos(5.91 + 10977.08*T0)
419     + 9 * cos(1.42 + 6275.96*T0)
420     + 9 * cos(0.27 + 5486.78*T0);
421     Result.RV := Result.RV + (A * T0 / 1.0E+8);
422    
423     A := 4359 * cos(5.7846 + 6283.0758*T0)
424     + 124 * cos(5.579 + 12566.152*T0)
425     + 12 * cos(3.14)
426     + 9 * cos(3.63 + 77713.77*T0)
427     + 6 * cos(1.87 + 5573.14*T0)
428     + 3 * cos(5.47 + 18849.23*T0);
429     Result.RV := Result.RV + (A * sqr(T0) / 1.0E+8);
430    
431     L := (L + PI);
432     L := Frac(L / 2.0 / PI) * 2.0 * Pi;
433     if L < 0 then
434     L := L + (2.0*PI);
435     B := -B;
436    
437     Result.SLong := L * radcor;
438     Result.SLat := B * radcor;
439    
440     X := Result.RV * cos(B) * cos(L);
441     Y := Result.RV * cos(B) * sin(L);
442     Z := Result.RV * sin(B);
443    
444     Result.SunX := X + 4.40360E-7 * Y - 1.90919E-7 * Z;
445     Result.SunY := -4.79966E-7 * X + 0.917482137087 * Y - 0.397776982902 * Z;
446     Result.SunZ := 0.397776982902 * Y + 0.917482137087 * Z;
447     end;
448    
449     function MoonPosPrim(UT : TStDateTimeRec) : TStMoonPosRec;
450     {-computes J2000 coordinates of the moon}
451     var
452     JD,
453     TD,
454     JCent,
455     JC2, JC3, JC4,
456     LP, D,
457     M, MP,
458     F, I,
459     A1,A2,A3,
460     MoonLong,
461     MoonLat,
462     MoonDst,
463     S1,C1,
464     SunRA,
465     SunDC,
466     EE,EES : Double;
467    
468     SP : TStSunXYZRec;
469    
470     begin
471     JD := AstJulianDate(UT.D) + UT.T/86400;
472     JCent := (JD - 2451545) / 36525;
473     JC2 := sqr(JCent);
474     JC3 := JC2 * JCent;
475     JC4 := sqr(JC2);
476    
477     SP := SunPosPrim(UT);
478     SunRA := StInvTan2(SP.SunX, SP.SunY) * radcor;
479     SunDC := StInvSin(SP.SunZ / SP.RV) * radcor;
480    
481    
482     {Lunar mean longitude}
483     LP := 218.3164591 + (481267.88134236 * JCent)
484     - (1.3268E-3 * JC2) + (JC3 / 538841) - (JC4 / 65194000);
485     LP := Frac(LP/360) * 360;
486     if LP < 0 then
487     LP := LP + 360;
488     LP := LP/radcor;
489    
490     {Lunar mean elongation}
491     D := 297.8502042 + (445267.1115168 * JCent)
492     - (1.63E-3 * JC2) + (JC3 / 545868) - (JC4 / 113065000);
493     D := Frac(D/360) * 360;
494     if D < 0 then
495     D := D + 360;
496     D := D/radcor;
497    
498     {Solar mean anomaly}
499     M := 357.5291092 + (35999.0502909 * JCent)
500     - (1.536E-4 * JC2) + (JC3 / 24490000);
501     M := Frac(M/360) * 360;
502     if M < 0 then
503     M := M + 360;
504     M := M/radcor;
505    
506     {Lunar mean anomaly}
507     MP := 134.9634114 + (477198.8676313 * JCent)
508     + (8.997E-3 * JC2) + (JC3 / 69699) - (JC4 / 14712000);
509     MP := Frac(MP/360) * 360;
510     if MP < 0 then
511     MP := MP + 360;
512     MP := MP/radcor;
513    
514     {Lunar argument of latitude}
515     F := 93.2720993 + (483202.0175273 * JCent)
516     - (3.4029E-3 * JC2) - (JC3 / 3526000) + (JC4 / 863310000);
517     F := Frac(F/360) * 360;
518     if F < 0 then
519     F := F + 360;
520     F := F/radcor;
521    
522    
523     {Other required arguments}
524     A1 := 119.75 + (131.849 * JCent);
525     A1 := Frac(A1/360) * 360;
526     if A1 < 0 then
527     A1 := A1 + 360;
528     A1 := A1/radcor;
529    
530     A2 := 53.09 + (479264.290 * JCent);
531     A2 := Frac(A2/360) * 360;
532     if A2 < 0 then
533     A2 := A2 + 360;
534     A2 := A2/radcor;
535    
536     A3 := 313.45 + (481266.484 * JCent);
537     A3 := Frac(A3/360) * 360;
538     if A3 < 0 then
539     A3 := A3 + 360;
540     A3 := A3/radcor;
541    
542     {Earth's orbital eccentricity}
543     EE := 1.0 - (2.516E-3 * JCent) - (7.4E-6 * JC2);
544     EES := sqr(EE);
545    
546     MoonLong := 6288774 * sin(MP)
547     + 1274027 * sin(2*D - MP)
548     + 658314 * sin(2*D)
549     + 213618 * sin(2*MP)
550     - 185116 * sin(M) * EE
551     - 114332 * sin(2*F)
552     + 58793 * sin(2*(D-MP))
553     + 57066 * sin(2*D-M-MP) * EE
554     + 53322 * sin(2*D-MP)
555     + 45758 * sin(2*D-M) * EE
556     - 40923 * sin(M-MP) * EE
557     - 34720 * sin(D)
558     - 30383 * sin(M+MP) * EE
559     + 15327 * sin(2*(D-F))
560     - 12528 * sin(MP+2*F)
561     + 10980 * sin(MP-2*F)
562     + 10675 * sin(4*D-MP)
563     + 10034 * sin(3*MP)
564     + 8548 * sin(4*D-2*MP)
565     - 7888 * sin(2*D+M-MP) * EE
566     - 6766 * sin(2*D+M) * EE
567     - 5163 * sin(D-MP)
568     + 4987 * sin(D+M) * EE
569     + 4036 * sin(2*D-M+MP) * EE
570     + 3994 * sin(2*(D+MP))
571     + 3861 * sin(4*D)
572     + 3665 * sin(2*D-3*MP)
573     - 2689 * sin(M-2*MP) * EE
574     - 2602 * sin(2*D-MP+2*F)
575     + 2390 * sin(2*D-M-2*MP) * EE
576     - 2348 * sin(D-MP)
577     + 2236 * sin(2*(D-M)) * EES
578     - 2120 * sin(M-2*MP) * EE
579     - 2069 * sin(2*M) * EE
580     + 2048 * sin(2*D-2*M-MP) * EES
581     - 1773 * sin(2*D+MP-2*F)
582     - 1595 * sin(2*(D+F))
583     + 1215 * sin(4*D-M-MP) * EE
584     - 1110 * sin(2*(MP+F))
585     - 892 * sin(3*D-MP)
586     - 810 * sin(2*D-M-MP) * EE
587     + 759 * sin(4*D-M-2*MP) * EE
588     - 713 * sin(2*M-MP) * EE
589     - 700 * sin(2*D+2*M-MP) * EES
590     + 691 * sin(2*D+M-2*MP) * EE
591     + 596 * sin(2*D-M-2*F) * EE
592     + 549 * sin(4*D+MP)
593     + 537 * sin(4*MP)
594     + 520 * sin(4*D-M) * EE;
595    
596     MoonDst := - 20905355 * cos(MP)
597     - 3699111 * cos(2*D - MP)
598     - 2955968 * cos(2*D)
599     - 569925 * cos(2*MP)
600     + 48888 * cos(M) * EE
601     - 3149 * cos(2*F)
602     + 246158 * cos(2*(D-MP))
603     - 152138 * cos(2*D-M-MP) * EE
604     - 170733 * cos(2*D-MP)
605     - 204586 * cos(2*D-M) * EE
606     - 129620 * cos(M-MP) * EE
607     + 108743 * cos(D)
608     + 104755 * cos(M-MP) * EE
609     + 10321 * cos(2*D-2*F)
610     + 79661 * cos(MP-2*F)
611     - 34782 * cos(4*D-MP)
612     - 23210 * cos(3*MP)
613     - 21636 * cos(4*D-2*MP)
614     + 24208 * cos(2*D+M-MP) * EE
615     + 30824 * cos(2*D-M) * EE
616     - 8379 * cos(D-MP)
617     - 16675 * cos(D+M) * EE
618     - 12831 * cos(2*D-M+MP) * EE
619     - 10445 * cos(2*D+2*MP)
620     - 11650 * cos(4*D) * EE
621     + 14403 * cos(2*D+3*MP)
622     - 7003 * cos(M-2*MP) * EE
623     + 10056 * cos(2*D-M-2*MP) * EE
624     + 6322 * cos(D+MP)
625     - 9884 * cos(2*D-2*M) * EES
626     + 5751 * cos(M+2*MP) * EE
627     - 4950 * cos(2*D-2*M-MP) * EES
628     + 4130 * cos(2*D+MP+2*F)
629     - 3958 * cos(4*D-M-MP) * EE
630     + 3258 * cos(3*D-MP)
631     + 2616 * cos(2*D+M+MP) * EE
632     - 1897 * cos(4*D-M-2*MP) * EE
633     - 2117 * cos(2*M-MP) * EES
634     + 2354 * cos(2*D+2*M-MP) * EES
635     - 1423 * cos(4*D+MP)
636     - 1117 * cos(4*MP)
637     - 1571 * cos(4*D-M) * EE
638     - 1739 * cos(D-2*MP)
639     - 4421 * cos(2*MP-2*F)
640     + 1165 * cos(2*M+MP)
641     + 8752 * cos(2*D-MP-2*F);
642    
643     MoonLat := 5128122 * sin(F)
644     + 280602 * sin(MP+F)
645     + 277693 * sin(MP-F)
646     + 173237 * sin(2*D-F)
647     + 55413 * sin(2*D-MP+F)
648     + 46271 * sin(2*D-MP-F)
649     + 32573 * sin(2*D+F)
650     + 17198 * sin(2*MP+F)
651     + 9266 * sin(2*D+MP-F)
652     + 8822 * sin(2*MP-F)
653     + 8216 * sin(2*D-M-F) * EE
654     + 4324 * sin(2*D-2*MP-F)
655     + 4200 * sin(2*D+MP+F)
656     - 3359 * sin(2*D+M-F) * EE
657     + 2463 * sin(2*D-M-MP+F) * EE
658     + 2211 * sin(2*D-M+F) * EE
659     + 2065 * sin(2*D-M-MP-F) * EE
660     - 1870 * sin(M-MP-F) * EE
661     + 1828 * sin(4*D-MP-F)
662     - 1794 * sin(M+F) * EE
663     - 1749 * sin(3*F)
664     - 1565 * sin(M-MP+F) * EE
665     - 1491 * sin(D+F)
666     - 1475 * sin(M+MP+F) * EE
667     - 1410 * sin(M+MP-F) * EE
668     - 1344 * sin(M-F) * EE
669     - 1335 * sin(D-F)
670     + 1107 * sin(3*MP+F)
671     + 1021 * sin(4*D-F)
672     + 833 * sin(4*D-MP+F)
673     + 777 * sin(MP-3*F)
674     + 671 * sin(4*D-2*MP+F)
675     + 607 * sin(2*D-3*F)
676     + 596 * sin(2*D+2*MP-F)
677     + 491 * sin(2*D-M+MP-F) * EE
678     - 451 * sin(2*D-2*MP+F)
679     + 439 * sin(3*MP-F)
680     + 422 * sin(2*D+2*MP+F)
681     + 421 * sin(2*D-3*MP-F)
682     - 366 * sin(2*D+M-MP+F) * EE
683     - 351 * sin(2*D+M+F) * EE
684     + 331 * sin(4*D+F)
685     + 315 * sin(2*D-M+MP+F) * EE
686     + 302 * sin(2*D-2*M-F) * EES
687     - 283 * sin(MP + 3*F)
688     - 229 * sin(2*D+M+MP-F) * EE
689     + 223 * sin(D+M-F) * EE
690     + 223 * sin(D+M+F) * EE;
691    
692     MoonLong := MoonLong
693     + 3958 * sin(A1)
694     + 1962 * sin(LP-F)
695     + 318 * sin(A2);
696    
697     MoonLat := MoonLat
698     - 2235 * sin(LP)
699     + 382 * sin(A3)
700     + 175 * sin(A1-F)
701     + 175 * sin(A1+F)
702     + 127 * sin(LP-MP)
703     - 115 * sin(LP+MP);
704    
705     MoonLong := LP + MoonLong/1000000/radcor;
706     MoonLat := MoonLat/1000000/radcor;
707     MoonDst := 385000.56 + MoonDst/1000;
708    
709     Result.Plx := StInvSin(6378.14/MoonDst) * radcor;
710     Result.Dia := 358473400 / MoonDst * 2 / 3600;
711    
712     S1 := sin(MoonLong) * cos(OB2000) - StTan(MoonLat) * sin(OB2000);
713     C1 := cos(MoonLong);
714     Result.RA := StInvTan2(C1, S1) * radcor;
715    
716     TD := sin(MoonLat) * cos(OB2000)
717     + cos(MoonLat) * sin(OB2000) * sin(MoonLong);
718     TD := StInvSin(TD);
719     Result.DC := TD * radcor;
720    
721     I := sin(SunDC/radcor) * sin(TD)
722     + cos(SunDC/radcor) * cos(TD) * cos((SunRA-Result.RA)/radcor);
723     Result.Phase := (1 - I) / 2;
724    
725     I := StInvCos(I) * radcor;
726     Result.Elong := (Result.RA - SunRA);
727     if Result.Elong < 0 then
728     Result.Elong := 360 + Result.Elong;
729     if Result.Elong >= 180 then begin
730     Result.Phase := -Result.Phase; {waning moon}
731     Result.Elong := -I
732     end else
733     Result.Elong := I;
734     end;
735    
736     function AmountOfSunlight(LD : TStDate; Longitude, Latitude : Double): TStTime;
737     {-compute the hours, min, sec of sunlight on a given date}
738     var
739     RS : TStRiseSetRec;
740     begin
741     RS := SunRiseSet(LD, Longitude, Latitude);
742     with RS do begin
743     if ORise = -3 then begin
744     {sun is always above the horizon}
745     Result := SecondsInDay;
746     Exit;
747     end;
748    
749     if ORise = -2 then begin
750     {sun is always below horizon}
751     Result := 0;
752     Exit;
753     end;
754    
755     if (ORise > -1) then begin
756     if (OSet > -1) then
757     Result := OSet - ORise
758     else
759     Result := SecondsInDay - ORise;
760     end else begin
761     if (OSet > -1) then
762     Result := OSet
763     else
764     Result := 0;
765     end;
766     end;
767     if (Result < 0) then
768     Result := Result + SecondsInDay
769     else if (Result >= SecondsInDay) then
770     Result := Result - SecondsInDay;
771     end;
772    
773     function SunPos(UT : TStDateTimeRec) : TStPosRec;
774     {-compute the RA/Declination of the Sun}
775     var
776     SP : TStSunXYZRec;
777     begin
778     if not CheckDate(UT) then begin
779     Result.RA := -1;
780     Result.DC := -99;
781     Exit;
782     end;
783    
784     SP := SunPosPrim(UT);
785     Result.RA := StInvTan2(SP.SunX, SP.SunY) * radcor;
786     Result.DC := StInvSin(SP.SunZ / SP.RV) * radcor;
787     end;
788    
789     function RiseSetPrim(LD : TStDate;
790     Longitude, Latitude, H0 : Double;
791     PR : TStPosRecArray;
792     ApproxOnly : Boolean) : TStRiseSetRec;
793     {-primitive routine for finding rise/set times}
794     var
795     ST,
796     NST,
797     HA,
798     LatR,
799     N1,
800     N2,
801     N3,
802     TTran,
803     TRise,
804     TSet,
805     TV1,
806     TV2,
807     A1,
808     A2,
809     DeltaR,
810     DeltaS,
811     RA,
812     DC,
813     Alt : Double;
814    
815     ICount : SmallInt;
816    
817     UT : TStDateTimeRec;
818     begin
819     H0 := H0/radcor;
820    
821     UT.D := LD;
822     UT.T := 0;
823     ST := SiderealTime(UT);
824    
825     LatR := Latitude/radcor;
826    
827     {check if object never rises/sets}
828     N1 := sin(H0) - sin(LatR) * sin(PR[2].DC/radcor);
829     N2 := cos(LatR) * cos(PR[2].DC/radcor);
830    
831     HA := N1 / N2;
832     if (abs(HA) >= 1) then begin
833     { if ((Latitude - 90) >= 90) then begin}
834     if (HA > 0) then begin
835     {object never rises}
836     Result.ORise := -2;
837     Result.OSet := -2;
838     end else begin
839     {object never sets, i.e., it is circumpolar}
840     Result.ORise := -3;
841     Result.OSet := -3;
842     end;
843     Exit;
844     end;
845    
846     HA := StInvCos(HA) * radcor;
847     if HA > 180 then
848     HA := HA - 180;
849     if HA < 0 then
850     HA := HA + 180;
851    
852     {compute approximate hour angle at transit}
853     TTran := (PR[2].RA - Longitude - ST) / 360;
854     if abs(TTran) >= 1 then
855     TTran:= Frac(TTran);
856     if TTran < 0 then
857     TTran := TTran + 1;
858    
859     TRise := TTran - HA/360;
860     TSet := TTran + HA/360;
861     if abs(TRise) >= 1 then
862     TRise:= Frac(TRise);
863     if TRise < 0 then
864     TRise := TRise + 1;
865    
866     if abs(TSet) >= 1 then
867     TSet := Frac(TSet);
868     if TSet < 0 then
869     TSet := TSet + 1;
870    
871     if not ApproxOnly then begin
872     {refine rise time by interpolation/iteration}
873     ICount := 0;
874     TV1 := 0;
875     A1 := 0;
876     repeat
877     NST := ST + 360.985647 * TRise;
878     NST := Frac(NST / 360.0) * 360;
879     N1 := PR[2].RA - PR[1].RA;
880     N2 := PR[3].RA - PR[2].RA;
881     N3 := N2 - N1;
882     RA := PR[2].RA + TRise/2 * (N1 + N2 + TRise*N3);
883    
884     N1 := PR[2].DC - PR[1].DC;
885     N2 := PR[3].DC - PR[2].DC;
886     N3 := N2 - N1;
887     DC := PR[2].DC + TRise/2 * (N1 + N2 + TRise*N3);
888     DC := DC/radcor;
889    
890     HA := (NST + Longitude - RA) / radcor;
891     Alt := StInvSin(sin(LatR) * sin(DC) + cos(LatR) * cos(DC) * cos(HA));
892     DeltaR := ((Alt - H0) * radcor) / (360 * cos(DC) * cos(LatR) * sin(HA));
893     TRise := TRise + DeltaR;
894     Inc(ICount);
895     if (ICount > 3) and (abs(DeltaR) >= 0.0005) then begin
896     if (ICount = 4) then begin
897     TV1 := TRise;
898     A1 := (Alt-H0) * radcor;
899     end else if (ICount = 5) then begin
900     TV2 := TRise;
901     A2 := (Alt-H0) * radcor;
902    
903     TRise := TV1 + (A1 / A2) * (TV1 - TV2);
904     break;
905     end;
906     end;
907     until (abs(DeltaR) < 0.0005); {0.0005d = 0.72 min}
908    
909     {refine set time by interpolation/iteration}
910     ICount := 0;
911     TV1 := 0;
912     A1 := 0;
913     repeat
914     NST := ST + 360.985647 * TSet;
915     NST := Frac(NST / 360.0) * 360;
916     N1 := PR[2].RA - PR[1].RA;
917     N2 := PR[3].RA - PR[2].RA;
918     N3 := N2 - N1;
919     RA := PR[2].RA + TSet/2 * (N1 + N2 + TSet*N3);
920    
921     N1 := PR[2].DC - PR[1].DC;
922     N2 := PR[3].DC - PR[2].DC;
923     N3 := N2 - N1;
924     DC := PR[2].DC + TSet/2 * (N1 + N2 + TSet*N3);
925     DC := DC/radcor;
926    
927     HA := (NST + Longitude - RA) / radcor;
928     Alt := StInvSin(sin(LatR) * sin(DC) + cos(LatR) * cos(DC) * cos(HA));
929     DeltaS := ((Alt - H0) * radcor) / (360 * cos(DC) * cos(LatR) * sin(HA));
930     TSet := TSet + DeltaS;
931     Inc(ICount);
932     if (ICount > 3) and (abs(DeltaS) >= 0.0005) then begin
933     if (ICount = 4) then begin
934     TV1 := TSet;
935     A1 := (Alt-H0) * radcor;
936     end else if (ICount = 5) then begin
937     TV2 := TSet;
938     A2 := (Alt-H0) * radcor;
939    
940     TSet := TV1 + (A1 / A2) * (TV1 - TV2);
941     break;
942     end;
943     end;
944     until (abs(DeltaS) < 0.0005); {0.0005d = 0.72 min}
945     end;
946    
947     if (TRise >= 0) and (TRise < 1) then
948     Result.ORise := Trunc(TRise * SecondsInDay)
949     else begin
950     if TRise < 0 then
951     Result.ORise := Trunc((TRise + 1) * SecondsInDay)
952     else
953     Result.ORise := Trunc(Frac(TRise) * SecondsInDay);
954     end;
955     if Result.ORise < 0 then
956     Inc(Result.ORise, SecondsInDay);
957     if Result.ORise >= SecondsInDay then
958     Dec(Result.ORise, SecondsInDay);
959    
960    
961     if (TSet >= 0) and (TSet < 1) then
962     Result.OSet := Trunc(TSet * SecondsInDay)
963     else begin
964     if TSet < 0 then
965     Result.OSet := Trunc((TSet + 1) * SecondsInDay)
966     else
967     Result.OSet := Trunc(Frac(TSet) * SecondsInDay);
968     end;
969     if Result.OSet < 0 then
970     Inc(Result.OSet, SecondsInDay);
971     if Result.OSet >= SecondsInDay then
972     Dec(Result.OSet, SecondsInDay);
973     end;
974    
975     function SunRiseSet(LD : TStDate; Longitude, Latitude : Double) : TStRiseSetRec;
976     {-compute the Sun rise or set time}
977     {the value for H0 accounts for approximate refraction of 0.5667 deg. and}
978     {that rise or set is based on the upper limb instead of the center of the solar disc}
979     var
980     I : Integer;
981     H0 : Double;
982     UT : TStDateTimeRec;
983     RP : TStPosRecArray;
984     begin
985     Dec(LD);
986     H0 := -0.8333;
987     UT.T := 0;
988     UT.D := LD-1;
989    
990     if CheckDate(UT) then begin
991     UT.D := UT.D + 2;
992     if (not CheckDate(UT)) then begin
993     Result.ORise := -4;
994     Result.OSet := -4;
995     Exit;
996     end else
997     UT.D := UT.D-2;
998     end else begin
999     Result.ORise := -4;
1000     Result.OSet := -4;
1001     Exit;
1002     end;
1003    
1004     for I := 1 to 3 do begin
1005     RP[I] := SunPos(UT);
1006     if I >= 2 then begin
1007     if RP[I].RA < RP[I-1].RA then
1008     RP[I].RA := RP[I].RA + 360;
1009     end;
1010     Inc(UT.D);
1011     end;
1012     Result := RiseSetPrim(LD, Longitude, Latitude, H0, RP, False);
1013     end;
1014    
1015     function Twilight(LD : TStDate; Longitude, Latitude : Double;
1016     TwiType : TStTwilight) : TStRiseSetRec;
1017     {-compute the beginning or end of twilight}
1018     {twilight computations are based on the zenith distance of the center }
1019     {of the solar disc.}
1020     {Civil = 6 deg. below the horizon}
1021     {Nautical = 12 deg. below the horizon}
1022     {Astronomical = 18 deg. below the horizon}
1023     var
1024     I : Integer;
1025     H0 : Double;
1026     UT : TStDateTimeRec;
1027     RP : TStPosRecArray;
1028     begin
1029     UT.D := LD-1;
1030     UT.T := 0;
1031    
1032     if CheckDate(UT) then begin
1033     UT.D := UT.D + 2;
1034     if (not CheckDate(UT)) then begin
1035     Result.ORise := -4;
1036     Result.OSet := -4;
1037     Exit;
1038     end else
1039     UT.D := UT.D-2;
1040     end else begin
1041     Result.ORise := -4;
1042     Result.OSet := -4;
1043     Exit;
1044     end;
1045    
1046     case TwiType of
1047     ttCivil : H0 := -6.0;
1048     ttNautical : H0 := -12.0;
1049     ttAstronomical : H0 := -18.0;
1050     else
1051     H0 := -18.0;
1052     end;
1053    
1054     for I := 1 to 3 do begin
1055     UT.D := LD + I-1;
1056     RP[I] := SunPos(UT);
1057     if (I > 1) then begin
1058     if RP[I].RA < RP[I-1].RA then
1059     RP[I].RA := RP[I].RA + 360.0;
1060     end;
1061     end;
1062     Result := RiseSetPrim(LD, Longitude, Latitude, H0, RP, False);
1063     end;
1064    
1065     function FixedRiseSet(LD : TStDate;
1066     RA, DC, Longitude, Latitude : Double) : TStRiseSetRec;
1067     {-compute the rise/set time for a fixed object, e.g., star}
1068     {the value for H0 accounts for approximate refraction of 0.5667 deg.}
1069     {this routine does not refine the intial estimate and so may be off by five}
1070     {minutes or so}
1071     var
1072     H0 : Double;
1073     UT : TStDateTimeRec;
1074     RP : TStPosRecArray;
1075     begin
1076     H0 := -0.5667;
1077     UT.T := 0;
1078     UT.D := LD;
1079    
1080     if not CheckDate(UT) then begin
1081     Result.ORise := -4;
1082     Result.OSet := -4;
1083     Exit;
1084     end;
1085    
1086     RP[2].RA := RA;
1087     RP[2].DC := DC;
1088     Result := RiseSetPrim(LD, Longitude, Latitude, H0, RP, True);
1089     end;
1090    
1091     function MoonPos(UT : TStDateTimeRec) : TStMoonPosRec;
1092     {-compute the J2000 RA/Declination of the moon}
1093     begin
1094     if not CheckDate(UT) then begin
1095     Result.RA := -1;
1096     Result.DC := -1;
1097     Exit;
1098     end;
1099     Result := MoonPosPrim(UT);
1100     end;
1101    
1102     function MoonRiseSet(LD : TStDate; Longitude, Latitude : Double) : TStRiseSetRec;
1103     {compute the Moon rise and set time}
1104     {the value for H0 accounts for approximate refraction of 0.5667 deg., }
1105     {that rise or set is based on the upper limb instead of the center of the}
1106     {lunar disc, and the lunar parallax. In accordance with American Ephemeris }
1107     {practice, the phase of the moon is not taken into account, i.e., the time}
1108     {is based on the upper limb whether it is lit or not}
1109     var
1110     I : Integer;
1111     H0 : Double;
1112     UT : TStDateTimeRec;
1113     RP : TStPosRecArray;
1114     MPR : TStMoonPosRec;
1115     begin
1116     H0 := 0.125; { default value }
1117    
1118     Dec(LD);
1119     UT.T := 0;
1120     UT.D := LD;
1121    
1122     if CheckDate(UT) then begin
1123     UT.D := UT.D + 2;
1124     if (not CheckDate(UT)) then begin
1125     Result.ORise := -4;
1126     Result.OSet := -4;
1127     Exit;
1128     end else
1129     UT.D := UT.D-2;
1130     end else begin
1131     Result.ORise := -4;
1132     Result.OSet := -4;
1133     Exit;
1134     end;
1135    
1136     for I := 1 to 3 do begin
1137     MPR := MoonPos(UT);
1138     RP[I].RA := MPR.RA;
1139     RP[I].DC := MPR.DC;
1140     if I >= 2 then begin
1141     if I = 2 then
1142     H0 := 0.7275*MPR.Plx - 0.5667;
1143     if RP[I].RA < RP[I-1].RA then
1144     RP[I].RA := RP[I].RA + 360;
1145     end;
1146     Inc(UT.D);
1147     end;
1148     Result := RiseSetPrim(LD, Longitude, Latitude, H0, RP, False);
1149     end;
1150    
1151     function LunarPhase(UT : TStDateTimeRec) : Double;
1152     {-compute the phase of the moon}
1153     {The value is positive if between New and Full Moon}
1154     { negative if between Full and New Moon}
1155     var
1156     MPR : TStMoonPosRec;
1157     begin
1158     MPR := MoonPosPrim(UT);
1159     Result := MPR.Phase;
1160     end;
1161    
1162     procedure GetPhases(K : Double; var PR : TStPhaseRecord);
1163     {primitive routine to find the date/time of phases in a lunar cycle}
1164     var
1165     JD,
1166     NK,
1167     TD,
1168     J1,
1169     J2,
1170     J3 : Double;
1171    
1172     step : Integer;
1173    
1174     E,
1175     FP,
1176     S1,
1177     M1,
1178     M2,
1179     M3 : Double;
1180    
1181     function AddCor : Double;
1182     begin
1183     AddCor := 0.000325 * sin((299.77 + 0.107408*K - 0.009173*J2)/radcor)
1184     + 0.000165 * sin((251.88 + 0.016321*K)/radcor)
1185     + 0.000164 * sin((251.83 + 26.651886*K)/radcor)
1186     + 0.000126 * sin((349.42 + 36.412478*K)/radcor)
1187     + 0.000110 * sin((84.660 + 18.206239*K)/radcor)
1188     + 0.000062 * sin((141.74 + 53.303771*K)/radcor)
1189     + 0.000060 * sin((207.14 + 2.453732*K)/radcor)
1190     + 0.000056 * sin((154.84 + 7.306860*K)/radcor)
1191     + 0.000047 * sin((34.520 + 27.261239*K)/radcor)
1192     + 0.000042 * sin((207.19 + 0.121824*K)/radcor)
1193     + 0.000040 * sin((291.34 + 1.844379*K)/radcor)
1194     + 0.000037 * sin((161.72 + 24.198154*K)/radcor)
1195     + 0.000035 * sin((239.56 + 25.513099*K)/radcor)
1196     + 0.000023 * sin((331.55 + 3.592518*K)/radcor);
1197     end;
1198    
1199     begin
1200     NK := K;
1201     FillChar(PR, SizeOf(TStPhaseRecord), #0);
1202     for step := 0 to 3 do begin
1203     K := NK + (step*0.25);
1204     FP := Frac(K);
1205     if FP < 0 then
1206     FP := FP + 1;
1207    
1208     {compute Julian Centuries}
1209     J1 := K / 1236.85;
1210     J2 := Sqr(J1);
1211     J3 := J2 * J1;
1212    
1213    
1214     {solar mean anomaly}
1215     S1 := 2.5534
1216     + 29.1053569 * K
1217     - 0.0000218 * J2
1218     - 0.00000011 * J3;
1219     S1 := Frac(S1 / 360.0) * 360;
1220     if S1 < 0 then
1221     S1 := S1 + 360.0;
1222    
1223     {lunar mean anomaly}
1224     M1 := 201.5643
1225     + 385.81693528 * K
1226     + 0.0107438 * J2
1227     + 0.00001239 * J3
1228     - 0.000000058 * J2 * J2;
1229     M1 := Frac(M1 / 360.0) * 360;
1230     if M1 < 0 then
1231     M1 := M1 + 360.0;
1232    
1233     {lunar argument of latitude}
1234     M2 := 160.7108
1235     + 390.67050274 * K
1236     - 0.0016341 * J2
1237     - 0.00000227 * J3
1238     + 0.000000011 * J2 * J2;
1239     M2 := Frac(M2 / 360.0) * 360;
1240     if M2 < 0 then
1241     M2 := M2 + 360.0;
1242    
1243     {lunar ascending node}
1244     M3 := 124.7746
1245     - 1.56375580 * K
1246     + 0.0020691 * J2
1247     + 0.00000215 * J3;
1248     M3 := Frac(M3 / 360.0) * 360;
1249     if M3 < 0 then
1250     M3 := M3 + 360.0;
1251    
1252     {convert to radians}
1253     S1 := S1/radcor;
1254     M1 := M1/radcor;
1255     M2 := M2/radcor;
1256     M3 := M3/radcor;
1257    
1258     {mean Julian Date for phase}
1259     JD := 2451550.09765
1260     + 29.530588853 * K
1261     + 0.0001337 * J2
1262     - 0.000000150 * J3
1263     + 0.00000000073 * J2 * J2;
1264    
1265     {earth's orbital eccentricity}
1266     E := 1.0 - 0.002516 * J1 - 0.0000074 * J2;
1267    
1268     {New Moon date time}
1269     if FP < 0.01 then begin
1270     TD := - 0.40720 * sin(M1)
1271     + 0.17241 * E * sin(S1)
1272     + 0.01608 * sin(2*M1)
1273     + 0.01039 * sin(2*M2)
1274     + 0.00739 * E * sin(M1-S1)
1275     - 0.00514 * E * sin(M1 + S1)
1276     + 0.00208 * E * E * sin(2 * S1)
1277     - 0.00111 * sin(M1 - 2*M2)
1278     - 0.00057 * sin(M1 + 2*M2)
1279     + 0.00056 * E * sin(2*M1 + S1)
1280     - 0.00042 * sin(3 * M1)
1281     + 0.00042 * E * sin(S1 + 2*M2)
1282     + 0.00038 * E * sin(S1 - 2*M2)
1283     - 0.00024 * E * sin(2*(M1-S1))
1284     - 0.00017 * sin(M3)
1285     - 0.00007 * sin(M1 + 2*S1);
1286     JD := JD + TD + AddCor;
1287     PR.NMDate := JD;
1288     end;
1289    
1290     {Full Moon date/time}
1291     if Abs(FP - 0.5) < 0.01 then begin
1292     TD := - 0.40614 * sin(M1)
1293     + 0.17302 * E * sin(S1)
1294     + 0.01614 * sin(2*M1)
1295     + 0.01043 * sin(2*M2)
1296     + 0.00734 * E * sin(M1-S1)
1297     - 0.00515 * E * sin(M1 + S1)
1298     + 0.00209 * E * E * sin(2 * S1)
1299     - 0.00111 * sin(M1 - 2*M2)
1300     - 0.00057 * sin(M1 + 2*M2)
1301     + 0.00056 * E * sin(2*M1 + S1)
1302     - 0.00042 * sin(3 * M1)
1303     + 0.00042 * E * sin(S1 + 2*M2)
1304     + 0.00038 * E * sin(S1 - 2*M2)
1305     - 0.00024 * E * sin(2*(M1-S1))
1306     - 0.00017 * sin(M3)
1307     - 0.00007 * sin(M1 + 2*S1);
1308     JD := JD + TD + AddCor;
1309     PR.FMDate := JD;
1310     end;
1311    
1312     {Quarters date/time}
1313     if (abs(FP - 0.25) < 0.01) or (abs(FP - 0.75) < 0.01) then begin
1314     TD := - 0.62801 * sin(M1)
1315     + 0.17172 * sin(S1) * E
1316     - 0.01183 * sin(M1+S1) * E
1317     + 0.00862 * sin(2*M1)
1318     + 0.00804 * sin(2*M2)
1319     + 0.00454 * sin(M1-S1) * E
1320     + 0.00204 * sin(2*S1) * E * E
1321     - 0.00180 * sin(M1 - 2*M2)
1322     - 0.00070 * sin(M1 + 2*M2)
1323     - 0.00040 * sin(3*M1)
1324     - 0.00034 * sin(2*M1-S1) * E
1325     + 0.00032 * sin(S1 + 2*M2) * E
1326     + 0.00032 * sin(S1 - 2*M2) * E
1327     - 0.00028 * sin(M1 + 2*S1) * E * E
1328     + 0.00027 * sin(2*M1 + S1) * E
1329     - 0.00017 * sin(M3)
1330     - 0.00005 * sin(M1 - S1 - 2*M2);
1331     JD := JD + TD + AddCor;
1332    
1333     {adjustment to computed Julian Date}
1334     TD := 0.00306
1335     - 0.00038 * E * cos(S1)
1336     + 0.00026 * cos(M1)
1337     - 0.00002 * cos(M1-S1)
1338     + 0.00002 * cos(M1+S1)
1339     + 0.00002 * cos(2*M2);
1340    
1341     if Abs(FP - 0.25) < 0.01 then
1342     PR.FQDate := JD + TD
1343     else
1344     PR.LQDate := JD - TD;
1345     end;
1346     end;
1347     end;
1348    
1349     procedure PhasePrim(LD : TStDate; var PhaseArray : TStPhaseArray);
1350     {-primitive phase calculation}
1351     var
1352     I,
1353     D,
1354     M,
1355     Y : Integer;
1356     K, TD,
1357     LYear : Double;
1358    
1359     begin
1360     StDateToDMY(LD, D, M, Y);
1361     LYear := Y - 0.05;
1362     K := (LYear - 2000) * 12.3685;
1363     K := Int(K);
1364     TD := K / 12.3685 + 2000;
1365     if TD > Y then
1366     K := K-1;
1367    
1368     {compute phases for each lunar cycle throughout the year}
1369     for I := 0 to 13 do begin
1370     GetPhases(K, PhaseArray[I]);
1371     K := K + 1;
1372     end;
1373     end;
1374    
1375     function GenSearchPhase(SD : TStDate; PV : Byte) : TStLunarRecord;
1376     {searches for the specified phase in the given month/year expressed by SD}
1377     var
1378     I,
1379     C,
1380     LD,
1381     LM,
1382     LY,
1383     TD,
1384     TM,
1385     TY : Integer;
1386     ADate : TStDate;
1387     JD : Double;
1388     PhaseArray : TStPhaseArray;
1389     begin
1390     C := 0;
1391     FillChar(Result, SizeOf(Result), $FF);
1392    
1393     StDateToDMY(SD, LD, LM, LY);
1394     PhasePrim(SD, PhaseArray);
1395     for I := LM-1 to LM+1 do begin
1396     if (PV = 0) then
1397     JD := PhaseArray[I].NMDate
1398     else if (PV = 1) then
1399     JD := PhaseArray[I].FQDate
1400     else if (PV = 2) then
1401     JD := PhaseArray[I].FMDate
1402     else
1403     JD := PhaseArray[I].LQDate;
1404     ADate := AstJulianDateToStDate(JD, True);
1405    
1406     StDateToDMY(ADate, TD, TM, TY);
1407     if TM < LM then
1408     continue
1409     else if TM = LM then begin
1410     Result.T[C].D := ADate;
1411     Result.T[C].T := Trunc((Frac(JD) + 0.5) * 86400);
1412     if Result.T[C].T >= SecondsInDay then
1413     Dec(Result.T[C].T, SecondsInDay);
1414     Inc(C);
1415     end;
1416     end;
1417     end;
1418    
1419     function FirstQuarter(D : TStDate) : TStLunarRecord;
1420     {-compute date/time of FirstQuarter(s)}
1421     begin
1422     Result := GenSearchPhase(D, 1);
1423     end;
1424    
1425     function FullMoon(D : TStDate) : TStLunarRecord;
1426     {-compute the date/time of FullMoon(s)}
1427     begin
1428     Result := GenSearchPhase(D, 2);
1429     end;
1430    
1431     function LastQuarter(D: TStDate) : TStLunarRecord;
1432     {-compute the date/time of LastQuarter(s)}
1433     begin
1434     Result := GenSearchPhase(D, 3);
1435     end;
1436    
1437     function NewMoon(D : TStDate) : TStLunarRecord;
1438     {-compute the date/time of NewMoon(s)}
1439     begin
1440     Result := GenSearchPhase(D, 0);
1441     end;
1442    
1443     function NextPrevPhase(D : TStDate; Ph : Byte;
1444     FindNext : Boolean) : TStDateTimeRec;
1445     var
1446     LD,
1447     LM,
1448     LY : Integer;
1449     K,
1450     JD,
1451     TJD : Double;
1452     PR : TStPhaseRecord;
1453     OK : Boolean;
1454     begin
1455     if (D < MinDate) or (D > MaxDate) then begin
1456     Result.D := BadDate;
1457     Result.T := BadTime;
1458     Exit;
1459     end;
1460    
1461     StDateToDMY(D, LD, LM, LY);
1462     K := ((LY + LM/12 + LD/365.25) - 2000) * 12.3685 - 0.5;
1463     if FindNext then
1464     K := Round(K)-1
1465     else
1466     K := Round(K)-2;
1467    
1468     OK := False;
1469     TJD := AstJulianDate(D);
1470     repeat
1471     GetPhases(K, PR);
1472    
1473     if (Ph = 0) then
1474     JD := PR.NMDate
1475     else if (Ph = 1) then
1476     JD := PR.FQDate
1477     else if (Ph = 2) then
1478     JD := PR.FMDate
1479     else
1480     JD := PR.LQDate;
1481    
1482     if (FindNext) then begin
1483     if (JD > TJD) then
1484     OK := True
1485     else
1486     K := K + 1;
1487     end else begin
1488     if (JD < TJD) then
1489     OK := True
1490     else
1491     K := K - 1;
1492     end;
1493     until OK;
1494    
1495     Result.D := AstJulianDateToStDate(JD, True);
1496     if (Result.D <> BadDate) then begin
1497     Result.T := Trunc((Frac(JD) + 0.5) * 86400);
1498     if Result.T >= SecondsInDay then
1499     Dec(Result.T, SecondsInDay);
1500     end else
1501     Result.T := BadTime;
1502     end;
1503    
1504     function NextFirstQuarter(D : TStDate) : TStDateTimeRec;
1505     {-compute the date/time of the next closest FirstQuarter}
1506     begin
1507     Result := NextPrevPhase(D, 1, True);
1508     end;
1509    
1510     function NextFullMoon(D : TStDate) : TStDateTimeRec;
1511     {-compute the date/time of the next closest FullMoon}
1512     begin
1513     Result := NextPrevPhase(D, 2, True);
1514     end;
1515    
1516     function NextLastQuarter(D : TStDate) : TStDateTimeRec;
1517     {-compute the date/time of the next closest LastQuarter}
1518     begin
1519     Result := NextPrevPhase(D, 3, True);
1520     end;
1521    
1522     function NextNewMoon(D : TStDate) : TStDateTimeRec;
1523     {-compute the date/time of the next closest NewMoon}
1524     begin
1525     Result := NextPrevPhase(D, 0, True);
1526     end;
1527    
1528     function PrevFirstQuarter(D : TStDate) : TStDateTimeRec;
1529     {-compute the date/time of the prev closest FirstQuarter}
1530     begin
1531     Result := NextPrevPhase(D, 1, False);
1532     end;
1533    
1534     function PrevFullMoon(D : TStDate) : TStDateTimeRec;
1535     {-compute the date/time of the prev closest FullMoon}
1536     begin
1537     Result := NextPrevPhase(D, 2, False);
1538     end;
1539    
1540     function PrevLastQuarter(D : TStDate) : TStDateTimeRec;
1541     {-compute the date/time of the prev closest LastQuarter}
1542     begin
1543     Result := NextPrevPhase(D, 3, False);
1544     end;
1545    
1546     function PrevNewMoon(D : TStDate) : TStDateTimeRec;
1547     {-compute the date/time of the prev closest NewMoon}
1548     begin
1549     Result := NextPrevPhase(D, 0, False);
1550     end;
1551    
1552     {Calendar procedures/functions}
1553    
1554     function SolEqPrim(Y : Integer; K : Byte) : TStDateTimeRec;
1555     {primitive routine for finding equinoxes and solstices}
1556     var
1557     JD, TD, LY,
1558     JCent, MA,
1559     SLong : Double;
1560    
1561     begin
1562     JD := 0;
1563     Result.D := BadDate;
1564     Result.T := BadTime;
1565    
1566     {the following algorithm is valid only in the range of [1000..3000 AD]}
1567     {but is limited to returning dates in [MinYear..MaxYear]}
1568     if (Y < MinYear) or (Y > MaxYear) then
1569     Exit;
1570    
1571     {compute approximate date/time for specified event}
1572     LY := (Y - 2000) / 1000;
1573     case K of
1574     0 : JD := 2451623.80984
1575     + 365242.37404 * LY
1576     + 0.05169 * sqr(LY)
1577     - 0.00411 * LY * sqr(LY)
1578     - 0.00057 * sqr(sqr(LY));
1579    
1580     1 : JD := 2451716.56767
1581     + 365241.62603 * LY
1582     + 0.00325 * sqr(LY)
1583     + 0.00888 * LY * sqr(LY)
1584     - 0.00030 * sqr(sqr(LY));
1585    
1586     2 : JD := 2451810.21715
1587     + 365242.01767 * LY
1588     - 0.11575 * sqr(LY)
1589     + 0.00337 * sqr(LY) * LY
1590     + 0.00078 * sqr(sqr(LY));
1591    
1592     3 : JD := 2451900.05952
1593     + 365242.74049 * LY
1594     - 0.06223 * sqr(LY)
1595     - 0.00823 * LY * sqr(LY)
1596     + 0.00032 * sqr(sqr(LY));
1597     end;
1598    
1599     {refine date/time by computing corrections due to solar longitude,}
1600     {nutation and abberation. Iterate using the corrected time until}
1601     {correction is less than one minute}
1602     repeat
1603     Result.D := AstJulianDateToStDate(JD, True);
1604     Result.T := Trunc((Frac(JD) + 0.5) * 86400);
1605     if Result.T >= SecondsInDay then
1606     Dec(Result.T, SecondsInDay);
1607     JCent := (JD - 2451545.0)/36525.0;
1608    
1609     {approximate solar longitude - no FK5 correction}
1610     SLong := 280.46645
1611     + 36000.76983 * JCent
1612     + 0.0003032 * sqr(JCent);
1613     SLong := Frac((SLong)/360.0) * 360.0;
1614     if SLong < 0 then
1615     SLong := SLong + 360;
1616    
1617     {Equation of the center correction}
1618     MA := 357.52910
1619     + 35999.05030 * JCent;
1620     MA := MA/radcor;
1621     SLong := SLong
1622     + (1.914600 - 0.004817 * JCent - 0.000014 * sqr(JCent)) * sin(MA)
1623     + (0.019993 - 0.000101 * JCent) * sin(2*MA);
1624    
1625     {approximate nutation}
1626     TD := 125.04452
1627     - 1934.136261 * JCent
1628     + 0.0020708 * sqr(JCent);
1629     TD := TD/radcor;
1630     TD := (-17.20 * sin(TD) - 1.32 * sin(2*SLong/radcor))/3600;
1631    
1632     {approximate abberation - solar distance is assumed to be 1 A.U.}
1633     SLong := SLong - (20.4989/3600) + TD;
1634    
1635     {correction to compute Julian Date for event}
1636     TD := 58 * sin((K*90 - SLong)/radcor);
1637     if abs(TD) >= 0.0005 then
1638     JD := JD + TD;
1639     until abs(TD) < 0.0005;
1640     end;
1641    
1642     function Solstice(Y, Epoch : Integer; Summer : Boolean) : TStDateTimeRec;
1643     {-compute the date/time of the summer or winter solstice}
1644     {if Summer = True, compute astronomical summer solstice (summer in N. Hem.)}
1645     { = False, compute astronomical winter solstice (winter in N. Hem.)}
1646     begin
1647     Y := CheckYear(Y, Epoch);
1648     if Summer then
1649     Result := SolEqPrim(Y, 1)
1650     else
1651     Result := SolEqPrim(Y, 3);
1652     end;
1653    
1654     function Equinox(Y, Epoch : Integer; Vernal : Boolean) : TStDateTimeRec;
1655     {-compute the date/time of the vernal/autumnal equinox}
1656     {if Vernal = True, compute astronomical vernal equinox (spring in N. Hem.)}
1657     { = False, compute astronomical autumnal equinox (fall in N. Hem.)}
1658     begin
1659     Y := CheckYear(Y, Epoch);
1660     if Vernal then
1661     Result := SolEqPrim(Y, 0)
1662     else
1663     Result := SolEqPrim(Y, 2);
1664     end;
1665    
1666     function Easter(Y : Integer; Epoch : Integer) : TStDate;
1667     {-compute the date of Easter}
1668     var
1669     A, B,
1670     C, D,
1671     E, F,
1672     G, H,
1673     I, K,
1674     L, M,
1675     N, P : LongInt;
1676     begin
1677     Y := CheckYear(Y, Epoch);
1678    
1679     if (Y < MinYear) or (Y > MaxYear) then begin
1680     Result := BadDate;
1681     Exit;
1682     end;
1683    
1684     A := Y mod 19;
1685     B := Y div 100;
1686     C := Y mod 100;
1687     D := B div 4;
1688     E := B mod 4;
1689     F := (B+8) div 25;
1690     G := (B-F+1) div 3;
1691     H := (19*A + B - D - G + 15) mod 30;
1692     I := C div 4;
1693     K := C mod 4;
1694     L := (32 + 2*E + 2*I - H - K) mod 7;
1695     M := (A + 11*H + 22*L) div 451;
1696     N := (H + L - 7*M + 114) div 31;
1697     P := (H + L - 7*M + 114) mod 31 + 1;
1698    
1699     Result := DMYToStDate(P, N, Y, Epoch);
1700     end;
1701    
1702     {Conversion routines}
1703     function HoursMin(RA : Double) : String;
1704     {-convert RA to formatted hh:mm:ss string}
1705     var
1706     HR, MR : Double;
1707     HS, MS : string[12];
1708    
1709     begin
1710     if abs(RA) >= 360.0 then
1711     RA := Frac(RA / 360.0) * 360;
1712     if RA < 0 then
1713     RA := RA + 360.0;
1714    
1715     HR := Int(RA / 15.0);
1716     MR := Frac(RA / 15.0) * 60;
1717    
1718     Str(MR:5:2, MS);
1719     if MS = '60.00' then begin
1720     MS := '00.00';
1721     HR := HR + 1;
1722     if HR = 24 then
1723     HS := '0'
1724     else
1725     Str(HR:2:0, HS);
1726     end else begin
1727     if MS[1] = ' ' then
1728     MS[1] := '0';
1729     Str(Hr:2:0, HS);
1730     end;
1731     Result := HS + ' ' + MS;
1732     end;
1733    
1734     function DegsMin(DC : Double) : String;
1735     {-convert Declination to formatted +/-dd:mm:ss string}
1736     var
1737     DR, MR : Double;
1738     DS, MS : string[12];
1739     begin
1740     if abs(DC) > 90 then
1741     DC := Frac(DC / 90.0) * 90.0;
1742    
1743     DR := Int(DC);
1744     MR := Frac(abs(DC)) * 60;
1745    
1746     Str(MR:4:1, MS);
1747     if MS = '60.0' then begin
1748     MS := '00.0';
1749     if DC >= 0 then
1750     DR := DR + 1
1751     else
1752     DR := DR - 1;
1753     end;
1754    
1755     if abs(DC) < 10 then begin
1756     Str(DR:2:0, DS);
1757     DS := TrimLeadS(DS);
1758     if DC < 0 then begin
1759     if DC > -1 then
1760     DS := '- 0'
1761     else
1762     DS := '- ' + DS[2];
1763     end else
1764     DS := '+ ' + DS;
1765     end else begin
1766     Str(DR:3:0, DS);
1767     DS := TrimLeadS(DS);
1768     if DC < 0 then begin
1769     Delete(DS,1,1);
1770     DS := '-' + DS;
1771     end else
1772     DS := '+' + DS;
1773     end;
1774     if MS[1] = ' ' then
1775     MS[1] := '0';
1776     Result := DS + ' ' + MS;
1777     end;
1778    
1779     function DateTimeToAJD(D : TDateTime) : Double;
1780     begin
1781     Result := D + AJDOffset;
1782     end;
1783    
1784     function AJDToDateTime(D : Double) : TDateTime;
1785     begin
1786     Result := D - AJDOffset;
1787     end;
1788    
1789    
1790     initialization
1791     AJDOffSet := AstJulianDatePrim(1600, 1, 1, 0) - EncodeDate(1600, 1, 1);
1792     end.

  ViewVC Help
Powered by ViewVC 1.1.20