Added orbital readouts, Time to AN/DN.
Added orbital readouts, Time to AN/DN.

--- a/KerbalEngineer/Extensions/DoubleExtensions.cs
+++ b/KerbalEngineer/Extensions/DoubleExtensions.cs
@@ -269,5 +269,20 @@
                 return "ERR";
             }
         }
+
+        public static double ClampTo(this double value, double min, double max)
+        {
+            while (value < min)
+            {
+                value += max;
+            }
+
+            while (value > max)
+            {
+                value -= max;
+            }
+
+            return value;
+        }
     }
 }

--- /dev/null
+++ b/KerbalEngineer/Extensions/OrbitExtensions.cs
@@ -1,1 +1,56 @@
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2014 CYBUTEK
+// 
+//     This program is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+// 
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+// 
+//     You should have received a copy of the GNU General Public License
+//     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// 
 
+using System;
+
+using UnityEngine;
+
+namespace KerbalEngineer.Extensions
+{
+    public static class OrbitExtensions
+    {
+        public const double Tau = Math.PI * 2.0;
+
+        public static double GetTimeToTrueAnomaly(this Orbit orbit, double tA)
+        {
+            var time = orbit.GetDTforTrueAnomaly(tA * Mathf.Deg2Rad, orbit.period);
+            return time < 0.0 ? time + orbit.period : time;
+        }
+
+        public static double GetTrueAnomalyOfAscendingNode(this Orbit orbit)
+        {
+            return 360.0 - orbit.argumentOfPeriapsis;
+        }
+
+        public static double GetTrueAnomalyOfDescendingNode(this Orbit orbit)
+        {
+            return 180.0 - orbit.argumentOfPeriapsis;
+        }
+
+        public static double GetTimeToAscendingNode(this Orbit orbit)
+        {
+            return GetTimeToTrueAnomaly(orbit, GetTrueAnomalyOfAscendingNode(orbit));
+        }
+
+        public static double GetTimeToDescendingNode(this Orbit orbit)
+        {
+            return GetTimeToTrueAnomaly(orbit, GetTrueAnomalyOfDescendingNode(orbit));
+        }
+    }
+}

--- a/KerbalEngineer/Flight/Readouts/Orbital/Eccentricity.cs
+++ b/KerbalEngineer/Flight/Readouts/Orbital/Eccentricity.cs
@@ -37,7 +37,7 @@
 
         public override void Draw()
         {
-            this.DrawLine(FlightGlobals.ship_orbit.eccentricity.ToAngle());
+            this.DrawLine(FlightGlobals.ship_orbit.eccentricity.ToString("F3"));
         }
     }
 }

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Orbital/TimeToAscendingNode.cs
@@ -1,1 +1,43 @@
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2014 CYBUTEK
+// 
+//     This program is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+// 
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+// 
+//     You should have received a copy of the GNU General Public License
+//     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// 
 
+#region Using Directives
+
+using KerbalEngineer.Extensions;
+
+#endregion
+
+namespace KerbalEngineer.Flight.Readouts.Orbital
+{
+    public class TimeToAscendingNode : ReadoutModule
+    {
+        public TimeToAscendingNode()
+        {
+            this.Name = "Time to AN";
+            this.Category = ReadoutCategory.GetCategory("Orbital");
+            this.HelpString = string.Empty;
+            this.IsDefault = true;
+        }
+
+        public override void Draw()
+        {
+            this.DrawLine(FlightGlobals.ActiveVessel.orbit.GetTimeToAscendingNode().ToTime());
+        }
+    }
+}

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Orbital/TimeToDescendingNode.cs
@@ -1,1 +1,43 @@
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2014 CYBUTEK
+// 
+//     This program is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+// 
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+// 
+//     You should have received a copy of the GNU General Public License
+//     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// 
 
+#region Using Directives
+
+using KerbalEngineer.Extensions;
+
+#endregion
+
+namespace KerbalEngineer.Flight.Readouts.Orbital
+{
+    public class TimeToDescendingNode : ReadoutModule
+    {
+        public TimeToDescendingNode()
+        {
+            this.Name = "Time to DN";
+            this.Category = ReadoutCategory.GetCategory("Orbital");
+            this.HelpString = string.Empty;
+            this.IsDefault = true;
+        }
+
+        public override void Draw()
+        {
+            this.DrawLine(FlightGlobals.ActiveVessel.orbit.GetTimeToDescendingNode().ToTime());
+        }
+    }
+}

