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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 52624 byte(s)
Added tpsystools component
1 // 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