ARMapRenderer: Try not to leak linerenderers when closing map mode.
ARMapRenderer: Try not to leak linerenderers when closing map mode.

--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -83,6 +83,9 @@
 			private set;
 		}
 
+		/// <summary>
+		/// Gets a value indicating whether AntennaRange will use additive ranges.
+		/// </summary>
 		public static bool UseAdditiveRanges
 		{
 			get;

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -38,6 +38,13 @@
 {
 	public class ARMapRenderer : MonoBehaviour
 	{
+		#if BENCH
+		private static ulong updateCount = 0u;
+		private static ulong updateTimer = 0u;
+		private readonly static RollingAverage averager = new RollingAverage();
+		private static long twiceAverageTime = long.MaxValue;
+		#endif
+
 		#region Fields
 		private Dictionary<Guid, LineRenderer> vesselLineRenderers;
 
@@ -59,18 +66,9 @@
 		{
 			get
 			{
-				if (this.vesselLineRenderers == null)
-				{
-					this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
-				}
-
 				LineRenderer lr;
 
-				if (this.vesselLineRenderers.TryGetValue(idx, out lr))
-				{
-					return lr;
-				}
-				else
+				if (!this.vesselLineRenderers.TryGetValue(idx, out lr))
 				{
 					GameObject obj = new GameObject();
 					obj.layer = 31;
@@ -85,6 +83,8 @@
 
 					return lr;
 				}
+
+				return lr;
 			}
 		}
 		#endregion
@@ -97,8 +97,10 @@
 				this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
 			}
 
+			#if DEBUG || BENCH
+			this.timer = new System.Diagnostics.Stopwatch();
+			#endif
 			#if DEBUG
-			this.timer = new System.Diagnostics.Stopwatch();
 			this.log = Tools.DebugLogger.New(this);
 			#endif
 		}
@@ -107,12 +109,12 @@
 		{
 			if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines)
 			{
-				this.Cleanup();
+				this.Cleanup(!HighLogic.LoadedSceneIsFlight);
 
 				return;
 			}
 
-			#if DEBUG
+			#if DEBUG || BENCH
 			timer.Restart();
 			#endif
 
@@ -191,7 +193,7 @@
 			catch (Exception ex)
 			{
 				this.LogError("Caught {0}: {1}\n{2}\n", ex.GetType().Name, ex.ToString(), ex.StackTrace.ToString());
-				this.Cleanup();
+				this.Cleanup(false);
 			}
 			#if DEBUG
 			finally
@@ -201,11 +203,32 @@
 				log.Print();
 			}
 			#endif
+
+			#if BENCH
+			ARMapRenderer.updateCount++;
+			ARMapRenderer.updateTimer += (ulong)this.timer.ElapsedTicks;
+
+			if (ARMapRenderer.updateCount >= (ulong)(8d / Time.smoothDeltaTime))
+			{
+				ARMapRenderer.averager.AddItem((double)ARMapRenderer.updateTimer / (double)ARMapRenderer.updateCount);
+				ARMapRenderer.updateTimer = 0u;
+				ARMapRenderer.updateCount = 0u;
+				ARMapRenderer.twiceAverageTime = (long)(ARMapRenderer.averager.Average * 2d);
+			}
+
+			if (this.timer.ElapsedTicks > ARMapRenderer.twiceAverageTime)
+			{
+				this.Log("PreCull took significant longer than usual ({0:S3}s vs {1:S3}s)",
+					(double)this.timer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency,
+					ARMapRenderer.averager.Average / (double)System.Diagnostics.Stopwatch.Frequency
+				);
+			}
+			#endif
 		}
 
 		private void OnDestroy()
 		{
-			this.Cleanup();
+			this.Cleanup(true);
 
 			this.Log("Destroyed");
 		}
@@ -261,7 +284,7 @@
 			}
 			else
 			{
-				if (relay.transmitDistance < relay.nominalTransmitDistance)
+				if (relay.LinkStatus == ConnectionStatus.Optimal)
 				{
 					thisColor = Color.green;
 				}
@@ -307,7 +330,7 @@
 			log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart);
 		}
 