--- a/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
+++ b/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
@@ -35,6 +35,7 @@
 using OrbitalPeriod = KerbalEngineer.Flight.Readouts.Orbital.OrbitalPeriod;
 using PeriapsisHeight = KerbalEngineer.Flight.Readouts.Orbital.PeriapsisHeight;
 using TimeToApoapsis = KerbalEngineer.Flight.Readouts.Orbital.TimeToApoapsis;
+using TimeToAscendingNode = KerbalEngineer.Flight.Readouts.Orbital.TimeToAscendingNode;
 using TimeToPeriapsis = KerbalEngineer.Flight.Readouts.Orbital.TimeToPeriapsis;
 
 #endregion
@@ -70,6 +71,8 @@
                 readouts.Add(new TimeToApoapsis());
                 readouts.Add(new TimeToPeriapsis());
                 readouts.Add(new Inclination());
+                readouts.Add(new TimeToAscendingNode());
+                readouts.Add(new TimeToDescendingNode());
                 readouts.Add(new Eccentricity());
                 readouts.Add(new OrbitalSpeed());
                 readouts.Add(new OrbitalPeriod());
@@ -111,6 +114,7 @@
                 readouts.Add(new PhaseAngle());
                 readouts.Add(new InterceptAngle());
                 readouts.Add(new RelativeInclination());
+                readouts.Add(new Rendezvous.TimeToAscendingNode());
                 readouts.Add(new AngleToAscendingNode());
                 readouts.Add(new AngleToDescendingNode());
                 readouts.Add(new Rendezvous.AltitudeSeaLevel());
@@ -202,19 +206,5 @@
         }
 
         #endregion
-
-        //#region Instance
-
-        //private static readonly ReadoutLibrary instance = new ReadoutLibrary();
-
-        ///// <summary>
-        /////     Gets the current instance of the readout library.
-        ///// </summary>
-        //public static ReadoutLibrary Instance
-        //{
-        //    get { return instance; }
-        //}
-
-        //#endregion
     }
 }

--- a/KerbalEngineer/Flight/Readouts/Rendezvous/RendezvousProcessor.cs
+++ b/KerbalEngineer/Flight/Readouts/Rendezvous/RendezvousProcessor.cs
@@ -1,257 +1,278 @@
-// 

-//     Kerbal Engineer Redux

-// 

-//     Copyright (C) 2014 CYBUTEK

-// 

-//     This program is free software: you can redistribute it and/or modify

-//     it under the terms of the GNU General Public License as published by

-//     the Free Software Foundation, either version 3 of the License, or

-//     (at your option) any later version.

-// 

-//     This program is distributed in the hope that it will be useful,

-//     but WITHOUT ANY WARRANTY; without even the implied warranty of

-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

-//     GNU General Public License for more details.

-// 

-//     You should have received a copy of the GNU General Public License

-//     along with this program.  If not, see <http://www.gnu.org/licenses/>.

-// 

-

-#region Using Directives

-

-using System;

-

-using UnityEngine;

-

-#endregion

-

-namespace KerbalEngineer.Flight.Readouts.Rendezvous

