Probably fixed the flickering lines by only checking one relay per inactive vessel, and by setting BestVesselRelay per vessel in RelayDatabase.
Probably fixed the flickering lines by only checking one relay per inactive vessel, and by setting BestVesselRelay per vessel in RelayDatabase.

--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -10,38 +10,59 @@
 
 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
 	{
+		/// <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;
 			private set;
 		}
+
+#pragma warning disable 1591
 
 		private bool showConfigWindow;
 		private Rect configWindowPos;

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -25,6 +25,8 @@
 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 // 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;
@@ -239,6 +241,35 @@
 				FlightGlobals.ActiveVessel != null
 			)
 			{
+				Vessel vessel;
+				IAntennaRelay relay;
+				IList<IAntennaRelay> activeVesselRelays;
+
+				for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
+				{
+					vessel = FlightGlobals.Vessels[vIdx];
+
+					if (vessel == FlightGlobals.ActiveVessel)
+					{
+						continue;
+					}
+
+					relay = vessel.GetBestRelay();
+
+					if (relay != null)
+					{
+						relay.FindNearestRelay();
+					}
+				}
+
+				activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];
+				for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
+				{
+					relay = activeVesselRelays[rIdx];
+
+					relay.FindNearestRelay();
+				}
+
 				log.Append("Checking vessel relay status.\n");
 
 				this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -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;
@@ -38,6 +40,18 @@
 	{
 		#region Fields
 		private Dictionary<Guid, LineRenderer> vesselLineRenderers;
+
+		// 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
@@ -50,21 +64,27 @@
 					this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
 				}
 
-				if (!this.vesselLineRenderers.ContainsKey(idx))
+				LineRenderer lr;
+
+				if (this.vesselLineRenderers.TryGetValue(idx, out lr))
+				{
+					return lr;
+				}
+				else
 				{
 					GameObject obj = new GameObject();
 					obj.layer = 31;
 
-					LineRenderer lr = obj.AddComponent<LineRenderer>();
-
-					lr.SetColors(Color.green, Color.green);
+					lr = obj.AddComponent<LineRenderer>();
+
+					// lr.SetColors(Color.green, Color.green);
 					lr.material = MapView.OrbitLinesMaterial;
-					lr.SetVertexCount(2);
+					// lr.SetVertexCount(2);
 
 					this.vesselLineRenderers[idx] = lr;
-				}
-
-				return this.vesselLineRenderers[idx];
+
+					return lr;
+				}
 			}
 		}
 		#endregion
@@ -76,6 +96,11 @@
 			{
 				this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
 			}
+
+			#if DEBUG
+			this.timer = new System.Diagnostics.Stopwatch();
+			this.log = Tools.DebugLogger.New(this);
+			#endif
 		}
 
 		private void OnPreCull()
@@ -87,10 +112,14 @@
 				return;
 			}
 
-			Tools.DebugLogger log = Tools.DebugLogger.New(this);
+			#if DEBUG
+			timer.Restart();
+			#endif
 
 			try
 			{
+				log.Clear();
+
 				log.AppendFormat("OnPreCull.\n");
 
 				log.AppendFormat("\tMapView: Draw3DLines: {0}\n" +
@@ -101,21 +130,21 @@
 					MapView.MapCamera.Distance
 				);
 
-				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)
+					for (int i = 0; i < FlightGlobals.Vessels.Count; i++)
 					{
+						Vessel vessel = FlightGlobals.Vessels[i];
+
+						log.AppendFormat("\nStarting check for vessel {0} at {1}ms", vessel, timer.ElapsedMilliseconds);
+
 						if (vessel == null)
 						{
-							log.AppendFormat("Skipping vessel {0} altogether because it is null.\n");
+							log.AppendFormat("\n\tSkipping vessel {0} altogether because it is null.", vessel);
 							continue;
 						}
-
-						log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName);
 
 						switch (vessel.vesselType)
 						{
@@ -123,27 +152,47 @@
 							case VesselType.EVA:
 							case VesselType.Unknown:
 							case VesselType.SpaceObject:
-								log.AppendFormat("\tDiscarded because vessel is of invalid type {0}\n",
+								log.AppendFormat("\n\tDiscarded because vessel is of invalid type {0}",
 									vessel.vesselType);
 								continue;
 						}
 
+						log.AppendFormat("\n\tChecking vessel {0}.", vessel.vesselName);
+
+						start = timer.ElapsedMilliseconds;
+
 						IAntennaRelay vesselRelay = vessel.GetBestRelay();
+
+						if (vesselRelay == null)
+						{
+							continue;
+						}
+
+						log.AppendFormat("\n\tGot best relay {0} ({3}) for vessel {1} in {2} ms",
+							vesselRelay, vessel, timer.ElapsedMilliseconds - start, vesselRelay.GetType().Name);
 
 						if (vesselRelay != null)
 						{
+							start = timer.ElapsedMilliseconds;
+
 							this.SetRelayVertices(vesselRelay);
+
+							log.AppendFormat("\n\tSet relay vertices for {0} in {1}ms",
+								vessel, timer.ElapsedMilliseconds - start);
 						}
 					}
 				}
 			}