-		private void Cleanup()
+		private void Cleanup(bool freeObjects)
 		{
 			if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
 			{
@@ -318,9 +341,17 @@
 				{
 					lineRenderer = enumerator.Current;
 					lineRenderer.enabled = false;
-					GameObject.Destroy(lineRenderer.gameObject);
-				}
-				this.vesselLineRenderers.Clear();
+
+					if (freeObjects)
+					{
+						GameObject.Destroy(lineRenderer.gameObject);
+					}
+				}
+
+				if (freeObjects)
+				{
+					this.vesselLineRenderers.Clear();
+				}
 			}
 		}
 		#endregion

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -56,8 +56,18 @@
 			}
 		}
 
+		#if BENCH
+		private static ushort relayCount = 0;
+		private static ulong searchCount = 0u;
+		private static ulong searchTimer = 0u;
+		private readonly static RollingAverage averager = new RollingAverage(16);
+		private static long doubleAverageTime = long.MaxValue;
+
+
+		private System.Diagnostics.Stopwatch performanceTimer = new System.Diagnostics.Stopwatch();
+		#endif
+
 		private bool canTransmit;
-		private bool isChecked;
 
 		private IAntennaRelay nearestRelay;
 		private IAntennaRelay bestOccludedRelay;
@@ -90,15 +100,43 @@
 		}
 
 		/// <summary>
-		/// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
-		/// </summary>
-		public virtual CelestialBody firstOccludingBody
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
+		public virtual bool KerbinDirect
 		{
 			get;
 			protected set;
 		}
 
 		/// <summary>
+		/// Gets or sets the nominal link distance, in meters.
+		/// </summary>
+		public virtual double NominalLinkDistance
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets or sets the maximum link distance, in meters.
+		/// </summary>
+		public virtual double MaximumLinkDistance
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
+		/// </summary>
+		public virtual CelestialBody firstOccludingBody
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
 		/// Gets the transmit distance.
 		/// </summary>
 		/// <value>The transmit distance.</value>
@@ -118,6 +156,15 @@
 		}
 
 		/// <summary>
+		/// Gets or sets the link status.
+		/// </summary>
+		public virtual ConnectionStatus LinkStatus
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
 		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
 		/// </summary>
 		public virtual double nominalTransmitDistance