-{

-    public class RendezvousProcessor : IUpdatable, IUpdateRequest

-    {

-        #region Instance

-

-        private static readonly RendezvousProcessor instance = new RendezvousProcessor();

-

-        /// <summary>

-        ///     Gets the current instance of the rendezvous processor.

-        /// </summary>

-        public static RendezvousProcessor Instance

-        {

-            get { return instance; }

-        }

-

-        #endregion

-

-        #region Fields

-

-        private Orbit originOrbit;

-        private Vector3d originPosition;

-        private Orbit targetOrbit;

-        private Vector3d targetPosition;

-

-        #endregion

-

-        #region Properties

-

-        /// <summary>

-        ///     Gets whether the details are ready to be shown.

-        /// </summary>

-        public static bool ShowDetails { get; private set; }

-

-        /// <summary>

-        ///     Gets the difference in angle from the origin position to the target position based on a common reference.

-        /// </summary>

-        public static double PhaseAngle { get; private set; }

-

-        /// <summary>

-        ///     Gets the difference in angle from the origin position to where it is most efficient to burn for an encounter.

-        /// </summary>

-        public static double InterceptAngle { get; private set; }

-

-        /// <summary>

-        ///     Gets the angular difference between the origin and target orbits.

-        /// </summary>

-        public static double RelativeInclination { get; private set; }

-

-        /// <summary>

-        ///     Gets the angle from the origin position to the ascending node.

-        /// </summary>

-        public static double AngleToAscendingNode { get; private set; }

-

-        /// <summary>

-        ///     Gets the angle from the origin position to the descending node.

-        /// </summary>

-        public static double AngleToDescendingNode { get; private set; }

-

-        /// <summary>

-        ///     Gets the target's altitude above its reference body.

-        /// </summary>

-        public static double AltitudeSeaLevel { get; private set; }

-

-        /// <summary>

-        ///     Gets the target's apoapsis above its reference body.

-        /// </summary>

-        public static double ApoapsisHeight { get; private set; }

-

-        /// <summary>

-        ///     Gets the target's periapsis above its reference body.

-        /// </summary>

-        public static double PeriapsisHeight { get; private set; }

-

-        /// <summary>

-        ///     Gets the target's time to apoapsis.

-        /// </summary>

-        public static double TimeToApoapsis { get; private set; }

-

-        /// <summary>

-        ///     Gets the target's time to periapsis.

-        /// </summary>

-        public static double TimeToPeriapsis { get; private set; }

-

-        /// <summary>

-        ///     Gets the distance from the origin position to the target position.

-        /// </summary>

-        public static double Distance { get; private set; }

-

-        /// <summary>

-        ///     Gets the orbital period of the target orbit.

-        /// </summary>

-        public static double OrbitalPeriod { get; private set; }

-

-        #endregion

-

-        #region IUpdatable Members

-

-        /// <summary>

-        ///     Updates the details by recalculating if requested.

-        /// </summary>

-        public void Update()

-        {

-            if (FlightGlobals.fetch.VesselTarget == null)

-            {

-                ShowDetails = false;

-                return;

-            }

-

-            ShowDetails = true;

-

-            this.targetOrbit = FlightGlobals.fetch.VesselTarget.GetOrbit();

-            this.originOrbit = (FlightGlobals.ship_orbit.referenceBody == Planetarium.fetch.Sun || FlightGlobals.ship_orbit.referenceBody == FlightGlobals.ActiveVessel.targetObject.GetOrbit().referenceBody)

-                ? FlightGlobals.ship_orbit

-                : FlightGlobals.ship_orbit.referenceBody.orbit;

-

-            this.targetPosition = this.targetOrbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());

-            this.originPosition = this.originOrbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());

-

-            PhaseAngle = this.CalcCurrentPhaseAngle();

-            InterceptAngle = this.CalcInterceptAngle();

-            RelativeInclination = Vector3d.Angle(this.originOrbit.GetOrbitNormal(), this.targetOrbit.GetOrbitNormal());

-            AngleToAscendingNode = this.CalcAngleToAscendingNode();

-            AngleToDescendingNode = this.CalcAngleToDescendingNode();

-            AltitudeSeaLevel = this.targetOrbit.altitude;

-            ApoapsisHeight = this.targetOrbit.ApA;

-            PeriapsisHeight = this.targetOrbit.PeA;

-            TimeToApoapsis = this.targetOrbit.timeToAp;

-            TimeToPeriapsis = this.targetOrbit.timeToPe;

-

-            Distance = Vector3d.Distance(this.targetPosition, this.originPosition);

-            OrbitalPeriod = this.targetOrbit.period;

-        }

-

-        #endregion

-

-        #region IUpdateRequest Members

-

-        /// <summary>

-        ///     Gets and sets whether the updatable object should be updated.

-        /// </summary>

-        public bool UpdateRequested { get; set; }

-

-        #endregion

-

-        /// <summary>

-        ///     Request and update to calculate the details.

-        /// </summary>

-        public static void RequestUpdate()

-        {

-            instance.UpdateRequested = true;

-        }

-

-        #region Calculations

-

-        private double CalcCurrentPhaseAngle()

-        {

-            return this.CalcPhaseAngle(this.originPosition, this.targetPosition);

-        }

-

-        private double CalcPhaseAngle(Vector3d originPos, Vector3d targetPos)

