A bit of cleanup around drawTooltipWidget
A bit of cleanup around drawTooltipWidget

--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -10,40 +10,124 @@
 
 namespace AntennaRange
 {
+	/// <summary>
+	/// A <see cref="UnityEngine.MonoBehaviour"/> responsible for managing configuration items for AntennaRange.
+	/// </summary>
 	[KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
 	public class ARConfiguration : MonoBehaviour
 	{
+		private const string WINDOW_POS_KEY = "configWindowPos";
+		private const string REQUIRE_LOS_KEY = "requireLineOfSight";
+		private const string GRACE_RATIO_KEY = "graceRatio";
+		private const string REQUIRE_PROBE_CONNECTION_KEY = "requireConnectionForControl";
+		private const string FIXED_POWER_KEY = "fixedPowerCost";
+		private const string PRETTY_LINES_KEY = "drawPrettyLines";
+		private const string UPDATE_DELAY_KEY = "updateDelay";
+		private const string USE_ADDITIVE_KEY = "useAdditiveRanges";
+
+		private const string TRACKING_STATION_RANGES_KEY = "TRACKING_STATION_RANGES";
+		private const string RANGE_KEY = "range";
+
+		/// <summary>
+		/// Indicates whether connections require line of sight.
+		/// </summary>
 		public static bool RequireLineOfSight
 		{
 			get;
 			private set;
 		}
 
+		/// <summary>
+		/// A "fudge factor" ratio that pretends planets and moons are slightly smaller than reality to make
+		/// building communication constellations easier.
+		/// </summary>
 		public static double RadiusRatio
 		{
 			get;
 			private set;
 		}
 
+		/// <summary>
+		/// Indicates whether unmanned vessels require a connection for control.
+		/// </summary>
 		public static bool RequireConnectionForControl
 		{
 			get;
 			private set;
 		}
 
+		/// <summary>
+		/// If true, relays will fix their power cost when above nominal range, decreasing data rate instead.
+		/// </summary>
 		public static bool FixedPowerCost
 		{
 			get;
 			private set;
 		}
+
+		/// <summary>
+		/// Indicates whether this AntennaRange will draw pretty lines in map view.
+		/// </summary>
+		public static bool PrettyLines
+		{
+			get;
+			set;
+		}
+
+		/// <summary>
+		/// Gets the update delay.
+		/// </summary>
+		public static long UpdateDelay
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether AntennaRange will use additive ranges.
+		/// </summary>
+		public static bool UseAdditiveRanges
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// Gets Kerbin's relay range based on the current tracking station level.
+		/// </summary>
+		public static double KerbinRelayRange
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// Gets Kerbin's nominal relay range based on the current tracking station level.
+		/// </summary>
+		public static double KerbinNominalRange
+		{
+			get
+			{
+				return KerbinRelayRange / 2.8284271247461901d;
+			}
+		}
+
+#pragma warning disable 1591
 
 		private bool showConfigWindow;
 		private Rect configWindowPos;
 
+		private string updateDelayStr;
+		private long updateDelay;
+
 		private IButton toolbarButton;
 		private ApplicationLauncherButton appLauncherButton;
 
+		private double[] trackingStationRanges;
+
 		private System.Version runningVersion;
+
+		private bool runOnce;
 
 		private KSP.IO.PluginConfiguration _config;
 		private KSP.IO.PluginConfiguration config
@@ -69,23 +153,82 @@
 			this.configWindowPos = new Rect(Screen.width / 4, Screen.height / 2, 180, 15);
 
 
-			this.configWindowPos = this.LoadConfigValue("configWindowPos", this.configWindowPos);
-
-			ARConfiguration.RequireLineOfSight = this.LoadConfigValue("requireLineOfSight", false);
-
-			ARConfiguration.RadiusRatio = (1 - this.LoadConfigValue("graceRatio", .05d));
+			this.configWindowPos = this.LoadConfigValue(WINDOW_POS_KEY, this.configWindowPos);
+
+			ARConfiguration.RequireLineOfSight = this.LoadConfigValue(REQUIRE_LOS_KEY, false);
+
+			ARConfiguration.RadiusRatio = (1 - this.LoadConfigValue(GRACE_RATIO_KEY, .05d));
 			ARConfiguration.RadiusRatio *= ARConfiguration.RadiusRatio;
 
 			ARConfiguration.RequireConnectionForControl =
-				this.LoadConfigValue("requireConnectionForControl", false);
-
-			ARConfiguration.FixedPowerCost = this.LoadConfigValue("fixedPowerCost", false);
+				this.LoadConfigValue(REQUIRE_PROBE_CONNECTION_KEY, false);
+
+			ARConfiguration.FixedPowerCost = this.LoadConfigValue(FIXED_POWER_KEY, false);
+
+			ARConfiguration.PrettyLines = this.LoadConfigValue(PRETTY_LINES_KEY, true);
+
+			ARConfiguration.UpdateDelay = this.LoadConfigValue(UPDATE_DELAY_KEY, 16L);
+
+			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);
+			GameEvents.OnKSCFacilityUpgraded.Add(this.onFacilityUpgraded);
 
 			Debug.Log(string.Format("{0} v{1} - ARConfiguration loaded!", this.GetType().Name, this.runningVersion));
 
+			ConfigNode[] tsRangeNodes = GameDatabase.Instance.GetConfigNodes(TRACKING_STATION_RANGES_KEY);
+
+			if (tsRangeNodes.Length > 0)
+			{
+				string[] rangeValues = tsRangeNodes[0].GetValues(RANGE_KEY);
+
+				this.trackingStationRanges = new double[rangeValues.Length];
+
+				for (int idx = 0; idx < rangeValues.Length; idx++)
+				{
+					if (!double.TryParse(rangeValues[idx], out this.trackingStationRanges[idx]))
+					{
+						this.LogError("Could not parse value '{0}' to double; Tracking Station ranges may not work!");
+						this.trackingStationRanges[idx] = 0d;
+					}
+				}
+
+				this.Log("Loaded Tracking Station ranges from config: [{0}]", this.trackingStationRanges.SPrint());
+			}
+			else
+			{
+				this.trackingStationRanges = new double[]
+				{
+					51696576d,
+					37152180000d,
+					224770770000d
+				};
+
+				this.LogWarning("Failed to load Tracking Station ranges from config, using hard-coded values: [{0}]",
+					this.trackingStationRanges.SPrint());
+			}
+
+			this.runOnce = true;
+
 			Tools.PostDebugMessage(this, "Awake.");
+		}
+
+		public void Update()
+		{
+			if (
+				this.runOnce &&
+				(ScenarioUpgradeableFacilities.Instance != null || HighLogic.CurrentGame.Mode != Game.Modes.CAREER)
+			)
+			{
+				this.runOnce = false;
+
+				this.SetKerbinRelayRange();
+			}
 		}
 
 		public void OnGUI()
@@ -138,7 +281,7 @@
 				if (configPos != this.configWindowPos)
 				{
 					this.configWindowPos = configPos;
-					this.SaveConfigValue("configWindowPos", this.configWindowPos);
+					this.SaveConfigValue(WINDOW_POS_KEY, this.configWindowPos);
 				}
 			}
 		}
@@ -153,7 +296,7 @@
 			if (requireLineOfSight != ARConfiguration.RequireLineOfSight)
 			{
 				ARConfiguration.RequireLineOfSight = requireLineOfSight;
-				this.SaveConfigValue("requireLineOfSight", requireLineOfSight);
+				this.SaveConfigValue(REQUIRE_LOS_KEY, requireLineOfSight);
 			}
 
 			GUILayout.EndHorizontal();
@@ -168,7 +311,7 @@
 			if (requireConnectionForControl != ARConfiguration.RequireConnectionForControl)
 			{
 				ARConfiguration.RequireConnectionForControl = requireConnectionForControl;
-				this.SaveConfigValue("requireConnectionForControl", requireConnectionForControl);
+				this.SaveConfigValue(REQUIRE_PROBE_CONNECTION_KEY, requireConnectionForControl);
 			}
 
 			GUILayout.EndHorizontal();
@@ -179,10 +322,48 @@
 			if (fixedPowerCost != ARConfiguration.FixedPowerCost)
 			{
 				ARConfiguration.FixedPowerCost = fixedPowerCost;
-				this.SaveConfigValue("fixedPowerCost", fixedPowerCost);
-			}
-
-			GUILayout.EndHorizontal();
+				this.SaveConfigValue(FIXED_POWER_KEY, fixedPowerCost);
+			}
+
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal();
+
+			bool useAdditive = GUITools.Toggle(ARConfiguration.UseAdditiveRanges, "Use Additive Ranges");
+			if (useAdditive != ARConfiguration.UseAdditiveRanges)
+			{
+				ARConfiguration.UseAdditiveRanges = useAdditive;
+				this.SaveConfigValue(USE_ADDITIVE_KEY, useAdditive);
+			}
+
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal();
+
+			bool prettyLines = GUITools.Toggle(ARConfiguration.PrettyLines, "Draw Pretty Lines");
+			if (prettyLines != ARConfiguration.PrettyLines)
+			{
+				ARConfiguration.PrettyLines = prettyLines;
+				this.SaveConfigValue(PRETTY_LINES_KEY, prettyLines);
+			}
+
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal();
+
+			GUILayout.Label("Update Delay", GUILayout.ExpandWidth(false));
+
+			this.updateDelayStr = GUILayout.TextField(this.updateDelayStr, 4, GUILayout.Width(40f));
+
+			GUILayout.Label("ms", GUILayout.ExpandWidth(false));
+
+			GUILayout.EndHorizontal();
+
+			if (this.updateDelayStr.Length > 1 && long.TryParse(this.updateDelayStr, out this.updateDelay))
+			{
+				ARConfiguration.UpdateDelay = Math.Min(Math.Max(this.updateDelay, 16), 2500);
+				this.updateDelayStr = ARConfiguration.UpdateDelay.ToString();
+			}
 
 			if (requireLineOfSight)
 			{
@@ -203,7 +384,7 @@
 				if (newRatio != graceRatio)
 				{
 					ARConfiguration.RadiusRatio = (1d - newRatio) * (1d - newRatio);
-					this.SaveConfigValue("graceRatio", newRatio);
+					this.SaveConfigValue(GRACE_RATIO_KEY, newRatio);
 				}
 
 				GUILayout.EndHorizontal();
@@ -217,19 +398,22 @@
 		public void OnDestroy()
 		{
 			GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested);
+			GameEvents.OnKSCFacilityUpgraded.Remove(this.onFacilityUpgraded);
 
 			if (this.toolbarButton != null)
 			{
 				this.toolbarButton.Destroy();
+				this.toolbarButton = null;
 			}
 
 			if (this.appLauncherButton != null)
 			{
 				ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton);
-			}
-		}
-
-		protected void onSceneChangeRequested(GameScenes scene)
+				this.appLauncherButton = null;
+			}
+		}
+
+		private void onSceneChangeRequested(GameScenes scene)
 		{
 			if (scene != GameScenes.SPACECENTER)
 			{
@@ -238,9 +422,46 @@
 			}
 		}
 
+		private void onFacilityUpgraded(Upgradeables.UpgradeableFacility fac, int lvl)
+		{
+			if (fac.id == "SpaceCenter/TrackingStation")
+			{
+				this.Log("Caught onFacilityUpgraded for {0} at level {1}", fac.id, lvl);
+				this.SetKerbinRelayRange();
+			}
+		}
+
+		private void SetKerbinRelayRange()
+		{
+			int tsLevel;
+
+			if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
+			{
+				tsLevel = ScenarioUpgradeableFacilities.protoUpgradeables["SpaceCenter/TrackingStation"]
+					.facilityRefs[0].FacilityLevel;
+			
+
+			}
+			else
+			{
+				tsLevel = this.trackingStationRanges.Length - 1;
+			}
+
+			if (tsLevel < this.trackingStationRanges.Length && tsLevel >= 0)
+			{
+				KerbinRelayRange = this.trackingStationRanges[tsLevel];
+				this.Log("Setting Kerbin's range to {0}", KerbinRelayRange);
+			}
+			else
+			{
+				this.LogError("Could not set Kerbin's range with invalid Tracking Station level {0}", tsLevel);
+			}
+		}
+
 		private void toggleConfigWindow()
 		{
 			this.showConfigWindow = !this.showConfigWindow;
+			this.updateDelayStr = ARConfiguration.UpdateDelay.ToString();
 		}
 
 		private T LoadConfigValue<T>(string key, T defaultValue)

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -2,7 +2,7 @@
 //
 // ARFlightController.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -26,6 +26,8 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#pragma warning disable 1591
