It was one of those rare clear nights we sometimes get this time of year in the Pacific Northwest. My wife had been outside looking up at the night sky. Soon she came inside the house and asked, “what’s the sidereal time right now?” It was good timing because I had just added a sidereal time object to the SquareWidget.Astronomy.Core code library. While she looked over my shoulder, I started to write some code to answer the question. I looked up our local longitude (L) and in four lines of code I had the answer:
using SquareWidget.Astronomy.Core.UnitsOfMeasure; Moment moment = new(DateTime.UtcNow); SiderealTime st = new(moment); SexigesimalAngle L = new(-123, 15, 43.34); Console.WriteLine(st.ToLocalMean(L));
It returned 7 hours and some odd arcminutes and arcseconds. She nodded approvingly because she already knew the answer having been outside and saw that the constellation Orion had recently crossed our local meridian.
It’s been about four weeks since I released the first version of the code library. It took a lot out of me, so my plan was to chill out and relax for a good long while. And I did. For about 24 hours. I kept identifying and making a list of new features that I wanted to add at some point. Soon I found myself peeling them off one at a time and building releases around each one. So now the library is up to v1.5.1 and I think it’s time to do “Chill Out 2.0” for real this time. But first, let me walk through some of the new features to illustrate them. See my previous blog post on the subject as well as the README documentation on GitHub for more details.
Solar Coordinates
The first version had a solar longitude calculator. The calculator has been deprecated and the functionality is now in the Sun object. The coordinates are returned as apparent (geocentric) equatorial coordinates at a given moment. Suppose I want to know the Sun’s coordinates on 4 Jul 2028:
using SquareWidget.Astronomy.Core.UnitsOfMeasure; using SquareWidget.Astronomy.Core.CelestialObjects.Stars; DateTime d = new(2028, 7, 4); Moment moment = new(d); Sun sun = new(moment); EquatorialCoordinates eqc = sun.GetGeocentricPosition(); Console.WriteLine($"RA {eqc.α.ToString()}"); Console.WriteLine($"Dec {eqc.δ.ToString()}."); /* Displays: RA 6h 54m 36.194953s Dec +22° 50' 36.22891852". */
For a deep dive into the algorithm on solar coordinates see my earlier blog post Astronomical Calculations: Solar Coordinates.
Moon Phases
There is now a calculator to return moon phases within a given date range. Some phases fall outside the range in order to get a baseline for the previous or next phase:
using SquareWidget.Astronomy.Core.Models; using SquareWidget.Astronomy.Core.Calculators; using SquareWidget.Astronomy.Core.CelestialObjects.Moons; DateOnly startDate = new(2025, 1, 1); DateOnly endDate = new(2025, 3, 31); DateRange dateRange = new(startDate, endDate); List<MoonPhase> list = MoonPhaseDatesCalculator.Calculate(dateRange); foreach (var item in list) { Console.WriteLine(item.PhaseName + " on " + item.Moment .ToDateTime() .ToString("MM/dd/yyyy hh:mm:ss.fff tt")); } /* Displays: NewMoon on 12/30/2024 10:26:46.440 PM FirstQuarter on 01/06/2025 11:56:49.386 PM LastQuarter on 01/21/2025 08:30:00.587 PM FullMoon on 02/12/2025 01:53:20.083 PM NewMoon on 01/29/2025 12:35:53.996 PM FirstQuarter on 02/05/2025 08:01:06.996 AM LastQuarter on 02/20/2025 05:31:34.392 PM FullMoon on 03/14/2025 06:54:32.963 AM NewMoon on 02/28/2025 12:44:39.649 AM FirstQuarter on 03/06/2025 04:30:40.312 PM LastQuarter on 03/22/2025 11:29:33.015 AM FullMoon on 04/13/2025 12:22:13.127 AM */
Solar Eclipses
As with moon phases above you build a date range and pass that into the calculator to get a list of solar eclipses within the range. Here I’m entering the current year 2024 and I see the total eclipse this April that will swing through the U.S. Midwest.
using SquareWidget.Astronomy.Core.Calculators; using SquareWidget.Astronomy.Core.Models; DateOnly startDate = new(2024, 1, 1); DateOnly endDate = new(2024, 12, 31); DateRange dateRange = new(startDate, endDate); IEnumerable<SolarEclipse> eclipses = SolarEclipseCalculator.Calculate(dateRange); foreach (var eclipse in eclipses) { Console.WriteLine(eclipse.ToString()); } /* Displays: 04-08-2024 18:18 UTC g: 0.3438 Total 10-02-2024 18:46 UTC g: -0.3515 Annular */
Check out NASA’s Five Millennium Canon of Solar Eclipses -1999 to +3000 to see a visual diagram that you can compare with the results.
Galilean Moons of Jupiter
A few years ago, I showed how to calculate, for a given date, the apparent rectangular coordinates (X, Y) of the four Galilean moons of Jupiter (here). You can see a visual representation of the data — a sort of corkscrew diagram — that accompanies that article here. This past month, I put the feature into the code library. You use the same date range pattern as with the other calculators above:
using SquareWidget.Astronomy.Core.Calculators; using SquareWidget.Astronomy.Core.Models; DateTime startDate = DateTime.Today; DateTime endDate = startDate.AddDays(1); for (var d = startDate; d <= endDate; d = d.AddDays(1)) { SatellitePositions positions = JupiterSatellitePositionCalculator.Calculate(d); Console.WriteLine(positions.ToString()); } /* Displays: 2/24/2024 12:01 AM Io - X: -4.06 Y: 0.22 Europa - X: -0.58 Y: 0.48 Ganymede - X: -14.48 Y: 0.20 Callisto - X: 10.73 Y: -1.24 2/25/2024 12:01 AM Io - X: 5.38 Y: -0.12 Europa - X: -9.02 Y: -0.12 Ganymede - X: -12.32 Y: -0.44 Callisto - X: 18.74 Y: -0.95 */
The SatellitePositions
object holds the date, all four moons, and their respective X, Y coordinates with respect to Jupiter.
Horizontal Coordinates
I’ve added horizontal coordinates as a third system for Alt-Az support. See the code sample for a detailed implementation. If you already have horizontal coordinates, you can convert them to equatorial:
// convert horizontal coordinates to equatorial coordinates EquatorialCoordinates eqc = hc.ToΕquatorialCoordinates(moment, φ, L);
You need a Moment
instance, and the observer’s latitude (φ) and longitude (L) for the conversion.
Sidereal Time
The code library uses sidereal time for conversion between coordinates. You can use it to find the Greenwich Mean Sidereal Time (GMST) or the Greenwich Apparent Sidereal Time (GAST) for any given moment in time. Always use UTC when initializing a SiderealTime
object:
using SquareWidget.Astronomy.Core.Planets; using SquareWidget.Astronomy.Core.UnitsOfMeasure; DateTime datetime = new(1987, 4, 10, 19, 21, 0); Moment moment = new Moment(datetime); SiderealTime st = new(moment); RightAscension gmst = new(st.GreenwichMean); RightAscension gast = new(st.GreenwichApparent); Console.WriteLine(gmst.ToString()); Console.WriteLine(gast.ToString()); /* Displays: 8h 34m 57.089579s 8h 34m 57.073455s */
If the observer’s longitude (L) is known, GMST can be converted to LMST:
using SquareWidget.Astronomy.Core.Planets; using SquareWidget.Astronomy.Core.UnitsOfMeasure; DateTime datetime = new(1987, 4, 10, 19, 21, 0); Moment moment = new Moment(datetime); SiderealTime st = new(moment); RightAscension gmst = new(st.GreenwichMean); // longitude in Corvallis, OR SexigesimalAngle L = new(-123, 15, 43.34); // local mean sidereal time on the date (PST -8 offset) RightAscension lmst = st.ToLocalMean(L); Console.WriteLine(gmst.ToString()); Console.WriteLine(lmst.ToString()); /* Displays: 8h 34m 57.089579s 0h 21m 54.200245s */
Finally, the hour angle (H) for an observer’s location and an object’s equatorial right ascension (α) is available by calling ToHourAngle
:
using SquareWidget.Astronomy.Core.Planets; using SquareWidget.Astronomy.Core.UnitsOfMeasure; DateTime datetime = new(2024, 7, 4, 17, 0, 0); // UTC Moment moment = new(datetime); SiderealTime st = new(moment); // longitude in Los Angeles, USA SexigesimalAngle L = new(-118, 14, 38); // An object's equatorial right ascension RightAscension α = new(5.838); Degrees H = st.ToHourAngle(L, α); Console.WriteLine(H.ToString()); /* Displays: 290°.6014494764589 */
I’d be lying if I said I wasn’t thinking of new features and yes, I’ve started another list of enhancements. But for now, I’m going to take a break from this project for a bit and recharge my batteries. When I pick it up again it will be to do more testing. Currently, there are about 60 unit tests but there’s always room for more coverage and more tests. I also want to focus on useability. Then over time I’ll look to add more enhancements: equinoxes, solstices, equation of time, transits, conjunctions, perihelion and aphelion… the list goes on!