-        {

-            var phaseAngle = Vector3d.Angle(targetPos, originPos);

-            if (Vector3d.Angle(Quaternion.AngleAxis(90.0f, Vector3d.forward) * originPos, targetPos) > 90.0)

-            {

-                phaseAngle = 360 - phaseAngle;

-            }

-            return (phaseAngle + 360) % 360;

-        }

-

-        private double CalcInterceptAngle()

-        {

-            var angle = 180.0 * (1.0 - Math.Pow((this.originOrbit.radius + this.targetOrbit.radius) / (2.0 * this.targetOrbit.radius), 1.5));

-            if (angle < 0)

-            {

-                PhaseAngle -= 360;

-                angle = (PhaseAngle - angle) + 360;

-            }

-            else

-            {

-                angle = PhaseAngle - angle;

-            }

-            if (angle < 0)

-            {

-                angle += 360;

-            }

-            return angle;

-        }

-

-        private double CalcAngleToAscendingNode()

-        {

-            var angleToNode = 0.0;

-            if (this.originOrbit.inclination < 90.0)

-            {

-                angleToNode = this.CalcPhaseAngle(this.originPosition, this.GetAscendingNode());

-            }

-            else

-            {

-                angleToNode = 360 - this.CalcPhaseAngle(this.originPosition, this.GetAscendingNode());

-            }

-            return angleToNode;

-        }

-

-        private double CalcAngleToDescendingNode()

-        {

-            var angleToNode = 0.0;

-            if (this.originOrbit.inclination < 90.0)

-            {

-                angleToNode = this.CalcPhaseAngle(this.originPosition, this.GetDescendingNode());

-            }

-            else

-            {

-                angleToNode = 360 - this.CalcPhaseAngle(this.originPosition, this.GetDescendingNode());

-            }

-            return angleToNode;

-        }

-

-        private Vector3d GetAscendingNode()

-        {

-            return Vector3d.Cross(this.targetOrbit.GetOrbitNormal(), this.originOrbit.GetOrbitNormal());

-        }

-

-        private Vector3d GetDescendingNode()

-        {

-            return Vector3d.Cross(this.originOrbit.GetOrbitNormal(), this.targetOrbit.GetOrbitNormal());

-        }

-

-        #endregion

-    }

