Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <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));
        }
Exemple #3
0
        /// <summary>
        /// Converts rectangular planetocentrical coordinates of satellite to equatorial coordinates as seen from Earth
        /// </summary>
        /// <param name="m">Rectangular planetocentrical coordinates of satellite</param>
        /// <param name="planet">Equatorial coordinates of parent planet (as seen from Earth)</param>
        /// <param name="P">Position angle of parent planet</param>
        /// <param name="semidiameter">Visible semidiameter of parent planet, in seconds of arc.</param>
        /// <returns>Equatorial coordinates of satellite as seen from Earth</returns>
        /// <remarks>
        /// The method is taken from geometrical drawing (own work)
        /// </remarks>
        public static CrdsEquatorial ToEquatorial(this CrdsRectangular m, CrdsEquatorial planet, double P, double semidiameter)
        {
            // convert rectangular planetocentrical coordinates to planetocentrical 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));

            // delta of RA
            double dAlpha = (1 / Math.Cos(Angle.ToRadians(planet.Delta))) * x * semidiameter / 3600;

            // delta of Declination
            // negative sign because positive delta means southward direction
            double dDelta = -y * semidiameter / 3600;

            return(new CrdsEquatorial(planet.Alpha - dAlpha, planet.Delta - dDelta));
        }
Exemple #4
0
        /// <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);
        }
Exemple #5
0
        // 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);
        }
Exemple #6
0
        /// <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($"Astrarium.Algorithms.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("");
        }
Exemple #7
0
        /// <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);
        }
Exemple #8
0
        /// <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);
        }
Exemple #9
0
        /// <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));
        }
Exemple #10
0
        /// <summary>
        /// Creates new instance
        /// </summary>
        public LunarEclipseLocalCircumstancesContactPoint(InstantLunarEclipseElements e, CrdsGeographical g)
        {
            CrdsEquatorial eq = new CrdsEquatorial(e.Alpha, e.Delta);

            // Nutation elements
            var nutation = Nutation.NutationElements(e.JulianDay);

            // True obliquity
            var epsilon = Date.TrueObliquity(e.JulianDay, nutation.deltaEpsilon);

            // Greenwich apparent sidereal time
            double siderealTime = Date.ApparentSiderealTime(e.JulianDay, nutation.deltaPsi, epsilon);

            // Geocenric distance to the Moon, in km
            double dist = 358473400.0 / (e.F3 * 3600);

            // Horizontal parallax of the Moon
            double parallax = LunarEphem.Parallax(dist);

            // Topocentrical equatorial coordinates
            var eqTopo = eq.ToTopocentric(g, siderealTime, parallax);

            // Horizontal coordinates
            var h = eqTopo.ToHorizontal(g, siderealTime);

            // Hour angle
            double H = Coordinates.HourAngle(siderealTime, g.Longitude, eqTopo.Alpha);

            // Position angles

            // Parallactic angle - AA(II), p.98, formula 14.1
            double qAngle = ToDegrees(Atan2(Sin(ToRadians(H)), Tan(ToRadians(g.Latitude)) * Cos(ToRadians(eqTopo.Delta)) - Sin(ToRadians(eqTopo.Delta)) * Cos(ToRadians(H))));

            // Position angle, measured from North to East (CCW)
            double pAngle = To360(ToDegrees(Atan2(e.X, e.Y)));

            // Position angle, measured from Zenith CCW
            double zAngle = To360(pAngle - qAngle);

            JulianDay     = e.JulianDay;
            LunarAltitude = h.Altitude;
            QAngle        = qAngle;
            PAngle        = pAngle;
            ZAngle        = zAngle;
            X             = e.X;
            Y             = e.Y;
            F1            = e.F1;
            F2            = e.F2;
            F3            = e.F3;
        }
Exemple #11
0
        /// <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 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);
        }
