예제 #1
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);
        }
예제 #2
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);
        }
예제 #3
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);
        }