-			catch (Exception)
-			{
+			catch (Exception ex)
+			{
+				this.LogError("Caught {0}: {1}\n{2}\n", ex.GetType().Name, ex.ToString(), ex.StackTrace.ToString());
 				this.Cleanup();
 			}
 			#if DEBUG
 			finally
 			{
+				log.AppendFormat("\n\tOnPreCull finished in {0}ms\n", timer.ElapsedMilliseconds);
+
 				log.Print();
 			}
 			#endif
@@ -157,88 +206,114 @@
 		}
 		#endregion
 
+		#region Utility
 		private void SetRelayVertices(IAntennaRelay relay)
 		{
-			if (relay == null)
-			{
+			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;
-			Vector3d end;
+			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;
+
+			relayStart = timer.ElapsedMilliseconds;
+
+			Vector3d nextPoint;
 
 			renderer.enabled = true;
 
 			if (!relay.CanTransmit())
 			{
-				renderer.SetColors(Color.red, Color.red);
+				thisColor = Color.red;
 			}
 			else
 			{
 				if (relay.transmitDistance < relay.nominalTransmitDistance)
 				{
-					renderer.SetColors(Color.green, Color.green);
+					thisColor = Color.green;
 				}
 				else
 				{
-					renderer.SetColors(Color.yellow, Color.yellow);
-				}
-			}
-
-			start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
+					thisColor = Color.yellow;
+				}
+			}
 
 			if (relay.KerbinDirect)
 			{
-				end = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+				relay = null;
 			}
 			else
 			{
 				if (relay.targetRelay == null)
 				{
+					renderer.enabled = false;
 					return;
 				}
-				end = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
-			}
-
-			float lineWidth;
-
-			if (MapView.Draw3DLines)
-			{
-				lineWidth = 0.005859375f * MapView.MapCamera.Distance;
-			}
-			else
-			{
-				lineWidth = 2f;
-
-				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);
-		}
-
-		public void Cleanup()
+
+				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
+				relay = relay.targetRelay;
+			}
+
+			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()
 		{
 			if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
 			{
-				foreach (LineRenderer lineRenderer in this.vesselLineRenderers.Values)
-				{
+				IEnumerator<LineRenderer> enumerator = this.vesselLineRenderers.Values.GetEnumerator();
+				LineRenderer lineRenderer;
+
+				while (enumerator.MoveNext())
+				{
+					lineRenderer = enumerator.Current;
 					lineRenderer.enabled = false;
 					GameObject.Destroy(lineRenderer.gameObject);
 				}
 				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>
@@ -79,6 +80,7 @@
     <Compile Include="ARConfiguration.cs" />
     <Compile Include="ARFlightController.cs" />
     <Compile Include="ARMapRenderer.cs" />
+    <Compile Include="VesselCache.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -28,15 +28,24 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using ToadicusTools;
 
 namespace AntennaRange
 {
+	/// <summary>
+	/// Relay code at the heart of AntennaRange
+	/// </summary>
 	public class AntennaRelay
 	{
+		public static readonly System.Diagnostics.Stopwatch searchTimer = new System.Diagnostics.Stopwatch();
+		public const long millisecondsBetweenSearches = 125L;
+
 		// 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
@@ -50,15 +59,19 @@
 			}
 		}
 
-		protected bool canTransmit;
-
-		protected IAntennaRelay moduleRef;
+		private long lastSearch;
+
+		private bool canTransmit;
+		private bool isChecked;
 
 		private IAntennaRelay nearestRelay;
 		private IAntennaRelay bestOccludedRelay;
 
-		protected System.Diagnostics.Stopwatch searchTimer;
-		protected long millisecondsBetweenSearches;
+		/// <summary>
+		/// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an
+		/// <see cref="AntennaRange.IAntennaRelay"/>
+		/// </summary>
+		protected IAntennaRelay moduleRef;
 
 		/// <summary>
 		/// Gets the parent Vessel.
@@ -72,6 +85,9 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+		/// </summary>
 		public IAntennaRelay targetRelay
 		{
 			get;
@@ -95,8 +111,6 @@
 		{
 			get
 			{
-				this.FindNearestRelay();
-
 				if (this.KerbinDirect || this.targetRelay == null)
 				{
 					return this.DistanceTo(Kerbin);
@@ -108,6 +122,9 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+		/// </summary>
 		public virtual double nominalTransmitDistance
 		{
 			get;
@@ -124,6 +141,10 @@
 			set;
 		}
 
+		/// <summary>
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
 		public virtual bool KerbinDirect
 		{
 			get;
@@ -136,7 +157,6 @@
 		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
 		public virtual bool CanTransmit()
 		{
-			this.FindNearestRelay();
 			return this.canTransmit;
 		}
 
@@ -144,40 +164,64 @@
 		/// Finds the nearest relay.
 		/// </summary>
 		/// <returns>The nearest relay or null, if no relays in range.</returns>
-		private void FindNearestRelay()
-		{
-			if (!this.searchTimer.IsRunning || this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
-			{
-				this.searchTimer.Reset();
-			}
-			else
+		public void FindNearestRelay()
+		{
+			if (!FlightGlobals.ready)
 			{
 				return;
 			}
 
+			if (!searchTimer.IsRunning)
+			{
+				searchTimer.Start();
+			}
+
+			Tools.DebugLogger log;
+			#if DEBUG
+			log = Tools.DebugLogger.New(this);
+			#endif
+
+			long searchTime = searchTimer.ElapsedMilliseconds;
+			long timeSinceLast = searchTime - this.lastSearch;
+
+			if (timeSinceLast < millisecondsBetweenSearches)
+			{
+				log.AppendFormat(
+					"{0}: Target search skipped because it's not time to search again yet ({1} - {2}) < {3})",
+					this, searchTime, this.lastSearch, millisecondsBetweenSearches
+				);
+				log.Print();
+				return;
+			}
+
 			// Skip vessels that have already been checked for a nearest relay this pass.
-			if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(this.vessel.id))
-			{
+			if (this.isChecked)
+			{
+				log.AppendFormat("{0}: Target search skipped because our vessel has been checked already this search.",
+					this);
+				log.Print();
 				return;
 			}
 
-			if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-			{
-				Tools.PostDebugMessage(string.Format(
-					"{0}: finding nearest relay for {1}",
-					this.GetType().Name,
-					this.ToString()
-				));
-			}
-
+			log.AppendFormat("{0}: Target search started at {1} ms ({2} ms since last search).",
+				this.ToString(), searchTime, timeSinceLast);
+
+			#if DEBUG
+			try {
+			#endif
 			// Set this vessel as checked, so that we don't check it again.
-			RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
+			this.isChecked = true;
+
+			this.lastSearch = searchTime;
 
 			// Blank everything we're trying to find before the search.
 			this.firstOccludingBody = null;
 			this.bestOccludedRelay = null;
 			this.targetRelay = null;
 			this.nearestRelay = null;
+
+			// Default to KerbinDirect = true in case something in here doesn't work right.
+			this.KerbinDirect = true;
 
 			CelestialBody bodyOccludingBestOccludedRelay = null;
 
@@ -191,9 +235,28 @@
 			 * 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)
-			{
+			Vessel potentialVessel;
+			IList<IAntennaRelay> vesselRelays;
+			for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
+			{
+				log.AppendFormat("\nFetching vessel at index {0}", vIdx);
+				potentialVessel = FlightGlobals.Vessels[vIdx];
+				
+				if (potentialVessel == null)
+				{
+					log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx);
+					log.Print();
+					return;
+				}
+				#if DEBUG
+				else
+				{
+					log.AppendFormat("\n\tGot vessel {0}", potentialVessel);
+				}
+				#endif
+
 				// Skip vessels of the wrong type.
+				log.Append("\n\tchecking vessel type");
 				switch (potentialVessel.vesselType)
 				{
 					case VesselType.Debris:
@@ -201,121 +264,142 @@
 					case VesselType.EVA:
 					case VesselType.SpaceObject:
 					case VesselType.Unknown:
+							log.Append("\n\tSkipping because vessel is the wrong type.");
 						continue;
 					default:
 						break;
 				}
-
+				
+				log.Append("\n\tchecking if vessel is this vessel");
 				// Skip vessels with the wrong ID
 				if (potentialVessel.id == vessel.id)
 				{
+					log.Append("\n\tSkipping because vessel is this vessel.");
 					continue;
 				}
 
 				// Find the distance from here to the vessel...
+				log.Append("\n\tgetting distance to potential vessel");
 				double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
+				log.Append("\n\tgetting vessel relays");
+				vesselRelays = potentialVessel.GetAntennaRelays();
+					log.AppendFormat("\n\t\tvesselRelays: {0}",
+						vesselRelays == null ? "null" : vesselRelays.Count.ToString());
 
 				CelestialBody fob = null;
-
+				
+				log.Append("\n\tdoing LOS check");
 				// Skip vessels to which we do not have line of sight.
 				if (
 					ARConfiguration.RequireLineOfSight &&
 					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
 				)
 				{
+					log.Append("\n\tfailed LOS check");
 					this.firstOccludingBody = fob;
-
-					if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-					{
-						Tools.PostDebugMessage("{6}: 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),
-							this.ToString()
-						);
-					}
+					
+					log.AppendFormat("\n\t{0}: Vessel {1} not in line of sight.",
+						this.ToString(), potentialVessel.vesselName);
+					
+					log.AppendFormat("\n\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
+					log.AppendFormat("\n\t\tbestOccludedSqrDistance: {0}", bestOccludedSqrDistance);
+					log.AppendFormat("\n\t\tmaxTransmitSqrDistance: {0}", maxTransmitSqrDistance);
 
 					if (
 						(potentialSqrDistance < bestOccludedSqrDistance) &&
 						(potentialSqrDistance < maxTransmitSqrDistance)
 					)
 					{
-						if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+						log.Append("\n\t\t...vessel is close enough to check for occluded relays");
+						log.AppendFormat("\n\t\tthis: {0}", this);
+						log.AppendFormat("\n\t\tpotentialVessel: {0}",
+							potentialVessel == null ? "null" : potentialVessel.ToString());
+						log.AppendFormat("\n\t\tvesselRelays: {0}",
+							vesselRelays == null ? "null" : vesselRelays.ToString());
+
+						log.AppendFormat("\n\t\t{0}: Checking {1} relays on occluded vessel {2}.",
+							this.ToString(),
+							vesselRelays.Count,
+							potentialVessel
+						);
+
+						IAntennaRelay occludedRelay;
+						for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
 						{
-							Tools.PostDebugMessage("{0}: Checking {1} relays on {2}.",
-								this.ToString(),
-								potentialVessel.GetAntennaRelays().Count(),
-								potentialVessel
+							occludedRelay = vesselRelays[rIdx];
+
+							log.AppendFormat(
+								"\n\t\t{0}: Checking candidate for bestOccludedRelay: {1}" +
+								"\n\t\tCanTransmit: {2}",
+								this.ToString(), occludedRelay, occludedRelay.CanTransmit()
 							);
-						}
-
-						foreach (IAntennaRelay occludedRelay in potentialVessel.GetAntennaRelays())
-						{
-							if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-							{
-								Tools.PostDebugMessage(this.ToString() +  " Checking candidate for bestOccludedRelay: {0}" +
-									"\n\tCanTransmit: {1}", occludedRelay, occludedRelay.CanTransmit());
-							}
-
+							
 							if (occludedRelay.CanTransmit())
 							{
 								this.bestOccludedRelay = occludedRelay;
 								bodyOccludingBestOccludedRelay = fob;
 								bestOccludedSqrDistance = potentialSqrDistance;
 
-								if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-								{
-									Tools.PostDebugMessage(this.ToString() + " Found new bestOccludedRelay: {0}" +
-										"\nfirstOccludingBody: {1}" +
-										"\nbestOccludedSqrDistance: {2}",
-										occludedRelay,
-										fob,
-										potentialSqrDistance
-									);
-								}
+								log.AppendFormat("\n\t{0}: Found new bestOccludedRelay: {1}" +
+									" (blocked by {2}; distance: {3} m)",
+									this.ToString(),
+									occludedRelay.ToString(),
+									fob,
+									potentialSqrDistance
+								);
 								break;
 							}
 						}
 					}
-
+					
+					log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
 					continue;
 				}
+
+				log.Append("\n\tpassed LOS check");
 
 				/*
 				 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
 				 * */
 				if (potentialSqrDistance > nearestRelaySqrDistance)
 				{
-					if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-					{
-						Tools.PostDebugMessage("{0}: Vessel {1} discarded because it is out of range, or farther than another relay.",
+					
+					log.AppendFormat("\n\t{0}: Vessel {1} discarded because it is farther than another the nearest relay.",
+						this.ToString(),
+						potentialVessel.vesselName
+					);
+					continue;
+				}
+
+				log.Append("\n\tpassed distance check");
+
+				IAntennaRelay potentialRelay;
+				for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
+				{
+					log.AppendFormat("\n\t\tfetching vessel relay at index {0}", rIdx);
+					potentialRelay = vesselRelays[rIdx];
+					log.AppendFormat("\n\t\tgot relay {0}", potentialRelay == null ? "null" : potentialRelay.ToString());
+
+					if (potentialRelay == null)
+					{
+						log.Append("\n\t\t...skipping null relay");
+						continue;
+					}
+
+					if (
+						potentialRelay.CanTransmit() &&
+						(potentialRelay.targetRelay == null || potentialRelay.targetRelay.vessel != this.vessel))
+					{
+						// @TODO: Moved this here from outside the loop; why was it there?
+						nearestRelaySqrDistance = potentialSqrDistance;
+						this.nearestRelay = potentialRelay;
+
+						log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
 							this.ToString(),
-							potentialVessel.vesselName
+							this.nearestRelay.ToString(),
+							Math.Sqrt(nearestRelaySqrDistance)
 						);
-					}
-					continue;
-				}
-
-				nearestRelaySqrDistance = potentialSqrDistance;
-
-				foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
-				{
-					if (potentialRelay.CanTransmit() && potentialRelay.targetRelay != this)
-					{
-						this.nearestRelay = potentialRelay;
-
-						if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
-						{
-							Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
-								this.ToString(),
-								this.nearestRelay.ToString(),
-								this.nearestRelay.vessel.id
-							));
-						}
+						
 						break;
 					}
 				}
@@ -326,9 +410,7 @@
 			double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
 			kerbinSqrDistance *= kerbinSqrDistance;
 
-			Tools.DebugLogger log = Tools.DebugLogger.New(this);
-
-			log.AppendFormat("{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
+			log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
 
 			// If we don't have LOS to Kerbin, focus on relays
 			if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio))
@@ -339,7 +421,7 @@
 				// Therefore, this will only be true if a valid relay is in range.
 				if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
 				{
-					log.AppendFormat("\n\tCan transmit to nearby relay {0} ({1} <= {2}).",
+					log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
 						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
 							nearestRelaySqrDistance, maxTransmitSqrDistance);
 
@@ -350,81 +432,118 @@
 				// If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
 				else
 				{
-					log.AppendFormat("\n\tCan't transmit to nearby relay {0} ({1} > {2}).",
+					log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
 						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
 							nearestRelaySqrDistance, maxTransmitSqrDistance);
 
 					this.canTransmit = false;
 
-					// If the best occluded relay is closer than Kerbin, target it.
+					// 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 (bestOccludedSqrDistance < kerbinSqrDistance)
 					{
-						log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).",
-							this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
-							bestOccludedSqrDistance, kerbinSqrDistance);
-
+						log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
+							bestOccludedRelay, kerbinSqrDistance);
+						
 						this.KerbinDirect = false;
-						this.targetRelay = this.bestOccludedRelay;
-						this.firstOccludingBody = bodyOccludingBestOccludedRelay;
-					}
-					// Otherwise, target Kerbin and report the first body blocking it.
+
+						// 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 (nearestRelaySqrDistance < bestOccludedSqrDistance)
+						{
+							log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+								nearestRelaySqrDistance, bestOccludedSqrDistance);
+							
+							this.targetRelay = 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.",
+								nearestRelaySqrDistance, bestOccludedSqrDistance);
+							
+							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\tPicking Kerbin as target ({0} >= {1}).",
-							bestOccludedSqrDistance, kerbinSqrDistance);
+						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 (nearestRelaySqrDistance < kerbinSqrDistance)
+						{
+							log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+								nearestRelaySqrDistance, kerbinSqrDistance);
+							
+							this.KerbinDirect = false;
+							this.targetRelay = nearestRelay;
+						}
+						// Otherwise, pick Kerbin.
+						else
+						{
+							log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+								nearestRelaySqrDistance, kerbinSqrDistance);
+							
+							this.KerbinDirect = true;
+							this.targetRelay = null;
+						}
+					}
+				}
+			}
+			// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
+			else
+			{
+				log.AppendFormat("\n\tKerbin is in LOS.");
+
+				// If the nearest relay is closer than Kerbin and in range, transmit to it.
+				if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
+				{
+					log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
+						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+							nearestRelaySqrDistance, maxTransmitSqrDistance);
+
+					this.canTransmit = true;
+
+					// If the nearestRelay is closer than Kerbin, use it.
+					if (nearestRelaySqrDistance < kerbinSqrDistance)
+					{
+						log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
+							this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+							nearestRelaySqrDistance, kerbinSqrDistance);
+
+						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(),
+								nearestRelaySqrDistance, kerbinSqrDistance);
 
 						this.KerbinDirect = true;
 						this.targetRelay = null;
-						this.firstOccludingBody = bodyOccludingKerbin;
-					}
-				}
-			}
-			// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
-			else
-			{
-				log.AppendFormat("\n\tKerbin is in LOS.");
-
-				// If the nearest relay is closer than Kerbin and in range, transmit to it.
-				if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
-				{
-					log.AppendFormat("\n\tCan transmit to nearby relay {0} ({1} <= {2}).",
+					}
+				}
+				// 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(),
 							nearestRelaySqrDistance, maxTransmitSqrDistance);
 
-					this.canTransmit = true;
-
-					// If the nearestRelay is closer than Kerbin, use it.
-					if (nearestRelaySqrDistance < kerbinSqrDistance)
-					{
-						log.AppendFormat("\n\tPicking relay {0} over Kerbin ({1} < {2}).",
-							this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
-							nearestRelaySqrDistance, kerbinSqrDistance);
-
-						this.KerbinDirect = false;
-						this.targetRelay = this.nearestRelay;
-					}
-					// Otherwise, Kerbin is closer, so use it.
-					else
-					{
-						log.AppendFormat("\n\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
-							this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
-								nearestRelaySqrDistance, kerbinSqrDistance);
-
-						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\tCan't transmit to nearby relay {0} ({1} > {2}).",
-						this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
-							nearestRelaySqrDistance, maxTransmitSqrDistance);
-
 					// If Kerbin is in range, use it.
 					if (kerbinSqrDistance <= maxTransmitSqrDistance)
 					{
-						log.AppendFormat("\n\tCan transmit to Kerbin ({0} <= {1}).",
+						log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).",
 							kerbinSqrDistance, maxTransmitSqrDistance);
 
 						this.canTransmit = true;
@@ -435,47 +554,97 @@
 					// Kerbin and bestOccludedRelay
 					else
 					{
-						log.AppendFormat("\n\tCan't transmit to Kerbin ({0} > {1}).",
+						log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).",
 							kerbinSqrDistance, maxTransmitSqrDistance);
 
 						this.canTransmit = false;
 
-						// If the best occluded relay is closer than Kerbin, use it.
-						// Since bestOccludedSqrDistance is infinity if there are no occluded relays,
-						// this is safe
+						// 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 (bestOccludedSqrDistance < kerbinSqrDistance)
 						{
-							log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).",
-								this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
-									bestOccludedSqrDistance, kerbinSqrDistance);
-
+							log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
+								bestOccludedRelay, kerbinSqrDistance);
+							
 							this.KerbinDirect = false;
