/// <summary> /// Performs reduction of equatorial coordinates from one epoch to another /// with using of precessional elements. /// </summary> /// <param name="eq0">Equatorial coordinates for initial epoch.</param> /// <param name="p">Precessional elements for reduction from initial epoch to target (final) epoch.</param> /// <returns>Equatorial coordinates for target (final) epoch.</returns> /// <remarks> /// This method is taken from AA(I), formula 20.4. /// </remarks> public static CrdsEquatorial GetEquatorialCoordinates(CrdsEquatorial eq0, PrecessionalElements p) { CrdsEquatorial eq = new CrdsEquatorial(); double sinDelta0 = Math.Sin(Angle.ToRadians(eq0.Delta)); double cosDelta0 = Math.Cos(Angle.ToRadians(eq0.Delta)); double sinTheta = Math.Sin(Angle.ToRadians(p.theta)); double cosTheta = Math.Cos(Angle.ToRadians(p.theta)); double sinAlpha0Zeta = Math.Sin(Angle.ToRadians(eq0.Alpha + p.zeta)); double cosAlpha0Zeta = Math.Cos(Angle.ToRadians(eq0.Alpha + p.zeta)); double A = cosDelta0 * sinAlpha0Zeta; double B = cosTheta * cosDelta0 * cosAlpha0Zeta - sinTheta * sinDelta0; double C = sinTheta * cosDelta0 * cosAlpha0Zeta + cosTheta * sinDelta0; eq.Alpha = Angle.ToDegrees(Math.Atan2(A, B)) + p.z; eq.Alpha = Angle.To360(eq.Alpha); if (Math.Abs(C) == 1) { eq.Delta = Angle.ToDegrees(Math.Acos(A * A + B * B)); } else { eq.Delta = Angle.ToDegrees(Math.Asin(C)); } return(eq); }
/// <summary> /// Finds constellation name by point with specified equatorial coordinates for any epoch. /// </summary> /// <param name="eq">Equatorial coordinates of the point for any epoch.</param> /// <param name="epoch">Epoch value, in Julian Days.</param> /// <returns>International 3-letter code of a constellation.</returns> /// <remarks> /// Implementation is based on <see href="ftp://cdsarc.u-strasbg.fr/pub/cats/VI/42/"/>. /// </remarks> public static string FindConstellation(CrdsEquatorial eq, double epoch) { var pe = Precession.ElementsFK5(epoch, Date.EPOCH_B1875); var eq1875 = Precession.GetEquatorialCoordinates(eq, pe); return(FindConstellation(eq1875)); }
/// <summary> /// Adds corrections to equatorial coordinates /// </summary> public static CrdsEquatorial operator +(CrdsEquatorial lhs, CrdsEquatorial rhs) { CrdsEquatorial eq = new CrdsEquatorial(); eq.Alpha = Angle.To360(lhs.Alpha + rhs.Alpha); eq.Delta = lhs.Delta + rhs.Delta; return(eq); }
/// <summary> /// Finds constellation name by point with specified equatorial coordinates for epoch B1875. /// </summary> /// <param name="eq1875">Equatorial coordinates of the point for epoch B1875.</param> /// <returns>International 3-letter code of a constellation.</returns> /// <remarks> /// Implementation is based on <see href="ftp://cdsarc.u-strasbg.fr/pub/cats/VI/42/"/>. /// </remarks> public static string FindConstellation(CrdsEquatorial eq1875) { // Load borders data if needed if (Borders == null) { using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"ADK.Data.Constells.dat")) using (var reader = new StreamReader(stream)) { Borders = new List <Border>(); string line; while ((line = reader.ReadLine()) != null) { float ra1 = float.Parse(line.Substring(0, 8), CultureInfo.InvariantCulture); float ra2 = float.Parse(line.Substring(9, 7), CultureInfo.InvariantCulture); float dec = float.Parse(line.Substring(17, 8), CultureInfo.InvariantCulture); string constName = line.Substring(26, 3); Borders.Add(new Border(ra1, ra2, dec, constName)); } } } double alpha = eq1875.Alpha / 15.0; double delta = eq1875.Delta; for (int i = 0; i < Borders.Count; i++) { if (Borders[i].Dec > delta) { continue; } if (Borders[i].RA1 <= alpha) { continue; } if (Borders[i].RA2 > alpha) { continue; } if (alpha >= Borders[i].RA2 && alpha < Borders[i].RA1 && Borders[i].Dec <= delta) { return(Borders[i].ConstName); } else if (Borders[i].RA1 < alpha) { continue; } } return(""); }
// TODO: description private static CrdsEquatorial InterpolateEq(double[] alpha, double[] delta, double n) { double[] x = new double[] { 0, 0.5, 1 }; CrdsEquatorial eq = new CrdsEquatorial(); eq.Alpha = Interpolation.Lagrange(x, alpha, n); eq.Delta = Interpolation.Lagrange(x, delta, n); return(eq); }
/// <summary> /// Returns nutation corrections for equatorial coordiantes. /// </summary> /// <param name="eq">Initial (not corrected) equatorial coordiantes.</param> /// <param name="ne">Nutation elements for given instant.</param> /// <param name="epsilon">True obliquity of the ecliptic (ε), in degrees.</param> /// <returns>Nutation corrections for equatorial coordiantes.</returns> /// <remarks>AA(II), formula 23.1</remarks> public static CrdsEquatorial NutationEffect(CrdsEquatorial eq, NutationElements ne, double epsilon) { CrdsEquatorial correction = new CrdsEquatorial(); epsilon = Angle.ToRadians(epsilon); double alpha = Angle.ToRadians(eq.Alpha); double delta = Angle.ToRadians(eq.Delta); correction.Alpha = (Math.Cos(epsilon) + Math.Sin(epsilon) * Math.Sin(alpha) * Math.Tan(delta)) * ne.deltaPsi - (Math.Cos(alpha) * Math.Tan(delta)) * ne.deltaEpsilon; correction.Delta = Math.Sin(epsilon) * Math.Cos(alpha) * ne.deltaPsi + Math.Sin(alpha) * ne.deltaEpsilon; return(correction); }
/// <summary> /// Calculates appearance of Saturn rings /// </summary> /// <param name="jd">Julian date to calculate for</param> /// <param name="saturn">Heliocentric coordinates of Saturn.</param> /// <param name="earth">Heliocentric coordinates of Earth.</param> /// <param name="epsilon">True obliquity of ecliptic.</param> /// <returns> /// Appearance data for Saturn rings. /// </returns> /// <remarks> /// Method is taken from AA(II), chapter 45. /// </remarks> public static RingsAppearance SaturnRings(double jd, CrdsHeliocentrical saturn, CrdsHeliocentrical earth, double epsilon) { RingsAppearance rings = new RingsAppearance(); double T = (jd - 2451545.0) / 36525.0; double T2 = T * T; double i = 28.075216 - 0.012998 * T + 0.000004 * T2; double Omega = 169.508470 + 1.394681 * T + 0.000412 * T2; double lambda0 = Omega - 90; double beta0 = 90 - i; i = Angle.ToRadians(i); Omega = Angle.ToRadians(Omega); CrdsEcliptical ecl = saturn.ToRectangular(earth).ToEcliptical(); double beta = Angle.ToRadians(ecl.Beta); double lambda = Angle.ToRadians(ecl.Lambda); rings.B = Angle.ToDegrees(Math.Asin(Math.Sin(i) * Math.Cos(beta) * Math.Sin(lambda - Omega) - Math.Cos(i) * Math.Sin(beta))); rings.a = 375.35 / ecl.Distance; rings.b = rings.a * Math.Sin(Math.Abs(Angle.ToRadians(rings.B))); double N = 113.6655 + 0.8771 * T; double l_ = Angle.ToRadians(saturn.L - 0.01759 / saturn.R); double b_ = Angle.ToRadians(saturn.B - 0.000764 * Math.Cos(Angle.ToRadians(saturn.L - N)) / saturn.R); double U1 = Angle.ToDegrees(Math.Atan((Math.Sin(i) * Math.Sin(b_) + Math.Cos(i) * Math.Cos(b_) * Math.Sin(l_ - Omega)) / (Math.Cos(b_) * Math.Cos(l_ - Omega)))); double U2 = Angle.ToDegrees(Math.Atan((Math.Sin(i) * Math.Sin(beta) + Math.Cos(i) * Math.Cos(beta) * Math.Sin(lambda - Omega)) / (Math.Cos(beta) * Math.Cos(lambda - Omega)))); rings.DeltaU = Math.Abs(U1 - U2); CrdsEcliptical eclPole = new CrdsEcliptical(); eclPole.Set(lambda0, beta0); CrdsEquatorial eq = ecl.ToEquatorial(epsilon); CrdsEquatorial eqPole = eclPole.ToEquatorial(epsilon); double alpha = Angle.ToRadians(eq.Alpha); double delta = Angle.ToRadians(eq.Delta); double alpha0 = Angle.ToRadians(eqPole.Alpha); double delta0 = Angle.ToRadians(eqPole.Delta); double y = Math.Cos(delta0) * Math.Sin(alpha0 - alpha); double x = Math.Sin(delta0) * Math.Cos(delta) - Math.Cos(delta0) * Math.Sin(delta) * Math.Cos(alpha0 - alpha); rings.P = Angle.ToDegrees(Math.Atan2(y, x)); return(rings); }
/// <summary> /// Calculates visible appearance of planet for given date. /// </summary> /// <param name="jd">Julian day</param> /// <param name="planet">Planet number to calculate appearance, 1 = Mercury, 2 = Venus and etc.</param> /// <param name="eq">Equatorial coordinates of the planet</param> /// <param name="distance">Distance from the planet to the Earth</param> /// <returns>Appearance parameters of the planet</returns> /// <remarks> /// This method is based on book "Practical Ephemeris Calculations", Montenbruck. /// See topic 6.4, pp. 88-92. /// </remarks> public static PlanetAppearance PlanetAppearance(double jd, int planet, CrdsEquatorial eq, double distance) { PlanetAppearance a = new PlanetAppearance(); double d = jd - 2451545.0; double T = d / 36525.0; // coordinates of the point to which the north pole of the planet is pointing. CrdsEquatorial eq0 = new CrdsEquatorial(); eq0.Alpha = Angle.To360(cAlpha0[planet - 1][0] + cAlpha0[planet - 1][1] * T + cAlpha0[planet - 1][2] * T); eq0.Delta = cDelta0[planet - 1][0] + cDelta0[planet - 1][1] * T + cDelta0[planet - 1][2] * T; // take light time effect into account d -= PlanetPositions.LightTimeEffect(distance); T = d / 36525.0; // position of null meridian double W = Angle.To360(cW[planet - 1][0] + cW[planet - 1][1] * d + cW[planet - 1][2] * T); double delta = Angle.ToRadians(eq.Delta); double alpha = Angle.ToRadians(eq.Alpha); double delta0 = Angle.ToRadians(eq0.Delta); double dAlpha0 = Angle.ToRadians(eq0.Alpha - eq.Alpha); double sinD = -Math.Sin(delta0) * Math.Sin(delta) - Math.Cos(delta0) * Math.Cos(delta) * Math.Cos(dAlpha0); // planetographic latitude of the Earth a.D = Angle.ToDegrees(Math.Asin(sinD)); double cosD = Math.Cos(Angle.ToRadians(a.D)); double sinP = Math.Cos(delta0) * Math.Sin(dAlpha0) / cosD; double cosP = (Math.Sin(delta0) * Math.Cos(delta) - Math.Cos(delta0) * Math.Sin(delta) * Math.Cos(dAlpha0)) / cosD; // position angle of the axis a.P = Angle.To360(Angle.ToDegrees(Math.Atan2(sinP, cosP))); double sinK = (-Math.Cos(delta0) * Math.Sin(delta) + Math.Sin(delta0) * Math.Cos(delta) * Math.Cos(dAlpha0)) / cosD; double cosK = Math.Cos(delta) * Math.Sin(dAlpha0) / cosD; double K = Angle.ToDegrees(Math.Atan2(sinK, cosK)); // planetographic longitude of the central meridian a.CM = planet == 5 ? JupiterCM2(jd) : Angle.To360(Math.Sign(W) * (W - K)); return(a); }
/// <summary> /// Calculates angular separation between two points with equatorial coordinates /// </summary> /// <param name="p1">Equatorial coordinates of the first point</param> /// <param name="p2">Equatorial coordinates of the second point</param> /// <returns>Angular separation in degrees</returns> public static double Separation(CrdsEquatorial p1, CrdsEquatorial p2) { double a1 = ToRadians(p1.Delta); double a2 = ToRadians(p2.Delta); double A1 = p1.Alpha; double A2 = p2.Alpha; double a = Math.Acos( Math.Sin(a1) * Math.Sin(a2) + Math.Cos(a1) * Math.Cos(a2) * Math.Cos(ToRadians(A1 - A2))); return(double.IsNaN(a) ? 0 : ToDegrees(a)); }
/// <summary> /// Converts equatorial coordinates (for equinox B1950.0) to galactical coordinates. /// </summary> /// <param name="eq">Equatorial coordinates for equinox B1950.0</param> /// <returns>Galactical coordinates.</returns> public static CrdsGalactical ToGalactical(this CrdsEquatorial eq) { CrdsGalactical gal = new CrdsGalactical(); double alpha0_alpha = Angle.ToRadians(192.25 - eq.Alpha); double delta = Angle.ToRadians(eq.Delta); double delta0 = Angle.ToRadians(27.4); double Y = Math.Sin(alpha0_alpha); double X = Math.Cos(alpha0_alpha) * Math.Sin(delta0) - Math.Tan(delta) * Math.Cos(delta0); double sinb = Math.Sin(delta) * Math.Sin(delta0) + Math.Cos(delta) * Math.Cos(delta0) * Math.Cos(alpha0_alpha); gal.l = Angle.To360(303 - Angle.ToDegrees(Math.Atan2(Y, X))); gal.b = Angle.ToDegrees(Math.Asin(sinb)); return(gal); }
/// <summary> /// Converts galactical coodinates to equatorial, for equinox B1950.0. /// </summary> /// <param name="gal">Galactical coodinates.</param> /// <returns>Equatorial coodinates, for equinox B1950.0.</returns> public static CrdsEquatorial ToEquatorial(this CrdsGalactical gal) { CrdsEquatorial eq = new CrdsEquatorial(); double l_l0 = Angle.ToRadians(gal.l - 123.0); double delta0 = Angle.ToRadians(27.4); double b = Angle.ToRadians(gal.b); double Y = Math.Sin(l_l0); double X = Math.Cos(l_l0) * Math.Sin(delta0) - Math.Tan(b) * Math.Cos(delta0); double sinDelta = Math.Sin(b) * Math.Sin(delta0) + Math.Cos(b) * Math.Cos(delta0) * Math.Cos(l_l0); eq.Alpha = Angle.To360(Angle.ToDegrees(Math.Atan2(Y, X)) + 12.25); eq.Delta = Angle.ToDegrees(Math.Asin(sinDelta)); return(eq); }
/// <summary> /// Converts equatorial coordinates to ecliptical coordinates. /// </summary> /// <param name="eq">Pair of equatorial coordinates.</param> /// <param name="epsilon">Obliquity of the ecliptic, in degrees.</param> /// <returns></returns> public static CrdsEcliptical ToEcliptical(this CrdsEquatorial eq, double epsilon) { CrdsEcliptical ecl = new CrdsEcliptical(); epsilon = Angle.ToRadians(epsilon); double alpha = Angle.ToRadians(eq.Alpha); double delta = Angle.ToRadians(eq.Delta); double Y = Math.Sin(alpha) * Math.Cos(epsilon) + Math.Tan(delta) * Math.Sin(epsilon); double X = Math.Cos(alpha); ecl.Lambda = Angle.ToDegrees(Math.Atan2(Y, X)); ecl.Beta = Angle.ToDegrees(Math.Asin(Math.Sin(delta) * Math.Cos(epsilon) - Math.Cos(delta) * Math.Sin(epsilon) * Math.Sin(alpha))); return(ecl); }
/// <summary> /// Converts ecliptical coordinates to equatorial. /// </summary> /// <param name="ecl">Pair of ecliptical cooordinates.</param> /// <param name="epsilon">Obliquity of the ecliptic, in degrees.</param> /// <returns>Pair of equatorial coordinates.</returns> public static CrdsEquatorial ToEquatorial(this CrdsEcliptical ecl, double epsilon) { CrdsEquatorial eq = new CrdsEquatorial(); epsilon = Angle.ToRadians(epsilon); double lambda = Angle.ToRadians(ecl.Lambda); double beta = Angle.ToRadians(ecl.Beta); double Y = Math.Sin(lambda) * Math.Cos(epsilon) - Math.Tan(beta) * Math.Sin(epsilon); double X = Math.Cos(lambda); eq.Alpha = Angle.To360(Angle.ToDegrees(Math.Atan2(Y, X))); eq.Delta = Angle.ToDegrees(Math.Asin(Math.Sin(beta) * Math.Cos(epsilon) + Math.Cos(beta) * Math.Sin(epsilon) * Math.Sin(lambda))); return(eq); }
/// <summary> /// Converts local horizontal coordinates to equatorial coordinates. /// </summary> /// <param name="hor">Pair of local horizontal coordinates.</param> /// <param name="geo">Geographical of the observer</param> /// <param name="theta0">Local sidereal time.</param> /// <returns>Pair of equatorial coordinates</returns> public static CrdsEquatorial ToEquatorial(this CrdsHorizontal hor, CrdsGeographical geo, double theta0) { CrdsEquatorial eq = new CrdsEquatorial(); double A = Angle.ToRadians(hor.Azimuth); double h = Angle.ToRadians(hor.Altitude); double phi = Angle.ToRadians(geo.Latitude); double Y = Math.Sin(A); double X = Math.Cos(A) * Math.Sin(phi) + Math.Tan(h) * Math.Cos(phi); double H = Angle.ToDegrees(Math.Atan2(Y, X)); eq.Alpha = Angle.To360(theta0 - geo.Longitude - H); eq.Delta = Angle.ToDegrees(Math.Asin(Math.Sin(phi) * Math.Sin(h) - Math.Cos(phi) * Math.Cos(h) * Math.Cos(A))); return(eq); }
/// <summary> /// Converts equatorial coodinates to local horizontal /// </summary> /// <param name="eq">Pair of equatorial coodinates</param> /// <param name="geo">Geographical coordinates of the observer</param> /// <param name="theta0">Local sidereal time</param> /// <remarks> /// Implementation is taken from AA(I), formulae 12.5, 12.6. /// </remarks> public static CrdsHorizontal ToHorizontal(this CrdsEquatorial eq, CrdsGeographical geo, double theta0) { double H = Angle.ToRadians(HourAngle(theta0, geo.Longitude, eq.Alpha)); double phi = Angle.ToRadians(geo.Latitude); double delta = Angle.ToRadians(eq.Delta); CrdsHorizontal hor = new CrdsHorizontal(); double Y = Math.Sin(H); double X = Math.Cos(H) * Math.Sin(phi) - Math.Tan(delta) * Math.Cos(phi); hor.Altitude = Angle.ToDegrees(Math.Asin(Math.Sin(phi) * Math.Sin(delta) + Math.Cos(phi) * Math.Cos(delta) * Math.Cos(H))); hor.Azimuth = Angle.ToDegrees(Math.Atan2(Y, X)); hor.Azimuth = Angle.To360(hor.Azimuth); return(hor); }
/// <summary> /// Calculates topocentric equatorial coordinates of celestial body /// with taking into account correction for parallax. /// </summary> /// <param name="eq">Geocentric equatorial coordinates of the body</param> /// <param name="geo">Geographical coordinates of the body</param> /// <param name="theta0">Apparent sidereal time at Greenwich</param> /// <param name="pi">Parallax of a body</param> /// <returns>Topocentric equatorial coordinates of the celestial body</returns> /// <remarks> /// Method is taken from AA(II), formulae 40.6-40.7. /// </remarks> public static CrdsEquatorial ToTopocentric(this CrdsEquatorial eq, CrdsGeographical geo, double theta0, double pi) { double H = Angle.ToRadians(HourAngle(theta0, geo.Longitude, eq.Alpha)); double delta = Angle.ToRadians(eq.Delta); double sinPi = Math.Sin(Angle.ToRadians(pi)); double A = Math.Cos(delta) * Math.Sin(H); double B = Math.Cos(delta) * Math.Cos(H) - geo.RhoCosPhi * sinPi; double C = Math.Sin(delta) - geo.RhoSinPhi * sinPi; double q = Math.Sqrt(A * A + B * B + C * C); double H_ = Angle.ToDegrees(Math.Atan2(A, B)); double alpha_ = Angle.To360(theta0 - geo.Longitude - H_); double delta_ = Angle.ToDegrees(Math.Asin(C / q)); return(new CrdsEquatorial(alpha_, delta_)); }
/// <summary> /// Calculates the aberration effect for a celestial body (star or planet) for given instant. /// </summary> /// <param name="eq">Equatorial coordinates of the body (not corrected).</param> /// <param name="ae">Aberration elements needed for calculation of aberration correction.</param> /// <returns>Returns aberration correction values for equatorial coordinates.</returns> /// <remarks>AA(II), formula 23.3</remarks> public static CrdsEquatorial AberrationEffect(CrdsEquatorial eq, AberrationElements ae, double epsilon) { double a = Angle.ToRadians(eq.Alpha); double d = Angle.ToRadians(eq.Delta); double theta = Angle.ToRadians(ae.lambda); double pi = Angle.ToRadians(ae.pi); epsilon = Angle.ToRadians(epsilon); double da = -k * (Math.Cos(a) * Math.Cos(theta) * Math.Cos(epsilon) + Math.Sin(a) * Math.Sin(theta)) / Math.Cos(d) + epsilon * k * (Math.Cos(a) * Math.Cos(pi) * Math.Cos(epsilon) + Math.Sin(a) * Math.Sin(pi)) / Math.Cos(d); double m = Math.Tan(epsilon) * Math.Cos(d) - Math.Sin(a) * Math.Sin(d); double dd = -k * (Math.Cos(theta) * Math.Cos(epsilon) * m + Math.Cos(a) * Math.Sin(d) * Math.Sin(theta)) + epsilon * k * (Math.Cos(pi) * Math.Cos(epsilon) * m + Math.Cos(a) * Math.Sin(d) * Math.Sin(pi)); return(new CrdsEquatorial(da / 3600, dd / 3600)); }
public static CrdsEquatorial ToEquatorial(this CrdsRectangular m, CrdsEquatorial planet, double P, double semidiameter) { // convert to polar coordinates // radius-vector of moon, in planet's equatorial radii double r = Math.Sqrt(m.X * m.X + m.Y * m.Y); // rotation angle double theta = Angle.ToDegrees(Math.Atan2(m.Y, m.X)); // rotate with position angle of the planet theta += P; // convert back to rectangular coordinates, but rotated with P angle: double x = r * Math.Cos(Angle.ToRadians(theta)); double y = r * Math.Sin(Angle.ToRadians(theta)); double dAlpha = (1 / Math.Cos(Angle.ToRadians(planet.Delta))) * x * semidiameter / 3600; double dDelta = y * semidiameter / 3600; return(new CrdsEquatorial(planet.Alpha - dAlpha, planet.Delta + dDelta)); }
/// <summary> /// Copying construtor /// </summary> /// <param name="eq">Equatorial coordinates to be copied</param> public CrdsEquatorial(CrdsEquatorial eq) { Alpha = eq.Alpha; Delta = eq.Delta; }
/// <summary> /// Calculates instants of rising, transit and setting for stationary celestial body for the desired date. /// Stationary in this particular case means that body has unchanged (or slightly changing) celestial coordinates during the day. /// </summary> /// <param name="eq">Equatorial coordinates of the celestial body.</param> /// <param name="location">Geographical location of the observation point.</param> /// <param name="theta0">Apparent sidereal time at Greenwich for local midnight of the desired date.</param> /// <param name="minAltitude">Minimal altitude of the body above the horizon, in degrees, to detect rise/set. Used only for calculating visibility conditions.</param> /// <returns>Instants of rising, transit and setting for the celestial body for the desired date.</returns> public static RTS RiseTransitSet(CrdsEquatorial eq, CrdsGeographical location, double theta0, double minAltitude = 0) { List <CrdsHorizontal> hor = new List <CrdsHorizontal>(); for (int i = 0; i <= 24; i++) { double n = i / 24.0; var sidTime = InterpolateSiderialTime(theta0, n); hor.Add(eq.ToHorizontal(location, sidTime)); } var result = new RTS(); for (int i = 0; i < 24; i++) { double n = (i + 0.5) / 24.0; var sidTime = InterpolateSiderialTime(theta0, n); var hor0 = eq.ToHorizontal(location, sidTime); if (double.IsNaN(result.Transit) && hor0.Altitude > 0) { double r = SolveParabola(Math.Sin(Angle.ToRadians(hor[i].Azimuth)), Math.Sin(Angle.ToRadians(hor0.Azimuth)), Math.Sin(Angle.ToRadians(hor[i + 1].Azimuth))); if (!double.IsNaN(r)) { double t = (i + r) / 24.0; sidTime = InterpolateSiderialTime(theta0, t); result.Transit = t; result.TransitAltitude = eq.ToHorizontal(location, sidTime).Altitude; } } if (double.IsNaN(result.Rise) || double.IsNaN(result.Set)) { double r = SolveParabola(hor[i].Altitude - minAltitude, hor0.Altitude - minAltitude, hor[i + 1].Altitude - minAltitude); if (!double.IsNaN(r)) { double t = (i + r) / 24.0; sidTime = InterpolateSiderialTime(theta0, t); if (double.IsNaN(result.Rise) && hor[i].Altitude - minAltitude < 0 && hor[i + 1].Altitude - minAltitude > 0) { result.Rise = t; result.RiseAzimuth = eq.ToHorizontal(location, sidTime).Azimuth; } if (double.IsNaN(result.Set) && hor[i].Altitude - minAltitude > 0 && hor[i + 1].Altitude - minAltitude < 0) { result.Set = t; result.SetAzimuth = eq.ToHorizontal(location, sidTime).Azimuth; } if (!double.IsNaN(result.Transit) && !double.IsNaN(result.Rise) && !double.IsNaN(result.Set)) { break; } } } } return(result); }
/// <summary> /// Calculates visibity details for the celestial body, /// </summary> /// <param name="eqBody">Mean equatorial coordinates of the body for the desired day.</param> /// <param name="eqSun">Mean equatorial coordinates of the Sun for the desired day.</param> /// <param name="minAltitude">Minimal altitude of the body, in degrees, to be considered as approproate for observations. By default it's 5 degrees for planet.</param> /// <returns><see cref="VisibilityDetails"/> instance describing details of visibility.</returns> // TODO: tests public static VisibilityDetails Details(CrdsEquatorial eqBody, CrdsEquatorial eqSun, CrdsGeographical location, double theta0, double minAltitude = 5) { var details = new VisibilityDetails(); // period when the planet is above the horizon and its altitude is larger than "minAltitude" RTS body = RiseTransitSet(eqBody, location, theta0, minAltitude); // period when the Sun is above the horizon RTS sun = RiseTransitSet(eqSun, location, theta0); // body reaches minimal altitude but Sun does not rise at all (polar night) if (body.TransitAltitude > minAltitude && sun.TransitAltitude <= 0) { details.Period = VisibilityPeriod.WholeNight; details.Duration = body.Duration * 24; } // body does not reach the minimal altitude during the day else if (body.TransitAltitude <= minAltitude) { details.Period = VisibilityPeriod.Invisible; details.Duration = 0; } // there is a day/night change during the day and body reaches minimal altitude else if (body.TransitAltitude > minAltitude) { // "Sun is below horizon" time range, expressed in degrees (0 is midnight, 180 is noon) var r1 = new AngleRange(sun.Set * 360, (1 - sun.Duration) * 360); // "body is above horizon" time range, expressed in degrees (0 is midnight, 180 is noon) var r2 = new AngleRange(body.Rise * 360, body.Duration * 360); // find the intersections of two ranges var ranges = r1.Overlaps(r2); // no intersections of time ranges if (!ranges.Any()) { details.Period = VisibilityPeriod.Invisible; details.Duration = 0; details.Begin = double.NaN; details.End = double.NaN; } // the body is observable during the day else { // duration of visibility details.Duration = ranges.Sum(i => i.Range / 360 * 24); // beginning of visibility details.Begin = ranges.First().Start / 360; // end of visibility details.End = (details.Begin + details.Duration / 24) % 1; // Evening time range, expressed in degrees // Start is a sunset time, range is a timespan from sunset to midnight. var rE = new AngleRange(sun.Set * 360, (1 - sun.Set) * 360); // Night time range, expressed in degrees // Start is a midnight time, range is a half of timespan from midnight to sunrise var rN = new AngleRange(0, sun.Rise / 2 * 360); // Morning time range, expressed in degrees // Start is a half of time from midnight to sunrise, range is a time to sunrise var rM = new AngleRange(sun.Rise / 2 * 360, sun.Rise / 2 * 360); foreach (var r in ranges) { var isEvening = r.Overlaps(rE); if (isEvening.Any()) { details.Period |= VisibilityPeriod.Evening; } var isNight = r.Overlaps(rN); if (isNight.Any()) { details.Period |= VisibilityPeriod.Night; } var isMorning = r.Overlaps(rM); if (isMorning.Any()) { details.Period |= VisibilityPeriod.Morning; } } } } return(details); }
/// <summary> /// Calculates instants of rising, transit and setting for non-stationary celestial body for the desired date. /// Non-stationary in this particular case means that body has fastly changing celestial coordinates during the day. /// </summary> /// <param name="eq">Array of three equatorial coordinates of the celestial body correspoding to local midnight, local noon, and local midnight of the following day after the desired date respectively.</param> /// <param name="location">Geographical location of the observation point.</param> /// <param name="theta0">Apparent sidereal time at Greenwich for local midnight of the desired date.</param> /// <param name="pi">Horizontal equatorial parallax of the body.</param> /// <param name="sd">Visible semidiameter of the body, expressed in degrees.</param> /// <returns>Instants of rising, transit and setting for the celestial body for the desired date.</returns> public static RTS RiseTransitSet(CrdsEquatorial[] eq, CrdsGeographical location, double theta0, double pi = 0, double sd = 0) { if (eq.Length != 3) { throw new ArgumentException("Number of equatorial coordinates in the array should be equal to 3."); } double[] alpha = new double[3]; double[] delta = new double[3]; for (int i = 0; i < 3; i++) { alpha[i] = eq[i].Alpha; delta[i] = eq[i].Delta; } Angle.Align(alpha); Angle.Align(delta); List <CrdsHorizontal> hor = new List <CrdsHorizontal>(); for (int i = 0; i <= 24; i++) { double n = i / 24.0; CrdsEquatorial eq0 = InterpolateEq(alpha, delta, n); var sidTime = InterpolateSiderialTime(theta0, n); hor.Add(eq0.ToTopocentric(location, sidTime, pi).ToHorizontal(location, sidTime)); } var result = new RTS(); for (int i = 0; i < 24; i++) { double n = (i + 0.5) / 24.0; CrdsEquatorial eq0 = InterpolateEq(alpha, delta, n); var sidTime = InterpolateSiderialTime(theta0, n); var hor0 = eq0.ToTopocentric(location, sidTime, pi).ToHorizontal(location, sidTime); if (double.IsNaN(result.Transit) && hor0.Altitude > 0) { double r = SolveParabola(Math.Sin(Angle.ToRadians(hor[i].Azimuth)), Math.Sin(Angle.ToRadians(hor0.Azimuth)), Math.Sin(Angle.ToRadians(hor[i + 1].Azimuth))); if (!double.IsNaN(r)) { double t = (i + r) / 24.0; eq0 = InterpolateEq(alpha, delta, t); sidTime = InterpolateSiderialTime(theta0, t); result.Transit = t; result.TransitAltitude = eq0.ToTopocentric(location, sidTime, pi).ToHorizontal(location, sidTime).Altitude; } } if (double.IsNaN(result.Rise) || double.IsNaN(result.Set)) { double r = SolveParabola(hor[i].Altitude + sd, hor0.Altitude + sd, hor[i + 1].Altitude + sd); if (!double.IsNaN(r)) { double t = (i + r) / 24.0; eq0 = InterpolateEq(alpha, delta, t); sidTime = InterpolateSiderialTime(theta0, t); if (double.IsNaN(result.Rise) && hor[i].Altitude + sd < 0 && hor[i + 1].Altitude + sd > 0) { result.Rise = t; result.RiseAzimuth = eq0.ToTopocentric(location, sidTime, pi).ToHorizontal(location, sidTime).Azimuth; } if (double.IsNaN(result.Set) && hor[i].Altitude + sd > 0 && hor[i + 1].Altitude + sd < 0) { result.Set = t; result.SetAzimuth = eq0.ToTopocentric(location, sidTime, pi).ToHorizontal(location, sidTime).Azimuth; } if (!double.IsNaN(result.Transit) && !double.IsNaN(result.Rise) && !double.IsNaN(result.Set)) { break; } } } } return(result); }