Hopefully the last bulk update for relays. Also includes a bunch of commenting.
Hopefully the last bulk update for relays. Also includes a bunch of commenting.

file:b/ARTools.cs (new)
--- /dev/null
+++ b/ARTools.cs
@@ -1,1 +1,113 @@
+using System;
 
+namespace AntennaRange
+{
+	public static class Tools
+	{
+		private static ScreenMessage debugmsg = new ScreenMessage("", 2f, ScreenMessageStyle.UPPER_RIGHT);
+
+		[System.Diagnostics.Conditional("DEBUG")]
+		public static void PostDebugMessage(string Msg)
+		{
+			if (HighLogic.LoadedScene > GameScenes.SPACECENTER)
+			{
+				debugmsg.message = Msg;
+				ScreenMessages.PostScreenMessage(debugmsg, true);
+			}
+
+			KSPLog.print(Msg);
+		}
+
+		/*
+		 * MuMech_ToSI is a part of the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
+		 * */
+		public static string MuMech_ToSI(double d, int digits = 3, int MinMagnitude = 0, int MaxMagnitude = int.MaxValue)
+		{
+			float exponent = (float)Math.Log10(Math.Abs(d));
+			exponent = UnityEngine.Mathf.Clamp(exponent, (float)MinMagnitude, (float)MaxMagnitude);
+
+			if (exponent >= 0)
+			{
+				switch ((int)Math.Floor(exponent))
+				{
+					case 0:
+						case 1:
+						case 2:
+						return d.ToString("F" + digits);
+						case 3:
+						case 4:
+						case 5:
+						return (d / 1e3).ToString("F" + digits) + "k";
+						case 6:
+						case 7:
+						case 8:
+						return (d / 1e6).ToString("F" + digits) + "M";
+						case 9:
+						case 10:
+						case 11:
+						return (d / 1e9).ToString("F" + digits) + "G";
+						case 12:
+						case 13:
+						case 14:
+						return (d / 1e12).ToString("F" + digits) + "T";
+						case 15:
+						case 16:
+						case 17:
+						return (d / 1e15).ToString("F" + digits) + "P";
+						case 18:
+						case 19:
+						case 20:
+						return (d / 1e18).ToString("F" + digits) + "E";
+						case 21:
+						case 22:
+						case 23:
+						return (d / 1e21).ToString("F" + digits) + "Z";
+						default:
+						return (d / 1e24).ToString("F" + digits) + "Y";
+				}
+			}
+			else if (exponent < 0)
+			{
+				switch ((int)Math.Floor(exponent))
+				{
+					case -1:
+						case -2:
+						case -3:
+						return (d * 1e3).ToString("F" + digits) + "m";
+						case -4:
+						case -5:
+						case -6:
+						return (d * 1e6).ToString("F" + digits) + "μ";
+						case -7:
+						case -8:
+						case -9:
+						return (d * 1e9).ToString("F" + digits) + "n";
+						case -10:
+						case -11:
+						case -12:
+						return (d * 1e12).ToString("F" + digits) + "p";
+						case -13:
+						case -14:
+						case -15:
+						return (d * 1e15).ToString("F" + digits) + "f";
+						case -16:
+						case -17:
+						case -18:
+						return (d * 1e18).ToString("F" + digits) + "a";
+						case -19:
+						case -20:
+						case -21:
+						return (d * 1e21).ToString("F" + digits) + "z";
+						default:
+						return (d * 1e24).ToString("F" + digits) + "y";
+				}
+			}
+			else
+			{
+				return "0";
+			}
+		}
+	}
+}
+
+

