Skip to content

Commit

Permalink
Clean I dataprovider, add SLERP and LERP (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvain-guillet authored Sep 30, 2024
1 parent 268ffc0 commit bb77c32
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void AngularSeparation()
command.AngularSeparation("Data", 399, 301, 10, new EpochParameters{Epoch = "0.0"});

var res = sb.ToString();
Assert.Equal($"0.9984998794278305{Environment.NewLine}", res);
Assert.Equal(0.9984998794278305, double.Parse(res),12);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<FileVersion>0.0.1</FileVersion>
<PackAsTool>true</PackAsTool>
<ToolCommandName>astro</ToolCommandName>
<Version>0.5.0-preview-6</Version>
<Version>0.5.0-preview-7</Version>
<Title>Astrodynamics command line interface</Title>
<Authors>Sylvain Guillet</Authors>
<Description>This CLI allows end user to exploit IO.Astrodynamics framework </Description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,53 @@ public void Zero()
{
Assert.Equal(new Quaternion(1.0, 0.0, 0.0, 0.0), Quaternion.Zero);
}

[Fact]
public void LERP()
{
Quaternion q1 = new Quaternion(Vector3.VectorX, 0.0);
Quaternion q2 = new Quaternion(Vector3.VectorX, IO.Astrodynamics.Constants.PI2);
Quaternion q3 = q1.Lerp(q2, 0.5);
Assert.Equal(0.92387953251128674, q3.W, 12);
Assert.Equal(0.38268343236508978, q3.VectorPart.X, 12);
Assert.Equal(0.0, q3.VectorPart.Y);
Assert.Equal(0.0, q3.VectorPart.Z);
}

[Fact]
public void SLERP()
{
Quaternion q1 = new Quaternion(Vector3.VectorX, 0.0);
Quaternion q2 = new Quaternion(Vector3.VectorX, IO.Astrodynamics.Constants.PI2);
Quaternion q3 = q1.SLERP(q2, 0.5);
Assert.Equal(0.92387953251128674, q3.W, 12);
Assert.Equal(0.38268343236508978, q3.VectorPart.X, 12);
Assert.Equal(0.0, q3.VectorPart.Y);
Assert.Equal(0.0, q3.VectorPart.Z);
}

[Fact]
public void LERP2()
{
Quaternion q1 = new Quaternion(Vector3.VectorX, 0.0);
Quaternion q2 = new Quaternion(Vector3.VectorX, IO.Astrodynamics.Constants._2PI - IO.Astrodynamics.Constants.PI2);
Quaternion q3 = q1.Lerp(q2, 0.5); //Expected 135° on X
Assert.Equal(0.38268343236500002, q3.W, 12);
Assert.Equal(0.92387953251099997, q3.VectorPart.X, 12);
Assert.Equal(0.0, q3.VectorPart.Y);
Assert.Equal(0.0, q3.VectorPart.Z);
}

[Fact]
public void SLERP2()
{
Quaternion q1 = new Quaternion(Vector3.VectorX, 0.0);
Quaternion q2 = new Quaternion(Vector3.VectorX, IO.Astrodynamics.Constants._2PI - IO.Astrodynamics.Constants.PI2);
Quaternion q3 = q1.SLERP(q2, 0.5); //Expected -45° on X
Assert.Equal(0.92387953251128674, q3.W, 12);
Assert.Equal(-0.38268343236508984, q3.VectorPart.X, 12);
Assert.Equal(0.0, q3.VectorPart.Y);
Assert.Equal(0.0, q3.VectorPart.Z);
}
}
}
22 changes: 22 additions & 0 deletions IO.Astrodynamics.Net/IO.Astrodynamics.Tests/Math/Vector3Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,26 @@ public void VectorToString()
Vector3 m1 = new Vector3(10, 20, 30);
Assert.Equal("X : 10 Y : 20 Z: 30", m1.ToString());
}

[Fact]
public void LinearInterpolation()
{
Vector3 m1 = new Vector3(10, 20, 30);
Vector3 m2 = new Vector3(20, 40, 60);
var m3 = m1.LinearInterpolation(m2, 0.5);
Assert.Equal(15.0, m3.X);
Assert.Equal(30.0, m3.Y);
Assert.Equal(45.0, m3.Z);
}