+
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -37,33 +39,41 @@
 	[KSPAddon(KSPAddon.Startup.Flight, false)]
 	public class ARFlightController : MonoBehaviour
 	{
+		#region Static
+		private static List<IAntennaRelay> usefulRelays;
+		public static IList<IAntennaRelay> UsefulRelays;
+		#endregion
+
 		#region Fields
-		protected Dictionary<ConnectionStatus, string> connectionTextures;
-		protected Dictionary<ConnectionStatus, Texture> appLauncherTextures;
-
-		protected ARMapRenderer mapRenderer;
-
-		protected IButton toolbarButton;
-
-		protected ApplicationLauncherButton appLauncherButton;
+		private Dictionary<ConnectionStatus, string> toolbarTextures;
+		private Dictionary<ConnectionStatus, Texture> appLauncherTextures;
+
+		private ARMapRenderer mapRenderer;
+
+		private IButton toolbarButton;
+
+		private ApplicationLauncherButton appLauncherButton;
+		private Tools.DebugLogger log;
+
+		private System.Diagnostics.Stopwatch updateTimer;
 		#endregion
 
 		#region Properties
 		public ConnectionStatus currentConnectionStatus
 		{
 			get;
-			protected set;
-		}
-
-		protected string currentConnectionTexture
-		{
-			get
-			{
-				return this.connectionTextures[this.currentConnectionStatus];
-			}
-		}
-
-		protected Texture currentAppLauncherTexture
+			private set;
+		}
+
+		private string currentConnectionTexture
+		{
+			get
+			{
+				return this.toolbarTextures[this.currentConnectionStatus];
+			}
+		}
+
+		private Texture currentAppLauncherTexture
 		{
 			get
 			{
@@ -87,7 +97,7 @@
 		public string lockID
 		{
 			get;
-			protected set;
+			private set;
 		}
 
 		public ControlTypes lockSet
@@ -113,15 +123,19 @@
 		#endregion
 
 		#region MonoBehaviour LifeCycle
-		protected void Awake()
+		private void Awake()
 		{
 			this.lockID = "ARConnectionRequired";
 
-			this.connectionTextures = new Dictionary<ConnectionStatus, string>();
-
-			this.connectionTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection";
-			this.connectionTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal";
-			this.connectionTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon";
+			this.log = Tools.DebugLogger.New(this);
+
+			this.updateTimer = new System.Diagnostics.Stopwatch();
+
+			this.toolbarTextures = new Dictionary<ConnectionStatus, string>();
+
+			this.toolbarTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection";
+			this.toolbarTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal";
+			this.toolbarTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon";
 
 			this.appLauncherTextures = new Dictionary<ConnectionStatus, Texture>();
 
@@ -136,32 +150,22 @@
 			{
 				this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus");
 
-				this.toolbarButton.TexturePath = this.connectionTextures[ConnectionStatus.None];
+				this.toolbarButton.TexturePath = this.toolbarTextures[ConnectionStatus.None];
 				this.toolbarButton.Text = "AntennaRange";
 				this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT);
-				this.toolbarButton.Enabled = false;
+				this.toolbarButton.OnClick += (e) => (this.buttonToggle());
 			}
 
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
 			GameEvents.onVesselChange.Add(this.onVesselChange);
-		}
-
-		protected void Start()
-		{
-			this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
-		}
-
-		protected void FixedUpdate()
-		{
-			if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
-			{
-				this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication(
-					ApplicationLauncher.AppScenes.FLIGHT,
-					this.appLauncherTextures[ConnectionStatus.None]
-				);
-			}
-
-			Tools.DebugLogger log = Tools.DebugLogger.New(this);
+
+			usefulRelays = new List<IAntennaRelay>();
+			UsefulRelays = usefulRelays.AsReadOnly();
+		}
+
+		private void FixedUpdate()
+		{
+			this.log.Clear();
 
 			VesselCommand availableCommand;
 
@@ -209,98 +213,115 @@
 				// ...unlock the controls.
 				InputLockManager.RemoveControlLock(this.lockID);
 			}
-				
-			if (
-				(this.toolbarButton != null || this.appLauncherButton != null) &&
-				HighLogic.LoadedSceneIsFlight &&
-				FlightGlobals.ActiveVessel != null
-			)
-			{
-				log.Append("Checking vessel relay status.\n");
-
-				List<ModuleLimitedDataTransmitter> relays =
-					FlightGlobals.ActiveVessel.getModulesOfType<ModuleLimitedDataTransmitter>();
-
-				log.AppendFormat("\t...found {0} relays\n", relays.Count);
-
-				bool vesselCanTransmit = false;
-				bool vesselHasOptimalRelay = false;
-
-				foreach (ModuleLimitedDataTransmitter relay in relays)
-				{
-					log.AppendFormat("\tvesselCanTransmit: {0}, vesselHasOptimalRelay: {1}\n",
-						vesselCanTransmit, vesselHasOptimalRelay);
-
-					log.AppendFormat("\tChecking relay {0}\n" +
-						"\t\tCanTransmit: {1}, transmitDistance: {2}, nominalRange: {3}\n",
-						relay,
-						relay.CanTransmit(),
-						relay.transmitDistance,
-						relay.nominalRange
-					);
-
-					bool relayCanTransmit = relay.CanTransmit();
-
-					if (!vesselCanTransmit && relayCanTransmit)
+
+			log.Print();
+		}
+
+		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;
+			}
+
+			if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
+			{
+				this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication(
+					this.buttonToggle, this.buttonToggle,
+					ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW,
+					this.appLauncherTextures[ConnectionStatus.None]
+				);
+			}
+
+			if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > ARConfiguration.UpdateDelay)
+			{
+				this.updateTimer.Restart();
+			}
+			else
+			{
+				return;
+			}
+
+			this.log.Clear();
+
+			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++)
+				{
+					vessel = FlightGlobals.Vessels[vIdx];
+
+					if (vessel == null || vessel == FlightGlobals.ActiveVessel)
 					{
-						vesselCanTransmit = true;
+						continue;
 					}
 
-					if (!vesselHasOptimalRelay &&
-						relayCanTransmit &&
-						relay.transmitDistance <= (double)relay.nominalRange)
+					log.AppendFormat("Fetching best relay for vessel {0}", vessel);
+
+					relay = vessel.GetBestRelay();
+
+					if (relay != null)
 					{
-						vesselHasOptimalRelay = true;
+						log.AppendFormat("Finding nearest relay for best relay {0}", relay);
+
+						usefulRelays.Add(relay);
+						relay.FindNearestRelay();
 					}
-
-					if (vesselCanTransmit && vesselHasOptimalRelay)
+				}
+
+				activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];
+				for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
+				{
+					relay = activeVesselRelays[rIdx];
+
+					relay.FindNearestRelay();
+				}
+
+				usefulRelays.Add(RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel));
+
+				if (this.toolbarButton != null || this.appLauncherButton != null)
+				{
+					log.Append("Checking vessel relay status.\n");
+
+					this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();
+
+					log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}",
+						this.currentConnectionStatus, this.currentConnectionTexture);
+
+					if (this.toolbarButton != null)
 					{
-						break;
+						this.toolbarButton.TexturePath = this.currentConnectionTexture;
+
+						if (this.currentConnectionStatus == ConnectionStatus.None)
+						{
+							if (!this.toolbarButton.Important) this.toolbarButton.Important = true;
+						}
+						else
+						{
+							if (this.toolbarButton.Important) this.toolbarButton.Important = false;
+						}
 					}
-				}
-
-				log.AppendFormat("Done checking.  vesselCanTransmit: {0}, vesselHasOptimalRelay: {1}\n",
-					vesselCanTransmit, vesselHasOptimalRelay);
-
-				if (vesselHasOptimalRelay)
-				{
-					this.currentConnectionStatus = ConnectionStatus.Optimal;
-				}
-				else if (vesselCanTransmit)
-				{
-					this.currentConnectionStatus = ConnectionStatus.Suboptimal;
-				}
-				else
-				{
-					this.currentConnectionStatus = ConnectionStatus.None;
-				}
-
-				log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}",
-					this.currentConnectionStatus, this.currentConnectionTexture);
-
-				if (this.toolbarButton != null)
-				{
-					this.toolbarButton.TexturePath = this.currentConnectionTexture;
-
-					if (this.currentConnectionStatus == ConnectionStatus.None)
+					if (this.appLauncherButton != null)
 					{
-						this.toolbarButton.Important = true;
+						this.appLauncherButton.SetTexture(this.currentAppLauncherTexture);
 					}
-					else
-					{
-						this.toolbarButton.Important = false;
-					}
-				}
-				if (this.appLauncherButton != null)
-				{
-					this.appLauncherButton.SetTexture(this.currentAppLauncherTexture);
 				}
 			}
 
 			log.Print();
 		}
 
-		protected void OnDestroy()
+		private void OnDestroy()
 		{
 			InputLockManager.RemoveControlLock(this.lockID);
 
@@ -327,25 +348,26 @@
 		}
 		#endregion
 
+		private void buttonToggle()
+		{
+			if (MapView.MapIsEnabled)
+			{
+				ARConfiguration.PrettyLines = !ARConfiguration.PrettyLines;
+			}
+		}
+
 		#region Event Handlers
-		protected void onSceneChangeRequested(GameScenes scene)
+		private void onSceneChangeRequested(GameScenes scene)
 		{
 			print("ARFlightController: Requesting Destruction.");
 			MonoBehaviour.Destroy(this);
 		}
 
-		protected void onVesselChange(Vessel vessel)
+		private void onVesselChange(Vessel vessel)
 		{
 			InputLockManager.RemoveControlLock(this.lockID);
 		}
 		#endregion
-
-		public enum ConnectionStatus
-		{
-			None,
-			Suboptimal,
-			Optimal
-		}
 	}
 }
 

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -2,7 +2,7 @@
 //
 // ARMapRenderer.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -26,6 +26,8 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#pragma warning disable 1591
+
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -36,9 +38,27 @@
 {
 	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;
-		private Dictionary<Guid, bool> vesselFrameCache;
+
+		// Debug Stuff
+		#pragma warning disable 649
+		private System.Diagnostics.Stopwatch timer;
+		private Tools.DebugLogger log;
+		private long relayStart;
+		private long start;
+		#pragma warning restore 649
+
+		#pragma warning disable 414
+		private Color thisColor;
+		#pragma warning restore 414
 		#endregion
 
 		#region Properties
@@ -46,26 +66,25 @@
 		{
 			get
 			{
-				if (this.vesselLineRenderers == null)
-				{
-					this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
-				}
-
-				if (!this.vesselLineRenderers.ContainsKey(idx))
+				LineRenderer lr;
+
+				if (!this.vesselLineRenderers.TryGetValue(idx, out lr))
 				{
 					GameObject obj = new GameObject();
 					obj.layer = 31;
 
-					LineRenderer lr = obj.AddComponent<LineRenderer>();
-
-					lr.SetColors(Color.green, Color.green);
-					lr.material = new Material(Shader.Find("Particles/Additive"));
-					lr.SetVertexCount(2);
+					lr = obj.AddComponent<LineRenderer>();
+
+					// lr.SetColors(Color.green, Color.green);
+					lr.material = MapView.OrbitLinesMaterial;
+					// lr.SetVertexCount(2);
 
 					this.vesselLineRenderers[idx] = lr;
-				}
-
-				return this.vesselLineRenderers[idx];
+
+					return lr;
+				}
+
+				return lr;
 			}
 		}
 		#endregion
@@ -73,23 +92,36 @@
 		#region MonoBehaviour Lifecycle
 		private void Awake()
 		{
-			this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
-			this.vesselFrameCache = new Dictionary<Guid, bool>();
+			if (ARConfiguration.PrettyLines)
+			{
+				this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
+			}
+
+			#if DEBUG || BENCH
+			this.timer = new System.Diagnostics.Stopwatch();
+			#endif
+			#if DEBUG
+			this.log = Tools.DebugLogger.New(this);
+			#endif
 		}
 
 		private void OnPreCull()
 		{
-			if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled)
-			{
-				this.Cleanup();
+			if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines)
+			{
+				this.Cleanup(!HighLogic.LoadedSceneIsFlight);
 
 				return;
 			}
 
-			Tools.DebugLogger log = Tools.DebugLogger.New(this);
+			#if DEBUG || BENCH
+			timer.Restart();
+			#endif
 
 			try
 			{
+				log.Clear();
+
 				log.AppendFormat("OnPreCull.\n");
 
 				log.AppendFormat("\tMapView: Draw3DLines: {0}\n" +
@@ -100,185 +132,197 @@
 					MapView.MapCamera.Distance
 				);
 
-				this.vesselFrameCache.Clear();
-
-				log.AppendLine("vesselFrameCache cleared.");
-
-				if (FlightGlobals.ready && FlightGlobals.Vessels != null)
-				{
-					log.AppendLine("FlightGlobals ready and Vessels list not null.");
-
-					foreach (Vessel vessel in FlightGlobals.Vessels)
+				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)
 					{
-						if (vessel == null)
-						{
-							log.AppendFormat("Skipping vessel {0} altogether because it is null.\n");
-							continue;
-						}
-
-						log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName);
-
-						switch (vessel.vesselType)
-						{
-							case VesselType.Debris:
-							case VesselType.EVA:
-							case VesselType.Unknown:
-							case VesselType.SpaceObject:
-								log.AppendFormat("\tDiscarded because vessel is of invalid type {0}\n",
-									vessel.vesselType);
-								continue;
-						}
-
-						log.Append("\tChecking connection status...\n");
-
-						/*if (vessel.HasConnectedRelay())
-						{
-							log.AppendLine("\tHas a connection, checking for the best relay to use for the line.");*/
-
-							IAntennaRelay vesselRelay = null;
-							float bestScore = float.PositiveInfinity;
-							float relayScore = float.NaN;
-
-							foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values)
-							{
-								relayScore = (float)relay.transmitDistance / relay.maxTransmitDistance;
-
-								if (relayScore < bestScore)
-								{
-									bestScore = relayScore;
-									vesselRelay = relay as IAntennaRelay;
-								}
-							}
-
-							if (vesselRelay != null)
-							{
-								log.AppendFormat("\t...picked relay {0} with a score of {1}", 
-									vesselRelay, relayScore
-								);
-
-								this.SetRelayVertices(vesselRelay);
-							}
-						/*}
-						else if (this.vesselLineRenderers.ContainsKey(vessel.id))
-						{
-							log.AppendLine("\tDisabling line because vessel has no connection.");
-							this[vessel.id].enabled = false;
-						}*/
+						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(false);
+			}
+			#if DEBUG
 			finally
 			{
+				log.AppendFormat("\n\tOnPreCull finished in {0}ms\n", timer.ElapsedMilliseconds);
+
 				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();
-
-			print("ARMapRenderer: Destroyed.");
+			this.Cleanup(true);
+
+			this.Log("Destroyed");
 		}
 		#endregion
 
