astro/basic/planet_physical.go

371 lines
13 KiB
Go
Raw Normal View History

package basic
import (
"math"
"b612.me/astro/planet"
. "b612.me/astro/tools"
)
const astronomicalUnitLightTimeDays = 0.0057755183
type planetPhysicalModel struct {
planetIndex int
positiveEast bool
equatorialRadius float64
polarRadius float64
poleRotation func(float64) (ra, dec, rotationEast float64)
}
type phaseAngleModel struct {
base float64
rate float64
accelRate float64
}
// PlanetPhysicalInfo 行星物理观测参数 / planetary physical observing parameters.
type PlanetPhysicalInfo struct {
// SubEarthLongitude 子地经度,单位度;正方向遵循该天体当前 IAU/Horizons 制图约定。
SubEarthLongitude float64
// SubEarthLatitude 子地纬度,单位度。
SubEarthLatitude float64
// SubSolarLongitude 子日经度,单位度;正方向遵循该天体当前 IAU/Horizons 制图约定。
SubSolarLongitude float64
// SubSolarLatitude 子日纬度,单位度。
SubSolarLatitude float64
// NorthPolePositionAngle 天体北极位置角,单位度。
NorthPolePositionAngle float64
}
var (
mercuryPhysicalModel = planetPhysicalModel{
planetIndex: 1,
positiveEast: false,
equatorialRadius: 2440.53,
polarRadius: 2438.26,
poleRotation: mercuryPoleRotation,
}
venusPhysicalModel = planetPhysicalModel{
planetIndex: 2,
positiveEast: true,
equatorialRadius: 6051.8,
polarRadius: 6051.8,
poleRotation: venusPoleRotation,
}
marsPhysicalModel = planetPhysicalModel{
planetIndex: 3,
positiveEast: false,
equatorialRadius: 3396.19,
polarRadius: 3376.20,
poleRotation: marsPoleRotation,
}
jupiterPhysicalModel = planetPhysicalModel{
planetIndex: 4,
positiveEast: false,
equatorialRadius: 71492.0,
polarRadius: 66854.0,
poleRotation: jupiterPoleRotation,
}
saturnPhysicalModel = planetPhysicalModel{
planetIndex: 5,
positiveEast: false,
equatorialRadius: 60268.0,
polarRadius: 54364.0,
poleRotation: saturnPoleRotation,
}
uranusPhysicalModel = planetPhysicalModel{
planetIndex: 6,
positiveEast: true,
equatorialRadius: 25559.0,
polarRadius: 24973.0,
poleRotation: uranusPoleRotation,
}
neptunePhysicalModel = planetPhysicalModel{
planetIndex: 7,
positiveEast: false,
equatorialRadius: 24764.0,
polarRadius: 24341.0,
poleRotation: neptunePoleRotation,
}
)
// MercuryPhysical 水星物理观测参数 / physical observing parameters of Mercury.
func MercuryPhysical(jd float64) PlanetPhysicalInfo {
return MercuryPhysicalN(jd, -1)
}
// MercuryPhysicalN 水星物理观测参数(截断版) / truncated physical observing parameters of Mercury.
func MercuryPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, mercuryPhysicalModel)
}
// VenusPhysical 金星物理观测参数 / physical observing parameters of Venus.
func VenusPhysical(jd float64) PlanetPhysicalInfo {
return VenusPhysicalN(jd, -1)
}
// VenusPhysicalN 金星物理观测参数(截断版) / truncated physical observing parameters of Venus.
func VenusPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, venusPhysicalModel)
}
// MarsPhysical 火星物理观测参数 / physical observing parameters of Mars.
func MarsPhysical(jd float64) PlanetPhysicalInfo {
return MarsPhysicalN(jd, -1)
}
// MarsPhysicalN 火星物理观测参数(截断版) / truncated physical observing parameters of Mars.
func MarsPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, marsPhysicalModel)
}
// JupiterPhysical 木星物理观测参数 / physical observing parameters of Jupiter.
func JupiterPhysical(jd float64) PlanetPhysicalInfo {
return JupiterPhysicalN(jd, -1)
}
// JupiterPhysicalN 木星物理观测参数(截断版) / truncated physical observing parameters of Jupiter.
func JupiterPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, jupiterPhysicalModel)
}
// SaturnPhysical 土星物理观测参数 / physical observing parameters of Saturn.
func SaturnPhysical(jd float64) PlanetPhysicalInfo {
return SaturnPhysicalN(jd, -1)
}
// SaturnPhysicalN 土星物理观测参数(截断版) / truncated physical observing parameters of Saturn.
func SaturnPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, saturnPhysicalModel)
}
// UranusPhysical 天王星物理观测参数 / physical observing parameters of Uranus.
func UranusPhysical(jd float64) PlanetPhysicalInfo {
return UranusPhysicalN(jd, -1)
}
// UranusPhysicalN 天王星物理观测参数(截断版) / truncated physical observing parameters of Uranus.
func UranusPhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, uranusPhysicalModel)
}
// NeptunePhysical 海王星物理观测参数 / physical observing parameters of Neptune.
func NeptunePhysical(jd float64) PlanetPhysicalInfo {
return NeptunePhysicalN(jd, -1)
}
// NeptunePhysicalN 海王星物理观测参数(截断版) / truncated physical observing parameters of Neptune.
func NeptunePhysicalN(jd float64, n int) PlanetPhysicalInfo {
return planetPhysicalN(jd, n, neptunePhysicalModel)
}
func planetPhysicalN(jd float64, n int, model planetPhysicalModel) PlanetPhysicalInfo {
initialX, initialY, initialZ := planetXYZN(model.planetIndex, jd, n)
targetVector := Vector3{initialX, initialY, initialZ}
lightTimeDays := astronomicalUnitLightTimeDays * vectorMagnitude(targetVector)
targetJD := jd - lightTimeDays
geoX, geoY, geoZ := planetXYZN(model.planetIndex, targetJD, n)
geocentricVector := Vector3{geoX, geoY, geoZ}
observerDirection := normalizeVector(Vector3{-geocentricVector[0], -geocentricVector[1], -geocentricVector[2]})
heliocentricLongitude := planet.WherePlanetN(model.planetIndex, 0, targetJD, n)
heliocentricLatitude := planet.WherePlanetN(model.planetIndex, 1, targetJD, n)
solarDirection := normalizeVector(eclipticCartesian(heliocentricLongitude+180, -heliocentricLatitude, 1))
obliquity := EclipticObliquity(targetJD, false)
observerEquatorial := normalizeVector(rotateEclipticToEquatorial(observerDirection, obliquity))
solarEquatorial := normalizeVector(rotateEclipticToEquatorial(solarDirection, obliquity))
poleRA, poleDec, rotationEast := model.poleRotation(targetJD)
poleJ2000 := raDecToVector(poleRA, poleDec)
nodeJ2000 := Vector3{-math.Sin(poleRA * rad), math.Cos(poleRA * rad), 0}
eastJ2000 := normalizeVector(pxp(poleJ2000, nodeJ2000))
primeMeridianJ2000 := normalizeVector(Vector3{
nodeJ2000[0]*Cos(rotationEast) + eastJ2000[0]*Sin(rotationEast),
nodeJ2000[1]*Cos(rotationEast) + eastJ2000[1]*Sin(rotationEast),
nodeJ2000[2]*Cos(rotationEast) + eastJ2000[2]*Sin(rotationEast),
})
j2000ToDate := precessionMatrix(2451545.0, targetJD)
poleDate := normalizeVector(applyMatrix3(j2000ToDate, poleJ2000))
primeMeridianDate := normalizeVector(applyMatrix3(j2000ToDate, primeMeridianJ2000))
eastDate := normalizeVector(pxp(poleDate, primeMeridianDate))
poleRAOfDate, poleDecOfDate := vectorToRaDec(poleDate)
planetRA, planetDec := vectorToRaDec(observerEquatorial)
return PlanetPhysicalInfo{
SubEarthLongitude: bodyLongitude(observerEquatorial, primeMeridianDate, eastDate, model.positiveEast),
SubEarthLatitude: bodyLatitude(observerEquatorial, poleDate, model.equatorialRadius, model.polarRadius),
SubSolarLongitude: bodyLongitude(solarEquatorial, primeMeridianDate, eastDate, model.positiveEast),
SubSolarLatitude: bodyLatitude(solarEquatorial, poleDate, model.equatorialRadius, model.polarRadius),
NorthPolePositionAngle: northPolePositionAngle(planetRA, planetDec, poleRAOfDate, poleDecOfDate),
}
}
func bodyLongitude(direction, primeMeridian, eastAxis Vector3, positiveEast bool) float64 {
eastLongitude := Limit360(math.Atan2(vectorDot(direction, eastAxis), vectorDot(direction, primeMeridian)) * deg)
if positiveEast {
return eastLongitude
}
return Limit360(360 - eastLongitude)
}
func bodyLatitude(direction, pole Vector3, equatorialRadius, polarRadius float64) float64 {
geocentricLatitude := ArcSin(vectorDot(direction, pole))
if equatorialRadius == polarRadius {
return geocentricLatitude
}
ratio := (polarRadius * polarRadius) / (equatorialRadius * equatorialRadius)
return math.Atan2(math.Sin(geocentricLatitude*rad), math.Cos(geocentricLatitude*rad)*ratio) * deg
}
func northPolePositionAngle(planetRA, planetDec, poleRA, poleDec float64) float64 {
y := math.Cos(poleDec*rad) * math.Sin((poleRA-planetRA)*rad)
x := math.Sin(poleDec*rad)*math.Cos(planetDec*rad) -
math.Cos(poleDec*rad)*math.Sin(planetDec*rad)*math.Cos((poleRA-planetRA)*rad)
return Limit360(-math.Atan2(y, x) * deg)
}
func vectorDot(a, b Vector3) float64 {
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
}
func vectorMagnitude(vector Vector3) float64 {
return math.Sqrt(vectorDot(vector, vector))
}
func normalizeVector(vector Vector3) Vector3 {
normalized, magnitude := pn(vector)
if magnitude == 0 {
return Vector3{}
}
return normalized
}
func mercuryPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
julianCentury := days / 36525.0
m1 := Limit360(174.7910857 + 4.092335*days)
m2 := Limit360(349.5821714 + 8.184670*days)
m3 := Limit360(164.3732571 + 12.277005*days)
m4 := Limit360(339.1643429 + 16.369340*days)
m5 := Limit360(153.9554286 + 20.461675*days)
ra = 281.0103 - 0.0328*julianCentury
dec = 61.4155 - 0.0049*julianCentury
rotationEast = 329.5988 + 6.1385108*days +
0.01067257*Sin(m1) -
0.00112309*Sin(m2) -
0.00011040*Sin(m3) -
0.00002539*Sin(m4) -
0.00000571*Sin(m5)
return
}
func venusPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
return 272.76, 67.16, 160.20 - 1.4813688*days
}
// Mars/Jupiter orientation terms follow the official NAIF text PCK `pck00011.tpc`.
var (
marsPoleRAAngles = [...]phaseAngleModel{
{198.991226, 19139.4819985, 0},
{226.292679, 38280.8511281, 0},
{249.663391, 57420.7251593, 0},
{266.183510, 76560.6367950, 0},
{79.398797, 0.5042615, 0},
}
marsPoleRACoefficients = [...]float64{0.000068, 0.000238, 0.000052, 0.000009, 0.419057}
marsPoleDECAngles = [...]phaseAngleModel{
{122.433576, 19139.9407476, 0},
{43.058401, 38280.8753272, 0},
{57.663379, 57420.7517205, 0},
{79.476401, 76560.6495004, 0},
{166.325722, 0.5042615, 0},
}
marsPoleDECCoefficients = [...]float64{0.000051, 0.000141, 0.000031, 0.000005, 1.591274}
marsPrimeMeridianAngles = [...]phaseAngleModel{
{129.071773, 19140.0328244, 0},
{36.352167, 38281.0473591, 0},
{56.668646, 57420.9295360, 0},
{67.364003, 76560.2552215, 0},
{104.792680, 95700.4387578, 0},
{95.391654, 0.5042615, 0},
}
marsPrimeMeridianCoefficients = [...]float64{0.000145, 0.000157, 0.000040, 0.000001, 0.000001, 0.584542}
jupiterPoleAngles = [...]phaseAngleModel{
{99.360714, 4850.4046, 0},
{175.895369, 1191.9605, 0},
{300.323162, 262.5475, 0},
{114.012305, 6070.2476, 0},
{49.511251, 64.3000, 0},
}
jupiterPoleRACoefficients = [...]float64{0.000117, 0.000938, 0.001432, 0.000030, 0.002150}
jupiterPoleDECCoefficients = [...]float64{0.000050, 0.000404, 0.000617, -0.000013, 0.000926}
)
func marsPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
julianCentury := days / 36525.0
ra = 317.269202 - 0.10927547*julianCentury + sumSinOrientationTerms(marsPoleRAAngles[:], marsPoleRACoefficients[:], julianCentury)
dec = 54.432516 - 0.05827105*julianCentury + sumCosOrientationTerms(marsPoleDECAngles[:], marsPoleDECCoefficients[:], julianCentury)
rotationEast = 176.049863 + 350.891982443297*days +
sumSinOrientationTerms(marsPrimeMeridianAngles[:], marsPrimeMeridianCoefficients[:], julianCentury)
return
}
func jupiterPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
julianCentury := days / 36525.0
ra = 268.056595 - 0.006499*julianCentury + sumSinOrientationTerms(jupiterPoleAngles[:], jupiterPoleRACoefficients[:], julianCentury)
dec = 64.495303 + 0.002413*julianCentury + sumCosOrientationTerms(jupiterPoleAngles[:], jupiterPoleDECCoefficients[:], julianCentury)
rotationEast = 284.95 + 870.5360000*days
return
}
func saturnPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
julianCentury := days / 36525.0
ra = 40.589 - 0.036*julianCentury
dec = 83.537 - 0.004*julianCentury
rotationEast = 38.90 + 810.7939024*days
return
}
func uranusPoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
return 257.311, -15.175, 203.81 - 501.1600928*days
}
func neptunePoleRotation(jd float64) (ra, dec, rotationEast float64) {
days := jd - 2451545.0
julianCentury := days / 36525.0
n := Limit360(357.85 + 52.316*julianCentury)
ra = 299.36 + 0.70*Sin(n)
dec = 43.46 - 0.51*Cos(n)
rotationEast = 249.978 + 541.1397757*days - 0.48*Sin(n)
return
}
func (model phaseAngleModel) at(julianCentury float64) float64 {
return Limit360(model.base + model.rate*julianCentury + model.accelRate*julianCentury*julianCentury)
}
func sumSinOrientationTerms(angles []phaseAngleModel, coefficients []float64, julianCentury float64) float64 {
sum := 0.0
for i, angle := range angles {
sum += coefficients[i] * Sin(angle.at(julianCentury))
}
return sum
}
func sumCosOrientationTerms(angles []phaseAngleModel, coefficients []float64, julianCentury float64) float64 {
sum := 0.0
for i, angle := range angles {
sum += coefficients[i] * Cos(angle.at(julianCentury))
}
return sum
}