@@ -134,16 +181,6 @@
 		{
 			get;
 			set;
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
-		/// directly with Kerbin.
-		/// </summary>
-		public virtual bool KerbinDirect
-		{
-			get;
-			protected set;
 		}
 
 		/// <summary>
@@ -171,22 +208,34 @@
 			log = Tools.DebugLogger.New(this);
 			#endif
 
-			// Skip vessels that have already been checked for a nearest relay this pass.
-			if (this.isChecked)
-			{
-				log.AppendFormat("{0}: Target search skipped because our vessel has been checked already this search.",
-					this);
-				log.Print();
-				return;
-			}
+			#if BENCH
+			this.performanceTimer.Restart();
+
+			long startVesselLoopTicks;
+			long totalVesselLoopTicks;
+
+			string slowestLOSVesselName = string.Empty;
+			long slowestLOSVesselTicks = long.MinValue;
+			long startLOSVesselTicks;
+			long totalLOSVesselTicks;
+
+			string slowestCircularVesselName = string.Empty;
+			long slowestCircularVesselTicks = long.MinValue;
+			long startCircularVesselTicks;
+			long totalCircularVesselTicks;
+
+			long startKerbinLOSTicks;
+			long totalKerbinLOSTicks;
+			long statusResolutionTicks;
+
+			ushort usefulVesselCount = 0;
+			#endif
 
 			log.AppendFormat("{0}: Target search started).", this.ToString());
 
 			#if DEBUG
 			try {
 			#endif
-			// Set this vessel as checked, so that we don't check it again.
-			this.isChecked = true;
 
 			// Blank everything we're trying to find before the search.
 			this.firstOccludingBody = null;
@@ -218,7 +267,10 @@
 			IAntennaRelay potentialBestRelay;
 			CelestialBody fob;
 
-			// IList<IAntennaRelay> vesselRelays;
+			#if BENCH
+			startVesselLoopTicks = performanceTimer.ElapsedTicks;
+			#endif
+
 			for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
 			{
 				log.AppendFormat("\nFetching vessel at index {0}", vIdx);
@@ -271,6 +323,10 @@
 					continue;
 				}
 
+				#if BENCH
+				usefulVesselCount++;
+				#endif
+
 				// Find the distance from here to the vessel...
 				log.Append("\n\tgetting distance to potential vessel");
 				double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
@@ -291,6 +347,10 @@
 				log.AppendFormat("\n\tmax link distance: {0}", maxLinkSqrDistance);
 
 				double potentialSqrQuotient = potentialSqrDistance / maxLinkSqrDistance;
+
+				#if BENCH
+				startLOSVesselTicks = performanceTimer.ElapsedTicks;
+				#endif
 
 				log.Append("\n\t\tdoing LOS check");
 				// Skip vessels to which we do not have line of sight.
@@ -299,6 +359,16 @@
 					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
 				)
 				{
+					#if BENCH
+					totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
+
+					if (totalLOSVesselTicks > slowestLOSVesselTicks)
+					{
+						slowestLOSVesselTicks = totalLOSVesselTicks;
+						slowestLOSVesselName = vessel.vesselName;
+					}
+					#endif
+
 					log.Append("\n\t\t...failed LOS check");
 
 					log.AppendFormat("\n\t\t\t{0}: Vessel {1} not in line of sight.",
@@ -328,6 +398,18 @@
 					
 					continue;
 				}
+				#if BENCH
+				else
+				{
+					totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
+				}
+
+				if (totalLOSVesselTicks > slowestLOSVesselTicks)
+				{
+					slowestLOSVesselTicks = totalLOSVesselTicks;
+					slowestLOSVesselName = vessel.vesselName;
+				}
+				#endif
 
 				log.Append("\n\t\t...passed LOS check");
 
@@ -348,6 +430,10 @@
 
 				if (potentialBestRelay.CanTransmit())
 				{
+					#if BENCH
+					startCircularVesselTicks = performanceTimer.ElapsedTicks;
+					#endif
+
 					needle = potentialBestRelay;
 					bool isCircular = false;
 
@@ -415,8 +501,23 @@
 							potentialBestRelay
 						);
 					}
-				}
-			}
+
+					#if BENCH
+					totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks;
+
+					if (totalCircularVesselTicks > slowestCircularVesselTicks)
+					{
+						slowestCircularVesselName = vessel.vesselName;
+						slowestCircularVesselTicks = totalCircularVesselTicks;
+					}
+
+					#endif
+				}
+			}
+
+			#if BENCH
+			totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks;
+			#endif
 
 			CelestialBody bodyOccludingKerbin = null;
 
@@ -447,12 +548,19 @@
 				kerbinSqrDistance
 			);
 
+			#if BENCH
+			startKerbinLOSTicks = this.performanceTimer.ElapsedTicks;
+			#endif
+
 			// If we don't have LOS to Kerbin, focus on relays
 			if (
 				ARConfiguration.RequireLineOfSight &&
 				!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
 			)
 			{
+				#if BENCH
+				totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
+				#endif
 				log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);
 
 				// nearestRelaySqrDistance will be infinity if all relays are occluded or none exist.
@@ -539,6 +647,10 @@
 			// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
 			else
 			{
+				#if BENCH
+				totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
+				#endif
+
 				log.AppendFormat("\n\tKerbin is in LOS.");
 
 				// If the nearest relay is closer than Kerbin and in range, transmit to it.
@@ -659,6 +771,45 @@
 				}
 			}
 
