AntennaRelay: Exclude flag vessels from the search.
AntennaRelay: Exclude flag vessels from the search.

// AntennaRange © 2014 toadicus // AntennaRange © 2014 toadicus
// //
// AntennaRange provides incentive and requirements for the use of the various antenna parts. // AntennaRange provides incentive and requirements for the use of the various antenna parts.
// Nominally, the breakdown is as follows: // Nominally, the breakdown is as follows:
// //
// Communotron 16 - Suitable up to Kerbalsynchronous Orbit // Communotron 16 - Suitable up to Kerbalsynchronous Orbit
// Comms DTS-M1 - Suitable throughout the Kerbin subsystem // Comms DTS-M1 - Suitable throughout the Kerbin subsystem
// Communotron 88-88 - Suitable throughout the Kerbol system. // 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 // 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/ // 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 // This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
// 3.0 Uported License. // 3.0 Uported License.
// //
// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3. // This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
   
namespace AntennaRange namespace AntennaRange
{ {
public class AntennaRelay public class AntennaRelay
{ {
// We don't have a Bard, so we'll hide Kerbin here. // We don't have a Bard, so we'll hide Kerbin here.
protected CelestialBody Kerbin; protected CelestialBody Kerbin;
   
protected System.Diagnostics.Stopwatch searchTimer; protected System.Diagnostics.Stopwatch searchTimer;
protected long millisecondsBetweenSearches; protected long millisecondsBetweenSearches;
   
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
/// <value>The parent Vessel.</value> /// <value>The parent Vessel.</value>
public Vessel vessel public Vessel vessel
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets or sets the nearest relay. /// Gets or sets the nearest relay.
/// </summary> /// </summary>
/// <value>The nearest relay</value> /// <value>The nearest relay</value>
public IAntennaRelay nearestRelay public IAntennaRelay nearestRelay
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets the transmit distance. /// Gets the transmit distance.
/// </summary> /// </summary>
/// <value>The transmit distance.</value> /// <value>The transmit distance.</value>
public double transmitDistance public double transmitDistance
{ {
get get
{ {
this.nearestRelay = this.FindNearestRelay(); this.nearestRelay = this.FindNearestRelay();
   
// If there is no available relay nearby... // If there is no available relay nearby...
if (nearestRelay == null) if (nearestRelay == null)
{ {
// .. return the distance to Kerbin // .. return the distance to Kerbin
return this.DistanceTo(this.Kerbin); return this.DistanceTo(this.Kerbin);
} }
else else
{ {
/// ...otherwise, return the distance to the nearest available relay. /// ...otherwise, return the distance to the nearest available relay.
return this.DistanceTo(nearestRelay); return this.DistanceTo(nearestRelay);
} }
} }
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
/// <value>The max transmit distance.</value> /// <value>The max transmit distance.</value>
public virtual float maxTransmitDistance public virtual float maxTransmitDistance
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
/// the current relay attempt. /// the current relay attempt.
/// </summary> /// </summary>
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
public virtual bool relayChecked public virtual bool relayChecked
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Determines whether this instance can transmit. /// Determines whether this instance can transmit.
/// </summary> /// </summary>
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
public virtual bool CanTransmit() public virtual bool CanTransmit()
{ {
if (this.transmitDistance > this.maxTransmitDistance) if (this.transmitDistance > this.maxTransmitDistance)
{ {
return false; return false;
} }
else else
{ {
return true; return true;
} }
} }
   
/// <summary> /// <summary>
/// Finds the nearest relay. /// Finds the nearest relay.
/// </summary> /// </summary>
/// <returns>The nearest relay or null, if no relays in range.</returns> /// <returns>The nearest relay or null, if no relays in range.</returns>
public IAntennaRelay FindNearestRelay() public IAntennaRelay FindNearestRelay()
{ {
if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches) if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
{ {
return this.nearestRelay; return this.nearestRelay;
} }
   
if (this.searchTimer.IsRunning) if (this.searchTimer.IsRunning)
{ {
this.searchTimer.Stop(); this.searchTimer.Stop();
this.searchTimer.Reset(); this.searchTimer.Reset();
} }
   
this.searchTimer.Start(); this.searchTimer.Start();
   
// Set this relay as checked, so that we don't check it again. // Set this relay as checked, so that we don't check it again.
this.relayChecked = true; this.relayChecked = true;
   
// Get a list of vessels within transmission range. // Get a list of vessels within transmission range.
List<Vessel> nearbyVessels = FlightGlobals.Vessels List<Vessel> nearbyVessels = FlightGlobals.Vessels
.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance) .Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance)
.ToList(); .ToList();
   
nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris); nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris);
  nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Flag);
Tools.PostDebugMessage(string.Format(  
"{0}: Non-debris vessels in range: {1}", Tools.PostDebugMessage(string.Format(
  "{0}: Non-debris, non-flag vessels in range: {1}",
this.GetType().Name, this.GetType().Name,
nearbyVessels.Count nearbyVessels.Count
)); ));
   
// Remove this vessel. // Remove this vessel.
nearbyVessels.RemoveAll(v => v.id == vessel.id); nearbyVessels.RemoveAll(v => v.id == vessel.id);
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Vessels in range excluding self: {1}", "{0}: Vessels in range excluding self: {1}",
this.GetType().Name, this.GetType().Name,
nearbyVessels.Count nearbyVessels.Count
)); ));
   
