What Sidereal Time Is It?

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!

 

 

 

 

 

 

 

 

 

SquareWidget.Astronomy.Core: A C# Code Library

I’m happy to announce the release of SquareWidget.Astronomy.Core, a new code library targeting .NET 8.0 and written in C#, that supports common astronomical calculations and algorithms. The current version is 1.1.0. You can reference the NuGet package at the link above. It has a baseline of functionality and I have plans to add more over time.

Why a library? I’ve been writing code (and snippets of code) over several years to solve various problems in astronomy. When something looked interesting enough to share, I published some of that code on my GitHub repo and on this blog. Recently I was ploughing through the position angle of Saturn’s north pole (Jean Meeus,  Astronomical Algorithms, Chapter 49). After some long debugging sessions, I realized I was using the double data type for everything. Several times I would forget whether a value was in degrees, radians, large angles, reduced angles, or something else entirely. So, I decided I needed a basic code library to bring a little order out of the chaos. The first thing I added were the units of measure. Then planets and calculators came later. I have two design goals in mind for the library: useability and robustness. Certainly, the library should return results accurate to within a few minutes of degrees. But it should also be intuitive and easy to use.

I’ve written more detailed documentation on the GitHub README. Here I’ll just introduce a few of the features. At the heart of the library is a Moment struct. You can pass in date and time parameters, a DateTime instance, or a Julian Day (JD) value:

Moment m1 = new(2024, 1, 31, 9, 36, 0);
Moment m2 = new(2024, 1, 31.4);
Moment m3 = new(2460340.90001);

string s1 = m1.ToDateTime().ToString("yyyy MMM dd HH:mm");
string s2 = m2.ToDateTime().ToString("yyyy MMM dd HH:mm");
string s3 = m3.ToDateTime().ToString("yyyy MMM dd HH:mm");

Console.WriteLine(s1); // 2024 Jan 31 09:36
Console.WriteLine(s2); // 2024 Jan 31 09:36
Console.WriteLine(s3); // 2024 Jan 31 09:36

Once you have a Moment you can get its JD, modified JDE, Day D, or Time T (measure of Julian centuries from epoch J2000.0).

The code library also includes several units of measure with the ability to convert between them. For example, suppose I want to model the Earth’s obliquity of the ecliptic.  I can create a Degrees object by passing in decimal degrees:

Degrees d = new(23.41);
Console.WriteLine(d.ToString()); // 23°.41

Units can be converted to other units. Here I’ll convert to Radians, then a SexesimalAngle, and back to Degrees again:

Degrees d = new(23.41);
Console.WriteLine(d.ToString()); // 23°.41

Radians r = d.ToRadians();
Console.WriteLine(r.ToString()); // 0.4085816

SexigesimalAngle sa = new(d);
Console.WriteLine(sa.ToString()); // +23° 24' 36"

Degrees d1 = new(sa);
Console.WriteLine(d1.ToString()); // 23°.41

Quite often in astronomical calculations we deal with very large angles. Those can be reduced to within the range [0, 360] with the ToReducedAngle function:

Degrees d = new(13512.45);
Console.WriteLine(d.ToString()); // 13512°.45
Console.WriteLine(d.ToReducedAngle().ToString()); // 192°.45

A RightAscension can be constructed with the standard hours, minutes, and seconds (HMS) or decimal degrees:

// passing in HMS
RightAscension ra = new(12, 37, 27);
Console.WriteLine(ra.ToString());   // 12h 37m 27s

// passing decimal degrees
RightAscension ra = new(189.3625);
Console.WriteLine(ra.ToString()); // 12h 37m 27s

Two coordinate systems have been implemented, equitorial and ecliptical,  and you can convert back and forth between them. Suppose we have the equatorial coordinates (α, δ) of Pollux from a star catalog or the wiki and we know Earth’s obliquity of the ecliptic (ε):

// Pollux (β Gem)
RightAscension α = new(7, 45, 18.946);
SexigesimalAngle δ = new(28, 01, 34.26);
Degrees ε = new(23.4392911);

EquitorialCoordinates eqc = new(δ, α, ε);
Console.WriteLine(eqc.α.ToString()); // 7h 45m 18.946s
Console.WriteLine(eqc.δ.ToString()); // +28° 1' 34.26"

We can take those coordinates and convert them to ecliptical coordinates:

EquitorialCoordinates eqc = new(δ, α, ε);
EclipticalCoordinates ec = eqc.ToΕclipticCoordinates();

Console.WriteLine(ec.λ.ToDegrees().ToReducedAngle().ToString()); // 113°.21
Console.WriteLine(ec.β.ToDegrees().ToString());                  //   6°.68

You can instantiate a planet with the new operator or use the factory method. You must pass in a Moment struct either way.

DateTime datetime = new(2028, 7, 1);
Moment moment = new(datetime);

// using new operator
Earth earth = new(moment);

// using factory method for polymorphism
Planet planet = PlanetFactory.Create(PlanetName.Earth, moment);

Once you have a planet you can get its properties which include orbital elements and heliocentric spherical coordinates.

The library has six calculators right now and I plan to add at least a couple more in the next few weeks.

    • Geocentric position calculator
    • Moon phase and dates calculator
    • Nutation calculator
    • Position Angle of Saturn’s semiminor axis calculator
    • Sundial calculator
    • Solar longitude calculator

Consult the README documentation and the unit tests for details. Let me just illustrate one here. Suppose I want to know the four main phases of the moon for the month of August 2024:

DateOnly startDate = new(2024, 7, 1);
DateOnly endDate = new(2024, 9, 30);
DateRange dateRange = new(startDate, endDate);

var list = MoonPhaseDatesCalculator.Calculate(dateRange);

// use a collection expression to get just August
list = [.. list
    .Where(i => i.Moment.Month == 8)
    .OrderBy(o => o.Moment.ToDateTime())];

foreach (var item in list)
{
    Console.WriteLine(item.PhaseName + " on " + 
        item.Moment.ToDateTime().ToString("MM/dd/yyyy hh:mm:ss.fff tt"));
}

This displays:

NewMoon on 08/04/2024 11:12:56.525 AM
FirstQuarter on 08/12/2024 03:19:31.125 PM
FullMoon on 08/19/2024 06:25:37.281 PM
LastQuarter on 08/26/2024 09:27:16.726 AM

What’s next? I’ve got two enhancements open right now. One is to add a feature to calculate solar eclipses and the other is to calculate the positions of the four Galilean moons of Jupiter. Sometime after that I’ll want to address magnitude, planetary perihelion and aphelion, and probably more fun stuff with the Moon. Drop me a line if you have any requests.

Let’s Make a Sundial!

Let’s make a sundial that tells the hour of the day. I know what you’re thinking. Can’t I just go to my local garden store and buy one? Nope. Those are just for decoration only. A real sundial, one that accurately tells the time, must be built for your location (latitude). All sundials are local.

I recommend reading my previous post on building a solar calendar. Also, if you’ve never given much thought to the apparent motion of the sun across the sky, then check out my post on sidereal time. If you know all about sundials and you just want to build one, then read on!

Continue reading “Let’s Make a Sundial!”

Kepler’s Equation

Introduction

Since at least the time of Hipparchus and Eudoxus the ancient world believed that the Sun, moon, planets, and stars moved around the earth in circular orbits. Aristotle’s Physics put the heavenly bodies on perfect crystal spheres. This theory was further formalized in the second century by Ptolemy in his Almagest which served the basic needs of astronomers for the next 1,500 years. Over that time, Aristotelian physics became an article of faith not to be questioned.

But there were unexplained irregularities that never quite fit the theory. The ancients knew that the seasons were of different lengths. For example, Winter is about 89 days in length while Summer is about 94 days. Why did the Sun sometimes speed up and slow down like that? They also noticed that Mars would move East across the night sky for a few years, slow down, and then reverse course and move West for a few months before looping back to its original course. (What we now call apparent retrograde motion.)

Continue reading “Kepler’s Equation”

Now Available: ASP.NET Core 2 HMAC Middleware

I won’t repeat the project home page except to say that if you need good strong security for clients (MVC or otherwise) calling services (micro or otherwise) then this is for you!

Basic authentication middleware is no longer available in Core 2 and I’ve blogged about that before and wrote a SquareWidget.BasicAuth.Core NuGet package. Even with TLS you should probably not use it unless you have no choice. The password goes over the wire in base64 encoding rather than ciphertext, it sits there in the request header for the whole session, the user can cache it permanently in the browser, and anyone on the network can sniff it out before it gets to the web server.