+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2014 CYBUTEK
+// 
+//     This program is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+// 
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+// 
+//     You should have received a copy of the GNU General Public License
+//     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// 
+
+#region Using Directives
+
+using System;
+
+using KerbalEngineer.Extensions;
+
+using UnityEngine;
+
+#endregion
+
+namespace KerbalEngineer.Flight.Readouts.Rendezvous
+{
+    public class RendezvousProcessor : IUpdatable, IUpdateRequest
+    {
+        #region Instance
+
+        private static readonly RendezvousProcessor instance = new RendezvousProcessor();
+
+        /// <summary>
+        ///     Gets the current instance of the rendezvous processor.
+        /// </summary>
+        public static RendezvousProcessor Instance
+        {
+            get { return instance; }
+        }
+
+        #endregion
+
+        #region Fields
+
+        private Orbit originOrbit;
+        private Vector3d originPosition;
+        private Orbit targetOrbit;
+        private Vector3d targetPosition;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        ///     Gets whether the details are ready to be shown.
+        /// </summary>
+        public static bool ShowDetails { get; private set; }
+
+        /// <summary>
+        ///     Gets the difference in angle from the origin position to the target position based on a common reference.
+        /// </summary>
+        public static double PhaseAngle { get; private set; }
+
+        /// <summary>
+        ///     Gets the difference in angle from the origin position to where it is most efficient to burn for an encounter.
+        /// </summary>
+        public static double InterceptAngle { get; private set; }
+
+        /// <summary>
+        ///     Gets the angular difference between the origin and target orbits.
+        /// </summary>
+        public static double RelativeInclination { get; private set; }
+
+        /// <summary>
+        ///     Gets the time it will take to reach the ascending node.
+        /// </summary>
+        public static double TimeToAscendingNode { get; private set; }
+
+        /// <summary>
+        ///     Gets the angle from the origin position to the ascending node.
+        /// </summary>
+        public static double AngleToAscendingNode { get; private set; }
+
+        /// <summary>
+        ///     Gets the angle from the origin position to the descending node.
+        /// </summary>
+        public static double AngleToDescendingNode { get; private set; }
+
+        /// <summary>
+        ///     Gets the target's altitude above its reference body.
+        /// </summary>
+        public static double AltitudeSeaLevel { get; private set; }
+
+        /// <summary>
+        ///     Gets the target's apoapsis above its reference body.
+        /// </summary>
+        public static double ApoapsisHeight { get; private set; }
+
+        /// <summary>
+        ///     Gets the target's periapsis above its reference body.
+        /// </summary>
+        public static double PeriapsisHeight { get; private set; }
+
+        /// <summary>
+        ///     Gets the target's time to apoapsis.
+        /// </summary>
+        public static double TimeToApoapsis { get; private set; }
+
+        /// <summary>
+        ///     Gets the target's time to periapsis.
+        /// </summary>
+        public static double TimeToPeriapsis { get; private set; }
+
+        /// <summary>
+        ///     Gets the distance from the origin position to the target position.
+        /// </summary>
+        public static double Distance { get; private set; }
+
+        /// <summary>
+        ///     Gets the orbital period of the target orbit.
+        /// </summary>
+        public static double OrbitalPeriod { get; private set; }
+
+        #endregion
+
+        #region IUpdatable Members
+
+        /// <summary>
+        ///     Updates the details by recalculating if requested.
+        /// </summary>
+        public void Update()
+        {
+            if (FlightGlobals.fetch.VesselTarget == null)
+            {
+                ShowDetails = false;
+                return;
+            }
+
+            ShowDetails = true;
+
+            this.targetOrbit = FlightGlobals.fetch.VesselTarget.GetOrbit();
+            this.originOrbit = (FlightGlobals.ship_orbit.referenceBody == Planetarium.fetch.Sun || FlightGlobals.ship_orbit.referenceBody == FlightGlobals.ActiveVessel.targetObject.GetOrbit().referenceBody)
+                ? FlightGlobals.ship_orbit
+                : FlightGlobals.ship_orbit.referenceBody.orbit;
+
+            this.targetPosition = this.targetOrbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
+            this.originPosition = this.originOrbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
+
+            PhaseAngle = this.CalcCurrentPhaseAngle();
+            InterceptAngle = this.CalcInterceptAngle();
+            RelativeInclination = Vector3d.Angle(this.originOrbit.GetOrbitNormal(), this.targetOrbit.GetOrbitNormal());
+            TimeToAscendingNode = this.originOrbit.GetTimeToTrueAnomaly(this.GetAngle(this.originOrbit.GetOrbitNormal(), this.GetAscendingNode()) + this.originOrbit.trueAnomaly);
+            AngleToAscendingNode = this.CalcAngleToAscendingNode();
+            AngleToDescendingNode = this.CalcAngleToDescendingNode();
+            AltitudeSeaLevel = this.targetOrbit.altitude;
+            ApoapsisHeight = this.targetOrbit.ApA;
+            PeriapsisHeight = this.targetOrbit.PeA;
+            TimeToApoapsis = this.targetOrbit.timeToAp;
+            TimeToPeriapsis = this.targetOrbit.timeToPe;
+
+            Distance = Vector3d.Distance(this.targetPosition, this.originPosition);
+            OrbitalPeriod = this.targetOrbit.period;
+        }
+
+        #endregion
+
+        #region IUpdateRequest Members
+
+        /// <summary>
+        ///     Gets and sets whether the updatable object should be updated.
+        /// </summary>
+        public bool UpdateRequested { get; set; }
+
+        #endregion
+
+        /// <summary>
+        ///     Request and update to calculate the details.
+        /// </summary>
+        public static void RequestUpdate()
+        {
+            instance.UpdateRequested = true;
+        }
+
+        #region Calculations
+
+        private double CalcCurrentPhaseAngle()
+        {
+            return this.CalcPhaseAngle(this.originPosition, this.targetPosition);
+        }
+
+        private double CalcPhaseAngle(Vector3d originPos, Vector3d targetPos)
+        {
+            var phaseAngle = Vector3d.Angle(targetPos, originPos);
+            if (Vector3d.Angle(Quaternion.AngleAxis(90.0f, Vector3d.forward) * originPos, targetPos) > 90.0)
+            {
+                phaseAngle = 360 - phaseAngle;
+            }
+            return (phaseAngle + 360) % 360;
+        }
+
+        private double CalcInterceptAngle()
+        {
+            var angle = 180.0 * (1.0 - Math.Pow((this.originOrbit.radius + this.targetOrbit.radius) / (2.0 * this.targetOrbit.radius), 1.5));
+            if (angle < 0)
+            {
+                PhaseAngle -= 360;
+                angle = (PhaseAngle - angle) + 360;
+            }
+            else
+            {
+                angle = PhaseAngle - angle;
+            }
+            if (angle < 0)
+            {
+                angle += 360;
+            }
+            return angle;
+        }
+
+        private double CalcAngleToAscendingNode()
+        {
+            var angleToNode = 0.0;
+            if (this.originOrbit.inclination < 90.0)
+            {
+                angleToNode = this.CalcPhaseAngle(this.originPosition, this.GetAscendingNode());
+            }
+            else
+            {
+                angleToNode = 360 - this.CalcPhaseAngle(this.originPosition, this.GetAscendingNode());
+            }
+            return angleToNode;
+        }
+
+        private double CalcAngleToDescendingNode()
+        {
+            var angleToNode = 0.0;
+            if (this.originOrbit.inclination < 90.0)
+            {
+                angleToNode = this.CalcPhaseAngle(this.originPosition, this.GetDescendingNode());
+            }
+            else
+            {
+                angleToNode = 360 - this.CalcPhaseAngle(this.originPosition, this.GetDescendingNode());
+            }
+            return angleToNode;
+        }
+
+        private Vector3d GetAscendingNode()
+        {
+            return Vector3d.Cross(this.targetOrbit.GetOrbitNormal(), this.originOrbit.GetOrbitNormal());
+        }
+
+        private Vector3d GetDescendingNode()
+        {
+            return Vector3d.Cross(this.originOrbit.GetOrbitNormal(), this.targetOrbit.GetOrbitNormal());
+        }
+
+        private double GetAngle(Vector3d from, Vector3d to)
+        {
+            var angle = Vector3d.Angle(from, to);
+            var direction = Vector3d.Dot(from, to);
+
+            if (direction < 0.0)
+            {
+                angle += 180.0;
+            }
+
+            return angle;
+        }
+
+        #endregion
+    }
 }

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Rendezvous/TimeToAscendingNode.cs
@@ -1,1 +1,58 @@
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2014 CYBUTEK
+// 
+//     This program is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+// 
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+// 
+//     You should have received a copy of the GNU General Public License
+//     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// 
 