--- a/AntennaRange.cs
+++ b/AntennaRange.cs
@@ -21,6 +21,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using KSP;
+using UnityEngine;
 
 namespace AntennaRange
 {
@@ -40,16 +41,20 @@
 	/*
 	 * Fields
 	 * */
-	public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter
+	public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
 	{
+		// Call this an antenna so that you don't have to.
+		[KSPField(isPersistant = true)]
+		protected bool IsAntenna = true;
+
 		// Stores the packetResourceCost as defined in the .cfg file.
 		protected float _basepacketResourceCost;
 
 		// Stores the packetSize as defined in the .cfg file.
 		protected float _basepacketSize;
 
-		// We don't have a Bard, so we're hiding Kerbin here.
-		protected CelestialBody _Kerbin;
+		// Every antenna is a relay.
+		protected AntennaRelay relay;
 
 		// Keep track of vessels with transmitters for relay purposes.
 		protected List<Vessel> _relayVessels;
@@ -74,27 +79,37 @@
 		[KSPField(isPersistant = false)]
 		public float maxDataFactor;
 
+		// This field exists to get saved to the persistence file so that relays can be found on unloaded Vessels.
+		[KSPField(isPersistant = true)]
+		protected float ARmaxTransmitDistance;
+
 		/*
 		 * Properties
 		 * */
-		// Returns the current distance to the center of Kerbin, which is totally where the Kerbals keep their radioes.
-		protected double transmitDistance
-		{
-			get
-			{
-				Vector3d KerbinPos = this._Kerbin.position;
-				Vector3d ActivePos = base.vessel.GetWorldPos3D();
-
-				return (ActivePos - KerbinPos).magnitude;
+		// Returns the parent vessel housing this antenna.
+		public new Vessel vessel
+		{
+			get
+			{
+				return base.vessel;
+			}
+		}
+
+		// Returns the distance to the nearest relay or Kerbin, whichever is closer.
+		public double transmitDistance
+		{
+			get
+			{
+				return this.relay.transmitDistance;
 			}
 		}
 
 		// Returns the maximum distance this module can transmit
-		public double maxTransmitDistance
-		{
-			get
-			{
-				return Math.Sqrt (this.maxPowerFactor) * this.nominalRange;
+		public float maxTransmitDistance
+		{
+			get
+			{
+				return this.ARmaxTransmitDistance;
 			}
 		}
 
@@ -154,10 +169,13 @@
 			}
 		}
 
+		// Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
 		public bool relayChecked
 		{
-			get;
-			protected set;
+			get
+			{
+				return this.relay.relayChecked;
+			}
 		}
 
 		/*
@@ -180,17 +198,12 @@
 		// At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
 		public override void OnStart (StartState state)
 		{
-			this.relayChecked = false;
-
 			base.OnStart (state);
 
 			if (state >= StartState.PreLaunch)
 			{
-				if (this._Kerbin == null)
-				{
-					// Go fetch Kerbin, because it is tricksy and hides from us.
-					this._Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
-				}
+				this.relay = new AntennaRelay(vessel);
+				this.relay.maxTransmitDistance = this.maxTransmitDistance;
 			}
 
 			// Pre-set the transmit cost and packet size when loading.
@@ -205,6 +218,8 @@
 		{
 			this.Fields.Load(node);
 			base.Fields.Load(node);
+
+			this.ARmaxTransmitDistance = Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
 
 			base.OnLoad (node);
 
@@ -233,7 +248,7 @@
 		{
 			string ErrorText = string.Format (
 				"Unable to transmit: out of range!  Maximum range = {0}m; Current range = {1}m.",
-				Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2),
+				Tools.MuMech_ToSI((double)this.ARmaxTransmitDistance, 2),
 				Tools.MuMech_ToSI((double)this.transmitDistance, 2)
 				);
 
@@ -279,121 +294,34 @@
 		{
 			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";
+			text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.ARmaxTransmitDistance, 2) + "m\n";
 			return text;
 		}
 
 		// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
 		public new bool CanTransmit()
 		{
-			if (this.transmitDistance < this.maxTransmitDistance)
-			{
-				return true;
-			}
-			else
-			{
-				this.relayChecked = true;
-
-				List<Vessel> nearbyVessels = FlightGlobals.Vessels
-					.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.transmitDistance)
-					.ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Vessels in range: {1}",
-					this.GetType().Name,
-					nearbyVessels.Count
-					));
-
-				nearbyVessels = nearbyVessels.Where(v => v.id != vessel.id).ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Vessels in range excluding self: {1}",
-					this.GetType().Name,
-					nearbyVessels.Count
-					));
-
-				List<Part> nearbyParts = nearbyVessels.SelectMany(v => v.Parts).ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Parts in nearby vessels: {1}",
-					this.GetType().Name,
-					nearbyParts.Count
-					));
-
-				List<ModuleLimitedDataTransmitter> nearbyTransmitters = nearbyParts
-					.SelectMany(p => p.Modules.OfType<ModuleLimitedDataTransmitter>())
-					.ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Transmitters in nearby parts: {1}",
-					this.GetType().Name,
-					nearbyTransmitters.Count
-					));
-
-				nearbyTransmitters = nearbyTransmitters.Where(m => !m.relayChecked).ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Transmitters in nearby parts not already checked: {1}",
-					this.GetType().Name,
-					nearbyTransmitters.Count
-					));
-
-				nearbyTransmitters = nearbyTransmitters.Where(m => m.CanTransmit()).ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Transmitters in nearby parts not already checked that can transmit: {1}",
-					this.GetType().Name,
-					nearbyTransmitters.Count
-					));
-
-				List<ModuleLimitedDataTransmitter> nearbyRelays = this._relayVessels
-					.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.transmitDistance)
-					.Where(v => v.id != vessel.id)
-					.SelectMany(v => v.Parts)
-					.SelectMany(p => p.Modules.OfType<ModuleLimitedDataTransmitter>())
-					.Where(m => !m.relayChecked)
-					.Where(m => m.CanTransmit())
-					.ToList();
-
-				Tools.PostDebugMessage(string.Format(
-					"{0}: Found {1} nearby relays.",
-					this.GetType().Name,
-					nearbyRelays.Count
-				));
-
-				this.relayChecked = false;
-
-				if (nearbyRelays.Count == 0)
-				{
-					return false;
-				}
-				else
-				{
-					return true;
-				}
-			}
+			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)
 		{
-			this.PreTransmit_SetPacketSize ();
-			this.PreTransmit_SetPacketResourceCost ();
+			if (this.CanTransmit())
+			{
+				base.TransmitData(dataQueue);
+			}
+			else
+			{
+				this.PostCannotTransmitError ();
+			}
 
 			Tools.PostDebugMessage (
 				"distance: " + this.transmitDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
 			);
-			if (this.CanTransmit())
-			{
-				base.TransmitData(dataQueue);
-			}
-			else
-			{
-				this.PostCannotTransmitError ();
-			}
 		}
 
 		// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
@@ -444,7 +372,7 @@
 				base.packetSize,
 				this._basepacketResourceCost,
 				base.packetResourceCost,
-				this.maxTransmitDistance,
+				this.ARmaxTransmitDistance,
 				this.transmitDistance,
 				this.nominalRange,
 				this.CanTransmit(),
@@ -456,111 +384,4 @@
 		}
 		#endif
 	}
-
-	public static class Tools
-	{
-		private static ScreenMessage debugmsg = new ScreenMessage("", 2f, ScreenMessageStyle.UPPER_RIGHT);
-
-		[System.Diagnostics.Conditional("DEBUG")]
-		public static void PostDebugMessage(string Msg)
-		{
-			if (HighLogic.LoadedScene > GameScenes.SPACECENTER)
-			{
-				debugmsg.message = Msg;
-				ScreenMessages.PostScreenMessage(debugmsg, true);
-			}
-
-			KSPLog.print(Msg);
-		}
-
-		/*
-		 * MuMech_ToSI is a part of the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
-		 * */
-		public static string MuMech_ToSI(double d, int digits = 3, int MinMagnitude = 0, int MaxMagnitude = int.MaxValue)
-		{
-			float exponent = (float)Math.Log10(Math.Abs(d));
-			exponent = UnityEngine.Mathf.Clamp(exponent, (float)MinMagnitude, (float)MaxMagnitude);
-
-			if (exponent >= 0)
-			{
-				switch ((int)Math.Floor(exponent))
-				{
-					case 0:
-						case 1:
-						case 2:
-						return d.ToString("F" + digits);
-						case 3:
-						case 4:
-						case 5:
-						return (d / 1e3).ToString("F" + digits) + "k";
-						case 6:
-						case 7:
-						case 8:
-						return (d / 1e6).ToString("F" + digits) + "M";
-						case 9:
-						case 10:
-						case 11:
-						return (d / 1e9).ToString("F" + digits) + "G";
-						case 12:
-						case 13:
-						case 14:
-						return (d / 1e12).ToString("F" + digits) + "T";
-						case 15:
-						case 16:
-						case 17:
-						return (d / 1e15).ToString("F" + digits) + "P";
-						case 18:
-						case 19:
-						case 20:
-						return (d / 1e18).ToString("F" + digits) + "E";
-						case 21:
-						case 22:
-						case 23:
-						return (d / 1e21).ToString("F" + digits) + "Z";
-						default:
-						return (d / 1e24).ToString("F" + digits) + "Y";
-				}
-			}
-			else if (exponent < 0)
-			{
-				switch ((int)Math.Floor(exponent))
-				{
-					case -1:
-						case -2:
-						case -3:
-						return (d * 1e3).ToString("F" + digits) + "m";
-						case -4:
-						case -5:
-						case -6:
-						return (d * 1e6).ToString("F" + digits) + "μ";
-						case -7:
-						case -8:
-						case -9:
-						return (d * 1e9).ToString("F" + digits) + "n";
-						case -10:
-						case -11:
-						case -12:
-						return (d * 1e12).ToString("F" + digits) + "p";
-						case -13:
-						case -14:
-						case -15:
-						return (d * 1e15).ToString("F" + digits) + "f";
-						case -16:
-						case -17:
-						case -18:
-						return (d * 1e18).ToString("F" + digits) + "a";
-						case -19:
-						case -20:
-						case -21:
-						return (d * 1e21).ToString("F" + digits) + "z";
-						default:
-						return (d * 1e24).ToString("F" + digits) + "y";
-				}
-			}
-			else
-			{
-				return "0";
-			}
-		}
-	}
 }

file:b/AntennaRelay.cs (new)
--- /dev/null
+++ b/AntennaRelay.cs
@@ -1,1 +1,209 @@
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AntennaRange
+{
+	public class AntennaRelay : IAntennaRelay
+	{
+		// We don't have a Bard, so we'll hide Kerbin here.
+		protected CelestialBody Kerbin;
+
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
+		/// <value>The parent Vessel.</value>
+		public Vessel vessel
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets the transmit distance.
+		/// </summary>
+		/// <value>The transmit distance.</value>
+		public double transmitDistance
+		{
+			get
+			{
+				IAntennaRelay nearestRelay = this.FindNearestRelay();
+
+				// If there is no available relay nearby...
+				if (nearestRelay == null)
+				{
+					// .. return the distance to Kerbin
+					return this.DistanceTo(this.Kerbin);
+				}
+				else
+				{
+					/// ...otherwise, return the distance to the nearest available relay.
+					return this.DistanceTo(nearestRelay);
+				}
+			}
+		}
+
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
+		/// <value>The max transmit distance.</value>
+		public virtual float 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;
+		}
+
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// </summary>
+		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
+		public bool CanTransmit()
+		{
+			if (this.transmitDistance > this.maxTransmitDistance)
+			{
+				return false;
+			}
+			else
+			{
+				return true;
+			}
+		}
+
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
+		/// <returns>The nearest relay or null, if no relays in range.</returns>
+		public IAntennaRelay FindNearestRelay()
+		{
+			// Set this relay as checked, so that we don't check it again.
+			this.relayChecked = true;
+
+			// Get a list of vessels within transmission range.
+			List<Vessel> nearbyVessels = FlightGlobals.Vessels
+				.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance)
+					.ToList();
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Vessels in range: {1}",
+				this.GetType().Name,
+				nearbyVessels.Count
+				));
+
+			// Remove this vessel.
+			nearbyVessels.RemoveAll(v => v.id == vessel.id);
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Vessels in range excluding self: {1}",
+				this.GetType().Name,
+				nearbyVessels.Count
+				));
+
+			// Get a flattened list of all IAntennaRelay modules and protomodules in transmission range.
+			List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList();
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Found {1} nearby relays.",
+				this.GetType().Name,
+				nearbyRelays.Count
+				));
+
+			// Remove all relays already checked this time.
+			nearbyRelays.RemoveAll(r => r.relayChecked);
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Found {1} nearby relays not already checked.",
+				this.GetType().Name,
+				nearbyRelays.Count
+				));
+
+			// Remove all relays that cannot transmit.
+			// This call to r.CanTransmit() starts a depth-first recursive search for relays with a path back to Kerbin.
+			nearbyRelays.RemoveAll(r => !r.CanTransmit());
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Found {1} nearby relays not already checked that can transmit.",
+				this.GetType().Name,
+				nearbyRelays.Count
+				));
+
+			// Sort the available relays by distance.
+			nearbyRelays.Sort(new RelayComparer(this.vessel));
+
+			// Get the nearest available relay, or null if there are no available relays nearby.
+			IAntennaRelay nearestRelay = nearbyRelays.FirstOrDefault();
+
+			// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
+			// used next time.
+			this.relayChecked = false;
+
+			// 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>
+		public AntennaRelay(Vessel v)
+		{
+			this.vessel = v;
+
+			// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start,
+			// we hope it is safe enough.
+			this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
+		}
+
+		/*
+		 * Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance.
+		 * */
+		internal class RelayComparer : IComparer<IAntennaRelay>
+		{
+			/// <summary>
+			/// The reference Vessel (usually the active vessel).
+			/// </summary>
+			protected Vessel referenceVessel;
+
+			// We don't want no stinking public parameterless constructors.
+			private RelayComparer() {}
+
+			/// <summary>
+			/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use
+			/// in sorting relays by distance.
+			/// </summary>
+			/// <param name="reference">The reference Vessel</param>
+			public RelayComparer(Vessel reference)
+			{
+				this.referenceVessel = reference;
+			}
+
+			/// <summary>
+			/// Compare the <see cref="IAntennaRelay"/>s "one" and "two".
+			/// </summary>
+			/// <param name="one">The first IAntennaRelay in the comparison</param>
+			/// <param name="two">The second IAntennaRelay in the comparison</param>
+			public int Compare(IAntennaRelay one, IAntennaRelay two)
+			{
+				double distanceOne;
+				double distanceTwo;
+
+				distanceOne = one.vessel.DistanceTo(referenceVessel);
+				distanceTwo = two.vessel.DistanceTo(referenceVessel);
+
+				return distanceOne.CompareTo(distanceTwo);
+			}
+		}
+	}
+}
+
+