Exemple #12
0
        /// <summary>
        /// Finds horizon circle point for a given geographical longitude
        /// for an instant of lunar eclipse, where Moon has zero altitude.
        /// </summary>
        /// <param name="e">Besselian elements of the Moon for the given instant.</param>
        /// <param name="L">Geographical longitude, positive west, negative east, from -180 to +180 degrees.</param>
        /// <returns>
        /// Geographical coordinates for the point.
        /// </returns>
        /// <remarks>
        /// The method core is based on formulae from the book:
        /// Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac,
        /// University Science Book, Mill Valley (California), 1992,
        /// Chapter 8 "Eclipses of the Sun and Moon"
        /// https://archive.org/download/131123ExplanatorySupplementAstronomicalAlmanac/131123-explanatory-supplement-astronomical-almanac.pdf
        /// </remarks>
        private static CrdsGeographical Project(InstantLunarEclipseElements e, double L)
        {
            CrdsGeographical g = null;

            // Nutation elements
            var nutation = Nutation.NutationElements(e.JulianDay);

            // True obliquity
            var epsilon = Date.TrueObliquity(e.JulianDay, nutation.deltaEpsilon);

            // Greenwich apparent sidereal time
            double siderealTime = Date.ApparentSiderealTime(e.JulianDay, nutation.deltaPsi, epsilon);

            // Geocenric distance to the Moon, in km
            double dist = 358473400.0 / (e.F3 * 3600);

            // Horizontal parallax of the Moon
            double parallax = LunarEphem.Parallax(dist);

            // Equatorial coordinates of the Moon, initial value is geocentric
            CrdsEquatorial eq = new CrdsEquatorial(e.Alpha, e.Delta);

            // two iterations:
            // 1st: find geo location needed to perform topocentric correction
            // 2nd: correct sublunar point with topocentric position and find true geoposition
            for (int i = 0; i < 2; i++)
            {
                // sublunar point latitude, preserve sign!
                double phi0 = Sign(e.Delta) * Abs(eq.Delta);

                // sublunar point longitude (formula 8.426-1)
                double lambda0 = siderealTime - eq.Alpha;

                // sublunar point latitude (formula 8.426-2)
                double tanPhi = -1.0 / Tan(ToRadians(phi0)) * Cos(ToRadians(lambda0 - L));
                double phi    = ToDegrees(Atan(tanPhi));

                g = new CrdsGeographical(L, phi);

                if (i == 0)
                {
                    // correct to topocentric
                    eq = eq.ToTopocentric(g, siderealTime, parallax);
                }
            }

            return(g);
        }
Exemple #13
0
        /// <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);
        }
Exemple #14
0
        /// <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);
        }
Exemple #15
0
        /// <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);
        }
Exemple #16
0
        /// <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);
        }
Exemple #17
0
        /// <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);
        }
Exemple #18
0
        /// <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_));
        }
Exemple #19
0
        /// <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));
        }
Exemple #20
0
 /// <summary>
 /// Copying construtor
 /// </summary>
 /// <param name="eq">Equatorial coordinates to be copied</param>
 public CrdsEquatorial(CrdsEquatorial eq)
 {
     Alpha = eq.Alpha;
     Delta = eq.Delta;
 }
        public static CrdsEcliptical Position(double jd, GenericSatelliteOrbit orbit, CrdsEcliptical planet)
        {
            NutationElements ne      = Nutation.NutationElements(jd);
            double           epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon);

            // convert current coordinates to epoch, as algorithm requires
            CrdsEquatorial       eq            = planet.ToEquatorial(epsilon);
            PrecessionalElements peEpoch       = Precession.ElementsFK5(jd, Date.EPOCH_J2000);
            CrdsEquatorial       eqPlanetEpoch = Precession.GetEquatorialCoordinates(eq, peEpoch);

            // ecliptical pole
            CrdsEquatorial pole = new CrdsEcliptical(0, 90).ToEquatorial(epsilon);

            double distance0;
            double distance = planet.Distance;

            CrdsEcliptical eclSatellite;

            do
            {
                distance0 = distance;

                // take light-time effect into account
                double tau = PlanetPositions.LightTimeEffect(distance);

                double t = jd - tau - orbit.jd;

                double M = To360(orbit.M + orbit.n * t);

                double omega = To360(orbit.w + t * 360.0 / (orbit.Pw * 365.25));
                double node  = To360(orbit.Om + t * 360.0 / (orbit.POm * 365.25));

                // Find eccentric anomaly by solving Kepler equation
                double E = SolveKepler(M, orbit.e);

                double X = orbit.a * (Cos(E) - orbit.e);
                double Y = orbit.a * Sqrt(1 - orbit.e * orbit.e) * Sin(E);

                // cartesian state vector of satellite
                var d =
                    Matrix.R2(ToRadians(-eqPlanetEpoch.Delta)) *
                    Matrix.R3(ToRadians(eqPlanetEpoch.Alpha)) *
                    Matrix.R3(ToRadians(-pole.Alpha - 90)) *
                    Matrix.R1(ToRadians(pole.Delta - 90)) *
                    Matrix.R3(ToRadians(-node)) *
                    Matrix.R1(ToRadians(-orbit.i)) *
                    Matrix.R3(ToRadians(-omega)) *
                    new Matrix(new double[, ] {
                    { X / distance }, { Y / distance }, { 0 }
                });

                // radial component, positive away from observer
                // converted to degrees
                double x = ToDegrees(d.Values[0, 0]);

                // semimajor axis, expressed in degrees, as visible from Earth
                double theta = ToDegrees(Atan(orbit.a / distance));

                // offsets values in degrees
                double dAlphaCosDelta = ToDegrees(d.Values[1, 0]);
                double dDelta         = ToDegrees(d.Values[2, 0]);

                double delta  = eqPlanetEpoch.Delta + dDelta;
                double dAlpha = dAlphaCosDelta / Cos(ToRadians(delta));
                double alpha  = eqPlanetEpoch.Alpha + dAlpha;

                CrdsEquatorial eqSatelliteEpoch = new CrdsEquatorial(alpha, delta);

                // convert jd0 equatorial coordinates to current epoch
                // and to ecliptical
                PrecessionalElements pe          = Precession.ElementsFK5(Date.EPOCH_J2000, jd);
                CrdsEquatorial       eqSatellite = Precession.GetEquatorialCoordinates(eqSatelliteEpoch, pe);
                eclSatellite = eqSatellite.ToEcliptical(epsilon);

                // calculate distance to Earth
                distance = planet.Distance + x / theta * orbit.a;
            }while (Abs(distance - distance0) > 1e-6);

            eclSatellite.Distance = distance;

            return(eclSatellite);
        }
