Tweaked the numeric ranges for the DTS, 88-88, and KSC3 down just a bit. Added simpleRange for EVA transmitters
Tweaked the numeric ranges for the DTS, 88-88, and KSC3 down just a bit. Added simpleRange for EVA transmitters

--- 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;
@@ -168,6 +171,9 @@
 
 			ARConfiguration.UseAdditiveRanges = this.LoadConfigValue(USE_ADDITIVE_KEY, true);
 
+			ARConfiguration.PrettyLines = this.LoadConfigValue(PRETTY_LINES_KEY, true);
+
+			ARConfiguration.UpdateDelay = this.LoadConfigValue(UPDATE_DELAY_KEY, 16L);
 			this.updateDelayStr = ARConfiguration.UpdateDelay.ToString();
 
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -39,6 +39,11 @@
 	[KSPAddon(KSPAddon.Startup.Flight, false)]
 	public class ARFlightController : MonoBehaviour
 	{
+		#region Static
+		private static List<IAntennaRelay> usefulRelays;
+		public static IList<IAntennaRelay> UsefulRelays;
+		#endregion
+
 		#region Fields
 		private Dictionary<ConnectionStatus, string> toolbarTextures;
 		private Dictionary<ConnectionStatus, Texture> appLauncherTextures;
@@ -153,11 +158,9 @@
 
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
 			GameEvents.onVesselChange.Add(this.onVesselChange);
-		}
-
-		private void Start()
-		{
-			this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
+
+			usefulRelays = new List<IAntennaRelay>();
+			UsefulRelays = usefulRelays.AsReadOnly();
 		}
 
 		private void FixedUpdate()
@@ -216,6 +219,11 @@
 
 		private void Update()
 		{
+			if (MapView.MapIsEnabled && this.mapRenderer == null)
+			{
+				this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
+			}
+
 			if (this.toolbarButton != null)
 			{
 				this.toolbarButton.Enabled = MapView.MapIsEnabled;
@@ -241,11 +249,13 @@
 
 			this.log.Clear();
 
-			if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ActiveVessel != null)
+			if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
 			{
 				Vessel vessel;
 				IAntennaRelay relay;
 				IList<IAntennaRelay> activeVesselRelays;
+
+				usefulRelays.Clear();
 
 				for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
 				{
@@ -264,6 +274,7 @@
 					{
 						log.AppendFormat("Finding nearest relay for best relay {0}", relay);
 
+						usefulRelays.Add(relay);
 						relay.FindNearestRelay();
 					}
 				}
@@ -275,6 +286,8 @@
 
 					relay.FindNearestRelay();
 				}
+
+				usefulRelays.Add(RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel));
 
 				if (this.toolbarButton != null || this.appLauncherButton != null)
 				{

--- 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
 
@@ -130,68 +132,36 @@
 					MapView.MapCamera.Distance
 				);
 
-				if (FlightGlobals.ready && FlightGlobals.Vessels != null)
-				{
-					log.AppendLine("FlightGlobals ready and Vessels list not null.");
-
-					for (int i = 0; i < FlightGlobals.Vessels.Count; i++)
+				log.AppendLine("FlightGlobals ready and Vessels list not null.");
+
+				IAntennaRelay relay;
+
+				for (int i = 0; i < ARFlightController.UsefulRelays.Count; i++)
+				{
+					relay = ARFlightController.UsefulRelays[i];
+
+					if (relay == null)
 					{
-						Vessel vessel = FlightGlobals.Vessels[i];
-
-						log.AppendFormat("\nStarting check for vessel {0} at {1}ms", vessel, timer.ElapsedMilliseconds);
-
-						if (vessel == null)
-						{
-							log.AppendFormat("\n\tSkipping vessel {0} altogether because it is null.", vessel);
-							continue;
-						}
-
-						switch (vessel.vesselType)
-						{
-							case VesselType.Debris:
-							case VesselType.EVA:
-							case VesselType.Unknown:
-							case VesselType.SpaceObject:
-								log.AppendFormat("\n\tDiscarded because vessel is of invalid type {0}",
-									vessel.vesselType);
-								continue;
-						}
-
-						log.AppendFormat("\n\tChecking vessel {0}.", vessel.vesselName);
-
-						#if DEBUG
-						start = timer.ElapsedMilliseconds;
-						#endif
-
-						IAntennaRelay vesselRelay = vessel.GetBestRelay();
-
-						if (vesselRelay == null)
-						{
-							log.AppendFormat("\n\tGot null relay for vessel {0}", vessel.vesselName);
-							continue;
-						}
-
-						log.AppendFormat("\n\tGot best relay {0} ({3}) for vessel {1} in {2} ms",
-							vesselRelay, vessel, timer.ElapsedMilliseconds - start, vesselRelay.GetType().Name);
-
-						if (vesselRelay != null)
-						{
-							#if DEBUG
-							start = timer.ElapsedMilliseconds;
-							#endif
-
-							this.SetRelayVertices(vesselRelay);
-
-							log.AppendFormat("\n\tSet relay vertices for {0} in {1}ms",
-								vessel, timer.ElapsedMilliseconds - start);
-						}
+						log.AppendFormat("\n\tGot null relay, skipping");
+						continue;
 					}
+
+					log.AppendFormat("\n\tDrawing pretty lines for useful relay {0}", relay);
+					
+					#if DEBUG
+					start = timer.ElapsedMilliseconds;
+					#endif
+
+					this.SetRelayVertices(relay);
+
+					log.AppendFormat("\n\tSet relay vertices for {0} in {1}ms",
+						relay, timer.ElapsedMilliseconds - start);
 				}
 			}
 			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 +171,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");
 		}
