astro/coord/research.go
starainrt 3ffdbe0034
feat: 扩展天文计算能力
- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息
- 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算
- 新增木星伽利略卫星位置、现象与接触事件计算
- 新增恒星星表、星座判定、自行修正与观测辅助能力
- 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包
- 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算
- 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试
- 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试
- 更新中文和英文 README,补充示例、精度说明、SVG 配图
2026-05-01 22:38:44 +08:00

221 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package coord
import "math"
// Galactic 银道坐标 / galactic coordinates.
type Galactic struct {
Lon float64 // 银经,单位度 / galactic longitude in degrees.
Lat float64 // 银纬,单位度 / galactic latitude in degrees.
}
// SOFA ICRS2G/G2ICRS fixed rotation matrix.
// Source: IAU SOFA 2023-10-11, iauIcrs2g/iauG2icrs.
var icrsToGalacticMatrix = [3][3]float64{
{-0.054875560416215368492398900454, -0.873437090234885048760383168409, -0.483835015548713226831774175116},
{+0.494109427875583673525222371358, -0.444829629960011178146614061616, +0.746982244497218890527388004556},
{-0.867666149019004701181616534570, -0.198076373431201528180486091412, +0.455983776175066922272100478348},
}
// EclipticToEquatorialByObliquity 黄道转赤道坐标(指定黄赤交角) / ecliptic to equatorial by obliquity.
//
// lon: 黄经,单位度
// lat: 黄纬,单位度
// obliquity: 黄赤交角,单位度
//
// 返回:
//
// 赤经 RA单位度赤纬 Dec单位度
func EclipticToEquatorialByObliquity(lon, lat, obliquity float64) Equatorial {
sinLon, cosLon := sinCosDeg(lon)
sinLat, cosLat := sinCosDeg(lat)
sinObliquity, cosObliquity := sinCosDeg(obliquity)
ra := normalize360(math.Atan2(sinLon*cosObliquity-math.Tan(lat*math.Pi/180)*sinObliquity, cosLon) * 180 / math.Pi)
dec := math.Asin(clampUnit(sinLat*cosObliquity+cosLat*sinObliquity*sinLon)) * 180 / math.Pi
return Equatorial{RA: ra, Dec: dec}
}
// EquatorialToEclipticByObliquity 赤道转黄道坐标(指定黄赤交角) / equatorial to ecliptic by obliquity.
//
// ra: 赤经,单位度
// dec: 赤纬,单位度
// obliquity: 黄赤交角,单位度
//
// 返回:
//
// 黄经 Lon单位度黄纬 Lat单位度
func EquatorialToEclipticByObliquity(ra, dec, obliquity float64) Ecliptic {
sinRA, cosRA := sinCosDeg(ra)
sinDec, cosDec := sinCosDeg(dec)
sinObliquity, cosObliquity := sinCosDeg(obliquity)
lon := normalize360(math.Atan2(sinRA*cosObliquity+math.Tan(dec*math.Pi/180)*sinObliquity, cosRA) * 180 / math.Pi)
lat := math.Asin(clampUnit(sinDec*cosObliquity-cosDec*sinObliquity*sinRA)) * 180 / math.Pi
return Ecliptic{Lon: lon, Lat: lat}
}
// HourAngleDeclinationToHorizontal 时角赤纬转地平坐标 / horizontal coordinates from hour angle and declination.
//
// hourAngle: 时角,单位度
// declination: 赤纬,单位度
// latitude: 观测者地理纬度,单位度,北正南负
//
// 返回:
//
// 方位角 Azimuth正北为0顺时针增加、高度角 Altitude、天顶距 Zenith均为度
func HourAngleDeclinationToHorizontal(hourAngle, declination, latitude float64) Horizontal {
sinLatitude, cosLatitude := sinCosDeg(latitude)
sinDeclination, cosDeclination := sinCosDeg(declination)
sinHourAngle, cosHourAngle := sinCosDeg(hourAngle)
altitude := math.Asin(clampUnit(sinLatitude*sinDeclination+cosLatitude*cosDeclination*cosHourAngle)) * 180 / math.Pi
azimuth := normalize360(math.Atan2(-cosDeclination*sinHourAngle, cosLatitude*sinDeclination-sinLatitude*cosDeclination*cosHourAngle) * 180 / math.Pi)
return Horizontal{
Azimuth: azimuth,
Altitude: altitude,
Zenith: 90 - altitude,
HourAngle: normalize360(hourAngle),
}
}
// HorizontalToHourAngleDeclination 地平坐标转时角赤纬 / hour angle and declination from horizontal coordinates.
//
// azimuth: 方位角单位度正北为0顺时针增加
// altitude: 高度角,单位度
// latitude: 观测者地理纬度,单位度,北正南负
//
// 返回:
//
// 时角 HourAngle单位度赤纬 Declination单位度
func HorizontalToHourAngleDeclination(azimuth, altitude, latitude float64) (hourAngle, declination float64) {
sinLatitude, cosLatitude := sinCosDeg(latitude)
sinAltitude, cosAltitude := sinCosDeg(altitude)
sinAzimuth, cosAzimuth := sinCosDeg(azimuth)
declination = math.Asin(clampUnit(sinLatitude*sinAltitude+cosLatitude*cosAltitude*cosAzimuth)) * 180 / math.Pi
sinHourAngle := -cosAltitude * sinAzimuth
cosHourAngle := sinAltitude*cosLatitude - cosAltitude*sinLatitude*cosAzimuth
hourAngle = normalize360(math.Atan2(sinHourAngle, cosHourAngle) * 180 / math.Pi)
return hourAngle, declination
}
// EquatorialToHorizontalByLocalSiderealTime 赤道转地平坐标(指定地方恒星时) / equatorial to horizontal by local sidereal time.
//
// localSiderealTimeHours: 站点本地恒星时,单位小时
// ra: 赤经,单位度
// dec: 赤纬,单位度
// latitude: 观测者地理纬度,单位度,北正南负
//
// 返回:
//
// 方位角 Azimuth正北为0顺时针增加、高度角 Altitude、天顶距 Zenith均为度
// 附带返回对应的时角 HourAngle单位度
//
// 例:
//
// hz := coord.EquatorialToHorizontalByLocalSiderealTime(10.5, 83.6331, 22.0145, 31.2)
func EquatorialToHorizontalByLocalSiderealTime(localSiderealTimeHours, ra, dec, latitude float64) Horizontal {
hourAngle := normalize360(localSiderealTimeHours*15 - ra)
return HourAngleDeclinationToHorizontal(hourAngle, dec, latitude)
}
// HorizontalToEquatorialByLocalSiderealTime 地平转赤道坐标(指定地方恒星时) / horizontal to equatorial by local sidereal time.
//
// localSiderealTimeHours: 站点本地恒星时,单位小时
// azimuth: 方位角单位度正北为0顺时针增加
// altitude: 高度角,单位度
// latitude: 观测者地理纬度,单位度,北正南负
//
// 返回:
//
// 赤经 RA单位度赤纬 Dec单位度
//
// 例:
//
// eq := coord.HorizontalToEquatorialByLocalSiderealTime(10.5, 128.2, 37.6, 31.2)
func HorizontalToEquatorialByLocalSiderealTime(localSiderealTimeHours, azimuth, altitude, latitude float64) Equatorial {
hourAngle, declination := HorizontalToHourAngleDeclination(azimuth, altitude, latitude)
ra := normalize360(localSiderealTimeHours*15 - hourAngle)
return Equatorial{RA: ra, Dec: declination}
}
// EquatorialToGalactic 赤道转银道坐标 / equatorial to galactic coordinates.
//
// ra: ICRS 赤经,单位度
// dec: ICRS 赤纬,单位度
//
// 返回:
//
// 银经 Lon单位度银纬 Lat单位度
func EquatorialToGalactic(ra, dec float64) Galactic {
vector := sphericalToVector(ra, dec)
rotated := matrixVectorMul(icrsToGalacticMatrix, vector)
lon, lat := vectorToSpherical(rotated)
return Galactic{Lon: lon, Lat: lat}
}
// GalacticToEquatorial 银道转赤道坐标 / galactic to equatorial coordinates.
//
// lon: 银经,单位度
// lat: 银纬,单位度
//
// 返回:
//
// ICRS 赤经 RA单位度ICRS 赤纬 Dec单位度
func GalacticToEquatorial(lon, lat float64) Equatorial {
vector := sphericalToVector(lon, lat)
rotated := matrixTransposeVectorMul(icrsToGalacticMatrix, vector)
ra, dec := vectorToSpherical(rotated)
return Equatorial{RA: ra, Dec: dec}
}
func sinCosDeg(angle float64) (sinValue, cosValue float64) {
return math.Sincos(angle * math.Pi / 180)
}
func normalize360(angle float64) float64 {
angle = math.Mod(angle, 360)
if angle < 0 {
angle += 360
}
return angle
}
func clampUnit(value float64) float64 {
if value > 1 {
return 1
}
if value < -1 {
return -1
}
return value
}
func sphericalToVector(lon, lat float64) [3]float64 {
sinLon, cosLon := sinCosDeg(lon)
sinLat, cosLat := sinCosDeg(lat)
return [3]float64{cosLat * cosLon, cosLat * sinLon, sinLat}
}
func vectorToSpherical(vector [3]float64) (lon, lat float64) {
lon = normalize360(math.Atan2(vector[1], vector[0]) * 180 / math.Pi)
lat = math.Asin(clampUnit(vector[2])) * 180 / math.Pi
return lon, lat
}
func matrixVectorMul(matrix [3][3]float64, vector [3]float64) [3]float64 {
return [3]float64{
matrix[0][0]*vector[0] + matrix[0][1]*vector[1] + matrix[0][2]*vector[2],
matrix[1][0]*vector[0] + matrix[1][1]*vector[1] + matrix[1][2]*vector[2],
matrix[2][0]*vector[0] + matrix[2][1]*vector[1] + matrix[2][2]*vector[2],
}
}
func matrixTransposeVectorMul(matrix [3][3]float64, vector [3]float64) [3]float64 {
return [3]float64{
matrix[0][0]*vector[0] + matrix[1][0]*vector[1] + matrix[2][0]*vector[2],
matrix[0][1]*vector[0] + matrix[1][1]*vector[1] + matrix[2][1]*vector[2],
matrix[0][2]*vector[0] + matrix[1][2]*vector[1] + matrix[2][2]*vector[2],
}
}