// Get a flattened list of all IAntennaRelay modules and protomodules in transmission range. // Get a flattened list of all IAntennaRelay modules and protomodules in transmission range.
List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList(); List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList();
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays.", "{0}: Found {1} nearby relays.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Remove all relays already checked this time. // Remove all relays already checked this time.
nearbyRelays.RemoveAll(r => r.relayChecked); nearbyRelays.RemoveAll(r => r.relayChecked);
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked.", "{0}: Found {1} nearby relays not already checked.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Remove all relays that cannot transmit. // 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. // This call to r.CanTransmit() starts a depth-first recursive search for relays with a path back to Kerbin.
nearbyRelays.RemoveAll(r => !r.CanTransmit()); nearbyRelays.RemoveAll(r => !r.CanTransmit());
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked that can transmit.", "{0}: Found {1} nearby relays not already checked that can transmit.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Sort the available relays by distance. // Sort the available relays by distance.
nearbyRelays.Sort(new RelayComparer(this.vessel)); nearbyRelays.Sort(new RelayComparer(this.vessel));
   
// Get the nearest available relay, or null if there are no available relays nearby. // Get the nearest available relay, or null if there are no available relays nearby.
IAntennaRelay _nearestRelay = nearbyRelays.FirstOrDefault(); IAntennaRelay _nearestRelay = nearbyRelays.FirstOrDefault();
   
// If we have a nearby relay... // If we have a nearby relay...
if (_nearestRelay != null) if (_nearestRelay != null)
{ {
// ...but that relay is farther than Kerbin... // ...but that relay is farther than Kerbin...
if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin)) if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin))
{ {
// ...just use Kerbin. // ...just use Kerbin.
_nearestRelay = null; _nearestRelay = null;
} }
} }
   
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be // Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
// used next time. // used next time.
this.relayChecked = false; this.relayChecked = false;
   
// Return the nearest available relay, or null if there are no available relays nearby. // Return the nearest available relay, or null if there are no available relays nearby.
return _nearestRelay; return _nearestRelay;
} }
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class. /// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
/// </summary> /// </summary>
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> /// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
public AntennaRelay(Vessel v) public AntennaRelay(Vessel v)
{ {
this.vessel = v; this.vessel = v;
   
this.searchTimer = new System.Diagnostics.Stopwatch(); this.searchTimer = new System.Diagnostics.Stopwatch();
this.millisecondsBetweenSearches = 5000; this.millisecondsBetweenSearches = 5000;
   
// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start, // HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start,
// we hope it is safe enough. // we hope it is safe enough.
this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin"); this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
} }
   
