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.
|