+#region Using Directives
+
+using KerbalEngineer.Extensions;
+
+#endregion
+
+namespace KerbalEngineer.Flight.Readouts.Rendezvous
+{
+    public class TimeToAscendingNode : ReadoutModule
+    {
+        public TimeToAscendingNode()
+        {
+            this.Name = "Time to AN";
+            this.Category = ReadoutCategory.GetCategory("Rendezvous");
+            this.HelpString = string.Empty;
+            this.IsDefault = true;
+        }
+
+        public override void Update()
+        {
+            RendezvousProcessor.RequestUpdate();
+        }
+
+        public override void Draw()
+        {
+            if (!RendezvousProcessor.ShowDetails)
+            {
+                return;
+            }
+
+            this.DrawLine(RendezvousProcessor.TimeToAscendingNode.ToTime());
+        }
+
+        public override void Reset()
+        {
+            FlightEngineerCore.Instance.AddUpdatable(RendezvousProcessor.Instance);
+        }
+    }
+}

--- a/KerbalEngineer/KerbalEngineer.csproj
+++ b/KerbalEngineer/KerbalEngineer.csproj
@@ -60,10 +60,14 @@
     <Compile Include="Editor\BuildOverlay.cs" />
     <Compile Include="CelestialBodies.cs" />
     <Compile Include="Editor\BuildToolbar.cs" />
+    <Compile Include="Extensions\OrbitExtensions.cs" />
     <Compile Include="Flight\ActionMenuGui.cs" />
     <Compile Include="Flight\FlightEngineerPartless.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" />
+    <Compile Include="Flight\Readouts\Orbital\TimeToAscendingNode.cs" />
+    <Compile Include="Flight\Readouts\Orbital\TimeToDescendingNode.cs" />
+    <Compile Include="Flight\Readouts\Rendezvous\TimeToAscendingNode.cs" />
     <Compile Include="Flight\Readouts\Surface\ImpactBiome.cs" />
     <Compile Include="Flight\Readouts\Surface\Slope.cs" />
     <Compile Include="Flight\Readouts\Surface\Biome.cs" />

 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