Exemple #22
0
        /// <summary>
        /// Calculates ecliptical coordinates of Nereid, the third-largest moon of Neptune.
        /// </summary>
        /// <param name="jd">Julian Day of calculation</param>
        /// <param name="neptune">Ecliptical coordinates of Neptune for the Julian Day specified.</param>
        /// <returns>Ecliptical coordinates of Nereid for specified date.</returns>
        /// <remarks>
        ///
        /// The method is based on work of F. Mignard (1981), "The Mean Elements of Nereid",
        /// The Astronomical Journal, Vol 86, Number 11, pages 1728-1729
        /// The work can be found by link: http://adsabs.harvard.edu/full/1981AJ.....86.1728M
        ///
        /// There are some changes from the original algorithm were made,
        /// to be compliant with ephemeris provided by Nasa JPL Horizons system (https://ssd.jpl.nasa.gov/?ephemerides):
        ///
        /// 1. Other value of mean motion (n) is used:
        ///    - original work : n = 0.999552
        ///    - implementation: n = 360.0 / 360.1362 (where 360.1362 is an orbital period)
        ///
        /// 2. Rotation around Z axis by angle OmegaN should by taken with NEGATIVE sign,
        ///    insted of POSITIVE sign in original work (possible typo?),
        ///    note the NEGATIVE sign for "Ne" angle (same meaning as "OmegaN" in original work) in the book:
        ///    Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac,
        ///    University Science Book, Mill Valley (California), 1992,
        ///    Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 376, formula 6.62-3
        ///
        /// </remarks>
        private static CrdsEcliptical NereidPosition(double jd, CrdsEcliptical neptune)
        {
            NutationElements ne      = Nutation.NutationElements(jd);
            double           epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon);

            // convert current coordinates to J1950 epoch, as algorithm requires
            CrdsEquatorial       eq            = neptune.ToEquatorial(epsilon);
            PrecessionalElements pe1950        = Precession.ElementsFK5(jd, Date.EPOCH_J1950);
            CrdsEquatorial       eqNeptune1950 = Precession.GetEquatorialCoordinates(eq, pe1950);

            const double jd0 = 2433680.5;           // Initial Epoch: 3.0 Feb 1951

            const double a      = 0.036868;         // Semi-major axis, in a.u.
            const double e0     = 0.74515;          // Orbit eccentricity for jd0 epoch
            const double i0     = 10.041;           // Inclination of the orbit for jd0 epoch, in degrees
            const double Omega0 = 329.3;            // Longitude of the node of the orbit for jd0 epoch, in degrees
            const double M0     = 358.91;           // Mean anomaly for jd0 epoch, in degrees
            const double n      = 360.0 / 360.1362; // Mean motion, in degrees per day
            const double OmegaN = 3.552;            // Longitude of ascending node of the orbit of Neptune, for J1950.0 epoch, in degrees
            const double gamma  = 22.313;           // Inclination of the orbit of Neptune, for J1950.0 epoch, in degrees

            // take light-time effect into account
            double tau = PlanetPositions.LightTimeEffect(neptune.Distance);

            double t = jd - tau - jd0;          // in days
            double T = t / 36525.0;             // in Julian centuries

            double psi      = ToRadians(To360(282.9 + 2.68 * T));
            double twoTheta = ToRadians(To360(107.4 + 0.01196 * t));

            // Equation to found omega, argument of pericenter
            Func <double, double> omegaEquation = (om) => To360(282.9 + 2.68 * T - 19.25 * Sin(2 * psi) + 3.23 * Sin(4 * psi) - 0.725 * Sin(6 * psi) - 0.351 * Sin(twoTheta) - 0.7 * Sin(ToRadians(2 * om) - twoTheta)) - om;

            // Solve equation (find root: omega value)
            double omega = ToRadians(FindRoots(omegaEquation, 0, 360, 1e-8));

            // Find longitude of the node
            double Omega = Omega0 - 2.4 * T + 19.7 * Sin(2 * psi) - 3.3 * Sin(4 * psi) + 0.7 * Sin(6 * psi) + 0.357 * Sin(twoTheta) + 0.276 * Sin(2 * omega - twoTheta);

            // Find orbit eccentricity
            double e = e0 - 0.006 * Cos(2 * psi) + 0.0056 * Cos(2 * omega - twoTheta);

            // Find mean anomaly
            double M = To360(M0 + n * t - 0.38 * Sin(2 * psi) + 1.0 * Sin(2 * omega - twoTheta));

            // Find inclination
            double cosi = Cos(ToRadians(i0)) - 9.4e-3 * Cos(2 * psi);
            double i    = Acos(cosi);

            // Find eccentric anomaly by solving Kepler equation
            double E = SolveKepler(M, e);

            double X = a * (Cos(E) - e);
            double Y = a * Sqrt(1 - e * e) * Sin(E);

            Matrix d =
                Matrix.R2(ToRadians(-eqNeptune1950.Delta)) *
                Matrix.R3(ToRadians(eqNeptune1950.Alpha)) *
                Matrix.R3(ToRadians(-OmegaN)) *
                Matrix.R1(ToRadians(-gamma)) *
                Matrix.R3(ToRadians(-Omega)) *
                Matrix.R1(-i) *
                Matrix.R3(-omega) *
                new Matrix(new double[, ] {
                { X / neptune.Distance }, { Y / neptune.Distance }, { 0 }
            });

            // radial component, positive away from observer
            // converted to degrees
            double x = ToDegrees(d.Values[0, 0]);

            // offsets values in degrees
            double dAlphaCosDelta = ToDegrees(d.Values[1, 0]);
            double dDelta         = ToDegrees(d.Values[2, 0]);

            double delta  = eqNeptune1950.Delta + dDelta;
            double dAlpha = dAlphaCosDelta / Cos(ToRadians(eqNeptune1950.Delta));
            double alpha  = eqNeptune1950.Alpha + dAlpha;

            CrdsEquatorial eqNereid1950 = new CrdsEquatorial(alpha, delta);

            // convert J1950 equatorial coordinates to current epoch
            // and to ecliptical
            PrecessionalElements pe        = Precession.ElementsFK5(Date.EPOCH_J1950, jd);
            CrdsEquatorial       eqNereid  = Precession.GetEquatorialCoordinates(eqNereid1950, pe);
            CrdsEcliptical       eclNereid = eqNereid.ToEcliptical(epsilon);

            // semimajor axis, expressed in degrees, as visible from Earth
            double theta = ToDegrees(Atan(a / neptune.Distance));

            // calculate distance to Earth
            eclNereid.Distance = neptune.Distance + x / theta * a;

            return(eclNereid);
        }