-							this.targetRelay = bestOccludedRelay;
-							this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+
+							// 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 (nearestRelaySqrDistance < bestOccludedSqrDistance)
+							{
+								log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+									nearestRelaySqrDistance, bestOccludedSqrDistance);
+								
+								this.targetRelay = 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.",
+									nearestRelaySqrDistance, bestOccludedSqrDistance);
+								
+								this.targetRelay = bestOccludedRelay;
+								this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+							}
 						}
-						// Otherwise, target Kerbin.  Since we have LOS, blank the first occluding body.
+						// Otherwise, check Kerbin against the nearest relay.
+						// Since we have LOS, blank the first occluding body.
 						else
 						{
-							log.AppendFormat("\n\t\tPicking Kerbin as target ({0} >= {1}).",
-								bestOccludedSqrDistance, kerbinSqrDistance);
-
-							this.KerbinDirect = true;
-							this.targetRelay = null;
+							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 (nearestRelaySqrDistance < kerbinSqrDistance)
+							{
+								log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
+									nearestRelaySqrDistance, kerbinSqrDistance);
+								
+								this.KerbinDirect = false;
+								this.targetRelay = nearestRelay;
+							}
+							// Otherwise, pick Kerbin.
+							else
+							{
+								log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
+									nearestRelaySqrDistance, kerbinSqrDistance);
+								
+								this.KerbinDirect = true;
+								this.targetRelay = null;
+							}
 						}
 					}
 				}
 			}
 
