예제 #1
0
        static void PrintEclipse(LunarEclipseInfo eclipse)
        {
            // Calculate beginning/ending of different phases
            // of an eclipse by subtracting/adding the peak time
            // with the number of minutes indicated by the "semi-duration"
            // fields sd_partial and sd_total.
            const double MINUTES_PER_DAY = 24 * 60;

            AstroTime p1 = eclipse.peak.AddDays(-eclipse.sd_partial / MINUTES_PER_DAY);

            Console.WriteLine("{0}  Partial eclipse begins.", p1);

            if (eclipse.sd_total > 0.0)
            {
                AstroTime t1 = eclipse.peak.AddDays(-eclipse.sd_total / MINUTES_PER_DAY);
                Console.WriteLine("{0}  Total eclipse begins.", t1);
            }

            Console.WriteLine("{0}  Peak of {1} eclipse.", eclipse.peak, eclipse.kind.ToString().ToLowerInvariant());

            if (eclipse.sd_total > 0.0)
            {
                AstroTime t2 = eclipse.peak.AddDays(+eclipse.sd_total / MINUTES_PER_DAY);
                Console.WriteLine("{0}  Total eclipse ends.", t2);
            }

            AstroTime p2 = eclipse.peak.AddDays(+eclipse.sd_partial / MINUTES_PER_DAY);

            Console.WriteLine("{0}  Partial eclipse ends.", p2);
            Console.WriteLine();
        }
예제 #2
0
        //=================== GET Info PW3 =========================//
        //private void GetPlanwaveInfo()
        //{
        //    double lst, altRadians, azRadians;
        //    String uRLString = "http://localhost:8220/status";
        //    XmlTextReader reader = new XmlTextReader(uRLString);
        //    textBox9.BeginInvoke((Action)(() =>
        //    {
        //        label20.ForeColor = System.Drawing.Color.Green;
        //        label20.Text = "Connected";
        //        String elementName = "";
        //        textBox9.Text = "";
        //        while (reader.Read())
        //        {
        //            switch (reader.NodeType)
        //            {
        //                case XmlNodeType.Element:
        //                    elementName = reader.Name;

        //                    if (elementName == "utc" ||
        //                        elementName == "jd" ||
        //                        elementName == "lst" ||
        //                        elementName == "azm_radian" ||
        //                        elementName == "alt_radian")
        //                    {
        //                        textBox9.Text += " " + reader.Name + " : ";
        //                    }
        //                    break;
        //                case XmlNodeType.Text:

        //                    if (elementName == "utc" ||
        //                        elementName == "jd" ||
        //                        elementName == "lst" ||
        //                        elementName == "azm_radian" ||
        //                        elementName == "alt_radian")
        //                    {
        //                        switch (elementName)
        //                        {
        //                            case "lst": string[] lst_split = reader.Value.Split(' '); lst = Convert.ToDouble(lst_split[0]); textBox9.Text += reader.Value; break;
        //                            case "azm_radian": textBox9.Text += Convert.ToString((Convert.ToDouble(reader.Value) * 57.2957795131)); string[] altSplit = reader.Value.Split(' '); altRadians = Convert.ToDouble(altSplit[0]) * 57.2957795131; break;
        //                            case "alt_radian": textBox9.Text += Convert.ToString((Convert.ToDouble(reader.Value) * 57.2957795131)); string[] azmSplit = reader.Value.Split(' '); azRadians = Convert.ToDouble(azmSplit[0]) * 57.2957795131; break;
        //                            default: textBox9.Text += reader.Value; break;
        //                        }
        //                        textBox9.Text += System.Environment.NewLine;
        //                    }
        //                    elementName = "";
        //                    break;
        //            }
        //        }
        //    }));
        //}

        //=============================================//

        //=============  Calculate Sun Position ==============//mmmmmm
        public void sunPosition(out double sunAlt, out double sunAzm)
        {
            try
            {
                RaDec    raDec             = Sun.GetRaDec(AstroTime.JulianDayUTC(DateTime.Now));
                AltAz    sunAltAzm         = AstroLib.RADecToAltAz(DateTime.Now, raDec);
                String   altSunBeforeSprit = Convert.ToString(sunAltAzm.Alt);
                String[] sunAltSprit       = altSunBeforeSprit.Split(' ');
                Double   sunAltSprited     = Convert.ToDouble(sunAltSprit[0]);
                String   azmSunBeforeSprit = Convert.ToString(sunAltAzm.Az);
                String[] sunAzmSprit       = azmSunBeforeSprit.Split(' ');
                Double   sunAzmSprited     = Convert.ToDouble(sunAzmSprit[0]);
                sunAlt = sunAltSprited;
                sunAzm = sunAzmSprited;
                double revertSunAzm = sunAzm - 180;
                if (revertSunAzm < 0)
                {
                    revertSunAzm = revertSunAzm + 360;
                }
                textBox10.Invoke((Action)(() =>
                {
                    textBox10.Text = "Local Date Time: " + DateTime.Now + Environment.NewLine +
                                     "Latitude Ref : " + LATITUDE + Environment.NewLine +
                                     "Longitude Ref : " + LONGITUDE + Environment.NewLine +
                                     "Sun Alt : " + Math.Round(Convert.ToDecimal(sunAltSprited), 5) + Environment.NewLine +
                                     "Sun Azm : " + Math.Round(Convert.ToDecimal(sunAzmSprited), 5) + Environment.NewLine +
                                     "Revert Azm : " + Math.Round(Convert.ToDecimal(revertSunAzm), 5);
                    textBox14.Text = Convert.ToString(Math.Round(Convert.ToDecimal(aDU), 2));
                    textBox13.Text = exposureTime;

                    textBox20.Text = Convert.ToString(Convert.ToDecimal(variableAdu)); //TEST
                }));
            }
            catch { sunAlt = 0; sunAzm = 0; }
        }