+			if (ARConfiguration.UseAdditiveRanges)
+			{
+				if (this.KerbinDirect)
+				{
+					this.NominalLinkDistance = Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange);
+					this.MaximumLinkDistance = Math.Sqrt(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange);
+				}
+				else
+				{
+					this.NominalLinkDistance = Math.Sqrt(this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance);
+					this.MaximumLinkDistance = Math.Sqrt(this.maxTransmitDistance * this.targetRelay.maxTransmitDistance);
+				}
+			}
+			else
+			{
+				this.NominalLinkDistance = this.nominalTransmitDistance;
+				this.MaximumLinkDistance = this.maxTransmitDistance;
+			}
+
+			if (this.canTransmit)
+			{
+				if (this.transmitDistance < this.NominalLinkDistance)
+				{
+					this.LinkStatus = ConnectionStatus.Optimal;
+				}
+				else
+				{
+					this.LinkStatus = ConnectionStatus.Suboptimal;
+				}
+			}
+			else
+			{
+				this.LinkStatus = ConnectionStatus.None;
+			}
+
+			#if BENCH
+			statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks;
+			#endif
+
 			log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());
 			
 			#if DEBUG
@@ -673,9 +824,69 @@
 			#if DEBUG
 			}
 			#endif
-			// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
-			// used next time.
-			this.isChecked = false;
+
+			#if BENCH
+			AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks;
+			AntennaRelay.searchCount++;
+			this.performanceTimer.Stop();
+
+			double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount;
+
+			if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay)
+			{
+				AntennaRelay.searchCount = 0u;
+				AntennaRelay.searchTimer = 0u;
+
+				AntennaRelay.averager.AddItem(averageSearchTime);
+				AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d);
+			}
+
+			if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime)
+			{
+				System.Text.StringBuilder sb = Tools.GetStringBuilder();
+
+				sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" +
+					" took significantly longer than average ({1:S3}s vs {2:S3}s)",
+					this.ToString(),
+					(double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency,
+					(double)AntennaRelay.averager.Average / (double)System.Diagnostics.Stopwatch.Frequency
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\tVessel loop time: {0:S3}s",
+					(double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\t\tAverage vessel time for each of {1} vessels: {0:S3}s",
+					(double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency /
+					(double)usefulVesselCount,
+					usefulVesselCount
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}",
+					(double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
+					slowestLOSVesselName
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}",
+					(double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
+					slowestCircularVesselName
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s",
+					(double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency
+				);
+
+				sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s",
+					(double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency
+				);
+
+				// sb.AppendFormat(Tools.SIFormatter, "", start)
+
+				Tools.PostWarningMessage(sb.ToString());
+
+				Tools.PutStringBuilder(sb);
+			}
+			#endif
 		}
 
 		/// <summary>
@@ -699,7 +910,10 @@
 		public AntennaRelay(IAntennaRelay module)
 		{
 			this.moduleRef = module;
-			this.isChecked = false;
+
+			#if BENCH
+			AntennaRelay.relayCount++;
+			#endif
 
 			Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
 		}

--- a/GameData/AntennaRange/AntennaRange.cfg
+++ b/GameData/AntennaRange/AntennaRange.cfg
@@ -37,13 +37,13 @@
 // maxDataFactor:	The multipler on packetSize that defines the maximum data bandwidth of the antenna.
 // 
 
-// Maximum distance 51696km, about 10% past Minmus
 @PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 18277500
+		nominalRange = 6364
+		simpleRange = 20500000
 		maxPowerFactor = 8
 		maxDataFactor = 4
 	}
@@ -59,14 +59,13 @@
 	}
 }
 
-// Maximum distance 37152180km, about 5% past Duna
-// Bonus data transmission when at short range
 @PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 18576090000
+		nominalRange = 3500000000
+		simpleRange = 18000000000
 		maxPowerFactor = 4
 		maxDataFactor = 8
 	}
@@ -82,14 +81,14 @@
 	}
 }
 
-// Maximum distance 224770770km, about 75% past Eeloo.
 @PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
 		@packetResourceCost /= 1.414213
-		nominalRange = 56192692500
+		nominalRange = 10000000000
+		simpleRange = 56250000000
 		maxPowerFactor = 16
 		maxDataFactor = 2
 	}
