Skip to content
This repository has been archived by the owner on Aug 5, 2024. It is now read-only.

Commit

Permalink
Feature/heliosynchronous (#67)
Browse files Browse the repository at this point in the history
* "Added geosynchronous orbit calculations and improved 'IsCircular' check

This update introduces the ability to calculate geosynchronous orbits with the 'GeosynchronousOrbit' method in the 'CelestialBody.cs' file. Additionally, the 'IsCircular' method in 'OrbitalParameters.cs' has been modified for better accuracy, now considering orbits with an eccentricity less than 1E-03 to be circular. To accommodate these changes, corresponding test cases have also been added or updated in 'CelestialBodyTests.cs' and 'KeplerianElementsTests.cs'. This feature is essential for various celestial physics calculations and for the practical application of launching and positioning satellites in geosynchronous orbits."

* Add HelioSynchronousOrbit and TrueSolarDay calculations in CelestialBody

The CelestialBody class was expanded to handle calculations for heliosynchronous orbit and true solar day. They were added to provide more orbital options and accurate scientific computations for solar days for the respective celestial bodies. J2, J3, and J4 properties were added to CelestialBody and DTO.CelestialBody classes to store the second, third, and fourth zonal harmonic coefficients respectively from the gravitational potential, essential for orbital mechanics. The Plane class was created to assist these calculations, and tests were updated accordingly. Adjustments were made in OrbitalParameters for consistency.

* Update accuracy of test assertions and refactor celestial calculations

The assertions in `CelestialBodyTests.cs` have been modified to include precision parameters to enhance accuracy of the tests. Additionally, a refactor has taken place in `CelestialBody.cs` for the calculations regarding ephemeris and mean anomalies. This refactoring involved updating formulas and adjusting calling parameters to improve the accuracy and reliability of the celestial calculations.

* Add phased heliosynchronous orbit support in CelestialBody class

Expanded the CelestialBody class to support calculations for a phased heliosynchronous orbit through methods and tests. The expanded class now allows computing for the Keplerian elements of a phased heliosynchronous orbit. Corresponding test cases were also added in CelestialBodyTests.cs to guarantee the functionality of these new computational methods.

* Add VectorComparer method for testing in Astrodynamics

A new VectorComparer method has been added to the Astrodynamics unit tests, providing a reliable technique for comparing vector values with a precision of 1E-03. The method is thread-safe using a lock and has been incorporated into the CelestialBodyTests.

---------

Co-authored-by: Sylvain Guillet <[email protected]>
  • Loading branch information
sylvain-guillet and Sylvain Guillet authored Nov 25, 2023
1 parent 8d5e3ed commit ab3d43c
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 19 deletions.
30 changes: 28 additions & 2 deletions IO.Astrodynamics.Tests/APITest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public void PropagateScenario2()
Assert.Equal("2021-03-04T04:18:01.4656137 (TDB)", maneuver.ManeuverWindow.EndDate.ToFormattedString());
Assert.Equal("2021-03-04T04:18:00.5620061 (TDB)", maneuver.ThrustWindow.StartDate.ToFormattedString());
Assert.Equal("2021-03-04T04:18:01.4656137 (TDB)", maneuver.ThrustWindow.EndDate.ToFormattedString());
Assert.Equal(0.90360759999999996, maneuver.ThrustWindow.Length.TotalSeconds,3);
Assert.Equal(0.90360759999999996, maneuver.ThrustWindow.Length.TotalSeconds, 3);
Assert.Equal(new Vector3(0.6113331463337078, 10.731242700644234, 16.97261190194155), ((ImpulseManeuver)maneuver).DeltaV);
Assert.Equal(45.180377988723926, maneuver.FuelBurned);
}
Expand Down Expand Up @@ -663,6 +663,29 @@ void GetCelestialBodyInformation()
Assert.Equal(6378136.5999999998, res.Radii.X);
Assert.Equal(6378136.5999999998, res.Radii.Y);
Assert.Equal(6356751.9000000002, res.Radii.Z);
Assert.Equal(0.001082616, res.J2);
Assert.Equal(-2.5388099999999996E-06, res.J3);
Assert.Equal(-1.6559699999999999E-06, res.J4);
}