+		#region Utility
 		private void SetRelayVertices(IAntennaRelay relay)
 		{
-			do
-			{
-				if (this.vesselFrameCache.ContainsKey(relay.vessel.id))
-				{
-					break;
-				}
-
-				LineRenderer renderer = this[relay.vessel.id];
-
-				Vector3d start;
-				Vector3d end;
-
-				renderer.enabled = true;
-
-				if (!relay.CanTransmit())
-				{
-					renderer.SetColors(Color.red, Color.red);
+			log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay);
+
+			if (relay.vessel == null)
+			{
+				log.Append("\n\t\tvessel is null, bailing out");
+				return;
+			}
+
+			LineRenderer renderer = this[relay.vessel.id];
+			Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
+
+			float lineWidth;
+			float d = Screen.height / 2f + 0.01f;
+
+			if (MapView.Draw3DLines)
+			{
+				lineWidth = 0.005859375f * MapView.MapCamera.Distance;
+			}
+			else
+			{
+				lineWidth = 2f;
+
+				start = MapView.MapCamera.camera.WorldToScreenPoint(start);
+
+				start.z = start.z >= 0f ? d : -d;
+			}
+
+			renderer.SetWidth(lineWidth, lineWidth);
+
+			renderer.SetPosition(0, start);
+
+			int idx = 0;
+
+			#if DEBUG
+			relayStart = timer.ElapsedMilliseconds;
+			#endif
+
+			Vector3d nextPoint;
+
+			renderer.enabled = true;
+
+			if (!relay.CanTransmit())
+			{
+				thisColor = Color.red;
+			}
+			else
+			{
+				if (relay.LinkStatus == ConnectionStatus.Optimal)
+				{
+					thisColor = Color.green;
 				}
 				else
 				{
-					if (relay.transmitDistance < relay.nominalTransmitDistance)
+					thisColor = Color.yellow;
+				}
+			}
+
+			if (relay.KerbinDirect)
+			{
+				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+			}
+			else
+			{
+				if (relay.targetRelay == null || relay.targetRelay.vessel == null)
+				{
+					this.LogError(
+						"SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!",
+						relay
+					);
+
+					renderer.enabled = false;
+					return;
+				}
+
+				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
+			}
+
+			renderer.SetColors(thisColor, thisColor);
+
+			if (!MapView.Draw3DLines)
+			{
+				nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
+				nextPoint.z = nextPoint.z >= 0f ? d : -d;
+			}
+
+			idx++;
+
+			renderer.SetVertexCount(idx + 1);
+			renderer.SetPosition(idx, nextPoint);
+
+			log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart);
+		}
+
+		private void Cleanup(bool freeObjects)
+		{
+			if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
+			{
+				IEnumerator<LineRenderer> enumerator = this.vesselLineRenderers.Values.GetEnumerator();
+				LineRenderer lineRenderer;
+
+				while (enumerator.MoveNext())
+				{
+					lineRenderer = enumerator.Current;
+					lineRenderer.enabled = false;
+
+					if (freeObjects)
 					{
-						renderer.SetColors(Color.green, Color.green);
+						GameObject.Destroy(lineRenderer.gameObject);
 					}
-					else
-					{
-						renderer.SetColors(Color.yellow, Color.yellow);
-					}
-				}
-
-				start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
-
-				if (relay.KerbinDirect)
-				{
-					end = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
-				}
-				else
-				{
-					if (relay.firstOccludingBody != null && relay.bestOccludedRelay != null)
-					{
-						end = ScaledSpace.LocalToScaledSpace(relay.bestOccludedRelay.vessel.GetWorldPos3D());
-					}
-					else
-					{
-						end = ScaledSpace.LocalToScaledSpace(relay.nearestRelay.vessel.GetWorldPos3D());
-					}
-				}
-
-				float lineWidth;
-
-				if (MapView.Draw3DLines)
-				{
-					lineWidth = 0.004f * MapView.MapCamera.Distance;
-				}
-				else
-				{
-					lineWidth = 1f;
-
-					start = MapView.MapCamera.camera.WorldToScreenPoint(start);
-					end = MapView.MapCamera.camera.WorldToScreenPoint(end);
-
-					float d = Screen.height / 2f + 0.01f;
-					start.z = start.z >= 0f ? d : -d;
-					end.z = end.z >= 0f ? d : -d;
-				}
-
-				renderer.SetWidth(lineWidth, lineWidth);
-
-				renderer.SetPosition(0, start);
-				renderer.SetPosition(1, end);
-
-				this.vesselFrameCache[relay.vessel.id] = true;
-
-				relay = relay.nearestRelay;
-			}
-			while (relay != null);
-		}
-
-		public void Cleanup()
-		{
-			if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
-			{
-				foreach (LineRenderer lineRenderer in this.vesselLineRenderers.Values)
-				{
-					lineRenderer.enabled = false;
-					GameObject.Destroy(lineRenderer.gameObject);
-				}
-				this.vesselLineRenderers.Clear();
-			}
-
-			if (this.vesselFrameCache != null && this.vesselFrameCache.Count > 0)
-			{
-				this.vesselFrameCache.Clear();
-			}
-		}
+				}
+
+				if (freeObjects)
+				{
+					this.vesselLineRenderers.Clear();
+				}
+			}
+		}
+		#endregion
 	}
 }
 
-

--- a/AntennaRange.csproj
+++ b/AntennaRange.csproj
@@ -40,6 +40,7 @@
         <Command type="AfterBuild" command="xcopy /y ${TargetFile} ${ProjectDir}\GameData\AntennaRange\" />
       </CustomCommands>
     </CustomCommands>
+    <DocumentationFile>bin\Release\AntennaRange.xml</DocumentationFile>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -2,7 +2,7 @@
 //
 // AntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -28,19 +28,21 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using ToadicusTools;
-
-// @DONE TODO: Retool nearestRelay to always contain the nearest relay, even if out of range.
-// @DONE TODO: Retool CanTransmit to not rely on nearestRelay == null.
-// TODO: Track occluded vessels somehow.
 
 namespace AntennaRange
 {
+	/// <summary>
+	/// Relay code at the heart of AntennaRange
+	/// </summary>
 	public class AntennaRelay
 	{
 		// We don't have a Bard, so we'll hide Kerbin here.
 		private static CelestialBody _Kerbin;
+
+		/// <summary>
+		/// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
+		/// </summary>
 		public static CelestialBody Kerbin
 		{
 			get
@@ -54,11 +56,27 @@
 			}
 		}
 
-		private IAntennaRelay _nearestRelayCache;
+		#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 IAntennaRelay nearestRelay;
+		private IAntennaRelay bestOccludedRelay;
+
+		/// <summary>
+		/// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an
+		/// <see cref="AntennaRange.IAntennaRelay"/>
+		/// </summary>
 		protected IAntennaRelay moduleRef;
-
-		protected System.Diagnostics.Stopwatch searchTimer;
-		protected long millisecondsBetweenSearches;
 
 		/// <summary>
 		/// Gets the parent Vessel.
@@ -73,82 +91,82 @@
 		}
 
 		/// <summary>
-		/// Gets or sets the nearest relay.
-		/// </summary>
-		/// <value>The nearest relay</value>
-		public IAntennaRelay nearestRelay
-		{
-			get
-			{
-				if (!this.searchTimer.IsRunning ||
-					this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
-				{
-					this._nearestRelayCache = this.FindNearestRelay();
-
-					this.searchTimer.Restart();
-				}
-
-				return this._nearestRelayCache;
-			}
-			protected set
-			{
-				this._nearestRelayCache = value;
-			}
-		}
-
-		public IAntennaRelay bestOccludedRelay
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+		/// </summary>
+		public IAntennaRelay targetRelay
 		{
 			get;
 			protected set;
 		}
 
 		/// <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 NominalLinkSqrDistance
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets or sets the maximum link distance, in meters.
+		/// </summary>
+		public virtual double MaximumLinkSqrDistance
+		{
+			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>
-		public double transmitDistance
+		public double CurrentLinkSqrDistance
 		{
 			get
 			{
-				// If there is no available relay nearby...
-				// @DONE TODO: Remove nearestRelay == null
-				double kerbinDistance = this.DistanceTo(Kerbin);
-
-				if (this.nearestRelay != null)
-				{
-					double relayDistance = this.DistanceTo(this.nearestRelay);
-
-					// If our nearest relay is nearer than Kerbin, use its distance.
-					if (relayDistance < kerbinDistance)
-					{
-						this.KerbinDirect = false;
-
-						return relayDistance;
-					}
-				}
-
-				if (this.firstOccludingBody != null && this.bestOccludedRelay != null)
-				{
-					this.KerbinDirect = false;
-					return this.DistanceTo(this.bestOccludedRelay);
-				}
-
-				this.KerbinDirect = true;
-
-				// .. return the distance to Kerbin
-				return kerbinDistance;
-			}
-		}
-
+				if (this.KerbinDirect || this.targetRelay == null)
+				{
+					return this.SqrDistanceTo(Kerbin);
+				}
+				else
+				{
+					return this.SqrDistanceTo(this.targetRelay);
+				}
+			}
+		}
+
+		/// <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
 		{
 			get;
@@ -159,251 +177,713 @@
 		/// The maximum distance at which this relay can operate.
 		/// </summary>
 		/// <value>The max transmit distance.</value>
-		public virtual float maxTransmitDistance
+		public virtual double maxTransmitDistance
 		{
 			get;
 			set;
 		}
 
 		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
-		/// the current relay attempt.
-		/// </summary>
-		/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
-		public virtual bool relayChecked
-		{
-			get;
-			protected set;
-		}
-
-		public virtual bool KerbinDirect
-		{
-			get;
-			protected set;
-		}
-
-		/// <summary>
 		/// Determines whether this instance can transmit.
 		/// </summary>
 		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
 		public virtual bool CanTransmit()
 		{
-			CelestialBody fob = null;
-
-			// @DONE TODO: Remove nearestRelay == null
-			// Because we're correctly falling back to Kerbin in transmitDistance the first test should always fail
-			// when we're out of range of anything, and the second will fail when LOS is blocked (and enforced).
-
-			Tools.PostDebugMessage(this.moduleRef, "Checking if CanTransmit with maxTransmitDistance={0}" +
-				"\n\ttransmitDistance={1}; KerbinDirect={2}, nearestRelay={3}, bestOccludedRelay={4}",
-				this.maxTransmitDistance, this.transmitDistance, this.KerbinDirect,
-				this.nearestRelay, this.bestOccludedRelay
-			);
-
-			// If our transmit distance is greater than our maximum range, we can't transmit and it doesn't matter why.
-			if (this.transmitDistance > this.maxTransmitDistance)
-			{
-				this.firstOccludingBody = null;
-				return false;
-			}
-			// ...if we're in range...
-			else
-			{
-				// ...check for LOS problems...
-				if (
-					ARConfiguration.RequireLineOfSight
-					&& this.KerbinDirect &&
-					!this.vessel.hasLineOfSightTo(Kerbin, out fob, ARConfiguration.RadiusRatio)
-				)
-				{
-					this.firstOccludingBody = fob;
-					return false;
-				}
-
-				this.firstOccludingBody = null;
-				return true;
-			}
+			return this.canTransmit;
 		}
 
 		/// <summary>
 		/// Finds the nearest relay.
 		/// </summary>
 		/// <returns>The nearest relay or null, if no relays in range.</returns>
-		private IAntennaRelay FindNearestRelay()
-		{
-			/*if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
-			{
-				return this.nearestRelay;
-			}
-
-			if (this.searchTimer.IsRunning)
-			{
-				this.searchTimer.Stop();
-				this.searchTimer.Reset();
-			}
-
-			this.searchTimer.Start();*/
-
-			Tools.PostDebugMessage(string.Format(
-				"{0}: finding nearest relay for {1} ({2})",
-				this.GetType().Name,
-				this,
-				this.vessel.id
-			));
-
+		public void FindNearestRelay()
+		{
+			if (!FlightGlobals.ready)
+			{
+				return;
+			}
+
+			Tools.DebugLogger log;
+			#if DEBUG
+			log = Tools.DebugLogger.New(this);
+			#endif
+
+			#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
+
+			// 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;
 			this.bestOccludedRelay = null;
-
-			// Set this vessel as checked, so that we don't check it again.
-			RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
-
-			double nearestSqrDistance = double.PositiveInfinity;
-			double bestOccludedSqrDistance = double.PositiveInfinity;
-
-			IAntennaRelay _nearestRelay = null;
+			this.targetRelay = null;
+			this.nearestRelay = null;
+
+			// Default to KerbinDirect = true in case something in here doesn't work right.
+			this.KerbinDirect = true;
 
 			/*
 			 * Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
 			 * far away.  When we find a candidate, get through its antennae for relays which have not been checked yet
-			 * and that can transmit.  Once we find a suitable candidate, assign it to _nearestRelay for comparison
+			 * and that can transmit.  Once we find a suitable candidate, assign it to nearestRelay for comparison
 			 * against future finds.
 			 * */
-			foreach (Vessel potentialVessel in FlightGlobals.Vessels)
-			{
-				// Skip vessels that have already been checked for a nearest relay this pass.
-				if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id))
-				{
+			// Vessel potentialVessel;
+			IAntennaRelay potentialBestRelay;
+			CelestialBody fob;
+
+			#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());
+
+				if (potentialBestRelay == null)
+				{
+					log.Append("\n\t\t...skipping null relay");
 					continue;
 				}
 
-				// Skip vessels of the wrong type.
-				switch (potentialVessel.vesselType)
-				{
-					case VesselType.Debris:
-					case VesselType.Flag:
-					case VesselType.EVA:
-					case VesselType.SpaceObject:
-					case VesselType.Unknown:
-						continue;
-					default:
-						break;
-				}
-
-				// Skip vessels with the wrong ID
-				if (potentialVessel.id == vessel.id)
+				if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel)
 				{
 					continue;
 				}
 
+				#if BENCH
+				usefulVesselCount++;
+				#endif
+
 				// Find the distance from here to the vessel...
-				double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
-
+				log.Append("\n\tgetting distance to potential vessel");
+				potentialSqrDistance = this.SqrDistanceTo(potentialBestRelay);
+				log.Append("\n\tgetting best vessel relay");
+
+				log.Append("\n\tgetting max link distance to potential relay");
+
+				if (ARConfiguration.UseAdditiveRanges)
+				{
+					maxLinkSqrDistance = this.maxTransmitDistance * potentialBestRelay.maxTransmitDistance;
+				}
+				else
+				{
+					maxLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
+				}
+
+				log.AppendFormat("\n\tmax link distance: {0}", 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.
-				CelestialBody fob = null;
-
 				if (
 					ARConfiguration.RequireLineOfSight &&
-					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
+					!this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio)
 				)
 				{
-					this.firstOccludingBody = fob;
-
-					Tools.PostDebugMessage(
-						this,
-						"Vessel {0} discarded because we do not have line of sight." +
-						"\npotentialSqrDistance: {1}, bestOccludedSqrDistance: {2}, maxTransmitSqrDistance: {3}" +
-						"\npotentialSqrDistance < bestOccludedSqrDistance: {4}" +
-						"\npotentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance): {5}",
-						potentialVessel.vesselName,
-						potentialSqrDistance, bestOccludedSqrDistance, this.maxTransmitDistance * this.maxTransmitDistance,
-						potentialSqrDistance < bestOccludedSqrDistance,
-						potentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance)
-					);
+					#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}: 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);
+					log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxLinkSqrDistance);
 
 					if (
-						(potentialSqrDistance < bestOccludedSqrDistance) &&
-						(potentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance))
+						(potentialSqrQuotient < bestOccludedSqrQuotient) &&
+						(potentialSqrQuotient <= 1d) &&
+						potentialBestRelay.CanTransmit()
 					)
 					{
-						Tools.PostDebugMessage(
-							this,
-							"Checking {0} relays on {1}.",
-							potentialVessel.GetAntennaRelays().Count(),
-							potentialVessel
-						);
-
-						foreach (IAntennaRelay occludedRelay in potentialVessel.GetAntennaRelays())
-						{
-							Tools.PostDebugMessage(this, "Checking candidate for bestOccludedRelay: {0}" +
-								"\n\tCanTransmit: {1}", occludedRelay, occludedRelay.CanTransmit());
-							if (occludedRelay.CanTransmit())
-							{
-								this.bestOccludedRelay = occludedRelay;
-								this.firstOccludingBody = fob;
-								bestOccludedSqrDistance = potentialSqrDistance;
-								Tools.PostDebugMessage(this, "Found new bestOccludedRelay: {0}" +
-									"\nfirstOccludingBodoy: {1}" +
-									"\nbestOccludedSqrDistance: {2}",
-									occludedRelay,
-									fob,
-									potentialSqrDistance
-								);
-								break;
-							}
-						}
-					}
-
+						log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit");
+						log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay);
+
+						this.bestOccludedRelay = potentialBestRelay;
+						bodyOccludingBestOccludedRelay = fob;
+						bestOccludedSqrQuotient = potentialSqrQuotient;
+					}
+					else
+					{
+						log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
+					}
+					
 					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");
 
 				/*
 				 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
 				 * */
