// EclToEq converts ecliptic coordinates to equatorial coordinates. // // λ: ecliptic longitude coordinate to transform // β: ecliptic latitude coordinate to transform // sε: sine of obliquity of the ecliptic // cε: cosine of obliquity of the ecliptic // // Results: // α: right ascension // δ: declination func EclToEq(λ, β unit.Angle, sε, cε float64) (α unit.RA, δ unit.Angle) { sλ, cλ := λ.Sincos() sβ, cβ := β.Sincos() α = unit.RAFromRad(math.Atan2(sλ*cε-(sβ/cβ)*sε, cλ)) // (13.3) p. 93 δ = unit.Angle(math.Asin(sβ*cε + cβ*sε*sλ)) // (13.4) p. 93 return }
// EclToEq converts ecliptic coordinates to equatorial coordinates. func (eq *Equatorial) EclToEq(ecl *Ecliptic, ε *Obliquity) *Equatorial { sβ, cβ := ecl.Lat.Sincos() sλ, cλ := ecl.Lon.Sincos() eq.RA = unit.RAFromRad(math.Atan2(sλ*ε.C-(sβ/cβ)*ε.S, cλ)) // (13.3) p. 93 eq.Dec = unit.Angle(math.Asin(sβ*ε.C + cβ*ε.S*sλ)) // (13.4) p. 93 return eq }
// Position precesses equatorial coordinates from one epoch to another, // including proper motions. // // If proper motions are not to be considered or are not applicable, pass 0, 0 // for mα, mδ // // Both eqFrom and eqTo must be non-nil, although they may point to the same // struct. EqTo is returned for convenience. func Position(eqFrom, eqTo *coord.Equatorial, epochFrom, epochTo float64, mα unit.HourAngle, mδ unit.Angle) *coord.Equatorial { p := NewPrecessor(epochFrom, epochTo) t := epochTo - epochFrom eqTo.RA = unit.RAFromRad(eqFrom.RA.Rad() + mα.Rad()*t) eqTo.Dec = eqFrom.Dec + mδ*unit.Angle(t) return p.Precess(eqTo, eqTo) }
// GalToEq converts galactic coordinates to equatorial coordinates. // // Resulting equatorial coordinates will be referred to the standard equinox of // B1950.0. For subsequent conversion to other epochs, see package precess and // utility functions in package meeus. func GalToEq(l, b unit.Angle) (α unit.RA, δ unit.Angle) { sdLon, cdLon := (l - galacticLon0).Sincos() sgδ, cgδ := galacticNorth.Dec.Sincos() sb, cb := b.Sincos() y := math.Atan2(sdLon, cdLon*sgδ-(sb/cb)*cgδ) α = unit.RAFromRad(y + galacticNorth.RA.Rad()) δ = unit.Angle(math.Asin(sb*sgδ + cb*cgδ*cdLon)) return }
// GalToEq converts galactic coordinates to equatorial coordinates. // // Resulting equatorial coordinates will be referred to the standard equinox of // B1950.0. For subsequent conversion to other epochs, see package precess and // utility functions in package meeus. func (eq *Equatorial) GalToEq(g *Galactic) *Equatorial { sdLon, cdLon := (g.Lon - galacticLon0).Sincos() sgδ, cgδ := galacticNorth.Dec.Sincos() sb, cb := g.Lat.Sincos() y := math.Atan2(sdLon, cdLon*sgδ-(sb/cb)*cgδ) eq.RA = unit.RAFromRad(y + galacticNorth.RA.Rad()) eq.Dec = unit.Angle(math.Asin(sb*sgδ + cb*cgδ*cdLon)) return eq }
// 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 // in the sense that tf coordinates are apparent, sidereal time must be // apparent as well. // // Results: // // α: right ascension // δ: declination func HzToEq(A, h, φ, ψ unit.Angle, st unit.Time) (α unit.RA, δ unit.Angle) { sA, cA := A.Sincos() sh, ch := h.Sincos() sφ, cφ := φ.Sincos() H := math.Atan2(sA, cA*sφ+sh/ch*cφ) α = unit.RAFromRad(st.Rad() - ψ.Rad() - H) δ = unit.Angle(math.Asin(sφ*sh - cφ*ch*cA)) return }
// HzToEq transforms horizontal coordinates to equatorial coordinates. // // Sidereal time st must be consistent with the equatorial coordinates // in the sense that if coordinates are apparent, sidereal time must be // apparent as well. func (eq *Equatorial) HzToEq(hz *Horizontal, g globe.Coord, st unit.Time) *Equatorial { sA, cA := hz.Az.Sincos() sh, ch := hz.Alt.Sincos() sφ, cφ := g.Lat.Sincos() H := math.Atan2(sA, cA*sφ+sh/ch*cφ) eq.RA = unit.RAFromRad(st.Rad() - g.Lon.Rad() - H) eq.Dec = unit.Angle(math.Asin(sφ*sh - cφ*ch*cA)) return eq }
// TrueEquatorial returns the true geometric position of the Sun as equatorial coordinates. func TrueEquatorial(jde float64) (α unit.RA, δ unit.Angle) { s, _ := True(base.J2000Century(jde)) ε := nutation.MeanObliquity(jde) ss, cs := s.Sincos() sε, cε := ε.Sincos() // (25.6, 25.7) p. 165 α = unit.RAFromRad(math.Atan2(cε*ss, cs)) δ = unit.Angle(math.Asin(sε * ss)) return }
// ApparentEquatorial returns the apparent position of the Sun as equatorial coordinates. // // α: right ascension in radians // δ: declination in radians func ApparentEquatorial(jde float64) (α unit.RA, δ unit.Angle) { T := base.J2000Century(jde) λ := ApparentLongitude(T) ε := nutation.MeanObliquity(jde) sλ, cλ := λ.Sincos() // (25.8) p. 165 ε += unit.AngleFromDeg(.00256).Mul(node(T).Cos()) sε, cε := ε.Sincos() α = unit.RAFromRad(math.Atan2(cε*sλ, cλ)) δ = unit.Angle(math.Asin(sε * sλ)) return }
// Precess precesses coordinates eqFrom, leaving result in eqTo. // // The same struct may be used for eqFrom and eqTo. // EqTo is returned for convenience. func (p *Precessor) Precess(eqFrom, eqTo *coord.Equatorial) *coord.Equatorial { // (21.4) p. 134 sδ, cδ := eqFrom.Dec.Sincos() sαζ, cαζ := (eqFrom.RA + p.ζ).Sincos() A := cδ * sαζ B := p.cθ*cδ*cαζ - p.sθ*sδ C := p.sθ*cδ*cαζ + p.cθ*sδ eqTo.RA = unit.RAFromRad(math.Atan2(A, B) + p.z.Rad()) if C < base.CosSmallAngle { eqTo.Dec = unit.Angle(math.Asin(C)) } else { eqTo.Dec = unit.Angle(math.Acos(math.Hypot(A, B))) // near pole } return eqTo }
// ProperMotion3D takes the 3D equatorial coordinates of an object // at one epoch and computes its coordinates at a new epoch, considering // proper motion and radial velocity. // // Radial distance (r) must be in parsecs, radial velocitiy (mr) in // parsecs per year. // // Both eqFrom and eqTo must be non-nil, although they may point to the same // struct. EqTo is returned for convenience. func ProperMotion3D(eqFrom, eqTo *coord.Equatorial, epochFrom, epochTo, r, mr float64, mα unit.HourAngle, mδ unit.Angle) *coord.Equatorial { sα, cα := eqFrom.RA.Sincos() sδ, cδ := eqFrom.Dec.Sincos() x := r * cδ * cα y := r * cδ * sα z := r * sδ mrr := mr / r zmδ := z * mδ.Rad() mx := x*mrr - zmδ*cα - y*mα.Rad() my := y*mrr - zmδ*sα + x*mα.Rad() mz := z*mrr + r*mδ.Rad()*cδ t := epochTo - epochFrom xp := x + t*mx yp := y + t*my zp := z + t*mz eqTo.RA = unit.RAFromRad(math.Atan2(yp, xp)) eqTo.Dec = unit.Angle(math.Atan2(zp, math.Hypot(xp, yp))) return eqTo }
// AstrometricJ2000 is a utility function for computing astrometric coordinates. // // It is used internally and only exported so that it can be used from // multiple packages. It is not otherwise expected to be used. // // Argument f is a function that returns J2000 equatorial rectangular // coodinates of a body. // // Results are J2000 right ascention, declination, and elongation. func AstrometricJ2000(f func(float64) (x, y, z float64), jde float64, e *pp.V87Planet) (α unit.RA, δ, ψ unit.Angle) { X, Y, Z := solarxyz.PositionJ2000(e, jde) x, y, z := f(jde) // (33.10) p. 229 ξ := X + x η := Y + y ζ := Z + z Δ := math.Sqrt(ξ*ξ + η*η + ζ*ζ) { τ := base.LightTime(Δ) x, y, z = f(jde - τ) ξ = X + x η = Y + y ζ = Z + z Δ = math.Sqrt(ξ*ξ + η*η + ζ*ζ) } α = unit.RAFromRad(math.Atan2(η, ξ)) δ = unit.Angle(math.Asin(ζ / Δ)) R0 := math.Sqrt(X*X + Y*Y + Z*Z) ψ = unit.Angle(math.Acos((ξ*X + η*Y + ζ*Z) / R0 / Δ)) return }
func ExampleTopocentric3() { // same test case as example 40.a, p. 280 α := unit.RAFromDeg(339.530208) δ := unit.AngleFromDeg(-15.771083) Δ := .37276 ρsφʹ := .546861 ρcφʹ := .836339 L := unit.Angle(unit.NewHourAngle(' ', 7, 47, 27)) jde := julian.CalendarGregorianToJD(2003, 8, 28+ unit.NewTime(' ', 3, 17, 0).Day()) Hʹ, δʹ := parallax.Topocentric3(α, δ, Δ, ρsφʹ, ρcφʹ, L, jde) fmt.Printf("Hʹ = %.2d\n", sexa.FmtHourAngle(Hʹ)) θ0 := sidereal.Apparent(jde) αʹ := unit.RAFromRad(θ0.Rad() - L.Rad() - Hʹ.Rad()) // same result as example 40.a, p. 280 fmt.Printf("αʹ = %.2d\n", sexa.FmtRA(αʹ)) fmt.Printf("δʹ = %.1d\n", sexa.FmtAngle(δʹ)) // Output: // Hʹ = -4ʰ44ᵐ50ˢ.28 // αʹ = 22ʰ38ᵐ8ˢ.54 // δʹ = -15°46′30″.0 }