astro/coord/research.go

221 lines
8.0 KiB
Go
Raw Normal View History

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],
}
}