file:b/Extensions.cs (new)
--- /dev/null
+++ b/Extensions.cs
@@ -1,1 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
 
+namespace AntennaRange
+{
+	/*
+	 * A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
+	 * */
+	public static class Extensions
+	{
+		/// <summary>
+		/// Returns the distance between this Vessel and another Vessel.
+		/// </summary>
+		/// <param name="vesselOne">This <see cref="Vessel"/><see ></param>
+		/// <param name="vesselTwo">Another <see cref="Vessel"/></param>
+		public static double DistanceTo(this Vessel vesselOne, Vessel vesselTwo)
+		{
+			return (vesselOne.GetWorldPos3D() - vesselTwo.GetWorldPos3D()).magnitude;
+		}
+
+		/// <summary>
+		/// Returns the distance between this Vessel and a CelestialBody
+		/// </summary>
+		/// <param name="vessel">This Vessel</param>
+		/// <param name="body">A <see cref="CelestialBody"/></param>
+		public static double DistanceTo(this Vessel vessel, CelestialBody body)
+		{
+			return (vessel.GetWorldPos3D() - body.position).magnitude;
+		}
+
+		/// <summary>
+		/// Returns 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 DistanceTo(this IAntennaRelay relay, Vessel Vessel)
+		{
+			return relay.vessel.DistanceTo(Vessel);
+		}
+
+		/// <summary>
+		/// Returns 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 DistanceTo(this IAntennaRelay relay, CelestialBody body)
+		{
+			return relay.vessel.DistanceTo(body);
+		}
+
+		/// <summary>
+		/// Returns 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 DistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
+		{
+			return relayOne.DistanceTo(relayTwo.vessel);
+		}
+
+		/// <summary>
+		/// 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)
+		{
+			Tools.PostDebugMessage(string.Format(
+				"{0}: Getting antenna relays from vessel {1}.",
+				"IAntennaRelay",
+				vessel.name
+			));
+
+			List<IAntennaRelay> Transmitters;
+
+			// If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
+			if (vessel.loaded) {
+				Tools.PostDebugMessage(string.Format(
+					"{0}: vessel {1} is loaded.",
+					"IAntennaRelay",
+					vessel.name
+					));
+
+				// Gets a list of PartModules implementing IAntennaRelay
+				Transmitters = vessel.Parts
+					.SelectMany (p => p.Modules.OfType<IAntennaRelay> ())
+					.ToList();
+			}
+			// If the vessel is not loaded, we need to find ProtoPartModuleSnapshots with a true IsAntenna field.
+			else
+			{
+				Tools.PostDebugMessage(string.Format(
+					"{0}: vessel {1} is not loaded.",
+					"IAntennaRelay",
+					vessel.name
+					));
+
+				Transmitters = new List<IAntennaRelay>();
+
+				// Loop through the ProtoPartModuleSnapshots in this Vessel
+				foreach (ProtoPartModuleSnapshot ms in vessel.protoVessel.protoPartSnapshots.SelectMany(ps => ps.modules))
+				{
+					// If they are antennas...
+					if (ms.IsAntenna())
+					{
+						// ...add a new ProtoAntennaRelay wrapper to the list.
+						Transmitters.Add(new ProtoAntennaRelay(ms, vessel));
+					}
+				}
+			}
+
+			Tools.PostDebugMessage(string.Format(
+				"{0}: vessel {1} has {2} transmitters.",
+				"IAntennaRelay",
+				vessel.name,
+				Transmitters.Count
+				));
+
+			// Return the list of IAntennaRelays
+			return Transmitters;
+		}
+
+		// Returns true if this PartModule contains a True IsAntenna field, false otherwise.
+		public static bool IsAntenna (this PartModule module)
+		{
+			return module.Fields.GetValue<bool> ("IsAntenna");
+		}
+
+		// Returns true if this ProtoPartModuleSnapshot contains a persistent True IsAntenna field, false otherwise
+		public static bool IsAntenna(this ProtoPartModuleSnapshot protomodule)
+		{
+			bool result;
+
+			return Boolean.TryParse (protomodule.moduleValues.GetValue ("IsAntenna") ?? "False", out result)
+				? result : false;
+		}
+	}
+}
+
+