예제 #3
0
        public void JDProperty()
        {
            DateTime  dt   = new DateTime(2003, 1, 1);
            AstroTime time = new AstroTime(dt);

            Assert.That(time.JD.ToDouble(), Is.EqualTo(2452640.5));
        }
예제 #4
0
        public static void ParseArgs(string program, string[] args, out Observer observer, out AstroTime time)
        {
            if (args.Length == 2 || args.Length == 3)
            {
                double latitude;
                if (!double.TryParse(args[0], out latitude) || (latitude < -90.0) || (latitude > +90.0))
                {
                    throw new ArgumentException(string.Format("ERROR({0}): Invalid latitude '{1}' on command line.", program, args[0]));
                }

                double longitude;
                if (!double.TryParse(args[1], out longitude) || (longitude < -180.0) || (longitude > +180.0))
                {
                    throw new ArgumentException(string.Format("ERROR({0}): Invalid longitude '{1}' on command line.", program, args[1]));
                }

                observer = new Observer(latitude, longitude, 0.0);

                if (args.Length == 3)
                {
                    // Time is present on the command line, so use it.
                    time = ParseTime(program, args[2]);
                }
                else
                {
                    // Time is absent on the command line, so use the current time.
                    time = new AstroTime(DateTime.UtcNow);
                }
            }
            else
            {
                throw new ArgumentException(string.Format("USAGE: {0} latitude longitude [yyyy-mm-ddThh:mm:ssZ]", program));
            }
        }
예제 #5
0
        public void DateProperty()
        {
            DateTime  dt   = new DateTime(2003, 1, 1);
            AstroTime time = new AstroTime(dt);

            Assert.That(time.Date, Is.EqualTo(dt));
        }
예제 #6
0
        public void TestGMST()
        {
            AstroTime time = new AstroTime(new DateTime(2003, 1, 1));
            DateTime  gmst = time.GMST;

            Assert.That(gmst.Hour, Is.EqualTo(6));
            Assert.That(gmst.Minute, Is.EqualTo(40));
            Assert.That(gmst.Second, Is.EqualTo(56));
            Assert.That(gmst.Millisecond, Is.EqualTo(954));
        }
예제 #7
0
 static int PrintEvent(string name, AstroTime time)
 {
     if (time == null)
     {
         Console.WriteLine("ERROR: Search failed for {0}", name);
         return(1);
     }
     Console.WriteLine("{0,-8} : {1}", name, time);
     return(0);
 }
예제 #8
0
        static AstroVector DirectionVector(AstroTime time, Observer observer, double altitude, double azimuth)
        {
            // Convert horizontal angles to a horizontal unit vector.
            var         hor  = new Spherical(altitude, azimuth, 1.0);
            AstroVector hvec = Astronomy.VectorFromHorizon(hor, time, Refraction.None);

            // Find the rotation matrix that converts horizontal vectors to equatorial vectors.
            RotationMatrix rot = Astronomy.Rotation_HOR_EQD(time, observer);

            // Rotate the horizontal (HOR) vector to an equator-of-date (EQD) vector.
            AstroVector evec = Astronomy.RotateVector(rot, hvec);

            return(evec);
        }