Exemple #23
0
        /// <summary>
        /// Calculates ecliptical coordinates of Triton, largest moon of Neptune.
        /// </summary>
        /// <param name="jd">Julian Day of calculation</param>
        /// <param name="neptune">Ecliptical coordinates of Neptune for the Julian Day specified.</param>
        /// <returns>Ecliptical coordinates of Triton for specified date.</returns>
        /// <remarks>
        ///
        /// The method is based on following works:
        ///
        /// 1. Harris, A.W. (1984), "Physical Properties of Neptune and Triton Inferred from the Orbit of Triton" NASA CP-2330, pages 357-373:
        ///    http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1984NASCP2330..357H&defaultprint=YES&filetype=.pdf
        ///
        /// 2. Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac,
        ///    University Science Book, Mill Valley (California), 1992,
        ///    Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 373, 6.61-1 Triton
        ///    https://archive.org/download/131123ExplanatorySupplementAstronomicalAlmanac/131123-explanatory-supplement-astronomical-almanac.pdf
        ///
        /// </remarks>
        private static CrdsEcliptical TritonPosition(double jd, CrdsEcliptical neptune)
        {
            NutationElements ne      = Nutation.NutationElements(jd);
            double           epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon);

            // convert current coordinates to J1950 epoch, as algorithm requires
            CrdsEquatorial       eq            = neptune.ToEquatorial(epsilon);
            PrecessionalElements pe1950        = Precession.ElementsFK5(jd, Date.EPOCH_J1950);
            CrdsEquatorial       eqNeptune1950 = Precession.GetEquatorialCoordinates(eq, pe1950);

            const double t0 = 2433282.5;       // 1.0 Jan 1950
            const double a  = 0.0023683;       // semimajor axis of Triton, in a.u.

            const double n       = 61.2588532; // nodal mean motion, degrees per day
            const double lambda0 = 200.913;    // longitude from ascending node through the invariable plane at epoch
            const double i       = 158.996;    // inclination of orbit to the invariable plane

            const double Omega0 = 151.401;     // angle from the intersection of invariable plane with the earth's
                                               // equatorial plane of 1950.0 to the ascending node
                                               // of the orbit through the invariable plane

            const double OmegaDot = 0.57806;   // nodal precision rate, degrees per year

            // Calculate J2000.0 RA and Declination of the pole of the invariable plane
            // These formulae are taken from the book:
            // Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac,
            // University Science Book, Mill Valley (California), 1992,
            // Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 373, 6.61-1 Triton
            double T  = (jd - 2451545.0) / 36525.0;
            double N  = ToRadians(359.28 + 54.308 * T);
            double ap = 298.72 + 2.58 * Sin(N) - 0.04 * Sin(2 * N);
            double dp = 42.63 - 1.90 * Cos(N) + 0.01 * Cos(2 * N);

            // Convert pole coordinates to J1950
            CrdsEquatorial eqPole1950 = Precession.GetEquatorialCoordinates(new CrdsEquatorial(ap, dp), pe1950);

            ap = eqPole1950.Alpha;
            dp = eqPole1950.Delta;

            // take light-time effect into account
            double tau = PlanetPositions.LightTimeEffect(neptune.Distance);

            double lambda = To360(lambda0 + n * (jd - t0 - tau));
            double omega  = Omega0 + OmegaDot * (jd - t0 - tau) / 365.25;

            // cartesian state vector of Triton
            var r =
                Matrix.R3(ToRadians(-ap - 90)) *
                Matrix.R1(ToRadians(dp - 90)) *
                Matrix.R3(ToRadians(-omega)) *
                Matrix.R1(ToRadians(-i)) *
                new Matrix(new[, ] {
                { a *Cos(ToRadians(lambda)) }, { a *Sin(ToRadians(lambda)) }, { 0 }
            });

            // normalize by distance to Neptune
            r.Values[0, 0] /= neptune.Distance;
            r.Values[1, 0] /= neptune.Distance;
            r.Values[2, 0] /= neptune.Distance;

            // offsets vector
            var d =
                Matrix.R2(ToRadians(-eqNeptune1950.Delta)) *
                Matrix.R3(ToRadians(eqNeptune1950.Alpha)) *
                r;

            // radial component, positive away from observer
            // converted to degrees
            double x = ToDegrees(d.Values[0, 0]);

            // semimajor axis, expressed in degrees, as visible from Earth
            double theta = ToDegrees(Atan(a / neptune.Distance));

            // offsets values in degrees
            double dAlphaCosDelta = ToDegrees(d.Values[1, 0]);
            double dDelta         = ToDegrees(d.Values[2, 0]);

            double delta  = eqNeptune1950.Delta + dDelta;
            double dAlpha = dAlphaCosDelta / Cos(ToRadians(eqNeptune1950.Delta));
            double alpha  = eqNeptune1950.Alpha + dAlpha;

            CrdsEquatorial eqTriton1950 = new CrdsEquatorial(alpha, delta);

            // convert J1950 equatorial coordinates to current epoch
            // and to ecliptical
            PrecessionalElements pe        = Precession.ElementsFK5(Date.EPOCH_J1950, jd);
            CrdsEquatorial       eqTriton  = Precession.GetEquatorialCoordinates(eqTriton1950, pe);
            CrdsEcliptical       eclTriton = eqTriton.ToEcliptical(epsilon);

            // calculate distance to Earth
            eclTriton.Distance = neptune.Distance + x / theta * a;

            return(eclTriton);
        }
Exemple #24
0
        /// <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);
        }
Exemple #25
0
        /// <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);
        }
Exemple #26
0
        /// <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);
        }