@@ -107,16 +106,16 @@
 
 TRACKING_STATION_RANGES
 {
-	range = 51696576
-	range = 37152180000
-	range = 224770770000
+	range = 800000
+	range = 200000000000
+	range = 2250000000000
 }
 
 EVA_MODULE
 {
 	name = ModuleLimitedDataTransmitter
 
-	nominalRange = 5000
+	nominalRange = 1389
 	maxPowerFactor = 1
 	maxDataFactor = 1
 

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -47,9 +47,31 @@
 		IAntennaRelay targetRelay { get; }
 
 		/// <summary>
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
+		bool KerbinDirect { get; }
+
+		/// <summary>
+		/// The link distance, in meters, at which this relay behaves nominally.
+		/// </summary>
+		double NominalLinkDistance { get; }
+
+
+		/// <summary>
+		/// The link distance, in meters, beyond which this relay cannot operate.
+		/// </summary>
+		double MaximumLinkDistance { get; }
+
+		/// <summary>
 		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
 		/// </summary>
 		double transmitDistance { get; }
+
+		/// <summary>
+		/// Gets the link status.
+		/// </summary>
+		ConnectionStatus LinkStatus { get; }
 
 		/// <summary>
 		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
@@ -65,12 +87,6 @@
 		/// The first CelestialBody blocking line of sight to a 
 		/// </summary>
 		CelestialBody firstOccludingBody { get; }
-
-		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
-		/// directly with Kerbin.
-		/// </summary>
-		bool KerbinDirect { get; }
 
 		/// <summary>
 		/// Gets the Part title.

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -61,13 +61,20 @@
 		private ScreenMessage ErrorMsg;
 
 		/// <summary>
-		/// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
-		/// and packetSize.
+		/// When additive ranges are enabled, the distance from Kerbin at which the antenna will perform exactly as
+		/// prescribed by packetResourceCost and packetSize.
 		/// </summary>
 		[KSPField(isPersistant = false)]
 		public double nominalRange;
 
 		/// <summary>
+		/// When additive ranges are disabled, the distance from Kerbin at which the antenna will perform exactly as
+		/// prescribed by packetResourceCost and packetSize.
+		/// </summary>
+		[KSPField(isPersistant = false)]
+		public double simpleRange;
+
+		/// <summary>
 		/// Relay status string for use in action menus.
 		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Status")]
@@ -86,9 +93,15 @@
 		public string UItransmitDistance;
 
 		/// <summary>
+		/// The nominal range string for use in action menus.
+		/// </summary>
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Nominal Range")]
+		public string UInominalLinkDistance;
+
+		/// <summary>
 		/// Maximum distance string for use in action menus.
 		/// </summary>
-		[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Range")]
 		public string UImaxTransmitDistance;
 
 		/// <summary>
@@ -182,6 +195,55 @@
 		}
 
 		/// <summary>
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
+		public bool KerbinDirect
+		{
+			get
+			{
+				if (this.relay != null)
+				{
+					return this.relay.KerbinDirect;
+				}
+
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the nominal link distance, in meters.
+		/// </summary>
+		public double NominalLinkDistance
+		{
+			get
+			{
+				if (this.relay != null)
+				{
+					return this.relay.NominalLinkDistance;
+				}
+
+				return 0d;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the maximum link distance, in meters.
+		/// </summary>
+		public double MaximumLinkDistance
+		{
+			get
+			{
+				if (this.relay != null)
+				{
+					return this.relay.MaximumLinkDistance;
+				}
+
+				return 0d;
+			}
+		}
+
+		/// <summary>
 		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
 		/// </summary>
 		public double transmitDistance
@@ -198,13 +260,36 @@
 		}
 
 		/// <summary>
+		/// Gets the link status.
+		/// </summary>
+		public ConnectionStatus LinkStatus
+		{
+			get
+			{
+				if (this.relay == null)
+				{
+					return ConnectionStatus.None;
+				}
+
+				return this.relay.LinkStatus;
+			}
+		}
+
+		/// <summary>
 		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
 		/// </summary>
 		public double nominalTransmitDistance
 		{
 			get
 			{
-				return this.nominalRange;
+				if (ARConfiguration.UseAdditiveRanges)
+				{
+					return this.nominalRange;
+				}
+				else
+				{
+					return this.simpleRange;
+				}
 			}
 		}
 
@@ -297,23 +382,6 @@
 		}
 
 		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
-		/// directly with Kerbin.
-		/// </summary>
-		public bool KerbinDirect
-		{
-			get
-			{
-				if (this.relay != null)
-				{
-					return this.relay.KerbinDirect;
-				}
-
-				return false;
-			}
-		}
-
-		/// <summary>
 		/// Gets the Part title.
 		/// </summary>
 		public string Title
@@ -353,13 +421,13 @@
 				"{0} loaded:\n" +
 				"packetSize: {1}\n" +
 				"packetResourceCost: {2}\n" +
-				"nominalRange: {3}\n" +
+				"nominalTransmitDistance: {3}\n" +
 				"maxPowerFactor: {4}\n" +
 				"maxDataFactor: {5}\n",
 				this.name,
 				base.packetSize,
 				this._basepacketResourceCost,
-				this.nominalRange,
+				this.nominalTransmitDistance,
 				this.maxPowerFactor,
 				this.maxDataFactor
 			));
@@ -375,11 +443,11 @@
 
 			if (state >= StartState.PreLaunch)
 			{
-				this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalRange;
+				this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
 
 				this.relay = new AntennaRelay(this);
+				this.relay.nominalTransmitDistance = this.nominalTransmitDistance;
 				this.relay.maxTransmitDistance = this.maxTransmitDistance;
-				this.relay.nominalTransmitDistance = this.nominalRange;
 
 				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.maxTransmitDistance);
 
@@ -401,7 +469,7 @@
 
 			base.OnLoad (node);
 
-			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalRange;
+			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
 		}
 
 		/// <summary>