-				if (potentialSqrDistance > nearestSqrDistance)
-				{
-					Tools.PostDebugMessage(
-						this,
-						"Vessel {0} discarded because it is out of range, or farther than another relay.",
-						potentialVessel.vesselName
+				if (potentialSqrQuotient > nearestRelaySqrQuotient)
+				{
+					
+					log.AppendFormat("\n\t{0}: Relay {1} discarded because it is farther than another the nearest relay.",
+						this.ToString(),
+						potentialBestRelay
 					);
 					continue;
 				}
 
-				nearestSqrDistance = potentialSqrDistance;
-
-				foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
-				{
-					if (potentialRelay.CanTransmit())
-					{
-						_nearestRelay = potentialRelay;
-						Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
-							this.GetType().Name,
-							_nearestRelay.ToString(),
-							_nearestRelay.vessel.id
-						));
-						break;
-					}
-				}
-			}
-
-			// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
-			// used next time.
-			RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
-
-			// Return the nearest available relay, or null if there are no available relays nearby.
-			return _nearestRelay;
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
-		/// </summary>
-		/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
+				log.Append("\n\t\t...passed distance check");
+
+				if (potentialBestRelay.CanTransmit())
+				{
+					#if BENCH
+					startCircularVesselTicks = performanceTimer.ElapsedTicks;
+					#endif
+
+					needle = potentialBestRelay;
+					isCircular = false;
+
+					iterCount = 0;
+					while (needle != null)
+					{
+						iterCount++;
+
+						if (needle.KerbinDirect)
+						{
+							break;
+						}
+
+						if (needle.targetRelay == null)
+						{
+							break;
+						}
+
+						if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
+						{
+							isCircular = true;
+							break;
+						}
+
+						// Avoid infinite loops when we're not catching things right.
+						if (iterCount > FlightGlobals.Vessels.Count)
+						{
+							Tools.PostErrorMessage(
+								"[{0}] iterCount exceeded while checking for circular network; assuming it is circular" +
+								"\n\tneedle={1}" +
+								"\n\tthis.moduleRef={2}",
+								this,
+								needle == null ? "null" : string.Format(
+									"{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
+									needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
+										"{0}\n\tneedle.targetRelay.vessel={1}",
+										needle.targetRelay,
+										needle.targetRelay.vessel == null ?
+											"null" : needle.targetRelay.vessel.vesselName
+									)
+								),
+								this.moduleRef == null ? "null" : this.moduleRef.ToString()
+							);
+							isCircular = true;
+							break;
+						}
+
+						needle = needle.targetRelay;
+					}
+
+					if (!isCircular)
+					{
+						nearestRelaySqrQuotient = potentialSqrQuotient;
+						this.nearestRelay = potentialBestRelay;
+
+						log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m²)",
+							this.ToString(),
+							this.nearestRelay.ToString(),
+							Math.Sqrt(nearestRelaySqrQuotient)
+						);
+					}
+					else
+					{
+						log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping",
+							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;
+
+			kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
+			kerbinSqrDistance *= kerbinSqrDistance;
+
+			if (ARConfiguration.UseAdditiveRanges)
+			{
+				kerbinSqrQuotient = kerbinSqrDistance /
+					(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange);
+			}
+			else
+			{
+				kerbinSqrQuotient = kerbinSqrDistance /
+					(this.maxTransmitDistance * this.maxTransmitDistance);
+			}
+
+			log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
+			log.AppendFormat(
+				"\n{0}: nearestRelay={1} ({2}m²)), bestOccludedRelay={3} ({4}m²), kerbinSqrDistance={5}m²)",
+				this,
+				this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+				nearestRelaySqrQuotient,
+				this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
+				bestOccludedSqrQuotient,
+				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.
+				// Therefore, this will only be true if a valid relay is in range.
+				if (nearestRelaySqrQuotient <= 1d)
+				{
+					log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
+						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+						nearestRelaySqrQuotient, 1d);
+
+					this.KerbinDirect = false;
+					this.canTransmit = true;
+					this.targetRelay = this.nearestRelay;
+				}
+				// If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
+				else
+				{
+					log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
+						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+						nearestRelaySqrQuotient, 1d);
+
+					this.canTransmit = false;
+
+					// If the best occluded relay is closer than Kerbin, check it against the nearest relay.
+					// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
+					if (bestOccludedSqrQuotient < kerbinSqrQuotient)
+					{
+						log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
+							bestOccludedRelay, kerbinSqrDistance);
+						
+						this.KerbinDirect = false;
+
+						// If the nearest relay is closer than the best occluded relay, pick it.
+						// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
+						if (nearestRelaySqrQuotient < bestOccludedSqrQuotient)
+						{
+							log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+								nearestRelaySqrQuotient, bestOccludedSqrQuotient);
+							
+							this.targetRelay = this.nearestRelay;
+							this.firstOccludingBody = null;
+						}
+						// Otherwise, target the best occluded relay.
+						else
+						{
+							log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+								nearestRelaySqrQuotient, bestOccludedSqrQuotient);
+							
+							this.targetRelay = bestOccludedRelay;
+							this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+						}
+					}
+					// Otherwise, check Kerbin against the nearest relay.
+					// Since we have LOS, blank the first occluding body.
+					else
+					{
+						log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
+							bestOccludedRelay, kerbinSqrDistance);
+						
+						// If the nearest relay is closer than Kerbin, pick it.
+						// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
+						if (nearestRelaySqrQuotient < kerbinSqrQuotient)
+						{
+							log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+								nearestRelaySqrQuotient, kerbinSqrQuotient);
+							
+							this.KerbinDirect = false;
+							this.firstOccludingBody = null;
+							this.targetRelay = this.nearestRelay;
+						}
+						// Otherwise, pick Kerbin.
+						else
+						{
+							log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+								nearestRelaySqrQuotient, kerbinSqrQuotient);
+							
+							this.KerbinDirect = true;
+							this.firstOccludingBody = bodyOccludingKerbin;
+							this.targetRelay = null;
+						}
+					}
+				}
+			}
+			// 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.
+				if (nearestRelaySqrQuotient <= 1d)
+				{
+					log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
+						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+						nearestRelaySqrQuotient, 1d);
+
+					this.canTransmit = true;
+
+					// If the nearestRelay is closer than Kerbin, use it.
+					if (nearestRelaySqrQuotient < kerbinSqrQuotient)
+					{
+						log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
+							this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+							nearestRelaySqrQuotient, kerbinSqrQuotient);
+
+						this.KerbinDirect = false;
+						this.targetRelay = this.nearestRelay;
+					}
+					// Otherwise, Kerbin is closer, so use it.
+					else
+					{
+						log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
+							this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+							nearestRelaySqrQuotient, kerbinSqrQuotient);
+
+						this.KerbinDirect = true;
+						this.targetRelay = null;
+					}
+				}
+				// If the nearest relay is out of range, we still need to check on Kerbin.
+				else
+				{
+					log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
+						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+							nearestRelaySqrQuotient, 1d);
+
+					// If Kerbin is in range, use it.
+					if (kerbinSqrQuotient <= 1d)
+					{
+						log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).",
+							kerbinSqrQuotient, 1d);
+
+						this.canTransmit = true;
+						this.KerbinDirect = true;
+						this.targetRelay = null;
+					}
+					// If Kerbin is out of range and the nearest relay is out of range, pick a second best between
+					// Kerbin and bestOccludedRelay
+					else
+					{
+						log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).",
+								kerbinSqrQuotient, 1d);
+
+						this.canTransmit = false;
+
+						// If the best occluded relay is closer than Kerbin, check it against the nearest relay.
+						// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
+						if (bestOccludedSqrQuotient < kerbinSqrQuotient)
+						{
+							log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
+								bestOccludedRelay, kerbinSqrDistance);
+							
+							this.KerbinDirect = false;
+
+							// If the nearest relay is closer than the best occluded relay, pick it.
+							// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
+							if (nearestRelaySqrQuotient < bestOccludedSqrQuotient)
+							{
+								log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+									nearestRelaySqrQuotient, bestOccludedSqrQuotient);
+								
+								this.targetRelay = this.nearestRelay;
+								this.firstOccludingBody = null;
+							}
+							// Otherwise, target the best occluded relay.
+							else
+							{
+								log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+									nearestRelaySqrQuotient, bestOccludedSqrQuotient);
+								
+								this.targetRelay = bestOccludedRelay;
+								this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+							}
+						}
+						// Otherwise, check Kerbin against the nearest relay.
+						// Since we have LOS, blank the first occluding body.
+						else
+						{
+							log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
+								bestOccludedRelay, kerbinSqrDistance);
+							
+							this.firstOccludingBody = null;
+
+							// If the nearest relay is closer than Kerbin, pick it.
+							// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
+							if (nearestRelaySqrQuotient < kerbinSqrQuotient)
+							{
+								log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+									nearestRelaySqrQuotient, kerbinSqrQuotient);
+								
+								this.KerbinDirect = false;
+								this.targetRelay = this.nearestRelay;
+							}
+							// Otherwise, pick Kerbin.
+							else
+							{
+								log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+									nearestRelaySqrQuotient, kerbinSqrQuotient);
+								
+								this.KerbinDirect = true;
+								this.targetRelay = null;
+							}
+						}
+					}
+				}
+			}
+
+			if (ARConfiguration.UseAdditiveRanges)
+			{
+				if (this.KerbinDirect)
+				{
+					this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange;
+					this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange;
+				}
+				else
+				{
+					this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance;
+					this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance;
+				}
+			}
+			else
+			{
+				this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
+				this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
+			}
+
+			if (this.canTransmit)
+			{
+				if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance)
+				{
+					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
+			} catch (Exception ex) {
+				log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
+			#if QUIT_ON_EXCEPTION
+				UnityEngine.Application.Quit();
+			#endif
+			} finally {
+			#endif
+			log.Print(false);
+			#if DEBUG
+			}
+			#endif
+
+			#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>
+		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.
+		/// </summary>
+		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.</returns>
+		public override string ToString()
+		{
+			if (this is ProtoAntennaRelay)
+			{
+				return (this as ProtoAntennaRelay).ToString();
+			}
+			return this.moduleRef.ToString();
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
+		/// </summary>
+		/// <param name="module">The module reference underlying this AntennaRelay,
+		/// as an <see cref="AntennaRange.IAntennaRelay"/></param>
 		public AntennaRelay(IAntennaRelay module)
 		{
 			this.moduleRef = module;
 
-			this.searchTimer = new System.Diagnostics.Stopwatch();
-			this.millisecondsBetweenSearches = 1250;
+			#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
@@ -2,7 +2,7 @@
 //
 // AntennaRange.cfg
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -42,7 +42,8 @@
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 1500000
+		nominalRange = 6364
+		simpleRange = 20500000
 		maxPowerFactor = 8
 		maxDataFactor = 4
 	}
@@ -63,9 +64,10 @@
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 30000000
-		maxPowerFactor = 8
-		maxDataFactor = 4
+		nominalRange = 3500000000
+		simpleRange = 18000000000
+		maxPowerFactor = 4
+		maxDataFactor = 8
 	}
 
 	MODULE