예제 #9
0
        static int Search(
            out double ecliptic_longitude_crossing,
            out Spherical hor_crossing,
            AstroTime time,
            RotationMatrix rot_ecl_hor,
            double e1, double e2)
        {
            int          error;
            double       e3;
            Spherical    h3;
            const double tolerance = 1.0e-6;        /* one-millionth of a degree is close enough! */

            /*
             *  Binary search: find the ecliptic longitude such that the horizontal altitude
             *  ascends through a zero value. The caller must pass e1, e2 such that the altitudes
             *  bound zero in ascending order.
             */

            ecliptic_longitude_crossing = 1.0e+99;      // initialize with impossible value
            hor_crossing = new Spherical();

            for (;;)
            {
                e3    = (e1 + e2) / 2.0;
                error = HorizontalCoords(out h3, e3, time, rot_ecl_hor);
                if (error != 0)
                {
                    return(error);
                }

                if (Math.Abs(e2 - e1) < tolerance)
                {
                    /* We have found the horizon crossing within tolerable limits. */
                    ecliptic_longitude_crossing = e3;
                    hor_crossing = h3;
                    return(0);
                }

                if (h3.lat < 0.0)
                {
                    e1 = e3;
                }
                else
                {
                    e2 = e3;
                }
            }
        }
예제 #10
0
        static int Main(string[] args)
        {
            AstroTime time;

            switch (args.Length)
            {
            case 0:
                time = new AstroTime(DateTime.Now);
                break;

            case 1:
                time = DemoHelper.ParseTime("moonphase", args[0]);
                break;

            default:
                Console.WriteLine("USAGE: moonphase [date]");
                return(1);
            }

            /*
             *  Calculate the Moon's current phase angle,
             *  which ranges from 0 to 360 degrees.
             *
             *  0 = new moon,
             *  90 = first quarter,
             *  180 = full moon,
             *  270 = third quarter.
             */
            double phase = Astronomy.MoonPhase(time);

            Console.WriteLine("{0} : Moon's phase angle = {1:0.000000} degrees.", time, phase);

            /* Find the next 10 lunar quarter phases. */
            Console.WriteLine();
            Console.WriteLine("The next 10 lunar quarters are:");
            MoonQuarterInfo mq = Astronomy.SearchMoonQuarter(time);

            for (int i = 0; i < 10; ++i)
            {
                if (i > 0)
                {
                    mq = Astronomy.NextMoonQuarter(mq);
                }
                Console.WriteLine("{0} : {1}", mq.time, QuarterName(mq.quarter));
            }
            return(0);
        }
예제 #11
0
        static int HorizontalCoords(
            out Spherical hor,
            double ecliptic_longitude,
            AstroTime time,
            RotationMatrix rot_ecl_hor)
        {
            var eclip = new Spherical(
                0.0,        /* being "on the ecliptic plane" means ecliptic latitude is zero. */
                ecliptic_longitude,
                1.0         /* any positive distance value will work fine. */
                );

            /* Convert ecliptic angular coordinates to ecliptic vector. */
            AstroVector ecl_vec = Astronomy.VectorFromSphere(eclip, time);

            /* Use the rotation matrix to convert ecliptic vector to horizontal vector. */
            AstroVector hor_vec = Astronomy.RotateVector(rot_ecl_hor, ecl_vec);

            /* Find horizontal angular coordinates, correcting for atmospheric refraction. */
            hor = Astronomy.HorizonFromVector(hor_vec, Refraction.Normal);

            return(0);   /* success */
        }
예제 #12
0
        static int Main(string[] args)
        {
            AstroTime time;

            switch (args.Length)
            {
            case 0:
                time = new AstroTime(DateTime.Now);
                break;

            case 1:
                time = DemoHelper.ParseTime("lunar_eclipse", args[0]);
                break;

            default:
                Console.WriteLine("USAGE: lunar_eclipse [date]");
                return(1);
            }

            int count = 0;
            LunarEclipseInfo eclipse = Astronomy.SearchLunarEclipse(time);

            for (;;)
            {
                if (eclipse.kind != EclipseKind.Penumbral)
                {
                    PrintEclipse(eclipse);
                    if (++count == 10)
                    {
                        break;
                    }
                }
                eclipse = Astronomy.NextLunarEclipse(eclipse.peak);
            }

            return(0);
        }
