A little refactoring and a new message to indicate if you are using a relay. Also fixed using distant relays instead of nearby Kerbin.
A little refactoring and a new message to indicate if you are using a relay. Also fixed using distant relays instead of nearby Kerbin.

file:b/ARTools.cs (new)
  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";
  }
  }
  }
  }
 
 
file:a/AntennaRange.cs (deleted)
/*  
* AntennaRange © 2013 toadicus  
*  
* AntennaRange provides incentive and requirements for the use of the various antenna parts.  
* Nominally, the breakdown is as follows:  
*  
* Communotron 16 - Suitable up to Kerbalsynchronous Orbit  
* Comms DTS-M1 - Suitable throughout the Kerbin subsystem  
* Communotron 88-88 - Suitable throughout the Kerbol system.  
*  
* 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/  
*  
* This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike 3.0 Uported License.  
*  
*/  
 
using System;  
using System.Collections.Generic;  
using KSP;  
 
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.  
*  
* */  
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter  
{  
// 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;  
 
// 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 maximum distance this module can transmit  
public double maxTransmitDistance  
{  
get  
{  
return Math.Sqrt (this.maxPowerFactor) * this.nominalRange;  
}  
}  
 
// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost  
// and packetSize.  
[KSPField(isPersistant = false)]  
public double nominalRange = 1500000d;  
 
// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power  
// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.  
[KSPField(isPersistant = false)]  
public float maxPowerFactor = 8f;  
 
// The multipler on packetSize that defines the maximum data bandwidth of the antenna.  
[KSPField(isPersistant = false)]  
public float maxDataFactor = 4f;  
 
// Override ModuleDataTransmitter.packetSize. Below the nominal range, scales up packetSize to  
// packetSize * maxDataFactor  
[KSPField(isPersistant = false)]  
public new float packetSize  
{  
get  
{  
if (this.transmitDistance >= this.nominalRange)  
{  
return this._basepacketSize;  
}  
else  
{  
// From above, data rate increases with the inverse square of the distance.  
return Math.Min(this._basepacketSize * (float)Math.Pow(this.nominalRange / this.transmitDistance, 2),  
this._basepacketSize * this.maxDataFactor);  
}  
}  
set  
{  
this._basepacketSize = value;  
}  
 
}  
 
// Override ModuleDataTransmitter.packetResourceCost. Above the nominal range, scales up packetResourceCost to  
// packetResourceCost * maxPowerFactor.  
[KSPField(isPersistant = false)]  
public new float packetResourceCost  
{  
get  
{  
if (this.transmitDistance <= this.nominalRange)  
{  
return this._basepacketResourceCost;  
}  
else  
{  
// From above, power increases with the square of the distance.  
return this._basepacketResourceCost * (float)Math.Pow (this.transmitDistance / this.nominalRange, 2);  
}  
}  
set  
{  
this._basepacketResourceCost = value;  
}  
 
}  
 
// Build ALL the objects.  
public ModuleLimitedDataTransmitter () : base()  
{  
// Go fetch Kerbin, because it is tricksy and hides from us.  
List<CelestialBody> bodies = FlightGlobals.Bodies;  
 
foreach (CelestialBody body in bodies)  
{  
if (body.name == "Kerbin")  
{  
this._Kerbin = body;  
break;  
}  
}  
}  
 
// 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.Format ("Unable to transmit: out of range! Maximum range = {0}; Current range = {1}.", this.maxTransmitDistance, this.transmitDistance);  
ScreenMessages.PostScreenMessage (new ScreenMessage (ErrorText, 4f, ScreenMessageStyle.UPPER_LEFT));  
}  
 
// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.  
public override string GetInfo()  
{  
string text = base.GetInfo();  
text += "Nominal Range: " + this.nominalRange.ToString() + "\n";  
text += "Maximum Range: " + this.maxTransmitDistance.ToString() + "\n";  
return text;  
}  
 
// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.  
public new bool CanTransmit()  
{  
if (this.transmitDistance > this.maxTransmitDistance)  
{  
return false;  
}  
return true;  
}  
 
// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit  
// returns false.  
public new void TransmitData(List<ScienceData> dataQueue)  
{  
if (this.CanTransmit())  
{  
base.TransmitData(dataQueue);  
}  
else  
{  
this.PostCannotTransmitError ();  
}  
}  
 
// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit  
// returns false.  
public new void StartTransmission()  
{  
if (this.CanTransmit())  
{  
base.StartTransmission();  
}  
else  
{  
this.PostCannotTransmitError ();  
}  
}  
}  
}  
 
 
file:b/AntennaRelay.cs (new)
  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 or sets the nearest relay.
  /// </summary>
  /// <value>The nearest relay</value>
  public IAntennaRelay nearestRelay
  {
  get;
  protected set;
  }
 
  /// <summary>
  /// Gets the transmit distance.
  /// </summary>
  /// <value>The transmit distance.</value>
  public double transmitDistance
  {
  get
  {
  this.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();
 
  nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris);
 
  Tools.PostDebugMessage(string.Format(
  "{0}: Non-debris 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();
 
  // If we have a nearby relay...
  if (_nearestRelay != null)
  {
  // ...but that relay is farther than Kerbin...
  if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin))
  {
  // ...just use Kerbin.
  _nearestRelay = null;
  }
  }
 
  // 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;
  }
 
  public override string ToString()
  {
  return string.Format(
  "Antenna relay on vessel {0} (range to relay: {1}m)",
  vessel,
  Tools.MuMech_ToSI(transmitDistance)
  );
  }
 
  /// <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)
  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 AntennaRelay 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 AntennaRelay 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 AntennaRelay 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)
  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();
  }
  }
 
 
  /*
  * AntennaRange © 2013 toadicus
  *
  * AntennaRange provides incentive and requirements for the use of the various antenna parts.
  * Nominally, the breakdown is as follows:
  *
  * Communotron 16 - Suitable up to Kerbalsynchronous Orbit
  * Comms DTS-M1 - Suitable throughout the Kerbin subsystem
  * Communotron 88-88 - Suitable throughout the Kerbol system.
  *
  * 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/
  *
  * This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
  * 3.0 Uported License.
  *
  * This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
  *
  */
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using KSP;
  using UnityEngine;
 
  namespace AntennaRange
  {
  /*
  * ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles range-
  * finding, power scaling, and data scaling for antennas during science transmission. Its functionality varies with
  * three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.
  *
  * In general, the scaling functions assume the following relation:
  *
  * D² α P/R,
  *
  * where D is the total transmission distance, P is the transmission power, and R is the data rate.
  *
  * */
 
  /*
  * Fields
  * */
  public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
  {
  // Call this an antenna so that you don't have to.
  [KSPField(isPersistant = true)]
  protected bool IsAntenna;
 
  // Stores the packetResourceCost as defined in the .cfg file.
  protected float _basepacketResourceCost;
 
  // Stores the packetSize as defined in the .cfg file.
  protected float _basepacketSize;
 
  // Every antenna is a relay.
  protected AntennaRelay relay;
 
  // Keep track of vessels with transmitters for relay purposes.
  protected List<Vessel> _relayVessels;
 
  // Sometimes we will need to communicate errors; this is how we do it.
  protected ScreenMessage ErrorMsg;
 
  // Let's make the error text pretty!
  protected UnityEngine.GUIStyle ErrorStyle;
 
  // The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
  // and packetSize.
  [KSPField(isPersistant = false)]
  public float nominalRange;
 
  // The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power
  // cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
  [KSPField(isPersistant = false)]
  public float maxPowerFactor;
 
  // The multipler on packetSize that defines the maximum data bandwidth of the antenna.
  [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 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 float maxTransmitDistance
  {
  get
  {
  return this.ARmaxTransmitDistance;
  }
  }
 
  /*
  * The next two functions overwrite the behavior of the stock functions and do not perform equivalently, except
  * in that they both return floats. Here's some quick justification:
  *
  * The stock implementation of GetTransmitterScore (which I cannot override) is:
  * Score = (1 + DataResourceCost) / DataRate
  *
  * The stock DataRate and DataResourceCost are:
  * DataRate = packetSize / packetInterval
  * DataResourceCost = packetResourceCost / packetSize
  *
  * So, the resulting score is essentially in terms of joules per byte per baud. Rearranging that a bit, it
  * could also look like joule-seconds per byte per byte, or newton-meter-seconds per byte per byte. Either way,
  * that metric is not a very reasonable one.
  *
  * Two metrics that might make more sense are joules per byte or joules per byte per second. The latter case
  * would look like:
  * DataRate = packetSize / packetInterval
  * DataResourceCost = packetResourceCost
  *
  * The former case, which I've chosen to implement below, is:
  * DataRate = packetSize
  * DataResourceCost = packetResourceCost
  *
  * 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
  public new float DataRate
  {
  get
  {
  this.PreTransmit_SetPacketSize();
  return this.packetSize;
  }
  }
 
  // Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
  // to be scored in terms of joules/byte
  public new float DataResourceCost
  {
  get
  {
  this.PreTransmit_SetPacketResourceCost();
 
  if (this.CanTransmit())
  {
  return this.packetResourceCost;
  }
  else
  {
  return float.PositiveInfinity;
  }
  }
  }
 
  // Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
  public bool relayChecked
  {
  get
  {
  return this.relay.relayChecked;
  }
  }
 
  /*
  * Methods
  * */
  // Build ALL the objects.
  public ModuleLimitedDataTransmitter () : base()
  {
  // Make the error posting prettier.
  this.ErrorStyle = new UnityEngine.GUIStyle();
  this.ErrorStyle.normal.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
  this.ErrorStyle.active.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
  this.ErrorStyle.hover.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
  this.ErrorStyle.fontStyle = UnityEngine.FontStyle.Normal;
  this.ErrorStyle.padding.top = 32;
 
  this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT, this.ErrorStyle);
  }
 
  // 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)
  {
  base.OnStart (state);
 
  if (state >= StartState.PreLaunch)
  {
  this.relay = new AntennaRelay(vessel);
  this.relay.maxTransmitDistance = this.maxTransmitDistance;
  }
  }
 
  // 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.
  public override void OnLoad(ConfigNode node)
  {
  this.Fields.Load(node);
  base.Fields.Load(node);
 
  this.ARmaxTransmitDistance = Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
  this.IsAntenna = true;
 
  base.OnLoad (node);
 
  this._basepacketSize = base.packetSize;
  this._basepacketResourceCost = base.packetResourceCost;
 
  Tools.PostDebugMessage(string.Format(
  "{0} loaded:\n" +
  "packetSize: {1}\n" +
  "packetResourceCost: {2}\n" +
  "nominalRange: {3}\n" +
  "maxPowerFactor: {4}\n" +
  "maxDataFactor: {5}\n",
  this.name,
  base.packetSize,
  this._basepacketResourceCost,
  this.nominalRange,
  this.maxPowerFactor,
  this.maxDataFactor
  ));
  }
 
  // 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.Format (
  "Unable to transmit: out of range! Maximum range = {0}m; Current range = {1}m.",
  Tools.MuMech_ToSI((double)this.ARmaxTransmitDistance, 2),
  Tools.MuMech_ToSI((double)this.transmitDistance, 2)
  );
 
  this.ErrorMsg.message = ErrorText;
 
  ScreenMessages.PostScreenMessage(this.ErrorMsg, true);
  }
 
  // 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 (this.transmitDistance <= this.nominalRange)
  {
  base.packetResourceCost = this._basepacketResourceCost;
  }
  else
  {
  base.packetResourceCost = this._basepacketResourceCost
  * (float)Math.Pow (this.transmitDistance / this.nominalRange, 2);
  }
  }
 
  // 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 (this.transmitDistance >= this.nominalRange)
  {
  base.packetSize = this._basepacketSize;
  }
  else
  {
  base.packetSize = Math.Min(
  this._basepacketSize * (float)Math.Pow (this.nominalRange / this.transmitDistance, 2),
  this._basepacketSize * this.maxDataFactor);
  }
  }
 
  // Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
  public override string GetInfo()
  {
  string text = base.GetInfo();
  text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n";
  text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.ARmaxTransmitDistance, 2) + "m\n";
  return text;
  }
 
  // Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
  public new bool CanTransmit()
  {
  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)
  {
  if (this.CanTransmit())
  {
  base.TransmitData(dataQueue);
  }
  else
  {
  this.PostCannotTransmitError ();
  }
 
  Tools.PostDebugMessage (
  "distance: " + this.transmitDistance
  + " packetSize: " + this.packetSize
  + " packetResourceCost: " + this.packetResourceCost
  );
  }
 
  // Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
  // returns false.
  public new void StartTransmission()
  {
  PreTransmit_SetPacketSize ();
  PreTransmit_SetPacketResourceCost ();
 
  Tools.PostDebugMessage (
  "distance: " + this.transmitDistance
  + " packetSize: " + this.packetSize
  + " packetResourceCost: " + this.packetResourceCost
  );
 
  if (this.CanTransmit())
  {
  this.ErrorMsg.message = "Beginning transmission ";
 
  if (this.relay.nearestRelay == null)
  {
  this.ErrorMsg.message += "directly to Kerbin.";
  }
  else
  {
  this.ErrorMsg.message += "via relay " + this.relay.nearestRelay;
  }
 
  ScreenMessages.PostScreenMessage(this.ErrorMsg);
 
  base.StartTransmission();
  }
  else
  {
  this.PostCannotTransmitError ();
  }
  }
 
  // When debugging, it's nice to have a button that just tells you everything.
  #if DEBUG
  [KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)]
  public void DebugInfo()
  {
  PreTransmit_SetPacketSize ();
  PreTransmit_SetPacketResourceCost ();
 
  string msg = string.Format(
  "'{0}'\n" +
  "_basepacketSize: {1}\n" +
  "packetSize: {2}\n" +
  "_basepacketResourceCost: {3}\n" +
  "packetResourceCost: {4}\n" +
  "maxTransmitDistance: {5}\n" +
  "transmitDistance: {6}\n" +
  "nominalRange: {7}\n" +
  "CanTransmit: {8}\n" +
  "DataRate: {9}\n" +
  "DataResourceCost: {10}\n" +
  "TransmitterScore: {11}\n" +
  "NearestRelay: {12}",
  this.name,
  this._basepacketSize,
  base.packetSize,
  this._basepacketResourceCost,
  base.packetResourceCost,
  this.ARmaxTransmitDistance,
  this.transmitDistance,
  this.nominalRange,
  this.CanTransmit(),
  this.DataRate,
  this.DataResourceCost,
  ScienceUtil.GetTransmitterScore(this),
  this.relay.FindNearestRelay()
  );
  ScreenMessages.PostScreenMessage (new ScreenMessage (msg, 4f, ScreenMessageStyle.UPPER_RIGHT));
  }
  #endif
  }
  }
  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;
  }
  }
  }