/* /*
* Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance. * Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance.
* */ * */
internal class RelayComparer : IComparer<IAntennaRelay> internal class RelayComparer : IComparer<IAntennaRelay>
{ {
/// <summary> /// <summary>
/// The reference Vessel (usually the active vessel). /// The reference Vessel (usually the active vessel).
/// </summary> /// </summary>
protected Vessel referenceVessel; protected Vessel referenceVessel;
   
// We don't want no stinking public parameterless constructors. // We don't want no stinking public parameterless constructors.
private RelayComparer() {} private RelayComparer() {}
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use
/// in sorting relays by distance. /// in sorting relays by distance.
/// </summary> /// </summary>
/// <param name="reference">The reference Vessel</param> /// <param name="reference">The reference Vessel</param>
public RelayComparer(Vessel reference) public RelayComparer(Vessel reference)
{ {
this.referenceVessel = reference; this.referenceVessel = reference;
} }
   
/// <summary> /// <summary>
/// Compare the <see cref="IAntennaRelay"/>s "one" and "two". /// Compare the <see cref="IAntennaRelay"/>s "one" and "two".
/// </summary> /// </summary>
/// <param name="one">The first IAntennaRelay in the comparison</param> /// <param name="one">The first IAntennaRelay in the comparison</param>
/// <param name="two">The second IAntennaRelay in the comparison</param> /// <param name="two">The second IAntennaRelay in the comparison</param>
public int Compare(IAntennaRelay one, IAntennaRelay two) public int Compare(IAntennaRelay one, IAntennaRelay two)
{ {
double distanceOne; double distanceOne;
double distanceTwo; double distanceTwo;
   
distanceOne = one.vessel.DistanceTo(referenceVessel); distanceOne = one.vessel.DistanceTo(referenceVessel);
distanceTwo = two.vessel.DistanceTo(referenceVessel); distanceTwo = two.vessel.DistanceTo(referenceVessel);
   
return distanceOne.CompareTo(distanceTwo); return distanceOne.CompareTo(distanceTwo);
} }
} }
} }
} }
   
   
file:b/ChangeLog (new)
  2014-01-14 toadicus <>
 
  * ModuleLimitedDataTransmitter.cs: Added a ":" to the
  transmission communications for consistency with stock
  behavior.
 
 
// AntennaRange © 2014 toadicus // AntennaRange © 2014 toadicus
// //
// AntennaRange provides incentive and requirements for the use of the various antenna parts. // AntennaRange provides incentive and requirements for the use of the various antenna parts.
// Nominally, the breakdown is as follows: // Nominally, the breakdown is as follows:
// //
// Communotron 16 - Suitable up to Kerbalsynchronous Orbit // Communotron 16 - Suitable up to Kerbalsynchronous Orbit
// Comms DTS-M1 - Suitable throughout the Kerbin subsystem // Comms DTS-M1 - Suitable throughout the Kerbin subsystem
// Communotron 88-88 - Suitable throughout the Kerbol system. // 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 // 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/ // 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 // This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
// 3.0 Uported License. // 3.0 Uported License.
// //
// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3. // This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using KSP; using KSP;
using UnityEngine; using UnityEngine;
   
namespace AntennaRange namespace AntennaRange
{ {
/* /*
* ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles range- * 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 * 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. * three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.
* *
* In general, the scaling functions assume the following relation: * In general, the scaling functions assume the following relation:
* *
* D² α P/R, * D² α P/R,
* *
* where D is the total transmission distance, P is the transmission power, and R is the data rate. * where D is the total transmission distance, P is the transmission power, and R is the data rate.
* *
* */ * */
   
/* /*
* Fields * Fields
* */ * */
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
{ {
// Stores the packetResourceCost as defined in the .cfg file. // Stores the packetResourceCost as defined in the .cfg file.
protected float _basepacketResourceCost; protected float _basepacketResourceCost;
   
// Stores the packetSize as defined in the .cfg file. // Stores the packetSize as defined in the .cfg file.
protected float _basepacketSize; protected float _basepacketSize;
   
// Every antenna is a relay. // Every antenna is a relay.
protected AntennaRelay relay; protected AntennaRelay relay;
   
// Keep track of vessels with transmitters for relay purposes. // Keep track of vessels with transmitters for relay purposes.
protected List<Vessel> _relayVessels; protected List<Vessel> _relayVessels;
   
// Sometimes we will need to communicate errors; this is how we do it. // Sometimes we will need to communicate errors; this is how we do it.
protected ScreenMessage ErrorMsg; protected ScreenMessage ErrorMsg;
   
// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost // The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
// and packetSize. // and packetSize.
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public float nominalRange; public float nominalRange;
   
[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")] [KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")]
public string UItransmitDistance; public string UItransmitDistance;
   
[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")] [KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
public string UImaxTransmitDistance; public string UImaxTransmitDistance;
   
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")] [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")]
public string UIpacketSize; public string UIpacketSize;
   
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")] [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")]
public string UIpacketCost; public string UIpacketCost;
   
// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power // The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power
// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail. // cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public float maxPowerFactor; public float maxPowerFactor;
   
// The multipler on packetSize that defines the maximum data bandwidth of the antenna. // The multipler on packetSize that defines the maximum data bandwidth of the antenna.
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public float maxDataFactor; public float maxDataFactor;
   
protected bool actionUIUpdate; protected bool actionUIUpdate;
   
/* /*
* Properties * Properties
* */ * */
// Returns the parent vessel housing this antenna. // Returns the parent vessel housing this antenna.
public new Vessel vessel public new Vessel vessel
{ {
get get
{ {
return base.vessel; return base.vessel;
} }
} }
   
// Returns the distance to the nearest relay or Kerbin, whichever is closer. // Returns the distance to the nearest relay or Kerbin, whichever is closer.
public double transmitDistance public double transmitDistance
{ {
get get
{ {
return this.relay.transmitDistance; return this.relay.transmitDistance;
} }
} }
   
// Returns the maximum distance this module can transmit // Returns the maximum distance this module can transmit
public float maxTransmitDistance public float maxTransmitDistance
{ {
get get
{ {
return Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange; return Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
} }
} }
   
/* /*
* The next two functions overwrite the behavior of the stock functions and do not perform equivalently, except * 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: * in that they both return floats. Here's some quick justification:
* *
* The stock implementation of GetTransmitterScore (which I cannot override) is: * The stock implementation of GetTransmitterScore (which I cannot override) is:
* Score = (1 + DataResourceCost) / DataRate * Score = (1 + DataResourceCost) / DataRate
* *
* The stock DataRate and DataResourceCost are: * The stock DataRate and DataResourceCost are:
* DataRate = packetSize / packetInterval * DataRate = packetSize / packetInterval
* DataResourceCost = packetResourceCost / packetSize * DataResourceCost = packetResourceCost / packetSize
* *
* So, the resulting score is essentially in terms of joules per byte per baud. Rearranging that a bit, it * 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, * 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. * 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 * Two metrics that might make more sense are joules per byte or joules per byte per second. The latter case
* would look like: * would look like:
* DataRate = packetSize / packetInterval * DataRate = packetSize / packetInterval
* DataResourceCost = packetResourceCost * DataResourceCost = packetResourceCost
* *
* The former case, which I've chosen to implement below, is: * The former case, which I've chosen to implement below, is:
* DataRate = packetSize * DataRate = packetSize
* DataResourceCost = packetResourceCost * DataResourceCost = packetResourceCost
* *
* So... hopefully that doesn't screw with anything else. * So... hopefully that doesn't screw with anything else.
* */ * */
// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in // Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
// terms of joules/byte // terms of joules/byte
public new float DataRate public new float DataRate
{ {
get get
{ {
this.PreTransmit_SetPacketSize(); this.PreTransmit_SetPacketSize();
   
if (this.CanTransmit()) if (this.CanTransmit())
{ {
return this.packetSize; return this.packetSize;
} }
else else
{ {
return float.Epsilon; return float.Epsilon;
} }
} }
} }
   
// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas // Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
// to be scored in terms of joules/byte // to be scored in terms of joules/byte
public new float DataResourceCost public new float DataResourceCost
{ {
get get
{ {
this.PreTransmit_SetPacketResourceCost(); this.PreTransmit_SetPacketResourceCost();
   
if (this.CanTransmit()) if (this.CanTransmit())
{ {
return this.packetResourceCost; return this.packetResourceCost;
} }
else else
{ {
return float.PositiveInfinity; return float.PositiveInfinity;
} }
} }
} }
   
// Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay. // Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
public bool relayChecked public bool relayChecked
{ {
get get
{ {
return this.relay.relayChecked; return this.relay.relayChecked;
} }
} }
   
/* /*
* Methods * Methods
* */ * */
// Build ALL the objects. // Build ALL the objects.
public ModuleLimitedDataTransmitter () : base() public ModuleLimitedDataTransmitter () : base()
{ {
this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT); this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT);
} }
   