+			log.AppendFormat("{0}: Target search completed at {1} ms ({2} ms elapsed).",
+				this.ToString(), searchTimer.ElapsedMilliseconds, searchTimer.ElapsedMilliseconds - searchTime);;
+
 			log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
-
-			log.Print();
-
+			
+			#if DEBUG
+			} catch (Exception ex) {
+				log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
+				UnityEngine.Application.Quit();
+			} finally {
+			#endif
+			log.Print(false);
+			#if DEBUG
+			}
+			#endif
 			// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
 			// used next time.
-			RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
-		}
-
+			this.isChecked = false;
+		}
+
+		/// <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)
@@ -486,15 +655,16 @@
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
-		/// </summary>
-		/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
+		/// 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 = 125L;
+			this.isChecked = false;
+
+			Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
 		}
 	}
 }

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -83,6 +83,8 @@
 		/// </summary>
 		bool CanTransmit();
 
+		void FindNearestRelay();
+
 		/// <summary>
 		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.IAntennaRelay"/>.
 		/// </summary>

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -29,78 +29,96 @@
 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
-	 * */
+	/// <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
 	{
 		// 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;
+
+		/// <summary>
+		/// 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>
+		/// 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;
 
+		/// <summary>
+		/// Maximum distance string for use in action menus.
+		/// </summary>
 		[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
 		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,12 +129,14 @@
 		[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
@@ -137,6 +157,9 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+		/// </summary>
 		public IAntennaRelay targetRelay
 		{
 			get
@@ -150,7 +173,9 @@
 			}
 		}
 
-		// Returns the distance to the target relay or Kerbin, whichever is closer.
+		/// <summary>
+		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// </summary>
 		public double transmitDistance
 		{
 			get
@@ -164,6 +189,9 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+		/// </summary>
 		public double nominalTransmitDistance
 		{
 			get
@@ -172,7 +200,9 @@
 			}
 		}
 
-		// Returns the maximum distance this module can transmit
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
 		public double maxTransmitDistance
 		{
 			get
@@ -182,6 +212,9 @@
 			}
 		}
 
+		/// <summary>
+		/// The first CelestialBody blocking line of sight to a 
+		/// </summary>
 		public CelestialBody firstOccludingBody
 		{
 			get
@@ -216,8 +249,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
@@ -235,9 +270,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
 			{
@@ -254,6 +291,10 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
 		public bool KerbinDirect
 		{
 			get
@@ -267,6 +308,9 @@
 			}
 		}
 
+		/// <summary>
+		/// Gets the Part title.
+		/// </summary>
 		public string Title
 		{
 			get
@@ -290,6 +334,9 @@
 			this.packetThrottle = 100f;
 		}
 
+		/// <summary>
+		/// PartModule OnAwake override; runs at Unity Awake.
+		/// </summary>
 		public override void OnAwake()
 		{
 			base.OnAwake();
@@ -313,7 +360,10 @@
 			));
 		}
 