예제 #13
0
        static int Main(string[] args)
        {
            if (args.Length != 10)
            {
                Console.WriteLine(UsageText);
                return(1);
            }

            // Validate and parse command line arguments.
            double lat1 = ParseNumber("lat1", args[0]);
            double lon1 = ParseNumber("lon1", args[1]);
            double elv1 = ParseNumber("elv1", args[2]);
            double az1  = ParseNumber("az1", args[3]);
            double alt1 = ParseNumber("alt1", args[4]);
            double lat2 = ParseNumber("lat2", args[5]);
            double lon2 = ParseNumber("lon2", args[6]);
            double elv2 = ParseNumber("elv2", args[7]);
            double az2  = ParseNumber("az2", args[8]);
            double alt2 = ParseNumber("alt2", args[9]);

            var obs1 = new Observer(lat1, lon1, elv1);
            var obs2 = new Observer(lat2, lon2, elv2);

            // Use an arbitrary but consistent time for the Earth's rotation.
            AstroTime time = new AstroTime(0.0);

            // Convert geographic coordinates of the observers to vectors.
            AstroVector pos1 = Astronomy.ObserverVector(time, obs1, EquatorEpoch.OfDate);
            AstroVector pos2 = Astronomy.ObserverVector(time, obs2, EquatorEpoch.OfDate);

            // Convert horizontal coordinates into unit direction vectors.
            AstroVector dir1 = DirectionVector(time, obs1, alt1, az1);
            AstroVector dir2 = DirectionVector(time, obs2, alt2, az2);

            // Find the closest point between the skew lines.
            return(Intersect(pos1, dir1, pos2, dir2));
        }
예제 #14
0
        static int CameraImage(Observer observer, AstroTime time)
        {
            const double tolerance = 1.0e-15;

            // Calculate the topocentric equatorial coordinates of date for the Moon.
            // Assume aberration does not matter because the Moon is so close and has such a small relative velocity.
            Equatorial moon_equ = Astronomy.Equator(Body.Moon, time, observer, EquatorEpoch.OfDate, Aberration.None);

            // Also calculate the Sun's topocentric position in the same coordinate system.
            Equatorial sun_equ = Astronomy.Equator(Body.Sun, time, observer, EquatorEpoch.OfDate, Aberration.None);

            // Get the Moon's horizontal coordinates, so we know how much to pivot azimuth and altitude.
            Topocentric moon_hor = Astronomy.Horizon(time, observer, moon_equ.ra, moon_equ.dec, Refraction.None);

            Console.WriteLine($"Moon horizontal position: azimuth = {moon_hor.azimuth:F3}, altitude = {moon_hor.altitude:F3}");

            // Get the rotation matrix that converts equatorial to horizontal coordintes for this place and time.
            RotationMatrix rot = Astronomy.Rotation_EQD_HOR(time, observer);

            // Modify the rotation matrix in two steps:
            // First, rotate the orientation so we are facing the Moon's azimuth.
            // We do this by pivoting around the zenith axis.
            // Horizontal axes are: 0 = north, 1 = west, 2 = zenith.
            // Tricky: because the pivot angle increases counterclockwise, and azimuth
            // increases clockwise, we undo the azimuth by adding the positive value.
            rot = Astronomy.Pivot(rot, 2, moon_hor.azimuth);

            // Second, pivot around the leftward axis to bring the Moon to the camera's altitude level.
            // From the point of view of the leftward axis, looking toward the camera,
            // adding the angle is the correct sense for subtracting the altitude.
            rot = Astronomy.Pivot(rot, 1, moon_hor.altitude);

            // As a sanity check, apply this rotation to the Moon's equatorial (EQD) coordinates and verify x=0, y=0.
            AstroVector vec = Astronomy.RotateVector(rot, moon_equ.vec);

            // Convert to unit vector.
            double radius = vec.Length();

            vec.x /= radius;
            vec.y /= radius;
            vec.z /= radius;
            Console.WriteLine($"Moon check: x = {vec.x}, y = {vec.y}, z = {vec.z}");
            if (!double.IsFinite(vec.x) || Math.Abs(vec.x - 1.0) > tolerance)
            {
                Console.WriteLine("Excessive error in moon check (x).");
                return(1);
            }

            if (!double.IsFinite(vec.y) || Math.Abs(vec.y) > tolerance)
            {
                Console.WriteLine("Excessive error in moon check (y).");
                return(1);
            }

            if (!double.IsFinite(vec.z) || Math.Abs(vec.z) > tolerance)
            {
                Console.WriteLine("Excessive error in moon check (z).");
                return(1);
            }

            // Apply the same rotation to the Sun's equatorial vector.
            // The x- and y-coordinates now tell us which side appears sunlit in the camera!

            vec = Astronomy.RotateVector(rot, sun_equ.vec);

            // Don't bother normalizing the Sun vector, because in AU it will be close to unit anyway.
            Console.WriteLine($"Sun vector: x = {vec.x:F6}, y = {vec.y:F6}, z = {vec.z:F6}");

            // Calculate the tilt angle of the sunlit side, as seen by the camera.
            // The x-axis is now pointing directly at the object, z is up in the camera image, y is to the left.
            double tilt = RAD2DEG * Math.Atan2(vec.z, vec.y);

            Console.WriteLine($"Tilt angle of sunlit side of the Moon = {tilt:F3} degrees counterclockwise from up.");

            IllumInfo illum = Astronomy.Illumination(Body.Moon, time);

            Console.WriteLine($"Moon magnitude = {illum.mag:F2}, phase angle = {illum.phase_angle:F2} degrees.");

            double angle = Astronomy.AngleFromSun(Body.Moon, time);

            Console.WriteLine($"Angle between Moon and Sun as seen from Earth = {angle:F2} degrees.");
            return(0);
        }