// At least once, when the module starts with a state on the launch pad or later, go find Kerbin. // 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) public override void OnStart (StartState state)
{ {
base.OnStart (state); base.OnStart (state);
   
if (state >= StartState.PreLaunch) if (state >= StartState.PreLaunch)
{ {
this.relay = new AntennaRelay(vessel); this.relay = new AntennaRelay(vessel);
this.relay.maxTransmitDistance = this.maxTransmitDistance; this.relay.maxTransmitDistance = this.maxTransmitDistance;
   
this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m"; this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m";
   
GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate); GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss); GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
} }
} }
   
// When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because // 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 // overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
// work. // work.
public override void OnLoad(ConfigNode node) public override void OnLoad(ConfigNode node)
{ {
this.Fields.Load(node); this.Fields.Load(node);
base.Fields.Load(node); base.Fields.Load(node);
   
base.OnLoad (node); base.OnLoad (node);
   
this._basepacketSize = base.packetSize; this._basepacketSize = base.packetSize;
this._basepacketResourceCost = base.packetResourceCost; this._basepacketResourceCost = base.packetResourceCost;
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0} loaded:\n" + "{0} loaded:\n" +
"packetSize: {1}\n" + "packetSize: {1}\n" +
"packetResourceCost: {2}\n" + "packetResourceCost: {2}\n" +
"nominalRange: {3}\n" + "nominalRange: {3}\n" +
"maxPowerFactor: {4}\n" + "maxPowerFactor: {4}\n" +
"maxDataFactor: {5}\n", "maxDataFactor: {5}\n",
this.name, this.name,
base.packetSize, base.packetSize,
this._basepacketResourceCost, this._basepacketResourceCost,
this.nominalRange, this.nominalRange,
this.maxPowerFactor, this.maxPowerFactor,
this.maxDataFactor this.maxDataFactor
)); ));
} }
   
// Post an error in the communication messages describing the reason transmission has failed. Currently there // Post an error in the communication messages describing the reason transmission has failed. Currently there
// is only one reason for this. // is only one reason for this.
protected void PostCannotTransmitError() protected void PostCannotTransmitError()
{ {
string ErrorText = string.Format ( string ErrorText = string.Format (
"Unable to transmit: out of range! Maximum range = {0}m; Current range = {1}m.", "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.maxTransmitDistance, 2),
Tools.MuMech_ToSI((double)this.transmitDistance, 2) Tools.MuMech_ToSI((double)this.transmitDistance, 2)
); );
   
this.ErrorMsg.message = string.Format( this.ErrorMsg.message = string.Format(
"<color='#{0}{1}{2}{3}'><b>{4}</b></color>", "<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"), ((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"), ((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"), ((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"), ((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
ErrorText ErrorText
); );
   
Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message); Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
   
ScreenMessages.PostScreenMessage(this.ErrorMsg, false); ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
} }
   
// Before transmission, set packetResourceCost. Per above, packet cost increases with the square of // Before transmission, set packetResourceCost. Per above, packet cost increases with the square of
// distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point // distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point
// transmission fails (see CanTransmit). // transmission fails (see CanTransmit).
protected void PreTransmit_SetPacketResourceCost() protected void PreTransmit_SetPacketResourceCost()
{ {
if (this.transmitDistance <= this.nominalRange) if (this.transmitDistance <= this.nominalRange)
{ {
base.packetResourceCost = this._basepacketResourceCost; base.packetResourceCost = this._basepacketResourceCost;
} }
else else
{ {
base.packetResourceCost = this._basepacketResourceCost base.packetResourceCost = this._basepacketResourceCost
* (float)Math.Pow (this.transmitDistance / this.nominalRange, 2); * (float)Math.Pow (this.transmitDistance / this.nominalRange, 2);
} }
} }
   
// Before transmission, set packetSize. Per above, packet size increases with the inverse square of // Before transmission, set packetSize. Per above, packet size increases with the inverse square of
// distance. packetSize maxes out at _basepacketSize * maxDataFactor. // distance. packetSize maxes out at _basepacketSize * maxDataFactor.
protected void PreTransmit_SetPacketSize() protected void PreTransmit_SetPacketSize()
{ {
if (this.transmitDistance >= this.nominalRange) if (this.transmitDistance >= this.nominalRange)
{ {
base.packetSize = this._basepacketSize; base.packetSize = this._basepacketSize;
} }
else else
{ {
base.packetSize = Math.Min( base.packetSize = Math.Min(
this._basepacketSize * (float)Math.Pow (this.nominalRange / this.transmitDistance, 2), this._basepacketSize * (float)Math.Pow (this.nominalRange / this.transmitDistance, 2),
this._basepacketSize * this.maxDataFactor); this._basepacketSize * this.maxDataFactor);
} }
} }
   
// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description. // Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
public override string GetInfo() public override string GetInfo()
{ {
string text = base.GetInfo(); string text = base.GetInfo();
text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n"; 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.maxTransmitDistance, 2) + "m\n";
return text; return text;
} }
   
// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible. // Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
public new bool CanTransmit() public new bool CanTransmit()
{ {
PartStates partState = this.part.State; PartStates partState = this.part.State;
if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED) if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
{ {
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: {1} on {2} cannot transmit: {3}", "{0}: {1} on {2} cannot transmit: {3}",
this.GetType().Name, this.GetType().Name,
this.part.partInfo.title, this.part.partInfo.title,
this.vessel.name, this.vessel.name,
Enum.GetName(typeof(PartStates), partState) Enum.GetName(typeof(PartStates), partState)
)); ));
return false; return false;
} }
return this.relay.CanTransmit(); return this.relay.CanTransmit();
} }
   
// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit // Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
// returns false. // returns false.
public new void TransmitData(List<ScienceData> dataQueue) public new void TransmitData(List<ScienceData> dataQueue)
{ {
if (this.CanTransmit()) if (this.CanTransmit())
{ {
base.TransmitData(dataQueue); base.TransmitData(dataQueue);
} }
else else
{ {
this.PostCannotTransmitError (); this.PostCannotTransmitError ();
} }
   
Tools.PostDebugMessage ( Tools.PostDebugMessage (
"distance: " + this.transmitDistance "distance: " + this.transmitDistance
+ " packetSize: " + this.packetSize + " packetSize: " + this.packetSize
+ " packetResourceCost: " + this.packetResourceCost + " packetResourceCost: " + this.packetResourceCost
); );
} }
   
// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit // Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
// returns false. // returns false.
public new void StartTransmission() public new void StartTransmission()
{ {
PreTransmit_SetPacketSize (); PreTransmit_SetPacketSize ();
PreTransmit_SetPacketResourceCost (); PreTransmit_SetPacketResourceCost ();
   
Tools.PostDebugMessage ( Tools.PostDebugMessage (
"distance: " + this.transmitDistance "distance: " + this.transmitDistance
+ " packetSize: " + this.packetSize + " packetSize: " + this.packetSize
+ " packetResourceCost: " + this.packetResourceCost + " packetResourceCost: " + this.packetResourceCost
); );
   
if (this.CanTransmit()) if (this.CanTransmit())
{ {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
   
message.Append("["); message.Append("[");
message.Append(base.part.partInfo.title); message.Append(base.part.partInfo.title);
message.Append("] "); message.Append("]: ");
   
message.Append("Beginning transmission "); message.Append("Beginning transmission ");
   
if (this.relay.nearestRelay == null) if (this.relay.nearestRelay == null)
{ {
message.Append("directly to Kerbin."); message.Append("directly to Kerbin.");
} }
else else
{ {
message.Append("via "); message.Append("via ");
message.Append(this.relay.nearestRelay); message.Append(this.relay.nearestRelay);
} }
   
ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT); ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
   
base.StartTransmission(); base.StartTransmission();
} }
else else
{ {
this.PostCannotTransmitError (); this.PostCannotTransmitError ();
} }
} }
   
public void Update() public void Update()
{ {
if (this.actionUIUpdate) if (this.actionUIUpdate)
{ {
this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m"; this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m";
this.UIpacketSize = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataRate) + "MiT" : "N/A"; this.UIpacketSize = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataRate) + "MiT" : "N/A";
this.UIpacketCost = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataResourceCost) + "E" : "N/A"; this.UIpacketCost = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataResourceCost) + "E" : "N/A";
} }
} }
   