-		// 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);
@@ -331,9 +381,12 @@
 			}
 		}
 
-		// 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);
@@ -342,81 +395,9 @@
 			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
-			);
-
-			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;
-			}
-			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.
+		/// <summary>
+		/// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
+		/// </summary>
 		public override string GetInfo()
 		{
 			string text = base.GetInfo();
@@ -425,7 +406,10 @@
 			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)
@@ -433,25 +417,43 @@
 				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)
-		{
+		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.FindNearestRelay();
+
 			this.PreTransmit_SetPacketSize();
 			this.PreTransmit_SetPacketResourceCost();
 
@@ -459,7 +461,7 @@
 			{
 				ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
 
-				base.TransmitData(dataQueue);
+				base.TransmitData(dataQueue, callback);
 			}
 			else
 			{
@@ -467,8 +469,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);
 
@@ -483,8 +489,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");
@@ -522,9 +530,11 @@
 					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)
+					ScienceData data;
+					for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
 					{
-						msg.AppendFormat("\n{0}\n", data.title);
+						data = dataQueue[dIdx];
+						msg.AppendFormat("\t{0}\n", data.title);
 					}
 
 					ScreenMessages.PostScreenMessage(msg.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
@@ -532,7 +542,7 @@
 					Tools.PostDebugMessage(msg.ToString());
 				}
 