예제 #15
0
        //=================== Ecposure ===================//
        public void exposeCamera(int hBin, int vBin, int tdi, double exposureTime)
        {
            Image <Gray, float> image;
            Image <Rgb, byte>   imageRGB;
            int ulX, ulY, lrX, lrY;

            fliCCD.GetVisibleArea(out ulX, out ulY, out lrX, out lrY);
            int width  = lrX - ulX;
            int height = lrY - ulY;

            ushort[][] data = new ushort[height][];
            fliCCD.SetImageArea(ulX, ulY, lrX, lrY);
            exposureTime = exposureTime * 1000;
            fliCCD.SetExposureTime(Convert.ToInt32(exposureTime));
            fliCCD.SetHBin(hBin);
            fliCCD.SetVBin(vBin);
            fliCCD.SetTDI(tdi);
            sunPosition(out double sunAltStart, out double sunAzmStart);
            fliCCD.Expose();

            while (!fliCCD.IsDownloadReady())
            {
                Thread.Sleep(100);
            }
            for (int y = 0; y < height; y++)
            {
                data[y] = new ushort[width];
                fliCCD.GrabRow(data[y]);
            }
            sunPosition(out double sunAltEnd, out double sunAzmEnd);
            Matrix <float> matrix = new Matrix <float>(height, width);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    matrix.Data[y, x] = data[y][x];
                }
            }

            image = matrix.Mat.ToImage <Gray, float>();
            //imageBox1.Image = image2;

            string ADU = Convert.ToString(image.GetAverage());

            string[] words = ADU.Split('[', ']');
            aDU = Convert.ToDouble(words[1]);
            string localDate    = DateTime.Now.ToString("dd/MM/yyyy_THH':'mm':'ss");
            string fileFitsName = "ImageFits/Sun_alt_ " + sunAltStart + "_ADU_" + aDU + ".fits";

            addRecord(localDate, Convert.ToString(sunAltStart), Convert.ToString(sunAltEnd), Convert.ToString(sunAzmStart), Convert.ToString(sunAzmEnd), Convert.ToString((exposureTime / 1000)), filterSelect, Convert.ToString(ADU), "dataRecord/info_expose.txt");

            //========== write .fits =========//
            SRSLib.ImageLib.ImageType imageFit = new SRSLib.ImageLib.ImageType();
            imageFit.N1            = height;
            imageFit.N2            = width;
            imageFit.N3            = 1;
            imageFit.Simple        = true;
            imageFit.HaveHistogram = true;
            imageFit.HaveDateTime  = true;
            imageFit.HaveLevels    = true;
            imageFit.XBinning      = hBin;
            imageFit.YBinning      = vBin;
            imageFit.NAxis         = 2;
            imageFit.BScale        = 1;
            imageFit.DateObsStr    = Convert.ToString(DateTime.UtcNow);
            imageFit.HaveDateTime  = true;
            String[] header_fit = new String[50];
            imageFit.Header     = header_fit;
            imageFit.BScale     = 1;
            imageFit.Filter     = filterSelect;
            imageFit.DateObsStr = localDate;
            imageFit.Exposure   = (exposureTime / 1000);
            imageFit.JD         = AstroCalculation.AstroLib.GetLocalJD();
            RaDec raDec = Sun.GetRaDec(AstroTime.JulianDayUTC(DateTime.Now));

            imageFit.DecRad      = Convert.ToDouble(raDec.Dec.Rads);
            imageFit.RARad       = Convert.ToDouble(raDec.Ra.Rads);
            imageFit.ImageType   = "Flat Field";
            imageFit.Data        = matrix.Data;
            imageFit.Temperature = fliCCD.GetTemperature();

            SRSLib.ImageLib.WriteFITS(ref imageFit, fileFitsName, false);

            //===================================================//

            //====================== Convert .Fits To .JPG ===========================//

            String stretchType = comboBox2.Text;

            strecthImage.GetStrecthType(stretchType, out double lowerPercen, out double upperPercen);
            Matrix <UInt16> NewImg   = strecthImage.ConvertStretchImageU16BitToJPG(matrix.Convert <UInt16>(), lowerPercen, upperPercen);
            string          imageJpg = "imageJPG/Sun_alt_ " + sunAltStart + "_ADU_" + aDU + " .jpg";

            NewImg.Save(imageJpg);

            pictureBox1.Image = Image.FromFile(imageJpg);
            //=======================================================================//
            //=========================Plot Graph AllSky=========================//
            AllskyPlotGraph(out string fileNameAllsky);

            //===================================================================//

            //======================Insert Mongo DB ====================//
            CCD_Mongo cCDMongo = new CCD_Mongo(sunAltStart, sunAltEnd, sunAzmStart, sunAzmEnd, filterSelect, (exposureTime / 1000), aDU, fileFitsName, imageJpg, fileNameAllsky, DateTime.Now);

            collection.InsertOne(cCDMongo);
            //==========================================================//

            NewImg = null;
            data   = null;
            matrix = null;
            image  = null;

            textBox14.Text    = Convert.ToString(Math.Round(Convert.ToDecimal(aDU), 2));
            this.exposureTime = Convert.ToString((exposureTime / 1000));
            textBox13.Text    = this.exposureTime;
        }