[Fact]
public void LinearInterpolation2()
{
Vector3 m1 = new Vector3(10, 20, 30);
Vector3 m2 = new Vector3(20, 40, 60);
var m3 = m1.LinearInterpolation(m2, 0.75);
Assert.Equal(17.5, m3.X);
Assert.Equal(35.0, m3.Y);
Assert.Equal(52.5, m3.Z);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ namespace IO.Astrodynamics.DataProvider;

public interface IDataProvider
{
Task<IEnumerable<StateOrientation>> FrameTransformationAsync(Window window, Frame source, Frame target, TimeSpan stepSize);
StateOrientation FrameTransformation(in Time date, Frame source, Frame target);
StateOrientation FrameTransformationToICRF(in Time date, Frame source);
OrbitalParameters.OrbitalParameters GetEphemeris(in Time date, ILocalizable target, ILocalizable observer, Frame frame, Aberration aberration);
Task<IEnumerable<OrbitalParameters.OrbitalParameters>> GetEphemerisAsync(Window window, ILocalizable target, ILocalizable observer, Frame frame, Aberration aberration, TimeSpan stepSize);
Task<DTO.CelestialBody> GetCelestialBodyInfoAsync(int naifId);
DTO.CelestialBody GetCelestialBodyInfo(int naifId);
void WriteEphemeris(FileInfo outputFile, INaifObject naifObject, IEnumerable<StateVector> stateVectors);
void WriteOrientation(FileInfo outputFile, INaifObject naifObject, IEnumerable<StateOrientation> stateOrientations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,16 @@ namespace IO.Astrodynamics.DataProvider;

public class SpiceDataProvider : IDataProvider
{
public Task<IEnumerable<StateOrientation>> FrameTransformationAsync(Window window, Frame source, Frame target, TimeSpan stepSize)
public StateOrientation FrameTransformationToICRF(in Time date, Frame source)
{
return Task.Run(() => API.Instance.TransformFrame(window, source, target, stepSize));
}

public StateOrientation FrameTransformation(in Time date, Frame source, Frame target)
{
return API.Instance.TransformFrame(date, source, target);
return API.Instance.TransformFrame(date, source, Frame.ICRF);
}

public OrbitalParameters.OrbitalParameters GetEphemeris(in Time date, ILocalizable target, ILocalizable observer, Frame frame, Aberration aberration)
{
return API.Instance.ReadEphemeris(date, observer, target, frame, aberration);
}

public Task<IEnumerable<OrbitalParameters.OrbitalParameters>> GetEphemerisAsync(Window window, ILocalizable target, ILocalizable observer, Frame frame, Aberration aberration,
TimeSpan stepSize)
{
return Task.Run(() => API.Instance.ReadEphemeris(window, observer, target, frame, aberration, stepSize));
}

public Task<DTO.CelestialBody> GetCelestialBodyInfoAsync(int naifId)
{
return Task.Run(() => API.Instance.GetCelestialBodyInfo(naifId));
}

CelestialBody IDataProvider.GetCelestialBodyInfo(int naifId)
{
return API.Instance.GetCelestialBodyInfo(naifId);
Expand Down
2 changes: 1 addition & 1 deletion IO.Astrodynamics.Net/IO.Astrodynamics/Frames/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Frame(string name, int? id = null)

public virtual StateOrientation GetStateOrientationToICRF(Time date)
{
return _stateOrientationsToICRF.GetOrAdd(date, dt => _dataProvider.FrameTransformation(dt, this, ICRF));
return _stateOrientationsToICRF.GetOrAdd(date, dt => _dataProvider.FrameTransformationToICRF(dt, this));
}

public bool AddStateOrientationToICRF(StateOrientation stateOrientation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<id>IO.Astrodynamics</id>
<authors>Sylvain Guillet</authors>
<copyright>Sylvain Guillet</copyright>
<version>6.0.0-preview-6</version>
<version>6.0.0-preview-7</version>
<title>Astrodynamics framework</title>
<icon>images\dragonfly-dark-trans.png</icon>
<readme>docs\README.md</readme>
Expand Down
70 changes: 68 additions & 2 deletions IO.Astrodynamics.Net/IO.Astrodynamics/Math/Quaternion.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

namespace IO.Astrodynamics.Math
{
public readonly record struct Quaternion
{
public static Quaternion Zero = new (1, 0, 0, 0);
public static Quaternion Zero = new(1, 0, 0, 0);
public double W { get; }

public Vector3 VectorPart { get; }
Expand Down Expand Up @@ -43,6 +42,7 @@ public Quaternion Normalize()
{
return this;
}

return new Quaternion(W / m, VectorPart / m);
}

Expand Down Expand Up @@ -90,5 +90,71 @@ public Vector3 ToEuler()

return new Vector3(x, y, z);
}

/// <summary>
/// Performs Spherical Linear Interpolation (SLERP) between the current quaternion and the specified quaternion.
/// </summary>
/// <param name="q">The target quaternion to interpolate towards.</param>
/// <param name="t">The interpolation factor, clamped between 0 and 1.</param>
/// <returns>The interpolated quaternion.</returns>
public Quaternion SLERP(Quaternion q, double t)
{
// Clamps the parameter t between 0 and 1
t = System.Math.Clamp(t, 0.0, 1.0);

// Calculates the cosine of the angle between the quaternions
double dot = W * q.W + VectorPart.X * q.VectorPart.X + VectorPart.Y * q.VectorPart.Y + VectorPart.Z * q.VectorPart.Z;

// If the dot product is negative, invert one of the quaternions to take the shortest path
if (dot < 0.0f)
{
q = new Quaternion(-q.W, -q.VectorPart.X, -q.VectorPart.Y, -q.VectorPart.Z);
dot = -dot;
}

// If the quaternions are very close, use linear interpolation to avoid numerical errors
const float epsilon = 0.0001f;
if (dot > 1.0f - epsilon)
{
// Linear interpolation
return Lerp(q, t);
}

// Calculates the angle between the quaternions
double theta_0 = System.Math.Acos(dot); // initial angle
double theta = theta_0 * t; // interpolated angle

// Calculates the intermediate quaternions
double sin_theta_0 = System.Math.Sin(theta_0);
double sin_theta = System.Math.Sin(theta);

double s1 = System.Math.Cos(theta) - dot * sin_theta / sin_theta_0;
double s2 = sin_theta / sin_theta_0;

// Interpolates the quaternions
return new Quaternion((s1 * W) + (s2 * q.W),
(s1 * VectorPart.X) + (s2 * q.VectorPart.X),
(s1 * VectorPart.Y) + (s2 * q.VectorPart.Y),
(s1 * VectorPart.Z) + (s2 * q.VectorPart.Z));
}

/// <summary>
/// Linearly interpolates between the current quaternion and the specified quaternion.
/// </summary>
/// <param name="q2">The target quaternion to interpolate towards.</param>
/// <param name="t">The interpolation factor, clamped between 0 and 1.</param>
/// <returns>The interpolated quaternion.</returns>
public Quaternion Lerp(Quaternion q2, double t)
{
t = System.Math.Clamp(t, 0.0, 1.0);

Quaternion result = new Quaternion(W + t * (q2.W - W),
VectorPart.X + t * (q2.VectorPart.X - VectorPart.X),
VectorPart.Y + t * (q2.VectorPart.Y - VectorPart.Y),
VectorPart.Z + t * (q2.VectorPart.Z - VectorPart.Z)
);

return result.Normalize();
}
}
}
12 changes: 12 additions & 0 deletions IO.Astrodynamics.Net/IO.Astrodynamics/Math/Vector3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ public Vector3 Rotate(in Quaternion quaternion)
return (quaternion * p * quaternion.Conjugate()).VectorPart;
}

/// <summary>
/// Performs linear interpolation between this vector and the specified vector.
/// </summary>
/// <param name="vector">The target vector to interpolate towards.</param>
/// <param name="t">The interpolation factor, clamped between 0.0 and 1.0.</param>
/// <returns>A new <see cref="Vector3"/> that is the result of the linear interpolation.</returns>
public Vector3 LinearInterpolation(in Vector3 vector, double t)
{
t = System.Math.Clamp(t, 0.0, 1.0);
return this + (vector - this) * t;
}

public override string ToString()
{
return $"X : {X} Y : {Y} Z: {Z}";
Expand Down

0 comments on commit bb77c32

Please sign in to comment.