public void onPartActionUICreate(Part eventPart) public void onPartActionUICreate(Part eventPart)
{ {
if (eventPart == base.part) if (eventPart == base.part)
{ {
this.actionUIUpdate = true; this.actionUIUpdate = true;
} }
} }
   
public void onPartActionUIDismiss(Part eventPart) public void onPartActionUIDismiss(Part eventPart)
{ {
if (eventPart == base.part) if (eventPart == base.part)
{ {
this.actionUIUpdate = false; this.actionUIUpdate = false;
} }
} }
   
public override string ToString() public override string ToString()
{ {
return string.Format( return string.Format(
"{0} on {1}.", "{0} on {1}.",
this.part.partInfo.title, this.part.partInfo.title,
vessel.name vessel.name
); );
} }
   
// When debugging, it's nice to have a button that just tells you everything. // When debugging, it's nice to have a button that just tells you everything.
#if DEBUG #if DEBUG
[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)] [KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)]
public void DebugInfo() public void DebugInfo()
{ {
PreTransmit_SetPacketSize (); PreTransmit_SetPacketSize ();
PreTransmit_SetPacketResourceCost (); PreTransmit_SetPacketResourceCost ();
   
string msg = string.Format( string msg = string.Format(
"'{0}'\n" + "'{0}'\n" +
"_basepacketSize: {1}\n" + "_basepacketSize: {1}\n" +
"packetSize: {2}\n" + "packetSize: {2}\n" +
"_basepacketResourceCost: {3}\n" + "_basepacketResourceCost: {3}\n" +
"packetResourceCost: {4}\n" + "packetResourceCost: {4}\n" +
"maxTransmitDistance: {5}\n" + "maxTransmitDistance: {5}\n" +
"transmitDistance: {6}\n" + "transmitDistance: {6}\n" +
"nominalRange: {7}\n" + "nominalRange: {7}\n" +
"CanTransmit: {8}\n" + "CanTransmit: {8}\n" +
"DataRate: {9}\n" + "DataRate: {9}\n" +
"DataResourceCost: {10}\n" + "DataResourceCost: {10}\n" +
"TransmitterScore: {11}\n" + "TransmitterScore: {11}\n" +
"NearestRelay: {12}", "NearestRelay: {12}",
this.name, this.name,
this._basepacketSize, this._basepacketSize,
base.packetSize, base.packetSize,
this._basepacketResourceCost, this._basepacketResourceCost,
base.packetResourceCost, base.packetResourceCost,
this.maxTransmitDistance, this.maxTransmitDistance,
this.transmitDistance, this.transmitDistance,
this.nominalRange, this.nominalRange,
this.CanTransmit(), this.CanTransmit(),
this.DataRate, this.DataRate,
this.DataResourceCost, this.DataResourceCost,
ScienceUtil.GetTransmitterScore(this), ScienceUtil.GetTransmitterScore(this),
this.relay.FindNearestRelay() this.relay.FindNearestRelay()
); );
ScreenMessages.PostScreenMessage (new ScreenMessage (msg, 4f, ScreenMessageStyle.UPPER_RIGHT)); ScreenMessages.PostScreenMessage (new ScreenMessage (msg, 4f, ScreenMessageStyle.UPPER_RIGHT));
} }
#endif #endif
} }
} }
  // AntennaRange © 2014 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.Reflection;
  using System.Runtime.CompilerServices;
 
  // Information about this assembly is defined by the following attributes.
  // Change them to the values specific to your project.
  [assembly: AssemblyTitle("AntennaRange")]
  [assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")]
  [assembly: AssemblyCopyright("toadicus")]
  // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
  // The form "{Major}.{Minor}.*" will automatically update the build and revision,
  // and "{Major}.{Minor}.{Build}.*" will update just the revision.
  [assembly: AssemblyVersion("0.6.2.*")]
  // The following attributes are used to specify the signing key for the assembly,
  // if desired. See the Mono documentation for more information about signing.
  //[assembly: AssemblyDelaySign(false)]
  //[assembly: AssemblyKeyFile("")]
 
 
  2014-01-14 toadicus <>
 
  * AssemblyInfo.cs: New AssemblyInfo file for reason.