예제 #16
0
        static int FindEclipticCrossings(Observer observer, AstroTime time)
        {
            int i;
            var hor = new Spherical[NUM_SAMPLES];

            /*
             *  The ecliptic is a celestial circle that describes the mean plane of
             *  the Earth's orbit around the Sun. We use J2000 ecliptic coordinates,
             *  meaning the x-axis is defined to where the plane of the Earth's
             *  equator on January 1, 2000 at noon UTC intersects the ecliptic plane.
             *  The positive x-axis points toward the March equinox.
             *  Calculate a rotation matrix that converts J2000 ecliptic vectors
             *  to horizontal vectors for this observer and time.
             */
            RotationMatrix rot = Astronomy.Rotation_ECL_HOR(time, observer);

            /*
             *  Sample several points around the ecliptic.
             *  Remember the horizontal coordinates for each sample.
             */
            for (i = 0; i < NUM_SAMPLES; ++i)
            {
                if (0 != HorizontalCoords(out hor[i], ECLIPLON(i), time, rot))
                {
                    return(1);   /* failure */
                }
            }
            for (i = 0; i < NUM_SAMPLES; ++i)
            {
                double    a1 = hor[i].lat;
                double    a2 = hor[(i + 1) % NUM_SAMPLES].lat;
                double    e1 = ECLIPLON(i);
                double    e2 = ECLIPLON(i + 1);
                double    ex;
                Spherical hx;
                int       error;

                if (a1 * a2 <= 0.0)
                {
                    /* Looks like a horizon crossing. Is altitude going up with longitude or down? */
                    if (a2 > a1)
                    {
                        /* Search for the ecliptic longitude and azimuth where altitude ascends through zero. */
                        error = Search(out ex, out hx, time, rot, e1, e2);
                    }
                    else
                    {
                        /* Search for the ecliptic longitude and azimuth where altitude descends through zero. */
                        error = Search(out ex, out hx, time, rot, e2, e1);
                    }

                    if (error != 0)
                    {
                        return(error);
                    }

                    string direction;
                    if (hx.lon > 0.0 && hx.lon < 180.0)
                    {
                        direction = "ascends ";     /* azimuth is more toward the east than the west */
                    }
                    else
                    {
                        direction = "descends";     /* azimuth is more toward the west than the east */
                    }
                    Console.WriteLine("Ecliptic longitude {0,9:0.0000} {1} through horizon az {2,9:0.0000}, alt {3,12:0.000e+00}", ex, direction, hx.lon, hx.lat);
                }
            }
            return(0);
        }