@@ -413,7 +481,7 @@
 			string text;
 
 			sb.Append(base.GetInfo());
-			sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalRange);
+			sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
 			sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m\n", this.maxTransmitDistance);
 
 			text = sb.ToString();
@@ -643,21 +711,21 @@
 		{
 			if (this.actionUIUpdate)
 			{
-				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", 
-					this.MaxLinkDistance());
+				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.MaximumLinkDistance);
+				this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.NominalLinkDistance);
 				
 				if (this.CanTransmit())
 				{
-					this.UIrelayStatus = "Connected";
-					this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m";
-					this.UIpacketSize = Tools.MuMech_ToSI(this.DataRate) + "MiT";
-					this.UIpacketCost = Tools.MuMech_ToSI(this.DataResourceCost) + "E";
+					this.UIrelayStatus = this.LinkStatus.ToString();
+					this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.transmitDistance);
+					this.UIpacketSize = string.Format(Tools.SIFormatter, "{0:S3}MiT", this.DataRate);
+					this.UIpacketCost = string.Format(Tools.SIFormatter, "{0:S3}EC", this.DataResourceCost);
 				}
 				else
 				{
 					if (this.relay.firstOccludingBody == null)
 					{
-						this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m";
+						this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.transmitDistance);
 						this.UIrelayStatus = "Out of range";
 					}
 					else