@@ -84,9 +86,11 @@
 	@MODULE[ModuleDataTransmitter]
 	{
 		@name = ModuleLimitedDataTransmitter
-		nominalRange = 80000000000
-		maxPowerFactor = 8
-		maxDataFactor = 4
+		@packetResourceCost /= 1.414213
+		nominalRange = 10000000000
+		simpleRange = 56250000000
+		maxPowerFactor = 16
+		maxDataFactor = 2
 	}
 
 	MODULE
@@ -100,11 +104,18 @@
 	}
 }
 
+TRACKING_STATION_RANGES
+{
+	range = 800000
+	range = 200000000000
+	range = 2250000000000
+}
+
 EVA_MODULE
 {
 	name = ModuleLimitedDataTransmitter
 
-	nominalRange = 5000
+	nominalRange = 1389
 	maxPowerFactor = 1
 	maxDataFactor = 1
 

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -2,7 +2,7 @@
 //
 // IAntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -31,47 +31,20 @@
 
 namespace AntennaRange
 {
-	/*
-	 * Interface defining the basic functionality of AntennaRelay modules for AntennaRange.
-	 * */
+	/// <summary>
+	/// Interface defining the basic functionality of AntennaRelay modules for AntennaRange.
+	/// </summary>
 	public interface IAntennaRelay
 	{
 		/// <summary>
 		/// Gets the parent Vessel.
 		/// </summary>
-		/// <value>The parent Vessel.</value>
 		Vessel vessel { get; }
 
-		IAntennaRelay nearestRelay { get; }
-
-		IAntennaRelay bestOccludedRelay { get; }
-
 		/// <summary>
-		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
 		/// </summary>
-		/// <value>The distance to the nearest relay or Kerbin, whichever is closer.</value>
-		double transmitDistance { get; }
-
-		double nominalTransmitDistance { get; }
-
-		/// <summary>
-		/// The maximum distance at which this relay can operate.
-		/// </summary>
-		/// <value>The max transmit distance.</value>
-		float maxTransmitDistance { get; }
-
-		/// <summary>
-		/// The first CelestialBody blocking line of sight to a 
-		/// </summary>
-		/// <value>The first occluding body.</value>
-		CelestialBody firstOccludingBody { get; }
-
-		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
-		/// the current relay attempt.
-		/// </summary>
-		/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
-		bool relayChecked { get; }
+		IAntennaRelay targetRelay { get; }
 
 		/// <summary>
 		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
@@ -80,10 +53,61 @@
 		bool KerbinDirect { get; }
 
 		/// <summary>
+		/// The link distance, in meters, at which this relay behaves nominally.
+		/// </summary>
+		double NominalLinkSqrDistance { get; }
+
+
+		/// <summary>
+		/// The link distance, in meters, beyond which this relay cannot operate.
+		/// </summary>
+		double MaximumLinkSqrDistance { get; }
+
+		/// <summary>
+		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// </summary>
+		double CurrentLinkSqrDistance { 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.
+		/// </summary>
+		double nominalTransmitDistance { get; }
+
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
+		double maxTransmitDistance { get; }
+
+		/// <summary>
+		/// The first CelestialBody blocking line of sight to a 
+		/// </summary>
+		CelestialBody firstOccludingBody { get; }
+
+		/// <summary>
+		/// Gets the Part title.
+		/// </summary>
+		string Title { get; }
+
+		/// <summary>
 		/// Determines whether this instance can transmit.
+		/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
 		/// </summary>
-		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
 		bool CanTransmit();
+
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
+		void FindNearestRelay();
+
+		/// <summary>
+		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.IAntennaRelay"/>.
+		/// </summary>
+		string ToString();
 	}
 }
 

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -2,7 +2,7 @@
 //
 // ModuleLimitedDataTransmitter.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -29,78 +29,113 @@
 using KSP;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Text;
 using ToadicusTools;
 using UnityEngine;
 
 namespace AntennaRange
 {
-	/*
-	 * ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles range-
-	 * finding, power scaling, and data scaling for antennas during science transmission.  Its functionality varies with
-	 * three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.
-	 * 
-	 * In general, the scaling functions assume the following relation:
-	 * 
-	 *     D² α P/R,
-	 * 
-	 * where D is the total transmission distance, P is the transmission power, and R is the data rate.
-	 * 
-	 * */
-
-	/*
-	 * Fields
-	 * */
-	public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
+	/// <summary>
+	/// <para>ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles
+	/// rangefinding, power scaling, and data scaling for antennas during science transmission.  Its functionality
+	/// varies with three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.</para>
+	/// 
+	/// <para>In general, the scaling functions assume the following relation:</para>
+	/// 
+	///	<para>	D² α P/R,</para>
+	/// 
+	/// <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, IModuleInfo
 	{
 		// Stores the packetResourceCost as defined in the .cfg file.
-		protected float _basepacketResourceCost;
+		private float _basepacketResourceCost;
 
 		// Stores the packetSize as defined in the .cfg file.
-		protected float _basepacketSize;
+		private float _basepacketSize;
 
 		// Every antenna is a relay.
-		protected AntennaRelay relay;
-
-		// Keep track of vessels with transmitters for relay purposes.
-		protected List<Vessel> _relayVessels;
+		private AntennaRelay relay;
 
 		// Sometimes we will need to communicate errors; this is how we do it.
-		protected ScreenMessage ErrorMsg;
-
-		// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
-		// and packetSize.
+		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
+		/// prescribed by packetResourceCost and packetSize.
+		/// </summary>
 		[KSPField(isPersistant = false)]
-		public float nominalRange;
-
+		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")]
 		public string UIrelayStatus;
 
+		/// <summary>
+		/// Relay target string for use in action menus.
+		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Relay")]
 		public string UIrelayTarget;
 
+		/// <summary>
+		/// Transmit distance string for use in action menus.
+		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")]
 		public string UItransmitDistance;
 
-		[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
+		/// <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 Range")]
 		public string UImaxTransmitDistance;
 
+		/// <summary>
+		/// Packet size string for use in action menus.
+		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")]
 		public string UIpacketSize;
 
+		/// <summary>
+		/// Packet cost string for use in action menus.
+		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")]
 		public string UIpacketCost;
 
-		// The multiplier on packetResourceCost that defines the maximum power output of the antenna.  When the power
-		// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
+		/// <summary>
+		/// The multiplier on packetResourceCost that defines the maximum power output of the antenna.  When the power
+		/// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
+		/// </summary>
 		[KSPField(isPersistant = false)]
 		public float maxPowerFactor;
 
-		// The multipler on packetSize that defines the maximum data bandwidth of the antenna.
+		/// <summary>
+		/// The multipler on packetSize that defines the maximum data bandwidth of the antenna.
+		/// </summary>
 		[KSPField(isPersistant = false)]
 		public float maxDataFactor;
 
+		/// <summary>
+		/// The packet throttle.
+		/// </summary>
 		[KSPField(
 			isPersistant = true,
 			guiName = "Packet Throttle",
@@ -111,21 +146,47 @@
 		[UI_FloatRange(maxValue = 100f, minValue = 2.5f, stepIncrement = 2.5f)]
 		public float packetThrottle;
 
-		protected bool actionUIUpdate;
+		private bool actionUIUpdate;
 
 		/*
 		 * Properties
 		 * */
-		// Returns the parent vessel housing this antenna.
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
 		public new Vessel vessel
 		{
 			get
 			{
-				return base.vessel;
-			}
-		}
-
-		public IAntennaRelay nearestRelay
+				if (base.vessel != null)
+				{
+					return base.vessel;
+				}
+				else if (this.part != null && this.part.vessel != null)
+				{
+					return this.part.vessel;
+				}
+				else if (
+					this.part.protoPartSnapshot != null &&
+					this.part.protoPartSnapshot.pVesselRef != null &&
+					this.part.protoPartSnapshot.pVesselRef.vesselRef != null
+				)
+				{
+					return this.part.protoPartSnapshot.pVesselRef.vesselRef;
+				}
+				else
+				{
+					this.LogError("Vessel and/or part reference are null, returning null vessel.");
+					this.LogError(new System.Diagnostics.StackTrace().ToString());
+					return null;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+		/// </summary>
+		public IAntennaRelay targetRelay
 		{
 			get
 			{
@@ -134,55 +195,121 @@
 					return null;
 				}
 
-				return this.relay.nearestRelay;
-			}
-		}
-
-		public IAntennaRelay bestOccludedRelay
+				return this.relay.targetRelay;
+			}
+		}
+
+		/// <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 NominalLinkSqrDistance
+		{
+			get
+			{
+				if (this.relay != null)
+				{
+					return this.relay.NominalLinkSqrDistance;
+				}
+
+				return 0d;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the maximum link distance, in meters.
+		/// </summary>
+		public double MaximumLinkSqrDistance
+		{
+			get
+			{
+				if (this.relay != null)
+				{
+					return this.relay.MaximumLinkSqrDistance;
+				}
+
+				return 0d;
+			}
+		}
+
+		/// <summary>
+		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// </summary>
+		public double CurrentLinkSqrDistance
 		{
 			get
 			{
 				if (this.relay == null)
 				{
-					return null;
-				}
-
-				return this.relay.bestOccludedRelay;
-			}
-		}
-
-		// Returns the distance to the nearest relay or Kerbin, whichever is closer.
-		public double transmitDistance
+					return double.PositiveInfinity;
+				}
+
+				return this.relay.CurrentLinkSqrDistance;
+			}
+		}
+
+		/// <summary>
+		/// Gets the link status.
+		/// </summary>
+		public ConnectionStatus LinkStatus
 		{
 			get
 			{
 				if (this.relay == null)
 				{
-					return double.PositiveInfinity;
-				}
-
-				return this.relay.transmitDistance;
-			}
-		}
-
+					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;
-			}
-		}
-
-		// Returns the maximum distance this module can transmit
-		public float maxTransmitDistance
-		{
-			get
-			{
-				// TODO: Cache this in a way that doesn't break everything.
-				return Mathf.Sqrt(this.maxPowerFactor) * this.nominalRange;
-			}
-		}
-
+				if (ARConfiguration.UseAdditiveRanges)
+				{
+					return this.nominalRange;
+				}
+				else
+				{
+					return this.simpleRange;
+				}
+			}
+		}
+
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
+		public double maxTransmitDistance
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// The first CelestialBody blocking line of sight to a 
+		/// </summary>
 		public CelestialBody firstOccludingBody
 		{
 			get
@@ -217,8 +344,10 @@
 		 * 
 		 * So... hopefully that doesn't screw with anything else.
 		 * */
-		// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
-		// terms of joules/byte
+		/// <summary>
+		/// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
+		/// terms of joules/byte
+		/// </summary>
 		public new float DataRate
 		{
 			get
@@ -236,9 +365,11 @@
 			}
 		}
 
-		// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
-		// to be scored in terms of joules/byte
-		public new float DataResourceCost
+		/// <summary>
+		/// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
+		/// to be scored in terms of joules/byte
+		/// </summary>
+		public new double DataResourceCost
 		{
 			get
 			{
@@ -255,31 +386,19 @@
 			}
 		}
 
-		// Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
-		public bool relayChecked
-		{
-			get
-			{
-				if (this.relay != null)
-				{
-					return this.relay.relayChecked;
-				}
-
-				// If our relay is null, always return null so we're never checked.
-				return true;
-			}
-		}
-
-		public bool KerbinDirect
-		{
-			get
-			{
-				if (this.relay != null)
-				{
-					return this.relay.KerbinDirect;
-				}
-
-				return false;
+		/// <summary>
+		/// Gets the Part title.
+		/// </summary>
+		public string Title
+		{
+			get
+			{
+				if (this.part != null && this.part.partInfo != null)
+				{
+					return this.part.partInfo.title;
+				}
+
+				return string.Empty;
 			}
 		}
 
@@ -293,142 +412,156 @@
 			this.packetThrottle = 100f;
 		}
 
+		/// <summary>
+		/// PartModule OnAwake override; runs at Unity Awake.
+		/// </summary>
 		public override void OnAwake()
 		{
 			base.OnAwake();
 
 			this._basepacketSize = base.packetSize;
 			this._basepacketResourceCost = base.packetResourceCost;
+			this.moduleInfoContent = new GUIContent();
 
 			Tools.PostDebugMessage(string.Format(
 				"{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
 			));
 		}
 
-		// At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
+		/// <summary>
+		/// PartModule OnStart override; runs at Unity Start.
+		/// </summary>
+		/// <param name="state">State.</param>
 		public override void OnStart (StartState state)
 		{
 			base.OnStart (state);
 
+			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
+
 			if (state >= StartState.PreLaunch)
 			{
 				this.relay = new AntennaRelay(this);
+				this.relay.nominalTransmitDistance = this.nominalTransmitDistance;
 				this.relay.maxTransmitDistance = this.maxTransmitDistance;
-				this.relay.nominalTransmitDistance = this.nominalRange;
-
-				this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m";
+
+				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.maxTransmitDistance);
 
 				GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
 				GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
 			}
 		}
 