-				this.PostCannotTransmitError ();
+				this.PostCannotTransmitError();
 			}
 
 			Tools.PostDebugMessage (
@@ -542,10 +552,24 @@
 			);
 		}
 
-		// 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.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 ();
 
@@ -567,6 +591,9 @@
 			}
 		}
 
+		/// <summary>
+		/// MonoBehaviour Update
+		/// </summary>
 		public void Update()
 		{
 			if (this.actionUIUpdate)
@@ -604,22 +631,10 @@
 			}
 		}
 
-		public void onPartActionUICreate(Part eventPart)
-		{
-			if (eventPart == base.part)
-			{
-				this.actionUIUpdate = true;
-			}
-		}
-
-		public void onPartActionUIDismiss(Part eventPart)
-		{
-			if (eventPart == base.part)
-			{
-				this.actionUIUpdate = false;
-			}
-		}
-
+		/// <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 msg = new StringBuilder();
@@ -643,6 +658,98 @@
 			}
 
 			return msg.ToString();
+		}
+
+		// 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)
+			{
+				this.actionUIUpdate = true;
+			}
+		}
+
+		// 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)
+			{
+				this.actionUIUpdate = false;
+			}
+		}
+
+		// 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.transmitDistance <= this.nominalRange)
+			{
+				base.packetResourceCost = this._basepacketResourceCost;
+			}
+			else
+			{
+				float rangeFactor = (float)(this.transmitDistance / this.nominalRange);
+				rangeFactor *= rangeFactor;
+
+				base.packetResourceCost = this._basepacketResourceCost
+					* 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.
+		private void PreTransmit_SetPacketSize()
+		{
+			if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.nominalRange)
+			{
+				base.packetSize = this._basepacketSize;
+			}
+			else
+			{
+				float rangeFactor = (float)(this.nominalRange / this.transmitDistance);
+				rangeFactor *= rangeFactor;
+
+				base.packetSize = Mathf.Min(
+					this._basepacketSize * 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;
 		}
 
 		private string buildTransmitMessage()
@@ -719,8 +826,10 @@
 
 			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);
 			}
 

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -28,28 +28,46 @@
 
 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)