@@ -307,7 +298,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 +309,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;
@@ -102,7 +112,7 @@
 		/// <summary>
 		/// Gets or sets the nominal link distance, in meters.
 		/// </summary>
-		public virtual double NominalLinkDistance
+		public virtual double NominalLinkSqrDistance
 		{
 			get;
 			protected set;
@@ -111,7 +121,7 @@
 		/// <summary>
 		/// Gets or sets the maximum link distance, in meters.
 		/// </summary>
-		public virtual double MaximumLinkDistance
+		public virtual double MaximumLinkSqrDistance
 		{
 			get;
 			protected set;
@@ -130,21 +140,24 @@
 		/// Gets the transmit distance.
 		/// </summary>
 		/// <value>The transmit distance.</value>
-		public double transmitDistance
+		public double CurrentLinkSqrDistance
 		{
 			get
 			{
 				if (this.KerbinDirect || this.targetRelay == null)
 				{
-					return this.DistanceTo(Kerbin);
+					return this.SqrDistanceTo(Kerbin);
 				}
 				else
 				{
-					return this.DistanceTo(this.targetRelay);
-				}
-			}
-		}
-
+					return this.SqrDistanceTo(this.targetRelay);
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the link status.
+		/// </summary>
 		public virtual ConnectionStatus LinkStatus
 		{
 			get;
@@ -195,22 +208,51 @@
 			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;
+
+			// Declare a bunch of variables we'll be using.
+			CelestialBody bodyOccludingBestOccludedRelay = null;
+			IAntennaRelay needle;
+
+			double nearestRelaySqrQuotient = double.PositiveInfinity;
+			double bestOccludedSqrQuotient = double.PositiveInfinity;
+
+			double potentialSqrDistance;
+			double maxLinkSqrDistance;
+			double potentialSqrQuotient;
+
+			double kerbinSqrDistance;
+			double kerbinSqrQuotient;
+
+			bool isCircular;
+			int iterCount;
 
 			// Blank everything we're trying to find before the search.
 			this.firstOccludingBody = null;
@@ -220,17 +262,6 @@
 
 			// Default to KerbinDirect = true in case something in here doesn't work right.
 			this.KerbinDirect = true;
-
-			CelestialBody bodyOccludingBestOccludedRelay = null;
-			IAntennaRelay needle;
-
-			// double nearestRelaySqrDistance = double.PositiveInfinity;
-			// double bestOccludedSqrDistance = double.PositiveInfinity;
-
-			// double maxTransmitSqrDistance = double.NegativeInfinity;
-
-			double nearestRelaySqrQuotient = double.PositiveInfinity;
-			double bestOccludedSqrQuotient = double.PositiveInfinity;
 
 			/*
 			 * Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
@@ -238,54 +269,17 @@
 			 * and that can transmit.  Once we find a suitable candidate, assign it to nearestRelay for comparison
 			 * against future finds.
 			 * */
-			Vessel potentialVessel;
+			// Vessel potentialVessel;
 			IAntennaRelay potentialBestRelay;
 			CelestialBody fob;
 
-			// IList<IAntennaRelay> vesselRelays;
-			for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
-			{
-				log.AppendFormat("\nFetching vessel at index {0}", vIdx);
-				potentialVessel = FlightGlobals.Vessels[vIdx];
-				
-				if (potentialVessel == null)
-				{
-					Tools.PostErrorMessage("{0}: Skipping vessel at index {1} because it is null.", this, vIdx);
-					log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx);
-					log.Print();
-					return;
-				}
-				#if DEBUG
-				else
-				{
-					log.AppendFormat("\n\tGot vessel {0}", potentialVessel);
-				}
-				#endif
-
-				// Skip vessels of the wrong type.
-				log.Append("\n\tchecking vessel type");
-				switch (potentialVessel.vesselType)
-				{
-					case VesselType.Debris:
-					case VesselType.Flag:
-					case VesselType.EVA:
-					case VesselType.SpaceObject:
-					case VesselType.Unknown:
-							log.Append("\n\tSkipping because vessel is the wrong type.");
-						continue;
-					default:
-						break;
-				}
-				
-				log.Append("\n\tchecking if vessel is this vessel");
-				// Skip vessels with the wrong ID
-				if (potentialVessel.id == vessel.id)
-				{
-					log.Append("\n\tSkipping because vessel is this vessel.");
-					continue;
-				}
-
-				potentialBestRelay = potentialVessel.GetBestRelay();
+			#if BENCH
+			startVesselLoopTicks = performanceTimer.ElapsedTicks;
+			#endif
+
+			for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++)
+			{
+				potentialBestRelay = ARFlightController.UsefulRelays[rIdx];
 				log.AppendFormat("\n\t\tgot best vessel relay {0}",
 					potentialBestRelay == null ? "null" : potentialBestRelay.ToString());
 
@@ -295,13 +289,21 @@
 					continue;
 				}
 
+				if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel)
+				{
+					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);
+				potentialSqrDistance = this.SqrDistanceTo(potentialBestRelay);
 				log.Append("\n\tgetting best vessel relay");
 
 				log.Append("\n\tgetting max link distance to potential relay");
-				double maxLinkSqrDistance;
 
 				if (ARConfiguration.UseAdditiveRanges)
 				{
@@ -314,19 +316,33 @@
 
 				log.AppendFormat("\n\tmax link distance: {0}", maxLinkSqrDistance);
 
-				double potentialSqrQuotient = potentialSqrDistance / maxLinkSqrDistance;
+				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.
 				if (
 					ARConfiguration.RequireLineOfSight &&
-					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
+					!this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, 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.",
-						this.ToString(), potentialVessel.vesselName);
+					log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.",
+						this.ToString(), potentialBestRelay);
 					
 					log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
 					log.AppendFormat("\n\t\t\tbestOccludedSqrQuotient: {0}", bestOccludedSqrQuotient);
@@ -352,6 +368,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");
 
@@ -361,9 +389,9 @@
 				if (potentialSqrQuotient > nearestRelaySqrQuotient)
 				{
 					
-					log.AppendFormat("\n\t{0}: Vessel {1} discarded because it is farther than another the nearest relay.",
+					log.AppendFormat("\n\t{0}: Relay {1} discarded because it is farther than another the nearest relay.",
 						this.ToString(),
-						potentialVessel.vesselName
+						potentialBestRelay
 					);
 					continue;
 				}
@@ -372,10 +400,14 @@
 
 				if (potentialBestRelay.CanTransmit())
 				{
+					#if BENCH
+					startCircularVesselTicks = performanceTimer.ElapsedTicks;
+					#endif
+
 					needle = potentialBestRelay;
-					bool isCircular = false;
-
-					int iterCount = 0;
+					isCircular = false;
+
+					iterCount = 0;
 					while (needle != null)
 					{
 						iterCount++;
@@ -439,15 +471,28 @@
 							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;
 
-			double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
+			kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
 			kerbinSqrDistance *= kerbinSqrDistance;
-
-			double kerbinSqrQuotient;
 
 			if (ARConfiguration.UseAdditiveRanges)
 			{
@@ -471,12 +516,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.
@@ -563,6 +615,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.
@@ -687,24 +743,24 @@
 			{
 				if (this.KerbinDirect)
 				{
-					this.NominalLinkDistance = Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange);
-					this.MaximumLinkDistance = Math.Sqrt(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange);
+					this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange;
+					this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange;
 				}
 				else
 				{
-					this.NominalLinkDistance = Math.Sqrt(this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance);
-					this.MaximumLinkDistance = Math.Sqrt(this.maxTransmitDistance * this.targetRelay.maxTransmitDistance);
+					this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance;
+					this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance;
 				}
 			}
 			else
 			{
-				this.NominalLinkDistance = this.nominalTransmitDistance;
-				this.MaximumLinkDistance = this.maxTransmitDistance;
+				this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
+				this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
 			}
 
 			if (this.canTransmit)
 			{
-				if (this.transmitDistance < this.NominalLinkDistance)
+				if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance)
 				{
 					this.LinkStatus = ConnectionStatus.Optimal;
 				}
@@ -717,6 +773,10 @@
 			{
 				this.LinkStatus = ConnectionStatus.None;
 			}
+
+			#if BENCH
+			statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks;
+			#endif
 
 			log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());
 			
@@ -732,9 +792,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>
@@ -758,7 +878,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
@@ -64,7 +64,7 @@
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 3500000000
+		nominalRange = 3150000000
 		simpleRange = 18000000000
 		maxPowerFactor = 4
 		maxDataFactor = 8
@@ -87,7 +87,7 @@
 	{
 		@name = ModuleLimitedDataTransmitter
 		@packetResourceCost /= 1.414213
-		nominalRange = 10000000000
+		nominalRange = 9250000000
 		simpleRange = 56250000000
 		maxPowerFactor = 16
 		maxDataFactor = 2
@@ -108,7 +108,7 @@
 {
 	range = 800000
 	range = 200000000000
-	range = 2250000000000
+	range = 2000000000000
 }
 
 EVA_MODULE
@@ -116,6 +116,7 @@
 	name = ModuleLimitedDataTransmitter
 
 	nominalRange = 1389
+	simpleRange = 5000
 	maxPowerFactor = 1
 	maxDataFactor = 1
 

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -55,18 +55,18 @@
 		/// <summary>
 		/// The link distance, in meters, at which this relay behaves nominally.
 		/// </summary>
-		double NominalLinkDistance { get; }
+		double NominalLinkSqrDistance { get; }
 
 
 		/// <summary>
 		/// The link distance, in meters, beyond which this relay cannot operate.
 		/// </summary>
-		double MaximumLinkDistance { get; }
+		double MaximumLinkSqrDistance { get; }
 
 		/// <summary>
 		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
 		/// </summary>
-		double transmitDistance { get; }
+		double CurrentLinkSqrDistance { get; }
 
 		/// <summary>
 		/// Gets the link status.

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -46,7 +46,8 @@
 	/// 
 	/// <para>where D is the total transmission distance, P is the transmission power, and R is the data rate.</para>
 	/// </summary>
-	public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
+	public class ModuleLimitedDataTransmitter
+		: ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay, IModuleInfo
 	{
 		// Stores the packetResourceCost as defined in the .cfg file.
 		private float _basepacketResourceCost;
@@ -59,6 +60,9 @@
 
 		// Sometimes we will need to communicate errors; this is how we do it.
 		private ScreenMessage ErrorMsg;
+
+		// Used in module info panes for part tooltips in the editor and R&D
+		private GUIContent moduleInfoContent;
 
 		/// <summary>
 		/// When additive ranges are enabled, the distance from Kerbin at which the antenna will perform exactly as
@@ -173,6 +177,7 @@
 				else
 				{
 					this.LogError("Vessel and/or part reference are null, returning null vessel.");
+					this.LogError(new System.Diagnostics.StackTrace().ToString());
 					return null;
 				}
 			}
@@ -214,13 +219,13 @@
 		/// <summary>
 		/// Gets or sets the nominal link distance, in meters.
 		/// </summary>
-		public double NominalLinkDistance
+		public double NominalLinkSqrDistance
 		{
 			get
 			{
 				if (this.relay != null)
 				{
-					return this.relay.NominalLinkDistance;
+					return this.relay.NominalLinkSqrDistance;
 				}
 
 				return 0d;
@@ -230,13 +235,13 @@
 		/// <summary>
 		/// Gets or sets the maximum link distance, in meters.
 		/// </summary>
-		public double MaximumLinkDistance
+		public double MaximumLinkSqrDistance
 		{
 			get
 			{
 				if (this.relay != null)
 				{
-					return this.relay.MaximumLinkDistance;
+					return this.relay.MaximumLinkSqrDistance;
 				}
 
 				return 0d;
@@ -246,7 +251,7 @@
 		/// <summary>
 		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
 		/// </summary>
-		public double transmitDistance
+		public double CurrentLinkSqrDistance
 		{
 			get
 			{
@@ -255,7 +260,7 @@
 					return double.PositiveInfinity;
 				}
 
-				return this.relay.transmitDistance;
+				return this.relay.CurrentLinkSqrDistance;
 			}
 		}
 
@@ -416,6 +421,7 @@
 
 			this._basepacketSize = base.packetSize;
 			this._basepacketResourceCost = base.packetResourceCost;
+			this.moduleInfoContent = new GUIContent();
 
 			Tools.PostDebugMessage(string.Format(
 				"{0} loaded:\n" +
@@ -441,10 +447,10 @@
 		{
 			base.OnStart (state);
 
+			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
+
 			if (state >= StartState.PreLaunch)
 			{
-				this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
-
 				this.relay = new AntennaRelay(this);
 				this.relay.nominalTransmitDistance = this.nominalTransmitDistance;
 				this.relay.maxTransmitDistance = this.maxTransmitDistance;
@@ -473,6 +479,54 @@
 		}
 
 		/// <summary>
+		/// Gets the human-friendly module title.
+		/// </summary>
+		public string GetModuleTitle()
+		{
+			return "Comms Transceiver";
+		}
+
+		/// <summary>
+		/// Returns drawTooltipWidget as a callback for part tooltips.
+		/// </summary>
+		public Callback<Rect> GetDrawModulePanelCallback()
+		{
+			return this.drawTooltipWidget;
+		}
+
+		// Called by Squad's part tooltip system when drawing tooltips.
+		// HACK: Currently hacks around Squad's extraneous layout box, see KSPModders issue #5118
+		private void drawTooltipWidget(Rect rect)
+		{
+			this.moduleInfoContent.text = this.GetInfo();
+
+			GUIStyle style0 = PartListTooltips.fetch.tooltipSkin.customStyles[0];
+			GUIStyle style1 = PartListTooltips.fetch.tooltipSkin.customStyles[1];
+
+			float width = rect.width;
+			float orgHeight = rect.height;
+			float height = style0.CalcHeight(this.moduleInfoContent, width);
+
+			rect.height = height;
+
+			GUI.Box(rect, this.moduleInfoContent, style0);
+			GUI.Label(rect, this.GetModuleTitle(), style1);
+
+			GUILayout.Space(height - orgHeight
+				- style0.padding.bottom - style0.padding.top
+				- 2f * (style0.margin.bottom + style0.margin.top)
+			);
+		}
+
+		/// <summary>
+		/// Returns an empty string, because we don't really have a "primary field" like some modules do.
+		/// </summary>
+		public string GetPrimaryField()
+		{
+			return string.Empty;
+		}
+
+		/// <summary>
 		/// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
 		/// </summary>
 		public override string GetInfo()
@@ -481,8 +535,21 @@
 			string text;
 
 			sb.Append(base.GetInfo());
-			sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
-			sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m\n", this.maxTransmitDistance);
+
+			if (ARConfiguration.UseAdditiveRanges)
+			{
+				sb.AppendFormat(Tools.SIFormatter, "Nominal Range to Kerbin: {0:S3}m\n",
+					Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange)
+				);
+				sb.AppendFormat(Tools.SIFormatter, "Maximum Range to Kerbin: {0:S3}m",
+					Math.Sqrt(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange)
+				);
+			}
+			else
+			{
+				sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
+				sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m", this.maxTransmitDistance);
+			}
 
 			text = sb.ToString();
 
@@ -654,7 +721,7 @@
 			}
 
 			Tools.PostDebugMessage (
-				"distance: " + this.transmitDistance
+				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
 			);
@@ -687,7 +754,7 @@
 			PreTransmit_SetPacketResourceCost ();
 
 			Tools.PostDebugMessage (
-				"distance: " + this.transmitDistance
+				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
 				);
@@ -711,13 +778,16 @@
 		{
 			if (this.actionUIUpdate)
 			{
-				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.MaximumLinkDistance);
-				this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.NominalLinkDistance);
+				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+					Math.Sqrt(this.MaximumLinkSqrDistance));
+				this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+					Math.Sqrt(this.NominalLinkSqrDistance));
 				
 				if (this.CanTransmit())
 				{
 					this.UIrelayStatus = this.LinkStatus.ToString();
-					this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.transmitDistance);
+					this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+						Math.Sqrt(this.CurrentLinkSqrDistance));
 					this.UIpacketSize = string.Format(Tools.SIFormatter, "{0:S3}MiT", this.DataRate);
 					this.UIpacketCost = string.Format(Tools.SIFormatter, "{0:S3}EC", this.DataResourceCost);
 				}
@@ -725,7 +795,8 @@
 				{
 					if (this.relay.firstOccludingBody == null)
 					{
-						this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.transmitDistance);
+						this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+							Math.Sqrt(this.CurrentLinkSqrDistance));
 						this.UIrelayStatus = "Out of range";
 					}
 					else
@@ -825,17 +896,15 @@
 		// transmission fails (see CanTransmit).
 		private void PreTransmit_SetPacketResourceCost()
 		{
-			if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.NominalLinkDistance)
+			if (ARConfiguration.FixedPowerCost || this.CurrentLinkSqrDistance <= this.NominalLinkSqrDistance)
 			{
 				base.packetResourceCost = this._basepacketResourceCost;
 			}
 			else
 			{
-				float rangeFactor = (float)(this.transmitDistance / this.NominalLinkDistance);
-				rangeFactor *= rangeFactor;
-
-				base.packetResourceCost = this._basepacketResourceCost
-					* rangeFactor;
+				float rangeFactor = (float)(this.CurrentLinkSqrDistance / this.NominalLinkSqrDistance);
+
+				base.packetResourceCost = this._basepacketResourceCost * rangeFactor;
 			}
 
 			base.packetResourceCost *= this.packetThrottle / 100f;
@@ -845,18 +914,18 @@
 		// distance.  packetSize maxes out at _basepacketSize * maxDataFactor.
 		private void PreTransmit_SetPacketSize()
 		{
-			if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.NominalLinkDistance)
+			if (!ARConfiguration.FixedPowerCost && this.CurrentLinkSqrDistance >= this.NominalLinkSqrDistance)
 			{
 				base.packetSize = this._basepacketSize;
 			}
 			else
 			{
-				float rangeFactor = (float)(this.NominalLinkDistance / this.transmitDistance);
-				rangeFactor *= rangeFactor;
+				float rangeFactor = (float)(this.NominalLinkSqrDistance / this.CurrentLinkSqrDistance);
 
 				base.packetSize = Mathf.Min(
 					this._basepacketSize * rangeFactor,
-					this._basepacketSize * this.maxDataFactor);
+					this._basepacketSize * this.maxDataFactor
+				);
 			}
 
 			base.packetSize *= this.packetThrottle / 100f;

--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -39,7 +39,7 @@
 // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("1.9.*")]
+[assembly: AssemblyVersion("1.9.1.*")]
 // The following attributes are used to specify the signing key for the assembly,
 // if desired. See the Mono documentation for more information about signing.
 //[assembly: AssemblyDelaySign(false)]

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -116,7 +116,7 @@
 		/// </summary>
 		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
 		/// <param name="vessel">A <see cref="Vessel"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relay, Vessel vessel)
+		public static double SqrDistanceTo(this AntennaRelay relay, Vessel vessel)
 		{
 			return relay.vessel.sqrDistanceTo(vessel);
 		}
@@ -126,9 +126,11 @@
 		/// </summary>
 		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
 		/// <param name="body">A <see cref="CelestialBody"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relay, CelestialBody body)
-		{
-			return relay.vessel.sqrDistanceTo(body);
+		public static double SqrDistanceTo(this AntennaRelay relay, CelestialBody body)
+		{
+			double dist = (relay.vessel.GetWorldPos3D() - body.position).magnitude - body.Radius;
+
+			return dist * dist;
 		}
 
 		/// <summary>
@@ -136,7 +138,7 @@
 		/// </summary>
 		/// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
 		/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
+		public static double SqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
 		{
 			return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
 		}