-		// When the module loads, fetch the Squad KSPFields from the base.  This is necessary in part because
-		// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
-		// work.
+		/// <summary>
+		/// When the module loads, fetch the Squad KSPFields from the base.  This is necessary in part because
+		/// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
+		/// work.
+		/// </summary>
+		/// <param name="node"><see cref="ConfigNode"/> with data for this module.</param>
 		public override void OnLoad(ConfigNode node)
 		{
 			this.Fields.Load(node);
 			base.Fields.Load(node);
 
 			base.OnLoad (node);
-		}
-
-		// Post an error in the communication messages describing the reason transmission has failed.  Currently there
-		// is only one reason for this.
-		protected void PostCannotTransmitError()
-		{
-			string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!");
-
-			this.ErrorMsg.message = string.Format(
-				"<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
-				((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
-				((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
-				((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
-				((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
-				ErrorText
+
+			this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
+		}
+
+		/// <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)
 			);
-
-			Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
-
-			ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
-		}
-
-		// Before transmission, set packetResourceCost.  Per above, packet cost increases with the square of
-		// distance.  packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point
-		// transmission fails (see CanTransmit).
-		protected void PreTransmit_SetPacketResourceCost()
-		{
-			if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.nominalRange)
-			{
-				base.packetResourceCost = this._basepacketResourceCost;
+		}
+
+		/// <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()
+		{
+			StringBuilder sb = Tools.GetStringBuilder();
+			string text;
+
+			sb.Append(base.GetInfo());
+
+			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
 			{
-				double rangeFactor = (this.transmitDistance / this.nominalRange);
-				rangeFactor *= rangeFactor;
-
-				base.packetResourceCost = this._basepacketResourceCost
-					* (float)rangeFactor;
-
-				Tools.PostDebugMessage(
-					this,
-					"Pretransmit: packet cost set to {0} before throttle (rangeFactor = {1}).",
-					base.packetResourceCost,
-					rangeFactor);
-			}
-
-			base.packetResourceCost *= this.packetThrottle / 100f;
-		}
-
-		// Before transmission, set packetSize.  Per above, packet size increases with the inverse square of
-		// distance.  packetSize maxes out at _basepacketSize * maxDataFactor.
-		protected void PreTransmit_SetPacketSize()
-		{
-			if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.nominalRange)
-			{
-				base.packetSize = this._basepacketSize;
-			}
-			else
-			{
-				double rangeFactor = (this.nominalRange / this.transmitDistance);
-				rangeFactor *= rangeFactor;
-
-				base.packetSize = Math.Min(
-					this._basepacketSize * (float)rangeFactor,
-					this._basepacketSize * this.maxDataFactor);
-
-				Tools.PostDebugMessage(
-					this,
-					"Pretransmit: packet size set to {0} before throttle (rangeFactor = {1}).",
-					base.packetSize,
-					rangeFactor);
-			}
-
-			base.packetSize *= this.packetThrottle / 100f;
-		}
-
-		// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
-		public override string GetInfo()
-		{
-			string text = base.GetInfo();
-			text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n";
-			text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2) + "m\n";
+				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();
+
+			Tools.PutStringBuilder(sb);
+
 			return text;
 		}
 
-		// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
+		/// </summary>
 		public new bool CanTransmit()
 		{
 			if (this.part == null || this.relay == null)
@@ -436,53 +569,72 @@
 				return false;
 			}
 
-			PartStates partState = this.part.State;
-			if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
-			{
-				Tools.PostDebugMessage(string.Format(
-					"{0}: {1} on {2} cannot transmit: {3}",
-					this.GetType().Name,
-					this.part.partInfo.title,
-					this.vessel.vesselName,
-					Enum.GetName(typeof(PartStates), partState)
-				));
-				return false;
-			}
+			switch (this.part.State)
+			{
+				case PartStates.DEAD:
+				case PartStates.DEACTIVATED:
+					Tools.PostDebugMessage(string.Format(
+						"{0}: {1} on {2} cannot transmit: {3}",
+						this.GetType().Name,
+						this.part.partInfo.title,
+						this.vessel.vesselName,
+						Enum.GetName(typeof(PartStates), this.part.State)
+					));
+					return false;
+				default:
+					break;
+			}
+
 			return this.relay.CanTransmit();
 		}
 
-		// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
-		// returns false.
-		public new void TransmitData(List<ScienceData> dataQueue)
-		{
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
+		public void FindNearestRelay()
+		{
+			if (this.relay != null)
+			{
+				this.relay.FindNearestRelay();
+			}
+		}
+
+		/// <summary>
+		/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
+		/// returns false.
+		/// </summary>
+		/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
+		/// <param name="callback">Callback function</param>
+		public new void TransmitData(List<ScienceData> dataQueue, Callback callback)
+		{
+			this.LogDebug(
+				"TransmitData(List<ScienceData> dataQueue, Callback callback) called.  dataQueue.Count={0}",
+				dataQueue.Count
+			);
+
+			this.FindNearestRelay();
+
 			this.PreTransmit_SetPacketSize();
 			this.PreTransmit_SetPacketResourceCost();
 
 			if (this.CanTransmit())
 			{
-				StringBuilder message = new StringBuilder();
-
-				message.Append("[");
-				message.Append(base.part.partInfo.title);
-				message.Append("]: ");
-
-				message.Append("Beginning transmission ");
-
-				// @DONE TODO: Fix this to fall back to Kerbin if nearestRelay cannot be contacted.
-				// @DONE TODO: Remove nearestRelay == null
-				if (this.KerbinDirect)
-				{
-					message.Append("directly to Kerbin.");
+				ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
+				this.LogDebug(
+					"CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}",
+					dataQueue.SPrint(),
+					callback == null ? "null" : callback.ToString()
+				);
+
+				if (callback == null)
+				{
+					base.TransmitData(dataQueue);
 				}
 				else
 				{
-					message.Append("via ");
-					message.Append(this.relay.nearestRelay);
-				}
-
-				ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
-
-				base.TransmitData(dataQueue);
+					base.TransmitData(dataQueue, callback);
+				}
 			}
 			else
 			{
@@ -490,8 +642,12 @@
 
 				var logger = Tools.DebugLogger.New(this);
 
-				foreach (ModuleScienceContainer	scienceContainer in this.vessel.getModulesOfType<ModuleScienceContainer>())
-				{
+				IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>();
+				ModuleScienceContainer scienceContainer;
+				for (int cIdx = 0; cIdx < vesselContainers.Count; cIdx++)
+				{
+					scienceContainer = vesselContainers[cIdx];
+
 					logger.AppendFormat("Checking ModuleScienceContainer in {0}\n",
 						scienceContainer.part.partInfo.title);
 
@@ -506,8 +662,10 @@
 
 					List<ScienceData> dataStored = new List<ScienceData>();
 
-					foreach (ScienceData data in dataQueue)
+					ScienceData data;
+					for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
 					{
+						data = dataQueue[dIdx];
 						if (!scienceContainer.allowRepeatedSubjects && scienceContainer.HasData(data))
 						{
 							logger.Append("\tAlready contains subject and repeated subjects not allowed, skipping.\n");
@@ -538,69 +696,72 @@
 
 				if (dataQueue.Count > 0)
 				{
-					StringBuilder msg = new StringBuilder();
-
-					msg.Append('[');
-					msg.Append(this.part.partInfo.title);
-					msg.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
-					msg.Append("Data to be discarded:\n");
-
-					foreach (ScienceData data in dataQueue)
+					StringBuilder sb = Tools.GetStringBuilder();
+
+					sb.Append('[');
+					sb.Append(this.part.partInfo.title);
+					sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
+					sb.Append("Data to be discarded:\n");
+
+					ScienceData data;
+					for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
 					{
-						msg.AppendFormat("\n{0}\n", data.title);
+						data = dataQueue[dIdx];
+						sb.AppendFormat("\t{0}\n", data.title);
 					}
 
-					ScreenMessages.PostScreenMessage(msg.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
-
-					Tools.PostDebugMessage(msg.ToString());
-				}
-
-				this.PostCannotTransmitError ();
+					ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
+					Tools.PostDebugMessage(sb.ToString());
+
+					Tools.PutStringBuilder(sb);
+				}
+
+				this.PostCannotTransmitError();
 			}
 
 			Tools.PostDebugMessage (
-				"distance: " + this.transmitDistance
+				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
 			);
 		}
 
-		// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
-		// returns false.
+		/// <summary>
+		/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
+		/// returns false.
+		/// </summary>
+		/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
+		public new void TransmitData(List<ScienceData> dataQueue)
+		{
+			this.LogDebug(
+				"TransmitData(List<ScienceData> dataQueue) called, dataQueue.Count={0}",
+				dataQueue.Count
+			);
+
+			this.TransmitData(dataQueue, null);
+		}
+
+		/// <summary>
+		/// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
+		/// returns false.
+		/// </summary>
 		public new void StartTransmission()
 		{
+			this.FindNearestRelay();
+
 			PreTransmit_SetPacketSize ();
 			PreTransmit_SetPacketResourceCost ();
 
 			Tools.PostDebugMessage (
-				"distance: " + this.transmitDistance
+				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
 				);
 
 			if (this.CanTransmit())
 			{
-				StringBuilder message = new StringBuilder();
-
-				message.Append("[");
-				message.Append(base.part.partInfo.title);
-				message.Append("]: ");
-
-				message.Append("Beginning transmission ");
-
-				// @DONE TODO: Fix this to fall back to Kerbin if nearestRelay cannot be contacted.
-				// @DONE TODO: Remove nearestRelay == null
-				if (this.KerbinDirect)
-				{
-					message.Append("directly to Kerbin.");
-				}
-				else
-				{
-					message.Append("via ");
-					message.Append(this.relay.nearestRelay);
-				}
-
-				ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+				ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
 
 				base.StartTransmission();
 			}
@@ -610,51 +771,90 @@
 			}
 		}
 
+		/// <summary>
+		/// MonoBehaviour Update
+		/// </summary>
 		public void Update()
 		{
 			if (this.actionUIUpdate)
 			{
+				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 = "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",
+						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);
 				}
 				else
 				{
 					if (this.relay.firstOccludingBody == null)
 					{
+						this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+							Math.Sqrt(this.CurrentLinkSqrDistance));
 						this.UIrelayStatus = "Out of range";
 					}
 					else
 					{
+						this.UItransmitDistance = "N/A";
 						this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName);
 					}
-					this.UImaxTransmitDistance = "N/A";
 					this.UIpacketSize = "N/A";
 					this.UIpacketCost = "N/A";
 				}
 
 				if (this.KerbinDirect)
 				{
-					if (this.relay.bestOccludedRelay != null)
-					{
-						this.UIrelayTarget = this.relay.bestOccludedRelay.ToString();
-					}
-					else
-					{
-						this.UIrelayTarget = "Kerbin";
-					}
+					this.UIrelayTarget = AntennaRelay.Kerbin.bodyName;
 				}
 				else
 				{
-					this.UIrelayTarget = this.relay.nearestRelay.ToString();
-				}
-			}
-		}
-
-		public void onPartActionUICreate(Part eventPart)
+					this.UIrelayTarget = this.targetRelay.ToString();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.
+		/// </summary>
+		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns>
+		public override string ToString()
+		{
+			StringBuilder sb = Tools.GetStringBuilder();
+			string msg;
+
+			sb.Append(this.part.partInfo.title);
+
+			if (vessel != null)
+			{
+				sb.Append(" on ");
+				sb.Append(vessel.vesselName);
+			}
+			else if (
+				this.part != null &&
+				this.part.protoPartSnapshot != null &&
+				this.part.protoPartSnapshot != null &&
+				this.part.protoPartSnapshot.pVesselRef != null
+			)
+			{
+				sb.Append(" on ");
+				sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
+			}
+
+			msg = sb.ToString();
+
+			Tools.PutStringBuilder(sb);
+
+			return msg;
+		}
+
+		// When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty.
+		private void onPartActionUICreate(Part eventPart)
 		{
 			if (eventPart == base.part)
 			{
@@ -662,7 +862,8 @@
 			}
 		}
 
-		public void onPartActionUIDismiss(Part eventPart)
+		// When we catch an onPartActionUIDismiss event for our part, stop updating every frame to look pretty.
+		private void onPartActionUIDismiss(Part eventPart)
 		{
 			if (eventPart == base.part)
 			{
@@ -670,86 +871,129 @@
 			}
 		}
 
-		public override string ToString()
-		{
-			StringBuilder msg = new StringBuilder();
-
-			msg.Append(this.part.partInfo.title);
-
-			if (vessel != null)
-			{
-				msg.Append(" on ");
-				msg.Append(vessel.vesselName);
-			}
-
-			return msg.ToString();
-		}
-
+		// Post an error in the communication messages describing the reason transmission has failed.  Currently there
+		// is only one reason for this.
+		private void PostCannotTransmitError()
+		{
+			string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!");
+
+			this.ErrorMsg.message = string.Format(
+				"<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
+				((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
+				ErrorText
+			);
+
+			Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
+
+			ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
+		}
+
+		// Before transmission, set packetResourceCost.  Per above, packet cost increases with the square of
+		// distance.  packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point
+		// transmission fails (see CanTransmit).
+		private void PreTransmit_SetPacketResourceCost()
+		{
+			if (ARConfiguration.FixedPowerCost || this.CurrentLinkSqrDistance <= this.NominalLinkSqrDistance)
+			{
+				base.packetResourceCost = this._basepacketResourceCost;
+			}
+			else
+			{
+				float rangeFactor = (float)(this.CurrentLinkSqrDistance / this.NominalLinkSqrDistance);
+
+				base.packetResourceCost = this._basepacketResourceCost * rangeFactor;
+			}
+
+			base.packetResourceCost *= this.packetThrottle / 100f;
+		}
+
+		// Before transmission, set packetSize.  Per above, packet size increases with the inverse square of
+		// distance.  packetSize maxes out at _basepacketSize * maxDataFactor.
+		private void PreTransmit_SetPacketSize()
+		{
+			if (!ARConfiguration.FixedPowerCost && this.CurrentLinkSqrDistance >= this.NominalLinkSqrDistance)
+			{
+				base.packetSize = this._basepacketSize;
+			}
+			else
+			{
+				float rangeFactor = (float)(this.NominalLinkSqrDistance / this.CurrentLinkSqrDistance);
+
+				base.packetSize = Mathf.Min(
+					this._basepacketSize * rangeFactor,
+					this._basepacketSize * this.maxDataFactor
+				);
+			}
+
+			base.packetSize *= this.packetThrottle / 100f;
+		}
+
+		private string buildTransmitMessage()
+		{
+			StringBuilder sb = Tools.GetStringBuilder();
+			string msg;
+
+			sb.Append("[");
+			sb.Append(base.part.partInfo.title);
+			sb.Append("]: ");
+
+			sb.Append("Beginning transmission ");
+
+			if (this.KerbinDirect)
+			{
+				sb.Append("directly to Kerbin.");
+			}
+			else
+			{
+				sb.Append("via ");
+				sb.Append(this.relay.targetRelay);
+			}
+
+			msg = sb.ToString();
+
+			Tools.PutStringBuilder(sb);
+
+			return msg;
+		}
+
+		#if DEBUG
 		// When debugging, it's nice to have a button that just tells you everything.
-
 		[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)]
 		public void DebugInfo()
 		{
 			PreTransmit_SetPacketSize ();
 			PreTransmit_SetPacketResourceCost ();
 
-			string msg = string.Format(
-				"'{0}'\n" + 
-				"_basepacketSize: {1}\n" +
-				"packetSize: {2}\n" +
-				"_basepacketResourceCost: {3}\n" +
-				"packetResourceCost: {4}\n" +
-				"maxTransmitDistance: {5}\n" +
-				"transmitDistance: {6}\n" +
-				"nominalRange: {7}\n" +
-				"CanTransmit: {8}\n" +
-				"DataRate: {9}\n" +
-				"DataResourceCost: {10}\n" +
-				"TransmitterScore: {11}\n" +
-				"NearestRelay: {12}\n" +
-				"BestOccludedRelay: {13}\n" +
-				"KerbinDirect: {14}\n" +
-				"Vessel ID: {15}",
-				this.name,
-				this._basepacketSize,
-				base.packetSize,
-				this._basepacketResourceCost,
-				base.packetResourceCost,
-				this.maxTransmitDistance,
-				this.transmitDistance,
-				this.nominalRange,
-				this.CanTransmit(),
-				this.DataRate,
-				this.DataResourceCost,
-				ScienceUtil.GetTransmitterScore(this),
-				this.relay.nearestRelay == null ? "null" : this.relay.nearestRelay.ToString(),
-				this.relay.bestOccludedRelay == null ? "null" : this.relay.bestOccludedRelay.ToString(),
-				this.KerbinDirect,
-				this.vessel.id
-				);
-
-			Tools.PostLogMessage(msg);
+			DebugPartModule.DumpClassObject(this);
 		}
 
 		[KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)]
 		public void PrintAllVessels()
 		{
-			StringBuilder sb = new StringBuilder();
-
+			StringBuilder sb = Tools.GetStringBuilder();
+			
 			sb.Append("Dumping FlightGlobals.Vessels:");
 
-			foreach (Vessel vessel in FlightGlobals.Vessels)
-			{
+			Vessel vessel;
+			for (int i = 0; i < FlightGlobals.Vessels.Count; i++)
+			{
+				vessel = FlightGlobals.Vessels[i];
 				sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id);
 			}
-
+		    
 			Tools.PostDebugMessage(sb.ToString());
-		}
-
-		/*[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)]
+
+			Tools.PutStringBuilder(sb);
+		}
+		 
+		[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)]
 		public void DumpRelayDB()
 		{
 			RelayDatabase.Instance.Dump();
-		}*/
+		}
+		#endif
 	}
 }

--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
 //
 // AssemblyInfo.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -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.8.*")]
+[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/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -2,7 +2,7 @@
 //
 // ProtoAntennaRelay.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -28,28 +28,53 @@
 
 using KSP;
 using System;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange
 {
-	/*
-	 * Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay.
-	 * This is used for finding relays in unloaded Vessels.
-	 * */
+	/// <summary>
+	/// Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay.
+	/// This is used for finding relays in unloaded Vessels.
+	/// </summary>
 	public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
 	{
 		// Stores the prototype part so we can make sure we haven't exploded or so.
-		protected ProtoPartSnapshot protoPart;
+		private ProtoPartSnapshot protoPart;
 
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
 		public override Vessel vessel
 		{
 			get
 			{
-				return this.protoPart.pVesselRef.vesselRef;
+				if (
+					this.protoPart != null &&
+					this.protoPart.pVesselRef != null &&
+					this.protoPart.pVesselRef.vesselRef != null
+				)
+				{
+					return this.protoPart.pVesselRef.vesselRef;
+				}
+				else
+				{
+					Tools.PostErrorMessage("{0}: Could not fetch vessel!  {1}{2}{3}",
+						this.ToString(),
+						this.protoPart == null ? "\n\tprotoPart=null" : string.Empty,
+						this.protoPart != null && this.protoPart.pVesselRef == null ?
+							"\n\tthis.protoPart.pVesselRef=null" : string.Empty,
+						this.protoPart != null && this.protoPart.pVesselRef != null &&
+							this.protoPart.pVesselRef.vesselRef == null ?
+							"\n\tthis.protoPart.pVesselRef.vesselRef=null" : string.Empty
+					);
+					return null;
+				}
 			}
 		}
 
+		/// <summary>
+		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+		/// </summary>
 		public override double nominalTransmitDistance
 		{
 			get
@@ -59,10 +84,9 @@
 		}
 
 		/// <summary>
-		/// The maximum distance at which this transmitter can operate.
+		/// The maximum distance at which this relay can operate.
 		/// </summary>
-		/// <value>The max transmit distance.</value>
-		public override float maxTransmitDistance
+		public override double maxTransmitDistance
 		{
 			get
 			{
@@ -71,28 +95,26 @@
 		}
 
 		/// <summary>
-		/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
-		/// the current relay attempt.
+		/// Gets the underlying part's title.
 		/// </summary>
-		/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
-		public override bool relayChecked
+		/// <value>The title.</value>
+		public string Title
 		{
-			get;
-			protected set;
+			get
+			{
+				if (this.protoPart != null && this.protoPart.partInfo != null)
+				{
+					return this.protoPart.partInfo.title;
+				}
+
+				return string.Empty;
+			}
 		}
 
 		/// <summary>
-		/// Gets the underlying part's title.
+		/// Determines whether this instance can transmit.
+		/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
 		/// </summary>
-		/// <value>The title.</value>
-		public string title
-		{
-			get
-			{
-				return this.protoPart.partInfo.title;
-			}
-		}
-
 		public override bool CanTransmit()
 		{
 			PartStates partState = (PartStates)this.protoPart.state;
@@ -101,7 +123,7 @@
 				Tools.PostDebugMessage(string.Format(
 					"{0}: {1} on {2} cannot transmit: {3}",
 					this.GetType().Name,
-					this.title,
+					this.Title,
 					this.vessel.vesselName,
 					Enum.GetName(typeof(PartStates), partState)
 				));
@@ -110,31 +132,37 @@
 			return base.CanTransmit();
 		}
 
+		/// <summary>
+		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.
+		/// </summary>
+		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns>
 		public override string ToString()
 		{
-			return string.Format(
-				"{0} on {1}",
-				this.title,
-				this.protoPart.pVesselRef.vesselName
-			);
+			System.Text.StringBuilder sb = Tools.GetStringBuilder();
+
+			sb.Append(this.Title);
+
+			if (this.protoPart != null && this.protoPart.pVesselRef != null)
+			{
+				sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
+			}
+
+			Tools.PutStringBuilder(sb);
+
+			return sb.ToString();
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="AntennaRange.ProtoAntennaRelay"/> class.
+		/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
 		/// </summary>
-		/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param>
-		/// <param name="vessel">The parent Vessel</param>
+		/// <param name="prefabRelay">The module reference underlying this AntennaRelay,
+		/// as an <see cref="AntennaRange.IAntennaRelay"/></param>
+		/// <param name="pps">The prototype partreference on which the module resides.</param>
 		public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay)
 		{
 			this.protoPart = pps;
-		}
 
-		~ProtoAntennaRelay()
-		{
-			Tools.PostDebugMessage(string.Format(
-				"{0}: destroyed",
-				this.ToString()
-			));
+			Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
 		}
 	}
 }

--- a/RelayDatabase.cs
+++ b/RelayDatabase.cs
@@ -2,7 +2,7 @@
 //
 // RelayDatabase.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -26,6 +26,8 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#pragma warning disable 1591
+
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -35,27 +37,8 @@
 
 namespace AntennaRange
 {
-	public class RelayDatabase
+	public class RelayDatabase : Singleton<RelayDatabase>
 	{
-		/*
-		 * Static members
-		 * */
-		// Singleton storage
-		protected static RelayDatabase _instance;
-		// Gets the singleton
-		public static RelayDatabase Instance
-		{
-			get
-			{	
-				if (_instance == null)
-				{
-					_instance = new RelayDatabase();
-				}
-
-				return _instance;
-			}
-		}
-
 		/*
 		 * Instance members
 		 * */
@@ -64,22 +47,20 @@
 		 * Fields
 		 * */
 		// Vessel.id-keyed hash table of Part.GetHashCode()-keyed tables of relay objects.
-		protected Dictionary<Guid, Dictionary<int, IAntennaRelay>> relayDatabase;
+		private Dictionary<Guid, List<IAntennaRelay>> relayDatabase;
+		private Dictionary<Guid, IAntennaRelay> bestRelayTable;
 
 		// Vessel.id-keyed hash table of part counts, used for caching
-		protected Dictionary<Guid, int> vesselPartCountTable;
-
-		// Vessel.id-keyed hash table of booleans to track what vessels have been checked so far this time.
-		public Dictionary<Guid, bool> CheckedVesselsTable;
-
-		protected int cacheHits;
-		protected int cacheMisses;
+		private Dictionary<Guid, int> vesselPartCountTable;
+
+		private int cacheHits;
+		private int cacheMisses;
 
 		/*
 		 * Properties
 		 * */
 		// Gets the Part-hashed table of relays in a given vessel
-		public Dictionary<int, IAntennaRelay> this [Vessel vessel]
+		public IList<IAntennaRelay> this [Vessel vessel]
 		{
 			get
 			{
@@ -104,16 +85,66 @@
 				}
 
 				// Return the Part-hashed table of relays for this vessel
-				return relayDatabase[vessel.id];
+				return relayDatabase[vessel.id].AsReadOnly();
 			}
 		}
 
 		/* 
 		 * Methods
 		 * */
+		// Remove a vessel from the cache, if it exists.
+		public void DirtyVessel(Vessel vessel)
+		{
+			#if DEBUG
+			Tools.PostDebugMessage("RelayDatabase: Dirtying cache for vessel {0} in frame {1}",
+				vessel, new System.Diagnostics.StackTrace().ToString());
+			#else
+			Tools.PostLogMessage("RelayDatabase: Dirtying cache for vessel {0}", vessel.vesselName);
+			#endif
+
+			this.relayDatabase.Remove(vessel.id);
+			this.vesselPartCountTable.Remove(vessel.id);
+			this.bestRelayTable.Remove(vessel.id);
+		}
+
+		public void ClearCache()
+		{
+			Tools.PostLogMessage("RelayDatabase: onSceneChange clearing entire cache.");
+
+			this.relayDatabase.Clear();
+			this.bestRelayTable.Clear();
+			this.vesselPartCountTable.Clear();
+		}
+
+		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
+		public bool ContainsKey(Guid key)
+		{
+			return this.relayDatabase.ContainsKey(key);
+		}
+
+		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel.
+		public bool ContainsKey(Vessel vessel)
+		{
+			return this.ContainsKey(vessel.id);
+		}
+
+		public IAntennaRelay GetBestVesselRelay(Vessel vessel)
+		{
+			IAntennaRelay relay;
+			if (this.bestRelayTable.TryGetValue(vessel.id, out relay))
+			{
+				return relay;
+			}
+			else
+			{
+				var dump = this[vessel];
+				return null;
+			}
+		}
+
 		// Adds a vessel to the database
 		// The return for this function isn't used yet, but seems useful for potential future API-uses
-		public bool AddVessel(Vessel vessel)
+		private bool AddVessel(Vessel vessel)
 		{
 			// If this vessel is already here...
 			if (this.ContainsKey(vessel))
@@ -133,7 +164,7 @@
 			else
 			{
 				// Build an empty table...
-				this.relayDatabase[vessel.id] = new Dictionary<int, IAntennaRelay>();
+				this.relayDatabase[vessel.id] = new List<IAntennaRelay>();
 
 				// Update the empty index
 				this.UpdateVessel(vessel);
@@ -144,7 +175,7 @@
 		}
 
 		// Update the vessel's entry in the table
-		public void UpdateVessel(Vessel vessel)
+		private void UpdateVessel(Vessel vessel)
 		{
 			// Squak if the database doesn't have the vessel
 			if (!this.ContainsKey(vessel))
@@ -157,37 +188,12 @@
 				));
 			}
 
-			Dictionary<int, IAntennaRelay> vesselTable = this.relayDatabase[vessel.id];
+			List<IAntennaRelay> vesselTable = this.relayDatabase[vessel.id];
 
 			// Actually build and assign the table
 			this.getVesselRelays(vessel, ref vesselTable);
 			// Set the part count
 			this.vesselPartCountTable[vessel.id] = vessel.Parts.Count;
-		}
-
-		// Remove a vessel from the cache, if it exists.
-		public void DirtyVessel(Vessel vessel)
-		{
-			if (this.relayDatabase.ContainsKey(vessel.id))
-			{
-				this.relayDatabase.Remove(vessel.id);
-			}
-			if (this.vesselPartCountTable.ContainsKey(vessel.id))
-			{
-				this.vesselPartCountTable.Remove(vessel.id);
-			}
-		}
-
-		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
-		public bool ContainsKey(Guid key)
-		{
-			return this.relayDatabase.ContainsKey(key);
-		}
-
-		// Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel.
-		public bool ContainsKey(Vessel vessel)
-		{
-			return this.ContainsKey(vessel.id);
 		}
 
 		// Runs when a vessel is modified (or when we switch to one, to catch docking events)
@@ -214,18 +220,41 @@
 		}
 
 		// Runs when the player requests a scene change, such as when changing vessels or leaving flight.
-		public void onSceneChange(GameScenes scene)
-		{
-			// If the active vessel is a real thing...
-			if (FlightGlobals.ActiveVessel != null)
-			{
-				// ... dirty its cache
-				this.onVesselEvent(FlightGlobals.ActiveVessel);
-			}
+		private void onSceneChange(GameScenes scene)
+		{
+			Tools.PostDebugMessage(
+				"RelayDatabase: caught onSceneChangeRequested in scene {0} to scene {1}.  ActiveVessel is {2}",
+				HighLogic.LoadedScene,
+				scene,
+				FlightGlobals.ActiveVessel == null ? "null" : FlightGlobals.ActiveVessel.vesselName
+			);
+
+			if (scene == GameScenes.FLIGHT)
+			{
+				if (scene == HighLogic.LoadedScene)
+				{
+					if (FlightGlobals.ActiveVessel != null)
+					{
+						Tools.PostDebugMessage("RelayDatabase: onSceneChange clearing {0} from cache.",
+							FlightGlobals.ActiveVessel.vesselName);
+
+						this.onVesselEvent(FlightGlobals.ActiveVessel);
+					}
+				}
+				else
+				{
+					this.ClearCache();
+				}
+			}
+		}
+
+		private void onGameLoaded(object data)
+		{
+			this.ClearCache();
 		}
 
 		// Runs when parts are undocked
-		public void onPartEvent(Part part)
+		private void onPartEvent(Part part)
 		{
 			if (part != null && part.vessel != null)
 			{
@@ -234,14 +263,14 @@
 		}
 
 		// Runs when parts are coupled, as in docking
-		public void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data)
+		private void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data)
 		{
 			this.onPartEvent(data.from);
 			this.onPartEvent(data.to);
 		}
 
 		// Produce a Part-hashed table of relays for the given vessel
-		protected void getVesselRelays(Vessel vessel, ref Dictionary<int, IAntennaRelay> relays)
+		private void getVesselRelays(Vessel vessel, ref List<IAntennaRelay> relays)
 		{
 			// We're going to completely regen this table, so dump the current contents.
 			relays.Clear();
@@ -251,6 +280,10 @@
 				"IAntennaRelay",
 				vessel.vesselName
 			));
