443 lines
10 KiB
Go
443 lines
10 KiB
Go
// Copyright 2022 HE Boliang
|
|
// All rights reserved.
|
|
|
|
package gofa
|
|
|
|
// Calendar
|
|
|
|
/*
|
|
Cal2jd Gregorian Calendar to Julian Date.
|
|
|
|
Given:
|
|
iy,im,id int year, month, day in Gregorian calendar (Note 1)
|
|
|
|
Returned:
|
|
djm0 float64 MJD zero-point: always 2400000.5
|
|
djm float64 Modified Julian Date for 0 hrs
|
|
|
|
Returned (function value):
|
|
int status:
|
|
0 = OK
|
|
-1 = bad year (Note 3: JD not computed)
|
|
-2 = bad month (JD not computed)
|
|
-3 = bad day (JD computed)
|
|
|
|
Notes:
|
|
|
|
1. The algorithm used is valid from -4800 March 1, but this
|
|
implementation rejects dates before -4799 January 1.
|
|
|
|
2. The Julian Date is returned in two pieces, in the usual SOFA
|
|
manner, which is designed to preserve time resolution. The
|
|
Julian Date is available as a single number by adding djm0 and
|
|
djm.
|
|
|
|
3. In early eras the conversion is from the "Proleptic Gregorian
|
|
Calendar"; no account is taken of the date(s) of adoption of
|
|
the Gregorian Calendar, nor is the AD/BC numbering convention
|
|
observed.
|
|
|
|
Reference:
|
|
|
|
Explanatory Supplement to the Astronomical Almanac,
|
|
P. Kenneth Seidelmann (ed), University Science Books (1992),
|
|
Section 12.92 (p604).
|
|
*/
|
|
func Cal2jd(iy, im, id int, djm0, djm *float64) int {
|
|
var j, ly, my int
|
|
var iypmy int
|
|
|
|
/* Earliest year allowed (4800BC) */
|
|
const IYMIN = -4799
|
|
|
|
/* Month lengths in days */
|
|
mtab := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
|
|
|
/* Preset status. */
|
|
j = 0
|
|
|
|
/* Validate year and month. */
|
|
if iy < IYMIN {
|
|
return -1
|
|
}
|
|
if im < 1 || im > 12 {
|
|
return -2
|
|
}
|
|
|
|
/* If February in a leap year, 1, otherwise 0. */
|
|
// ly = ((im == 2) && !(iy%4) && (iy%100 || !(iy%400)));
|
|
if (im == 2) && (iy%4 == 0) && ((iy%100 != 0) || (iy%400 == 0)) {
|
|
ly = 1
|
|
} else {
|
|
ly = 0
|
|
}
|
|
|
|
/* Validate day, taking into account leap years. */
|
|
if (id < 1) || (id > (mtab[im-1] + ly)) {
|
|
j = -3
|
|
}
|
|
|
|
/* Return result. */
|
|
my = (im - 14) / 12
|
|
iypmy = (iy + my)
|
|
*djm0 = DJM0
|
|
*djm = float64((1461*(iypmy+4800))/4 + (367*(im-2-12*my))/12 - (3*((iypmy+4900)/100))/4 + id - 2432076)
|
|
|
|
/* Return status. */
|
|
return j
|
|
|
|
}
|
|
|
|
/*
|
|
Jd2cal Julian Date to Gregorian year, month, day, and fraction of a day.
|
|
|
|
Given:
|
|
dj1,dj2 float64 Julian Date (Notes 1, 2)
|
|
|
|
Returned (arguments):
|
|
iy int year
|
|
im int month
|
|
id int day
|
|
fd float64 fraction of day
|
|
|
|
Returned (function value):
|
|
int status:
|
|
0 = OK
|
|
-1 = unacceptable date (Note 1)
|
|
|
|
Notes:
|
|
|
|
1) The earliest valid date is -68569.5 (-4900 March 1). The
|
|
largest value accepted is 1e9.
|
|
|
|
2) The Julian Date is apportioned in any convenient way between
|
|
the arguments dj1 and dj2. For example, JD=2450123.7 could
|
|
be expressed in any of these ways, among others:
|
|
|
|
dj1 dj2
|
|
|
|
2450123.7 0.0 (JD method)
|
|
2451545.0 -1421.3 (J2000 method)
|
|
2400000.5 50123.2 (MJD method)
|
|
2450123.5 0.2 (date & time method)
|
|
|
|
Separating integer and fraction uses the "compensated summation"
|
|
algorithm of Kahan-Neumaier to preserve as much precision as
|
|
possible irrespective of the jd1+jd2 apportionment.
|
|
|
|
3) In early eras the conversion is from the "proleptic Gregorian
|
|
calendar"; no account is taken of the date(s) of adoption of
|
|
the Gregorian calendar, nor is the AD/BC numbering convention
|
|
observed.
|
|
|
|
References:
|
|
|
|
Explanatory Supplement to the Astronomical Almanac,
|
|
P. Kenneth Seidelmann (ed), University Science Books (1992),
|
|
Section 12.92 (p604).
|
|
|
|
Klein, A., A Generalized Kahan-Babuska-Summation-Algorithm.
|
|
Computing, 76, 279-293 (2006), Section 3.
|
|
*/
|
|
func Jd2cal(dj1, dj2 float64, iy, im, id *int, fd *float64) int {
|
|
/* Minimum and maximum allowed JD */
|
|
const DJMIN = -68569.5
|
|
const DJMAX = 1e9
|
|
|
|
var jd, i, l, n, k int64
|
|
var dj, f1, f2, d, s, cs, x, t, f float64
|
|
var v [2]float64
|
|
|
|
/* Verify date is acceptable. */
|
|
dj = dj1 + dj2
|
|
if dj < DJMIN || dj > DJMAX {
|
|
return -1
|
|
}
|
|
|
|
/* Separate day and fraction (where -0.5 <= fraction < 0.5). */
|
|
d = dnint(dj1)
|
|
f1 = dj1 - d
|
|
jd = int64(d)
|
|
d = dnint(dj2)
|
|
f2 = dj2 - d
|
|
jd += int64(d)
|
|
|
|
/* Compute f1+f2+0.5 using compensated summation (Klein 2006). */
|
|
s = 0.5
|
|
cs = 0.0
|
|
v[0] = f1
|
|
v[1] = f2
|
|
for i := 0; i < 2; i++ {
|
|
x = v[i]
|
|
t = s + x
|
|
if fabs(s) >= fabs(x) {
|
|
cs += (s - t) + x
|
|
} else {
|
|
cs += (x - t) + s
|
|
}
|
|
|
|
s = t
|
|
if s >= 1.0 {
|
|
jd++
|
|
s -= 1.0
|
|
}
|
|
}
|
|
f = s + cs
|
|
cs = f - s
|
|
|
|
/* Deal with negative f. */
|
|
if f < 0.0 {
|
|
|
|
/* Compensated summation: assume that |s| <= 1.0. */
|
|
f = s + 1.0
|
|
cs += (1.0 - f) + s
|
|
s = f
|
|
f = s + cs
|
|
cs = f - s
|
|
jd--
|
|
}
|
|
|
|
/* Deal with f that is 1.0 or more (when rounded to double). */
|
|
if (f - 1.0) >= -DBL_EPSILON/4.0 {
|
|
|
|
/* Compensated summation: assume that |s| <= 1.0. */
|
|
t = s - 1.0
|
|
cs += (s - t) - 1.0
|
|
s = t
|
|
f = s + cs
|
|
if -DBL_EPSILON/2.0 < f {
|
|
jd++
|
|
f = gmax(f, 0.0)
|
|
}
|
|
}
|
|
|
|
/* Express day in Gregorian calendar. */
|
|
l = jd + 68569
|
|
n = (4 * l) / 146097
|
|
l -= (146097*n + 3) / 4
|
|
i = (4000 * (l + 1)) / 1461001
|
|
l -= (1461*i)/4 - 31
|
|
k = (80 * l) / 2447
|
|
*id = int(l - (2447*k)/80)
|
|
l = k / 11
|
|
*im = int(k + 2 - 12*l)
|
|
*iy = int(100*(n-49) + i + l)
|
|
*fd = f
|
|
|
|
/* Success. */
|
|
return 0
|
|
}
|
|
|
|
/*
|
|
Jdcalf Julian Date to Gregorian Calendar, expressed in a form convenient
|
|
for formatting messages: rounded to a specified precision.
|
|
|
|
Given:
|
|
ndp int number of decimal places of days in fraction
|
|
dj1,dj2 float64 dj1+dj2 = Julian Date (Note 1)
|
|
|
|
Returned:
|
|
iymdf [4]int year, month, day, fraction in Gregorian calendar
|
|
|
|
Returned (function value):
|
|
int status:
|
|
-1 = date out of range
|
|
0 = OK
|
|
+1 = NDP not 0-9 (interpreted as 0)
|
|
|
|
Notes:
|
|
|
|
1) The Julian Date is apportioned in any convenient way between
|
|
the arguments dj1 and dj2. For example, JD=2450123.7 could
|
|
be expressed in any of these ways, among others:
|
|
|
|
dj1 dj2
|
|
|
|
2450123.7 0.0 (JD method)
|
|
2451545.0 -1421.3 (J2000 method)
|
|
2400000.5 50123.2 (MJD method)
|
|
2450123.5 0.2 (date & time method)
|
|
|
|
2) In early eras the conversion is from the "Proleptic Gregorian
|
|
Calendar"; no account is taken of the date(s) of adoption of
|
|
the Gregorian Calendar, nor is the AD/BC numbering convention
|
|
observed.
|
|
|
|
3) See also the function Jd2cal.
|
|
|
|
4) The number of decimal places ndp should be 4 or less if internal
|
|
overflows are to be avoided on platforms which use 16-bit
|
|
integers.
|
|
|
|
Called:
|
|
Jd2cal JD to Gregorian calendar
|
|
|
|
Reference:
|
|
|
|
Explanatory Supplement to the Astronomical Almanac,
|
|
P. Kenneth Seidelmann (ed), University Science Books (1992),
|
|
Section 12.92 (p604).
|
|
|
|
*/
|
|
func Jdcalf(ndp int, dj1, dj2 float64, iymdf *[4]int) int {
|
|
var j, js int
|
|
var denom, d1, d2, f1, f2, d, djd, f, rf float64
|
|
|
|
/* Denominator of fraction (e.g. 100 for 2 decimal places). */
|
|
if (ndp >= 0) && (ndp <= 9) {
|
|
j = 0
|
|
denom = pow10(ndp)
|
|
} else {
|
|
j = 1
|
|
denom = 1.0
|
|
}
|
|
|
|
/* Copy the date, big then small. */
|
|
if fabs(dj1) >= fabs(dj2) {
|
|
d1 = dj1
|
|
d2 = dj2
|
|
} else {
|
|
d1 = dj2
|
|
d2 = dj1
|
|
}
|
|
|
|
/* Realign to midnight (without rounding error). */
|
|
d1 -= 0.5
|
|
|
|
/* Separate day and fraction (as precisely as possible). */
|
|
d = dnint(d1)
|
|
f1 = d1 - d
|
|
djd = d
|
|
d = dnint(d2)
|
|
f2 = d2 - d
|
|
djd += d
|
|
d = dnint(f1 + f2)
|
|
f = (f1 - d) + f2
|
|
if f < 0.0 {
|
|
f += 1.0
|
|
d -= 1.0
|
|
}
|
|
djd += d
|
|
|
|
/* Round the total fraction to the specified number of places. */
|
|
rf = dnint(f*denom) / denom
|
|
|
|
/* Re-align to noon. */
|
|
djd += 0.5
|
|
|
|
/* Convert to Gregorian calendar. */
|
|
js = Jd2cal(djd, rf, &iymdf[0], &iymdf[1], &iymdf[2], &f)
|
|
if js == 0 {
|
|
iymdf[3] = int(dnint(f * denom))
|
|
} else {
|
|
j = js
|
|
}
|
|
|
|
/* Return the status. */
|
|
return j
|
|
}
|
|
|
|
/*
|
|
Epb Julian Date to Besselian Epoch.
|
|
|
|
Given:
|
|
dj1,dj2 float64 Julian Date (see note)
|
|
|
|
Returned (function value):
|
|
float64 Besselian Epoch.
|
|
|
|
Note:
|
|
|
|
The Julian Date is supplied in two pieces, in the usual SOFA
|
|
manner, which is designed to preserve time resolution. The
|
|
Julian Date is available as a single number by adding dj1 and
|
|
dj2. The maximum resolution is achieved if dj1 is 2451545.0
|
|
(J2000.0).
|
|
|
|
Reference:
|
|
|
|
Lieske, J.H., 1979. Astron.Astrophys., 73, 282.
|
|
*/
|
|
func Epb(dj1, dj2 float64) float64 {
|
|
/* J2000.0-B1900.0 (2415019.81352) in days */
|
|
const D1900 = 36524.68648
|
|
|
|
return 1900.0 + ((dj1-DJ00)+(dj2+D1900))/DTY
|
|
}
|
|
|
|
/*
|
|
Epb2jd Besselian Epoch to Julian Date.
|
|
|
|
Given:
|
|
epb float64 Besselian Epoch (e.g. 1957.3)
|
|
|
|
Returned:
|
|
djm0 float64 MJD zero-point: always 2400000.5
|
|
djm float64 Modified Julian Date
|
|
|
|
Note:
|
|
|
|
The Julian Date is returned in two pieces, in the usual SOFA
|
|
manner, which is designed to preserve time resolution. The
|
|
Julian Date is available as a single number by adding djm0 and
|
|
djm.
|
|
|
|
Reference:
|
|
Lieske, J.H., 1979, Astron.Astrophys. 73, 282.
|
|
*/
|
|
func Epb2jd(epb float64, djm0, djm *float64) {
|
|
*djm0 = DJM0
|
|
*djm = 15019.81352 + (epb-1900.0)*DTY
|
|
}
|
|
|
|
/*
|
|
Epj Julian Date to Julian Epoch.
|
|
|
|
Given:
|
|
dj1,dj2 float64 Julian Date (see note)
|
|
|
|
Returned (function value):
|
|
float64 Julian Epoch
|
|
|
|
Note:
|
|
|
|
The Julian Date is supplied in two pieces, in the usual SOFA
|
|
manner, which is designed to preserve time resolution. The
|
|
Julian Date is available as a single number by adding dj1 and
|
|
dj2. The maximum resolution is achieved if dj1 is 2451545.0
|
|
(J2000.0).
|
|
|
|
Reference:
|
|
Lieske, J.H., 1979, Astron.Astrophys. 73, 282.
|
|
*/
|
|
func Epj(dj1, dj2 float64) float64 {
|
|
epj := 2000.0 + ((dj1-DJ00)+dj2)/DJY
|
|
|
|
return epj
|
|
}
|
|
|
|
/*
|
|
Epj2jd Julian Epoch to Julian Date.
|
|
|
|
Given:
|
|
epj float64 Julian Epoch (e.g. 1996.8)
|
|
|
|
Returned:
|
|
djm0 float64 MJD zero-point: always 2400000.5
|
|
djm float64 Modified Julian Date
|
|
|
|
Note:
|
|
|
|
The Julian Date is returned in two pieces, in the usual SOFA
|
|
manner, which is designed to preserve time resolution. The
|
|
Julian Date is available as a single number by adding djm0 and
|
|
djm.
|
|
|
|
Reference:
|
|
Lieske, J.H., 1979, Astron.Astrophys. 73, 282.
|
|
*/
|
|
func Epj2jd(epj float64, djm0, djm *float64) {
|
|
*djm0 = DJM0
|
|
*djm = DJM00 + (epj-2000.0)*365.25
|
|
}
|