// Times computes UT rise, transit and set times for a celestial object on // a day of interest. // // The function argurments do not actually include the day, but do include // a number of values computed from the day. // // p is geographic coordinates of observer. // ΔT is delta T. // h0 is "standard altitude" of the body. // Th0 is apparent sidereal time at 0h UT at Greenwich. // α3, δ3 are slices of three right ascensions and declinations. // // h0 unit is radians. // // Th0 must be the time on the day of interest, in seconds. // See sidereal.Apparent0UT. // // α3, δ3 must be values at 0h dynamical time for the day before, the day of, // and the day after the day of interest. Units are radians. // // Result units are seconds of day and are in the range [0,86400). func Times(p globe.Coord, ΔT unit.Time, h0 unit.Angle, Th0 unit.Time, α3 []unit.RA, δ3 []unit.Angle) (tRise, tTransit, tSet unit.Time, err error) { tRise, tTransit, tSet, err = ApproxTimes(p, h0, Th0, α3[1], δ3[1]) if err != nil { return } αf := make([]float64, 3) for i, α := range α3 { αf[i] = α.Rad() } δf := make([]float64, 3) for i, δ := range δ3 { δf[i] = δ.Rad() } var d3α, d3δ *interp.Len3 d3α, err = interp.NewLen3(-86400, 86400, αf) if err != nil { return } d3δ, err = interp.NewLen3(-86400, 86400, δf) if err != nil { return } // adjust tTransit { th0 := (Th0 + tTransit.Mul(360.985647/360)).Mod1() α := d3α.InterpolateX((tTransit + ΔT).Sec()) // local hour angle as Time H := th0 - unit.TimeFromRad(p.Lon.Rad()+α) tTransit -= H } // adjust tRise, tSet sLat, cLat := p.Lat.Sincos() adjustRS := func(m unit.Time) (unit.Time, error) { th0 := (Th0 + m.Mul(360.985647/360)).Mod1() ut := (m + ΔT).Sec() α := d3α.InterpolateX(ut) δ := d3δ.InterpolateX(ut) Hrad := th0.Rad() - p.Lon.Rad() - α sδ, cδ := math.Sincos(δ) sH, cH := math.Sincos(Hrad) h := math.Asin(sLat*sδ + cLat*cδ*cH) md := (unit.TimeFromRad(h) - h0.Time()).Div(cδ * cLat * sH) return m + md, nil } tRise, err = adjustRS(tRise) if err != nil { return } tSet, err = adjustRS(tSet) return }
// MinSepRect returns the minimum separation between two moving objects. // // Like MinSep, but using a method of rectangular coordinates that gives // accurate results even for close approaches. func MinSepRect(jd1, jd3 float64, r1, d1, r2, d2 []float64) (float64, error) { if len(r1) != 3 || len(d1) != 3 || len(r2) != 3 || len(d2) != 3 { return 0, interp.ErrorNot3 } uv := func(r1, d1, r2, d2 float64) (u, v float64) { sd1, cd1 := math.Sincos(d1) Δr := r2 - r1 tΔr := math.Tan(Δr) thΔr := math.Tan(Δr / 2) K := 1 / (1 + sd1*sd1*tΔr*thΔr) sΔd := math.Sin(d2 - d1) u = -K * (1 - (sd1/cd1)*sΔd) * cd1 * tΔr v = K * (sΔd + sd1*cd1*tΔr*thΔr) return } us := make([]float64, 3, 6) vs := us[3:6] for x, r := range r1 { us[x], vs[x] = uv(r, d1[x], r2[x], d2[x]) } u3, err := interp.NewLen3(-1, 1, us) if err != nil { panic(err) // bug not caller's fault. } v3, err := interp.NewLen3(-1, 1, vs) if err != nil { panic(err) // bug not caller's fault. } up0 := (us[2] - us[0]) / 2 vp0 := (vs[2] - vs[0]) / 2 up1 := us[0] + us[2] - 2*us[1] vp1 := vs[0] + vs[2] - 2*vs[1] up := up0 vp := vp0 dn := -(us[1]*up + vs[1]*vp) / (up*up + vp*vp) n := dn var u, v float64 for limit := 0; limit < 10; limit++ { u = u3.InterpolateN(n) v = v3.InterpolateN(n) if math.Abs(dn) < 1e-5 { return math.Hypot(u, v), nil // success } up := up0 + n*up1 vp := vp0 + n*vp1 dn = -(u*up + v*vp) / (up*up + vp*vp) n += dn } return 0, errors.New("MinSepRect: failure to converge") }
func ExampleLen3_Zero() { // Example 3.c, p. 26. x1 := 26. x3 := 28. // the y unit doesn't matter. working in degrees is fine yTable := []float64{ base.DMSToDeg(true, 0, 28, 13.4), base.DMSToDeg(false, 0, 6, 46.3), base.DMSToDeg(false, 0, 38, 23.2), } d3, err := interp.NewLen3(x1, x3, yTable) if err != nil { fmt.Println(err) return } x, err := d3.Zero(false) if err != nil { fmt.Println(err) return } fmt.Printf("February %.5f\n", x) i, frac := math.Modf(x) fmt.Printf("February %d, at %.62s TD", int(i), base.NewFmtTime(frac*24*3600)) // Output: // February 26.79873 // February 26, at 19ʰ10ᵐ TD }
func ExampleLen3_Zero() { // Example 3.c, p. 26. x1 := 26. x3 := 28. // the y unit doesn't matter. working in degrees is fine yTable := []float64{ unit.FromSexa('-', 0, 28, 13.4), unit.FromSexa(' ', 0, 6, 46.3), unit.FromSexa(' ', 0, 38, 23.2), } d3, err := interp.NewLen3(x1, x3, yTable) if err != nil { fmt.Println(err) return } x, err := d3.Zero(false) if err != nil { fmt.Println(err) return } fmt.Printf("February %.5f\n", x) i, frac := math.Modf(x) fmt.Printf("February %d, at %m TD", int(i), sexa.FmtTime(unit.TimeFromDay(frac))) // Output: // February 26.79873 // February 26, at 19ʰ10ᵐ TD }
func ExampleLen3_Extremum() { // Example 3.b, p. 26. d3, err := interp.NewLen3(12, 20, []float64{ 1.3814294, 1.3812213, 1.3812453, }) if err != nil { fmt.Println(err) return } x, y, err := d3.Extremum() if err != nil { fmt.Println(err) return } fmt.Printf("distance: %.7f AU\n", y) fmt.Printf("date: %.4f\n", x) i, frac := math.Modf(x) fmt.Printf("1992 May %d, at %.64s TD", int(i), base.NewFmtTime(frac*24*3600)) // Output: // distance: 1.3812030 AU // date: 17.5864 // 1992 May 17, at 14ʰ TD }
// Times computes UT rise, transit and set times for a celestial object on // a day of interest. // // The function argurments do not actually include the day, but do include // a number of values computed from the day. // // p is geographic coordinates of observer. // ΔT is delta T. // h0 is "standard altitude" of the body. // Th0 is apparent sidereal time at 0h UT at Greenwich. // α3, δ3 are slices of three right ascensions and declinations. // // ΔT unit is seconds. See package deltat. // // h0 unit is radians. // // Th0 must be the time on the day of interest, in seconds. // See sidereal.Apparent0UT. // // α3, δ3 must be values at 0h dynamical time for the day before, the day of, // and the day after the day of interest. Units are radians. // // Result units are seconds and are in the range [0,86400) func Times(p globe.Coord, ΔT, h0, Th0 float64, α3, δ3 []float64) (mRise, mTransit, mSet float64, err error) { mRise, mTransit, mSet, err = ApproxTimes(p, h0, Th0, α3[1], δ3[1]) if err != nil { return } var d3α, d3δ *interp.Len3 d3α, err = interp.NewLen3(-86400, 86400, α3) if err != nil { return } d3δ, err = interp.NewLen3(-86400, 86400, δ3) if err != nil { return } // adjust mTransit { th0 := base.PMod(Th0+mTransit*360.985647/360, 86400) α := d3α.InterpolateX(mTransit + ΔT) H := th0 - (p.Lon+α)*43200/math.Pi mTransit -= H } // adjust mRise, mSet sLat, cLat := math.Sincos(p.Lat) adjustRS := func(m float64) (float64, error) { th0 := base.PMod(Th0+m*360.985647/360, 86400) ut := m + ΔT α := d3α.InterpolateX(ut) δ := d3δ.InterpolateX(ut) H := th0 - (p.Lon+α)*43200/math.Pi sδ, cδ := math.Sincos(δ) h := sLat*sδ + cLat*cδ*math.Cos(H) return m + (h-h0)/cδ*cLat*math.Sin(H), nil } mRise, err = adjustRS(mRise) if err != nil { return } mSet, err = adjustRS(mSet) return }
func ap2a(j1, d float64, a bool, v *pp.V87Planet) (jde, r float64) { // Meeus doesn't give a complete algorithm for finding accurate answers. // The algorithm here starts with the approximate result algorithm // then crawls along the curve at resultion d until three points // are found containing the desired extremum. It's slow if the starting // point is far away (the case of Neptune) or if high accuracy is // demanded. 1 day accuracy is generally quick. j0 := j1 - d j2 := j1 + d rr := make([]float64, 3) _, _, rr[0] = v.Position2000(j0) _, _, rr[1] = v.Position2000(j1) _, _, rr[2] = v.Position2000(j2) for { if a { if rr[1] > rr[0] && rr[1] > rr[2] { break } } else { if rr[1] < rr[0] && rr[1] < rr[2] { break } } if rr[0] < rr[2] == a { j0 = j1 j1 = j2 j2 += d rr[0] = rr[1] rr[1] = rr[2] _, _, rr[2] = v.Position2000(j2) } else { j2 = j1 j1 = j0 j0 -= d rr[2] = rr[1] rr[1] = rr[0] _, _, rr[0] = v.Position2000(j0) } } l, err := interp.NewLen3(j0, j2, rr) if err != nil { panic(err) // unexpected. } jde, r, err = l.Extremum() if err != nil { panic(err) // unexpected. } return }
// MinSep returns the minimum separation between two moving objects. // // The motion is represented as an ephemeris of three rows, equally spaced // in time. Jd1, jd3 are julian day times of the first and last rows. // R1, d1, r2, d2 are coordinates at the three times. They must each be // slices of length 3. // // Result is obtained by computing separation at each of the three times // and interpolating a minimum. This may be invalid for sufficiently close // approaches. func MinSep(jd1, jd3 float64, r1, d1, r2, d2 []float64) (float64, error) { if len(r1) != 3 || len(d1) != 3 || len(r2) != 3 || len(d2) != 3 { return 0, interp.ErrorNot3 } y := make([]float64, 3) for x, r := range r1 { y[x] = Sep(r, d1[x], r2[x], d2[x]) } d3, err := interp.NewLen3(jd1, jd3, y) if err != nil { return 0, err } _, dMin, err := d3.Extremum() return dMin, err }
func ExampleLen3_InterpolateX() { // Example 3.a, p. 25. d3, err := interp.NewLen3(7, 9, []float64{ .884226, .877366, .870531, }) if err != nil { fmt.Println(err) return } x := 8 + base.NewTime(false, 4, 21, 0).Day() // 8th day at 4:21 y := d3.InterpolateX(x) fmt.Printf("%.6f\n", y) // Output: // 0.876125 }
func ExampleLen5_Zero() { // Exercise, p. 30. x1 := 25. x5 := 29. yTable := []float64{ base.DMSToDeg(true, 1, 11, 21.23), base.DMSToDeg(true, 0, 28, 12.31), base.DMSToDeg(false, 0, 16, 07.02), base.DMSToDeg(false, 1, 01, 00.13), base.DMSToDeg(false, 1, 45, 46.33), } d5, err := interp.NewLen5(x1, x5, yTable) if err != nil { fmt.Println(err) return } z, err := d5.Zero(false) if err != nil { fmt.Println(err) return } fmt.Printf("1988 January %.6f\n", z) zInt, zFrac := math.Modf(z) fmt.Printf("1988 January %d at %.62s TD\n", int(zInt), base.NewFmtTime(zFrac*24*3600)) // compare result to that from just three central values d3, err := interp.NewLen3(26, 28, yTable[1:4]) if err != nil { fmt.Println(err) return } z3, err := d3.Zero(false) if err != nil { fmt.Println(err) return } dz := z - z3 fmt.Printf("%.6f day\n", dz) fmt.Printf("%.1f minute\n", dz*24*60) // Output: // 1988 January 26.638587 // 1988 January 26 at 15ʰ20ᵐ TD // 0.000753 day // 1.1 minute }
func ExampleLen3_InterpolateN() { // Example 3.a, p. 25. d3, err := interp.NewLen3(7, 9, []float64{ .884226, .877366, .870531, }) if err != nil { fmt.Println(err) return } n := 4.35 / 24 y := d3.InterpolateN(n) fmt.Printf("%.6f\n", y) // Output: // 0.876125 }
func ExampleLen3_Zero_strong() { // Example 3.d, p. 27. x1 := -1. x3 := 1. yTable := []float64{-2, 3, 2} d3, err := interp.NewLen3(x1, x3, yTable) if err != nil { fmt.Println(err) return } x, err := d3.Zero(true) if err != nil { fmt.Println(err) return } fmt.Printf("%.12f\n", x) // Output: // -0.720759220056 }
func ap2a(j1, d float64, a bool, v *pp.V87Planet) (jde, r float64) { j0 := j1 - d j2 := j1 + d rr := make([]float64, 3) _, _, rr[0] = v.Position2000(j0) _, _, rr[1] = v.Position2000(j1) _, _, rr[2] = v.Position2000(j2) for { if a { if rr[1] > rr[0] && rr[1] > rr[2] { break } } else { if rr[1] < rr[0] && rr[1] < rr[2] { break } } if rr[0] < rr[2] == a { j0 = j1 j1 = j2 j2 += d rr[0] = rr[1] rr[1] = rr[2] _, _, rr[2] = v.Position2000(j2) } else { j2 = j1 j1 = j0 j0 -= d rr[2] = rr[1] rr[1] = rr[0] _, _, rr[0] = v.Position2000(j0) } } l, err := interp.NewLen3(j0, j2, rr) if err != nil { panic(err) // unexpected. } jde, r, err = l.Extremum() if err != nil { panic(err) // unexpected. } return }
func ExampleLen3_InterpolateN() { // Example 3.a, p. 25. d3, err := interp.NewLen3(7, 9, []float64{ .884226, .877366, .870531, }) if err != nil { fmt.Println(err) return } h := unit.FromSexa(0, 4, 21, 0) fmt.Println(h, "hours") n := h / 24 y := d3.InterpolateN(n) fmt.Printf("%.6f\n", y) // Output: // 4.35 hours // 0.876125 }