+				{
+					return this.protoPart.pVesselRef.vesselRef;
+				}
+				else
+				{
+					Tools.PostLogMessage("{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,9 +77,8 @@
 		}
 
 		/// <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 double maxTransmitDistance
 		{
 			get
@@ -87,6 +104,10 @@
 			}
 		}
 
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
+		/// </summary>
 		public override bool CanTransmit()
 		{
 			PartStates partState = (PartStates)this.protoPart.state;
@@ -104,31 +125,35 @@
 			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 = new System.Text.StringBuilder();
+
+			sb.Append(this.Title);
+
+			if (this.protoPart != null && this.protoPart.pVesselRef != null)
+			{
+				sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
+			}
+
+			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
@@ -25,6 +25,8 @@
 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 // 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;
@@ -41,7 +43,7 @@
 		 * Static members
 		 * */
 		// Singleton storage
-		protected static RelayDatabase _instance;
+		private static RelayDatabase _instance;
 		// Gets the singleton
 		public static RelayDatabase Instance
 		{
@@ -64,22 +66,23 @@
 		 * 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;
+		private 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 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 +107,50 @@
 				}
 
 				// 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)
+		{
+			this.relayDatabase.Remove(vessel.id);
+			this.vesselPartCountTable.Remove(vessel.id);
+			this.relayDatabase.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);
+		}
+
+		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 +170,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 +181,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 +194,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,7 +226,7 @@
 		}
 
 		// Runs when the player requests a scene change, such as when changing vessels or leaving flight.
