Files
sjy01-image-proc/vendor/github.com/hebl/gofa/jd.go
2024-10-24 15:46:01 +08:00

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
}