@@ -757,13 +825,13 @@
 		// transmission fails (see CanTransmit).
 		private void PreTransmit_SetPacketResourceCost()
 		{
-			if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.NominalLinkDistance())
+			if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.NominalLinkDistance)
 			{
 				base.packetResourceCost = this._basepacketResourceCost;
 			}
 			else
 			{
-				float rangeFactor = (float)(this.transmitDistance / this.NominalLinkDistance());
+				float rangeFactor = (float)(this.transmitDistance / this.NominalLinkDistance);
 				rangeFactor *= rangeFactor;
 
 				base.packetResourceCost = this._basepacketResourceCost
@@ -777,13 +845,13 @@
 		// distance.  packetSize maxes out at _basepacketSize * maxDataFactor.
 		private void PreTransmit_SetPacketSize()
 		{
-			if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.NominalLinkDistance())
+			if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.NominalLinkDistance)
 			{
 				base.packetSize = this._basepacketSize;
 			}
 			else
 			{
-				float rangeFactor = (float)(this.NominalLinkDistance() / this.transmitDistance);
+				float rangeFactor = (float)(this.NominalLinkDistance / this.transmitDistance);
 				rangeFactor *= rangeFactor;
 
 				base.packetSize = Mathf.Min(

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -38,6 +38,50 @@
 	public static class RelayExtensions
 	{
 		/// <summary>
+		/// Returns the distance between two IAntennaRelays.
+		/// </summary>
+		/// <param name="relayOne">Relay one.</param>
+		/// <param name="relayTwo">Relay two.</param>
+		public static double DistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
+		{
+			return relayOne.vessel.DistanceTo(relayTwo.vessel);
+		}
+
+		/// <summary>
+		/// Returns the distance from this IAntennaRelay to the given CelestialBody
+		/// </summary>
+		/// <param name="relay">Relay.</param>
+		/// <param name="body">Body.</param>
+		public static double SqrDistanceTo(this IAntennaRelay relay, CelestialBody body)
+		{
+			double range = relay.vessel.DistanceTo(body) - body.Radius;
+
+			return range * range;
+		}
+
+		/// <summary>
+		/// Returns the distance between two IAntennaRelays.
+		/// </summary>
+		/// <param name="relayOne">Relay one.</param>
+		/// <param name="relayTwo">Relay two.</param>
+		public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
+		{
+			return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
+		}
+
+		/// <summary>
+		/// Returns the distance from this IAntennaRelay to the given CelestialBody
+		/// </summary>
+		/// <param name="relay">Relay.</param>
+		/// <param name="body">Body.</param>
+		public static double DistanceTo(this IAntennaRelay relay, CelestialBody body)
+		{
+			double range = relay.vessel.DistanceTo(body) - body.Radius;
+
+			return range;
+		}
+
+		/// <summary>
 		/// Returns the distance between this IAntennaRelay and a Vessel
 		/// </summary>
 		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
@@ -128,50 +172,6 @@
 		}
 
 		/// <summary>
-		/// Calculates the nominal link distance.
-		/// </summary>
-		public static double NominalLinkDistance(this IAntennaRelay relay)
-		{
-			if (ARConfiguration.UseAdditiveRanges)
-			{
-				if (relay.KerbinDirect)
-				{
-					return Math.Sqrt(relay.nominalTransmitDistance * ARConfiguration.KerbinNominalRange);
-				}
-				else
-				{
-					return Math.Sqrt(relay.nominalTransmitDistance * relay.targetRelay.nominalTransmitDistance);
-				}
-			}
-			else
-			{
-				return relay.nominalTransmitDistance;
-			}
-		}
-
-		/// <summary>
-		/// Calculates the maximum link distance.
-		/// </summary>
-		public static double MaxLinkDistance(this IAntennaRelay relay)
-		{
-			if (ARConfiguration.UseAdditiveRanges)
-			{
-				if (relay.KerbinDirect)
-				{
-					return Math.Sqrt(relay.maxTransmitDistance * ARConfiguration.KerbinRelayRange);
-				}
-				else
-				{
-					return Math.Sqrt(relay.maxTransmitDistance * relay.targetRelay.maxTransmitDistance);
-				}
-			}
-			else
-			{
-				return relay.maxTransmitDistance;
-			}
-		}
-
-		/// <summary>
 		/// Gets the <see cref="AntennaRange.ConnectionStatus"/> for this <see cref="Vessel"/>
 		/// </summary>
 		/// <param name="vessel">This <see cref="Vessel"/></param>
@@ -184,13 +184,11 @@
 			for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
 			{
 				relay = vesselRelays[rIdx];
-				if (relay.CanTransmit())
+				if (relay.LinkStatus > ConnectionStatus.None)
 				{
 					canTransmit = true;
 
-					double quo = relay.transmitDistance / relay.NominalLinkDistance();
-
-					if (quo <= 1d)
+					if (relay.LinkStatus == ConnectionStatus.Optimal)
 					{
 						return ConnectionStatus.Optimal;
 					}