+
+			double bestRelayRange = double.NegativeInfinity;
+			IAntennaRelay bestRelay = null;
+			IAntennaRelay relay;
 
 			// If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
 			if (vessel.loaded) {
@@ -261,16 +294,30 @@
 				));
 
 				// Loop through the Parts in the Vessel...
-				foreach (Part part in vessel.Parts)
-				{
+				Part part;
+				for (int partIdx = 0; partIdx < vessel.Parts.Count; partIdx++)
+				{
+					part = vessel.Parts[partIdx];
+
 					// ...loop through the PartModules in the Part...
-					foreach (PartModule module in part.Modules)
+					PartModule module;
+					for (int modIdx = 0; modIdx < part.Modules.Count; modIdx++)
 					{
+						module = part.Modules[modIdx];
+
 						// ...if the module is a relay...
 						if (module is IAntennaRelay)
 						{
+							relay = (module as IAntennaRelay);
+
+							if (relay.maxTransmitDistance > bestRelayRange)
+							{
+								bestRelayRange = relay.maxTransmitDistance;
+								bestRelay = relay;
+							}
+
 							// ...add the module to the table
-							relays.Add(part.GetHashCode(), module as IAntennaRelay);
+							relays.Add(relay);
 							// ...neglect relay objects after the first in each part.
 							break;
 						}
@@ -287,8 +334,11 @@
 				));
 
 				// Loop through the ProtoPartModuleSnapshots in the Vessel...
-				foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots)
-				{
+				ProtoPartSnapshot pps;
+				for (int ppsIdx = 0; ppsIdx < vessel.protoVessel.protoPartSnapshots.Count; ppsIdx++)
+				{
+					pps = vessel.protoVessel.protoPartSnapshots[ppsIdx];
+
 					Tools.PostDebugMessage(string.Format(
 						"{0}: Searching in protopartsnapshot {1}",
 						this.GetType().Name,
@@ -306,8 +356,11 @@
 					));
 
 					// ...loop through the PartModules in the prefab...
-					foreach (PartModule module in partPrefab.Modules)
+					PartModule module;
+					for (int modIdx = 0; modIdx < partPrefab.Modules.Count; modIdx++)
 					{
+						module = partPrefab.Modules[modIdx];
+
 						Tools.PostDebugMessage(string.Format(
 							"{0}: Searching in partmodule {1}",
 							this.GetType().Name,
@@ -323,14 +376,24 @@
 								module
 							));
 
+							relay = new ProtoAntennaRelay(module as IAntennaRelay, pps);
+
+							if (relay.maxTransmitDistance > bestRelayRange)
+							{
+								bestRelayRange = relay.maxTransmitDistance;
+								bestRelay = relay;
+							}
+
 							// ...build a new ProtoAntennaRelay and add it to the table
-							relays.Add(pps.GetHashCode(), new ProtoAntennaRelay(module as IAntennaRelay, pps));
+							relays.Add(relay);
 							// ...neglect relay objects after the first in each part.
 							break;
 						}
 					}
 				}
 			}