[Fact]
void GetCelestialBodyInformationWithoutJ()
{
//Read celestial celestialItem information from spice kernels
var res = API.Instance.GetCelestialBodyInfo(TestHelpers.MoonAtJ2000.NaifId);
Assert.Equal(PlanetsAndMoons.MOON.NaifId, res.Id);
Assert.Equal(PlanetsAndMoons.EARTH.NaifId, res.CenterOfMotionId);
Assert.Equal(Barycenters.EARTH_BARYCENTER.NaifId, res.BarycenterOfMotionId);
Assert.Equal(PlanetsAndMoons.MOON.Name, res.Name);
Assert.Equal(31001, res.FrameId);
Assert.Equal("MOON_ME", res.FrameName);
Assert.Equal(4902800066163.7959, res.GM);
Assert.Equal(1737400.0, res.Radii.X);
Assert.Equal(1737400.0, res.Radii.Y);
Assert.Equal(1737400.0, res.Radii.Z);
Assert.Equal(double.NaN, res.J2);
Assert.Equal(double.NaN, res.J3);
Assert.Equal(double.NaN, res.J4);
}

[Fact]
Expand Down Expand Up @@ -749,7 +772,7 @@ void LoadKernelException()
[Fact]
void CelestialBody()
{
DTO.CelestialBody celestialBody = new CelestialBody(1, 2, 3, "celestialItem", new Vector3D(1.0, 2.0, 3.0), 123, "frame", 147);
DTO.CelestialBody celestialBody = new CelestialBody(1, 2, 3, "celestialItem", new Vector3D(1.0, 2.0, 3.0), 123, "frame", 147, 1.0, 2.0, 3.0);
Assert.Equal(1, celestialBody.Id);
Assert.Equal(2, celestialBody.CenterOfMotionId);
Assert.Equal(3, celestialBody.BarycenterOfMotionId);
Expand All @@ -758,6 +781,9 @@ void CelestialBody()
Assert.Equal(147, celestialBody.FrameId);
Assert.Equal("frame", celestialBody.FrameName);
Assert.Equal(123, celestialBody.GM);
Assert.Equal(1.0, celestialBody.J2);
Assert.Equal(2.0, celestialBody.J3);
Assert.Equal(3.0, celestialBody.J4);
}

[Fact]
Expand Down
99 changes: 99 additions & 0 deletions IO.Astrodynamics.Tests/Body/CelestialBodyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,103 @@ public void GetOrientation()
Assert.Equal(DateTimeExtension.J2000, orientation.Epoch);
Assert.Equal(Frames.Frame.ICRF, orientation.ReferenceFrame);
}

[Fact]
public void EarthSideralRotationPerdiod()
{
var duration = TestHelpers.EarthAtJ2000.SideralRotationPeriod(DateTimeExtension.J2000);
Assert.Equal(TimeSpan.FromTicks(861640998130), duration);
}

[Fact]
public void MoonSideralRotationPerdiod()
{
var duration = TestHelpers.MoonAtJ2000.SideralRotationPeriod(DateTimeExtension.J2000);
Assert.Equal(TimeSpan.FromTicks(23603596749416), duration);
}

