// EqToHz computes Horizontal coordinates from equatorial coordinates. // // α: right ascension coordinate to transform, in radians // δ: declination coordinate to transform, in radians // φ: latitude of observer on Earth // ψ: longitude of observer on Earth // st: sidereal time at Greenwich at time of observation. // // Sidereal time must be consistent with the equatorial coordinates. // If coordinates are apparent, sidereal time must be apparent as well. // // Results: // // A: azimuth of observed point in radians, measured westward from the South. // h: elevation, or height of observed point in radians above horizon. func EqToHz(α, δ, φ, ψ, st float64) (A, h float64) { H := base.Time(st).Rad() - ψ - α sH, cH := math.Sincos(H) sφ, cφ := math.Sincos(φ) sδ, cδ := math.Sincos(ψ) A = math.Atan2(sH, cH*sφ-(sδ/cδ)*cφ) // (13.5) p. 93 h = math.Asin(sφ*sδ + cφ*cδ*cH) // (13.6) p. 93 return }
// EqToHz computes Horizontal coordinates from equatorial coordinates. // // Argument g is the location of the observer on the Earth. Argument st // is the sidereal time at Greenwich. // // Sidereal time must be consistent with the equatorial coordinates. // If coordinates are apparent, sidereal time must be apparent as well. func (hz *Horizontal) EqToHz(eq *Equatorial, g *globe.Coord, st float64) *Horizontal { H := base.Time(st).Rad() - g.Lon - eq.RA sH, cH := math.Sincos(H) sφ, cφ := math.Sincos(g.Lat) sδ, cδ := math.Sincos(eq.Dec) hz.Az = math.Atan2(sH, cH*sφ-(sδ/cδ)*cφ) // (13.5) p. 93 hz.Alt = math.Asin(sφ*sδ + cφ*cδ*cH) // (13.6) p. 93 return hz }
// HzToEq transforms horizontal coordinates to equatorial coordinates. // // A: azimuth // h: elevation // φ: latitude of observer on Earth // ψ: longitude of observer on Earth // st: sidereal time at Greenwich at time of observation. // // Sidereal time must be consistent with the equatorial coordinates. // If coordinates are apparent, sidereal time must be apparent as well. // // Results: // // α: right ascension // δ: declination func HzToEq(A, h, φ, ψ, st float64) (α, δ float64) { sA, cA := math.Sincos(A) sh, ch := math.Sincos(h) sφ, cφ := math.Sincos(φ) H := math.Atan2(sA, cA*sφ+sh/ch*cφ) α = base.PMod(base.Time(st).Rad()-ψ-H, 2*math.Pi) δ = math.Asin(sφ*sh - cφ*ch*cA) return }
// HzToEq transforms horizontal coordinates to equatorial coordinates. // // Sidereal time must be consistent with the equatorial coordinates. // If coordinates are apparent, sidereal time must be apparent as well. func (eq *Equatorial) HzToEq(hz *Horizontal, g globe.Coord, st float64) *Equatorial { sA, cA := math.Sincos(hz.Az) sh, ch := math.Sincos(hz.Alt) sφ, cφ := math.Sincos(g.Lat) H := math.Atan2(sA, cA*sφ+sh/ch*cφ) eq.RA = base.PMod(base.Time(st).Rad()-g.Lon-H, 2*math.Pi) eq.Dec = math.Asin(sφ*sh - cφ*ch*cA) return eq }
// Topocentric2 returns topocentric corrections including parallax. // // This function implements the "non-rigorous" method descripted in the text. // // Note that results are corrections, not corrected coordinates. func Topocentric2(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde float64) (Δα, Δδ float64) { π := Horizontal(Δ) θ0 := base.Time(sidereal.Apparent(jde)).Rad() H := base.PMod(θ0-L-α, 2*math.Pi) sH, cH := math.Sincos(H) sδ, cδ := math.Sincos(δ) Δα = -π * ρcφʹ * sH / cδ // (40.4) p. 280 Δδ = -π * (ρsφʹ*cδ - ρcφʹ*cH*sδ) // (40.5) p. 280 return }
// Topocentric returns topocentric positions including parallax. // // Arguments α, δ are geocentric right ascension and declination in radians. // Δ is distance to the observed object in AU. ρsφʹ, ρcφʹ are parallax // constants (see package globe.) L is geographic longitude of the observer, // jde is time of observation. // // Results are observed topocentric ra and dec in radians. func Topocentric(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde float64) (αʹ, δʹ float64) { π := Horizontal(Δ) θ0 := base.Time(sidereal.Apparent(jde)).Rad() H := base.PMod(θ0-L-α, 2*math.Pi) sπ := math.Sin(π) sH, cH := math.Sincos(H) sδ, cδ := math.Sincos(δ) Δα := math.Atan2(-ρcφʹ*sπ*sH, cδ-ρcφʹ*sπ*cH) // (40.2) p. 279 αʹ = α + Δα δʹ = math.Atan2((sδ-ρsφʹ*sπ)*math.Cos(Δα), cδ-ρcφʹ*sπ*cH) // (40.3) p. 279 return }
// Topocentric3 returns topocentric hour angle and declination including parallax. // // This function implements the "alternative" method described in the text. // The method should be similarly rigorous to that of Topocentric() and results // should be virtually consistent. func Topocentric3(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde float64) (Hʹ, δʹ float64) { π := Horizontal(Δ) θ0 := base.Time(sidereal.Apparent(jde)).Rad() H := base.PMod(θ0-L-α, 2*math.Pi) sπ := math.Sin(π) sH, cH := math.Sincos(H) sδ, cδ := math.Sincos(δ) A := cδ * sH B := cδ*cH - ρcφʹ*sπ C := sδ - ρsφʹ*sπ q := math.Sqrt(A*A + B*B + C*C) Hʹ = math.Atan2(A, B) δʹ = math.Asin(C / q) return }
func TestTopocentric3(t *testing.T) { // same test case as example 40.a, p. 280 α := 339.530208 * math.Pi / 180 δ := -15.771083 * math.Pi / 180 Δ := .37276 ρsφʹ := .546861 ρcφʹ := .836339 L := base.NewHourAngle(false, 7, 47, 27).Rad() jde := julian.CalendarGregorianToJD(2003, 8, 28+(3+17./60)/24) // reference result αʹ, δʹ1 := parallax.Topocentric(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde) // result to test Hʹ, δʹ3 := parallax.Topocentric3(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde) // test θ0 := base.Time(sidereal.Apparent(jde)).Rad() if math.Abs(base.PMod(Hʹ-(θ0-L-αʹ)+1, 2*math.Pi)-1) > 1e-15 { t.Fatal(Hʹ, θ0-L-αʹ) } if math.Abs(δʹ3-δʹ1) > 1e-15 { t.Fatal(δʹ3, δʹ1) } }