+
+			this.bestRelayTable[vessel.id] = bestRelay;
 
 			Tools.PostDebugMessage(string.Format(
 				"{0}: vessel '{1}' ({2}) has {3} transmitters.",
@@ -342,12 +405,12 @@
 		}
 
 		// Construct the singleton
-		protected RelayDatabase()
+		private RelayDatabase()
 		{
 			// Initialize the databases
-			this.relayDatabase = new Dictionary<Guid, Dictionary<int, IAntennaRelay>>();
+			this.relayDatabase = new Dictionary<Guid, List<IAntennaRelay>>();
+			this.bestRelayTable = new Dictionary<Guid, IAntennaRelay>();
 			this.vesselPartCountTable = new Dictionary<Guid, int>();
-			this.CheckedVesselsTable = new Dictionary<Guid, bool>();
 
 			this.cacheHits = 0;
 			this.cacheMisses = 0;
@@ -359,6 +422,7 @@
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange);
 			GameEvents.onPartCouple.Add(this.onFromPartToPartEvent);
 			GameEvents.onPartUndock.Add(this.onPartEvent);
+			GameEvents.onGameStateLoad.Add(this.onGameLoaded);
 		}
 
 		~RelayDatabase()
@@ -370,6 +434,7 @@
 			GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange);
 			GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent);
 			GameEvents.onPartUndock.Remove(this.onPartEvent);
+			GameEvents.onGameStateLoad.Remove(this.onGameLoaded);
 
 			Tools.PostDebugMessage(this.GetType().Name + " destroyed.");
 
@@ -385,21 +450,28 @@
 		#if DEBUG
 		public void Dump()
 		{
-			StringBuilder sb = new StringBuilder();
+			StringBuilder sb = Tools.GetStringBuilder();
 
 			sb.Append("Dumping RelayDatabase:");
 
-			foreach (Guid id in this.relayDatabase.Keys)
-			{
-				sb.AppendFormat("\nVessel {0}:", id);
-
-				foreach (IAntennaRelay relay in this.relayDatabase[id].Values)
-				{
+			var dbEnum = this.relayDatabase.GetEnumerator();
+			IList<IAntennaRelay> vesselRelays;
+			while (dbEnum.MoveNext())
+			{
+				sb.AppendFormat("\nVessel {0}:", dbEnum.Current.Key);
+
+				vesselRelays = dbEnum.Current.Value;
+				IAntennaRelay relay;
+				for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
+				{
+					relay = vesselRelays[rIdx];
 					sb.AppendFormat("\n\t{0}", relay.ToString());
 				}
 			}
 
 			Tools.PostDebugMessage(sb.ToString());
+
+			Tools.PutStringBuilder(sb);
 		}
 		#endif
 	}

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -2,7 +2,7 @@
 //
 // Extensions.cs
 //
-// Copyright © 2014, toadicus
+// Copyright © 2014-2015, toadicus
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -28,16 +28,59 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange
 {
-	/*
-	 * A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
-	 * */
+	/// <summary>
+	/// A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
+	/// </summary>
 	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>
@@ -68,17 +111,34 @@
 			return relayOne.DistanceTo(relayTwo.vessel);
 		}
 
-		public static double sqrDistanceTo(this AntennaRelay relay, Vessel vessel)
+		/// <summary>
+		/// Returns the square of the distance between this IAntennaRelay and a Vessel
+		/// </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)
 		{
 			return relay.vessel.sqrDistanceTo(vessel);
 		}
 
-		public static double sqrDistanceTo(this AntennaRelay relay, CelestialBody body)
-		{
-			return relay.vessel.sqrDistanceTo(body);
-		}
-
-		public static double sqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
+		/// <summary>
+		/// Returns the square of the distance between this IAntennaRelay and a CelestialBody
+		/// </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)
+		{
+			double dist = (relay.vessel.GetWorldPos3D() - body.position).magnitude - body.Radius;
+
+			return dist * dist;
+		}
+
+		/// <summary>
+		/// Returns the square of the distance between this IAntennaRelay and another IAntennaRelay
+		/// </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)
 		{
 			return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
 		}
@@ -87,9 +147,9 @@
 		/// Returns all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel.
 		/// </summary>
 		/// <param name="vessel">This <see cref="Vessel"/></param>
-		public static IEnumerable<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
-		{
-			return RelayDatabase.Instance[vessel].Values.ToList();
+		public static IList<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
+		{
+			return RelayDatabase.Instance[vessel];
 		}
 
 		/// <summary>
@@ -99,8 +159,11 @@
 		/// <param name="vessel"></param>
 		public static bool HasConnectedRelay(this Vessel vessel)
 		{
-			foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values)
-			{
+			IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel];
+			IAntennaRelay relay;
+			for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
+			{
+				relay = vesselRelays[rIdx];
 				if (relay.CanTransmit())
 				{
 					return true;
@@ -109,6 +172,60 @@
 
 			return false;
 		}
+
+		/// <summary>
+		/// Gets the <see cref="AntennaRange.ConnectionStatus"/> for this <see cref="Vessel"/>
+		/// </summary>
+		/// <param name="vessel">This <see cref="Vessel"/></param>
+		public static ConnectionStatus GetConnectionStatus(this Vessel vessel)
+		{
+			bool canTransmit = false;
+
+			IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel];
+			IAntennaRelay relay;
+			for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
+			{
+				relay = vesselRelays[rIdx];
+				if (relay.LinkStatus > ConnectionStatus.None)
+				{
+					canTransmit = true;
+
+					if (relay.LinkStatus == ConnectionStatus.Optimal)
+					{
+						return ConnectionStatus.Optimal;
+					}
+				}
+			}
+
+			if (canTransmit)
+			{
+				return ConnectionStatus.Suboptimal;
+			}
+			else
+			{
+				return ConnectionStatus.None;
+			}
+		}
+
+		/// <summary>
+		/// Gets the best relay on this Vessel.  The best relay may not be able to transmit.
+		/// </summary>
+		/// <param name="vessel">This <see cref="Vessel"/></param>
+		public static IAntennaRelay GetBestRelay(this Vessel vessel)
+		{
+			return RelayDatabase.Instance.GetBestVesselRelay(vessel);
+		}
+	}
+
+	#pragma warning disable 1591
+	/// <summary>
+	/// An Enum describing the connection status of a vessel or relay.
+	/// </summary>
+	public enum ConnectionStatus
+	{
+		None,
+		Suboptimal,
+		Optimal
 	}
 }