Files
Nonemm/tests/Nonemm.Core.Tests/GraylineTests.cs
ericek111 53c15d4061 Draw the grey line map
Window > Grey Line opens the world with the night on it. The dark area is
where the sun is down, and the band along its edge is the twilight from the
horizon to six degrees below it, which is the grey line an operator watches
for on the low bands.

Grayline works out where the sun stands: the subsolar point, the elevation at
a place, and the latitude the terminator crosses a meridian at, which is what
the map fills the night between. It is the same question SunTimes answers from
the other end, and the two are checked against each other in the tests.

The coastlines are Natural Earth's 110m coastline, public domain, cut to a
tenth of a degree.

Our own station is drawn from the grid square, the station being called and
the spots on the band from the country file. The map follows the clock.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-31 22:09:16 +00:00

74 lines
2.4 KiB
C#

using Nonemm.Core;
namespace Nonemm.Core.Tests;
/// Where the sun stands, checked against what the calendar says it must be.
public class GraylineTests
{
[Fact]
public void TheSunIsOverTheTropicAtTheSolstice()
{
(double latitude, _) = Grayline.SubsolarPoint(new DateTime(2026, 6, 21, 12, 0, 0, DateTimeKind.Utc));
Assert.Equal(23.4, latitude, 0.1);
}
[Fact]
public void TheSunIsOverTheEquatorAtTheEquinox()
{
(double latitude, _) = Grayline.SubsolarPoint(new DateTime(2026, 3, 20, 14, 46, 0, DateTimeKind.Utc));
Assert.Equal(0, latitude, 0.1);
}
/// At noon UTC the sun is over Greenwich, give or take the quarter of an
/// hour the equation of time moves it by.
[Fact]
public void TheSunIsOverGreenwichAtNoon()
{
(_, double longitude) = Grayline.SubsolarPoint(new DateTime(2026, 6, 21, 12, 0, 0, DateTimeKind.Utc));
Assert.Equal(0, longitude, 1.0);
}
/// The two ways of asking have to agree: at the moment `SunTimes` says the
/// sun rises, it is on the horizon.
[Fact]
public void SunriseAgreesWithTheSunStandingOnTheHorizon()
{
(DateTime rise, DateTime set) =
SunTimes.For(48.15, 17.11, new DateTime(2026, 8, 31, 0, 0, 0, DateTimeKind.Utc))!.Value;
Assert.Equal(Grayline.Horizon, Grayline.Elevation(48.15, 17.11, rise), 0.3);
Assert.Equal(Grayline.Horizon, Grayline.Elevation(48.15, 17.11, set), 0.3);
}
[Fact]
public void MiddayIsDaylightAndMidnightIsNot()
{
DateTime noon = new(2026, 8, 31, 12, 0, 0, DateTimeKind.Utc);
Assert.True(Grayline.IsDaylight(0, 0, noon));
Assert.False(Grayline.IsDaylight(0, 180, noon));
}
/// The terminator crosses every meridian once between the poles while the
/// sun is over the equator, and the polar day has no crossing at all.
[Fact]
public void TheTerminatorCrossesAMeridianWhereTheSunIsOnTheHorizon()
{
DateTime moment = new(2026, 8, 31, 12, 0, 0, DateTimeKind.Utc);
double crossing = Grayline.LatitudeAt(30, moment)!.Value;
Assert.Equal(Grayline.Horizon, Grayline.Elevation(crossing, 30, moment), 0.3);
}
[Fact]
public void TheNorthPoleIsLitInJuneAndDarkInDecember()
{
Assert.True(Grayline.IsNorthPoleLit(new DateTime(2026, 6, 21, 0, 0, 0, DateTimeKind.Utc)));
Assert.False(Grayline.IsNorthPoleLit(new DateTime(2026, 12, 21, 0, 0, 0, DateTimeKind.Utc)));
}
}