So why do people use basic auth so much? One word: convenience.  Developers fall back on the  tried and true rather than take the time to do the right thing. So my aim with this middleware is to encapsulate all the goodness of HMAC and keep it dead simple so that the developer has no excuse for not using a more secure algorithm.

Part 1: Publish to an Azure DevOps Repository

In this series I’m going to use the free Visual Studio 2017 Community Edition and the free Azure DevOps to target a full-blown CI/CD pipeline to deploy a web application to Azure.  So in addition to those tools make sure you have an Azure subscription. Last, I’ve got the latest Git for Windows installed (v2.19.1.windows.1). So let’s get started after the jump.

Continue reading “Part 1: Publish to an Azure DevOps Repository”

ASP.NET Core 2.1 Web API Using Multiple Authentication Schemes

There’s very little guidance from Microsoft on writing your own custom authentication handlers for Core 2. If you look at the documentation you’ll find detailed guidance on the built-in Core Identity model that they want you to use. They also provide links to third-party open-source providers like Identity Server which is what I use in this example. There is an article on custom cookie authentication. But generally speaking because security is hard and it’s way too easy to screw up Microsoft would rather you did not roll your own. It’s best to stick to the prescriptive guidance Microsoft offers. Now that I’ve said that I’m going to ignore completely my own advice. Read on if you’re with me.

Continue reading “ASP.NET Core 2.1 Web API Using Multiple Authentication Schemes”

Microservices with IdentityServer4 and Ocelot Fronting a .NET Core API

Well just like the title says I want to show a complete microservice-based architecture using the lightweight IdentityServer4 for authentication and Ocelot as an API gateway. Ocelot will act as a reverse proxy for a secured internal ASP.NET Core Web API. Everything here is open-source .NET Core 2.0 or later.

The main source of guidance I consulted for this architecture is the eShopContainers project and the white paper they published (which I read cover-to-cover at my favorite coffee shop and I recommend you do the same). There are a few helpful blog posts out there too. Dan Patrascu-Baba wrote a couple posts (here and here), Scott Brady wrote a helpful intro to IdentityServer4, and Catcher Wong wrote a nice series on Ocelot. But I couldn’t find a “complete picture” presentation of the whole architecture so I decided to write it myself. My goal here is to present a bare bones framework in one place to help bootstrap a serious microservices project.

I’ve organized this post into three parts: (1) The Big Picture; (2) The Configuration; and (3) The Deep Dive. Let’s get started right after the jump…

Continue reading “Microservices with IdentityServer4 and Ocelot Fronting a .NET Core API”

A Simple CQRS Pattern Using C# in .NET

For years in my apps I’ve used a Data Mapper pattern or even the Repository pattern to mediate between the business domain and the database. One thing I’ve learned is that something like this interface is no help at all:

public interface IEntity
{
    int ID { get; set; }
}

public interface IRepository where T : IEntity
{
    T Get(int id);
    IEnumerable GetAll();
    int Add(T item);
    bool Update(T item);
    bool Delete(T item);
}

In all but the most trivial app it proves too inflexible to be useful. This is because no two entities are alike. In one case calling a Get() function and passing in an ID is just fine. But I might have a two-part key for another entity. Or I might need GetByLastName() instead. So I end up adding extra functions to the concrete repository class. But If I’m adding functions outside the contract then I might as well not use the contract at all. Another problem with a non-trivial app is the repository even for a single entity quickly becomes a big ball of mud (if you’re lucky) or a God class if several developers are working together and there’s no discipline. If I have to wade through 30 fetch functions to get to the one I want that’s not maintainable. There are other growing pains that emerge a few years down the road. But others — notably Ayende — have documented those problems so I won’t rehash that here. Instead I want to describe a simple CQRS pattern as an alternative that I’ve found to be flexible and maintainable over the long haul.

Continue reading “A Simple CQRS Pattern Using C# in .NET”

Polymorphic Associations in Entity Framework

In this post I’m going to show how to use EF 6.1 Code First to model polymorphic associations between a base class and two derived classes. In EF this is called “table-per-type” (TPT) inheritance. Microsoft has a walkthrough on using EF to map TPT inheritance in a domain model. Unfortunately it was written before EF Code First and is now dated. A search turned up some information here and there but it too was dated. It took me the better part of an afternoon to get it working in EF Code First so I thought I should post the solution. More after the jump…

Continue reading “Polymorphic Associations in Entity Framework”