[Fact]
public void GeosynchronousOrbit()
{
var orbit = TestHelpers.EarthAtJ2000.GeosynchronousOrbit(0.0, 0.0, new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(42164171.958719358, orbit.ToStateVector().Position.Magnitude());
Assert.Equal(3074.6599898500758, orbit.ToStateVector().Velocity.Magnitude());
Assert.Equal(Frames.Frame.ICRF, orbit.Frame);
}

[Fact]
public void GeosynchronousOrbit2()
{
var orbit = TestHelpers.EarthAtJ2000.GeosynchronousOrbit(1.0, 1.0, new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(42164171.95871935, orbit.ToStateVector().Position.Magnitude(), 3);
Assert.Equal(3074.6599898500763, orbit.ToStateVector().Velocity.Magnitude(), 3);
Assert.Equal(Frames.Frame.ICRF, orbit.Frame);
Assert.Equal(42164171.95871935, orbit.SemiMajorAxis());
Assert.Equal(0.0, orbit.Eccentricity());
Assert.Equal(1.0, orbit.Inclination(), 2);
Assert.Equal(1.1804318466570587, orbit.AscendingNode(), 2);
Assert.Equal(1.569, orbit.ArgumentOfPeriapsis(), 2);
Assert.Equal(0.0, orbit.MeanAnomaly(), 2);
Assert.Equal(new Vector3(-20992029.30827995, 8679264.319395786, 35522140.607779175), orbit.ToStateVector().Position, TestHelpers.VectorComparer);
Assert.Equal(new Vector3(-1171.3783810266016, -2842.7805399479103, 2.354430257176734), orbit.ToStateVector().Velocity, TestHelpers.VectorComparer);
}


[Fact]
public void TrueSolarDayJan()
{
var res1 = TestHelpers.Earth.TrueSolarDay(new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(86407.306035452566, res1.TotalSeconds, 3);
}

[Fact]
public void TrueSolarDayJMar()
{
var res1 = TestHelpers.Earth.TrueSolarDay(new DateTime(2021, 3, 26, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(86400.359514701879, res1.TotalSeconds, 3);
}

[Fact]
public void TrueSolarDayJul()
{
var res1 = TestHelpers.Earth.TrueSolarDay(new DateTime(2021, 7, 25, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(86392.011764653842, res1.TotalSeconds, 3);
}

[Fact]
public void TrueSolarDayDec()
{
var res1 = TestHelpers.Earth.TrueSolarDay(new DateTime(2021, 12, 22, 0, 0, 0, DateTimeKind.Unspecified));
Assert.Equal(86407.114275442393, res1.TotalSeconds, 3);
}

[Fact]
public void HelioSynchronousOrbit()
{
var epoch = new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
var res = TestHelpers.Earth.HelioSynchronousOrbit(7080636.3, 0.0001724, epoch);
Assert.Equal(7080636.3, res.A);
Assert.Equal(0.0001724, res.E, 6);
Assert.Equal(98.208156353447507, res.I * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(11.457000000000001, res.RAAN * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.0, res.AOP * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.0, res.TrueAnomaly() * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.01999999999998, res.MeanAnomaly() * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(epoch, res.Epoch);
}

[Fact]
public void PhaseHelioSynchronousOrbit()
{
var epoch = new DateTime(2021, 11, 22, 0, 0, 0, DateTimeKind.Unspecified);
var res = TestHelpers.Earth.HelioSynchronousOrbit(0.0001724, epoch, 14);
Assert.Equal(7272221.8761325106, res.A, 3);
Assert.Equal(0.0001724, res.E, 6);
Assert.Equal(99.018, res.I * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(327.43000000000001, res.RAAN * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.0, res.AOP * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.0, res.TrueAnomaly() * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(270.01999999999998, res.MeanAnomaly() * Astrodynamics.Constants.Rad2Deg, 3);
Assert.Equal(epoch, res.Epoch);
}
}
44 changes: 35 additions & 9 deletions IO.Astrodynamics.Tests/OrbitalParameters/KeplerianElementsTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Data;
using IO.Astrodynamics.Body;
using IO.Astrodynamics.Math;
using IO.Astrodynamics.OrbitalParameters;
Expand Down Expand Up @@ -33,35 +34,35 @@ public void Create()
Assert.Equal(earth, ke.Observer);
Assert.Equal(epoch, ke.Epoch);
Assert.Equal(Frames.Frame.ICRF, ke.Frame);
Assert.Throws<ArgumentException>(()=>new KeplerianElements(-20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(-20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentException>(()=>new KeplerianElements(20000, -0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(20000, -0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentException>(()=>new KeplerianElements(20000, 0.5, 181 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(20000, 0.5, 181 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentException>(()=>new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
-40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentException>(()=>new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
-50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentException>(()=>new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentException>(() => new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, -10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentNullException>(()=>new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentNullException>(() => new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, null, epoch,
Frames.Frame.ICRF));
Assert.Throws<ArgumentNullException>(()=>new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
Assert.Throws<ArgumentNullException>(() => new KeplerianElements(20000, 0.5, 30.0 * IO.Astrodynamics.Constants.Deg2Rad,
40.0 * IO.Astrodynamics.Constants.Deg2Rad,
50.0 * IO.Astrodynamics.Constants.Deg2Rad, 10.0 * IO.Astrodynamics.Constants.Deg2Rad, earth, epoch,
null));
Expand Down Expand Up @@ -99,7 +100,7 @@ public void ToStateVector()
Assert.Equal(-5477.646280596454, sv.Velocity.Y, 6);
Assert.Equal(-5297.633402454895, sv.Velocity.Z, 6);
}

[Fact]
public void ToStateVector2()
{
Expand All @@ -120,6 +121,31 @@ public void ToStateVector2()
Assert.Equal(-5297.633402454895, sv.Velocity.Z, 6);
}

[Fact]
public void SingularityZeroEccentricity()
{
KeplerianElements original = new KeplerianElements(42000000, 0.0, 1.0, 2.0, 1.0, 0.5, TestHelpers.EarthAtJ2000, DateTimeExtension.J2000, Frames.Frame.ICRF);
KeplerianElements transformed = original.ToStateVector().ToKeplerianElements();
Assert.Equal(original.A, transformed.A);
Assert.Equal(original.E, transformed.E, 6);
Assert.Equal(original.I, transformed.I, 6);
Assert.Equal(original.RAAN, transformed.RAAN);
Assert.Equal(original.MeanLongitude(), transformed.MeanLongitude());
Assert.Equal(original.AOP + original.M, transformed.AOP + transformed.M);
}

[Fact]
public void SingularityZeroEccentricityZeroInclination()
{
KeplerianElements original = new KeplerianElements(42000000, 0.0, 0.0, 2.0, 1.0, 0.5, TestHelpers.EarthAtJ2000, DateTimeExtension.J2000, Frames.Frame.ICRF);
var originalSv = original.ToStateVector();
KeplerianElements transformed = originalSv.ToKeplerianElements();
Assert.Equal(original.A, transformed.A);
Assert.Equal(original.E, transformed.E, 6);
Assert.Equal(original.I, transformed.I, 6);
Assert.Equal(original.MeanLongitude(), transformed.MeanLongitude(),6);
}

[Fact]
public void TrueAnomaly10()
{
Expand Down
10 changes: 10 additions & 0 deletions IO.Astrodynamics.Tests/TestHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using IO.Astrodynamics.Body;
using IO.Astrodynamics.Math;
using IO.Astrodynamics.SolarSystemObjects;

namespace IO.Astrodynamics.Tests
Expand All @@ -17,5 +18,14 @@ internal static class TestHelpers
internal static CelestialBody MoonAtJ2000 => new(PlanetsAndMoons.MOON, Frames.Frame.ICRF, new DateTime(2000, 1, 1, 12, 0, 0));

internal static CelestialBody MoonAt20011214 => new(PlanetsAndMoons.MOON, Frames.Frame.ICRF, new DateTime(2001, 12, 14, 0, 0, 0));
private static object LockObj = new object();

internal static bool VectorComparer(Vector3 v1, Vector3 v2)
{
lock (LockObj)
{
return System.Math.Abs(v1.X - v2.X) < 1E-03 && System.Math.Abs(v1.Y - v2.Y) < 1E-03 && System.Math.Abs(v1.Z - v2.Z) < 1E-03;
}
}
}
}
Loading

0 comments on commit ab3d43c

Please sign in to comment.