-		public void onSceneChange(GameScenes scene)
+		private void onSceneChange(GameScenes scene)
 		{
 			// If the active vessel is a real thing...
 			if (FlightGlobals.ActiveVessel != null)
@@ -225,7 +237,7 @@
 		}
 
 		// Runs when parts are undocked
-		public void onPartEvent(Part part)
+		private void onPartEvent(Part part)
 		{
 			if (part != null && part.vessel != null)
 			{
@@ -234,14 +246,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 +263,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 +277,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 +317,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 +339,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 +359,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,10 +388,11 @@
 		}
 
 		// 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>();
 
@@ -389,12 +436,17 @@
 
 			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());
 				}
 			}

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -28,14 +28,13 @@
 
 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>
@@ -68,16 +67,31 @@
 			return relayOne.DistanceTo(relayTwo.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);
 		}
 
+		/// <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)
 		{
 			return relay.vessel.sqrDistanceTo(body);
 		}
 
+		/// <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 +101,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)
+		public static IList<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
 		{
-			return RelayDatabase.Instance[vessel].Values.ToList().AsReadOnly();
+			return RelayDatabase.Instance[vessel];
 		}
 
 		/// <summary>
@@ -99,8 +113,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;
@@ -110,12 +127,19 @@
 			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;
 
-			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())
 				{
 					canTransmit = true;
@@ -136,27 +160,20 @@
 			}
 		}
 
+		/// <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)
 		{
-			IAntennaRelay bestRelay = null;
-			double bestScore = double.PositiveInfinity;
-			double relayScore = double.NaN;
-
-			foreach (IAntennaRelay relay in vessel.GetAntennaRelays())
-			{
-				relayScore = relay.transmitDistance / relay.maxTransmitDistance;
-
-				if (relayScore < bestScore)
-				{
-					bestScore = relayScore;
-					bestRelay = relay;
-				}
-			}
-
-			return bestRelay;
+			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,

file:b/VesselCache.cs (new)
--- /dev/null
+++ b/VesselCache.cs
@@ -1,1 +1,16 @@
+// AntennaRange © 2015 toadicus
+//
+// This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a
+// copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/
+using System;
 
+namespace AntennaRange
+{
+	public class Relay
+	{
+		public Vessel vessel;
+		public IAntennaRelay targetRelay;
+	}
+}
+
+