file:b/IAntennaRelay.cs (new)
--- /dev/null
+++ b/IAntennaRelay.cs
@@ -1,1 +1,44 @@
+using KSP;
+using System;
 
+namespace AntennaRange
+{
+	/*
+	 * Interface defining the basic functionality of AntennaRelay modules for AntennaRange.
+	 * */
+	public interface IAntennaRelay
+	{
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
+		/// <value>The parent Vessel.</value>
+		Vessel vessel { get; }
+
+		/// <summary>
+		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// </summary>
+		/// <value>The distance to the nearest relay or Kerbin, whichever is closer.</value>
+		double transmitDistance { get; }
+
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
+		/// <value>The max transmit distance.</value>
+		float maxTransmitDistance { 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; }
+
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// </summary>
+		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
+		bool CanTransmit();
+	}
+}
+
+

--- /dev/null
+++ b/ProtoAntennaRelay.cs
@@ -1,1 +1,65 @@
+using System;
 
+namespace AntennaRange
+{
+	/*
+	 * Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay.
+	 * This is used for finding relays in unloaded Vessels.
+	 * */
+	public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
+	{
+		protected ProtoPartModuleSnapshot snapshot;
+
+		/// <summary>
+		/// The maximum distance at which this transmitter can operate.
+		/// </summary>
+		/// <value>The max transmit distance.</value>
+		public override float maxTransmitDistance
+		{
+			get
+			{
+				double result;
+				Double.TryParse(snapshot.moduleValues.GetValue ("ARmaxTransmitDistance") ?? "0", out result);
+				return (float)result;
+			}
+		}
+
+		/// <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 override bool relayChecked
+		{
+			get
+			{
+				bool result;
+				Boolean.TryParse(this.snapshot.moduleValues.GetValue("relayChecked"), out result);
+				return result;
+			}
+			protected set
+			{
+				if (this.snapshot.moduleValues.HasValue("relayChecked"))
+				{
+					this.snapshot.moduleValues.SetValue("relayChecked", value.ToString ());
+				}
+				else
+				{
+					this.snapshot.moduleValues.AddValue("relayChecked", value);
+				}
+			}
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="AntennaRange.ProtoAntennaRelay"/> class.
+		/// </summary>
+		/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param>
+		/// <param name="vessel">The parent Vessel</param>
+		public ProtoAntennaRelay(ProtoPartModuleSnapshot ms, Vessel vessel) : base(vessel)
+		{
+			this.snapshot = ms;
+		}
+	}
+}
+
+