Example #1
0
        /// <summary>
        /// Gets the potential link cost, in EC/MiT.
        /// </summary>
        /// <returns>The potential link cost, in EC/MiT.</returns>
        /// <param name="potentialTarget">Potential target relay.</param>
        public RelayDataCost GetPotentialLinkCost(IAntennaRelay potentialTarget)
        {
            if (potentialTarget == null)
            {
                return(RelayDataCost.Infinity);
            }

            double currentSqrDistance = this.SqrDistanceTo(potentialTarget);

            if (currentSqrDistance > this.MaxLinkSqrDistanceTo(potentialTarget))
            {
                return(RelayDataCost.Infinity);
            }

            double nominalSqrDistance;

            if (ARConfiguration.UseAdditiveRanges)
            {
                nominalSqrDistance = this.nominalTransmitDistance * potentialTarget.nominalTransmitDistance;
            }
            else
            {
                nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
            }

            return(GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
        /// </summary>
        /// <param name="prefabRelay">The module reference underlying this AntennaRelay,
        /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
        /// <param name="pps">The prototype partreference on which the module resides.</param>
        public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay)
        {
            this.protoPart = pps;

            this.Log("constructed ({0})", this.GetType().Name);

            this.RecalculateMaxRange();
        }
Example #3
0
        /// <summary>
        /// Determines if relayOne is in range of the specified relayTwo.
        /// </summary>
        /// <returns><c>true</c> if relayOne is in range of the specifie relayTwo; otherwise, <c>false</c>.</returns>
        /// <param name="relayOne">Relay one.</param>
        /// <param name="relayTwo">Relay two.</param>
        public static bool IsInRangeOf(this AntennaRelay relayOne, IAntennaRelay relayTwo)
        {
            if (relayOne == null || relayTwo == null)
            {
                return(false);
            }

            return(relayOne.SqrDistanceTo(relayTwo) <= relayOne.MaxLinkSqrDistanceTo(relayTwo));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
        /// </summary>
        /// <param name="module">The module reference underlying this AntennaRelay,
        /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
        public AntennaRelay(IAntennaRelay module)
        {
            this.KerbinDirect = true;
            this.moduleRef = module;

            #if BENCH
            AntennaRelay.relayCount++;
            #endif

            this.LogDebug("{0}: constructed {1}", this.GetType().Name, this.ToString());
        }
Example #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
        /// </summary>
        /// <param name="module">The module reference underlying this AntennaRelay,
        /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
        public AntennaRelay(IAntennaRelay module)
        {
            this.KerbinDirect = true;
            this.moduleRef    = module;

                        #if BENCH
            AntennaRelay.relayCount++;
                        #endif

            this.LogDebug("{0}: constructed {1}", this.GetType().Name, this.ToString());
        }
Example #6
0
 /// <summary>
 /// Returns the square of the maximum link range between two relays.
 /// </summary>
 /// <returns>The maximum link range between two relays.</returns>
 /// <param name="relayOne">Relay one.</param>
 /// <param name="relayTwo">Relay two.</param>
 public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
 {
     if (ARConfiguration.UseAdditiveRanges)
     {
         return(relayOne.maxTransmitDistance * relayTwo.maxTransmitDistance);
     }
     else
     {
         return(relayOne.maxTransmitDistance * relayOne.maxTransmitDistance);
     }
 }
        /// <summary>
        /// Finds the nearest relay.
        /// </summary>
        /// <returns>The nearest relay or null, if no relays in range.</returns>
        public void FindNearestRelay()
        {
            if (!FlightGlobals.ready)
            {
                return;
            }

            PooledDebugLogger log;
            #if DEBUG
            log = PooledDebugLogger.New(this);
            #endif

            #if BENCH
            this.performanceTimer.Restart();

            long startVesselLoopTicks;
            long totalVesselLoopTicks;

            string slowestLOSVesselName = string.Empty;
            long slowestLOSVesselTicks = long.MinValue;
            long startLOSVesselTicks;
            long totalLOSVesselTicks;

            string slowestCircularVesselName = string.Empty;
            long slowestCircularVesselTicks = long.MinValue;
            long startCircularVesselTicks;
            long totalCircularVesselTicks;

            long startKerbinLOSTicks;
            long totalKerbinLOSTicks;
            long statusResolutionTicks;

            ushort usefulVesselCount = 0;
            #endif

            log.AppendFormat("{0}: Target search started).", this.ToString());

            #if DEBUG
            try {
            #endif

            // Declare a bunch of variables we'll be using.
            CelestialBody bodyOccludingBestOccludedRelay = null;
            IAntennaRelay needle;

            RelayDataCost cheapestRelayRate = RelayDataCost.Infinity;
            RelayDataCost cheapestOccludedRelayRate = RelayDataCost.Infinity;

            RelayDataCost potentialRelayRate;

            RelayDataCost kerbinRelayRate = this.GetPotentialLinkCost(Kerbin);

            bool isCircular;
            int iterCount;

            // Blank everything we're trying to find before the search.
            this.firstOccludingBody = null;
            this.bestOccludedRelay = null;
            this.targetRelay = null;
            this.nearestRelay = null;

            // Default to KerbinDirect = true in case something in here doesn't work right.
            this.KerbinDirect = true;

            /*
             * Loop through the useful relays as determined by ARFlightController and check each for line of sight and
             * distance, searching for the relay with the best distance/maxRange ratio that is in sight, in range, and
             * can transmit, also stashing the "best" relay outside of line of sight for failure report.
             * */
            IAntennaRelay potentialBestRelay;
            CelestialBody fob;

            #if BENCH
            startVesselLoopTicks = performanceTimer.ElapsedTicks;
            #endif

            for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++)
            {
                potentialBestRelay = ARFlightController.UsefulRelays[rIdx];
                log.AppendFormat("\n\tgot useful relay {0}",
                    potentialBestRelay == null ? "null" : potentialBestRelay.ToString());

                if (potentialBestRelay == null)
                {
                    log.Append("\n\t...skipping null relay");
                    continue;
                }

                if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel)
                {
                    log.AppendFormat(
                        "\n\t...skipping relay {0} because it or its vessel ({1}) is the same as ours" +
                        "\n\t\t(our vessel is {2})",
                        potentialBestRelay,
                        potentialBestRelay.vessel == null ? "null" : potentialBestRelay.vessel.vesselName,
                        this.vessel == null ? "null" : this.vessel.vesselName
                    );
                    continue;
                }

                #if BENCH
                usefulVesselCount++;
                #endif

                // Find the distance from here to the vessel...
                log.Append("\n\tgetting cost to potential vessel");
                potentialRelayRate = potentialBestRelay.CurrentNetworkLinkCost +
                    this.GetPotentialLinkCost(potentialBestRelay);

                log.AppendFormat(
                    "\n\tpotentialRelayRate = {0} ({1} + {2})",
                    potentialRelayRate,
                    potentialBestRelay.CurrentNetworkLinkCost,
                    this.GetPotentialLinkCost(potentialBestRelay)
                );

                #if BENCH
                startLOSVesselTicks = performanceTimer.ElapsedTicks;
                #endif

                log.Append("\n\t\tdoing LOS check");
                // Skip vessels to which we do not have line of sight.
                if (
                    ARConfiguration.RequireLineOfSight &&
                    !this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio)
                )
                {
                    #if BENCH
                    totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;

                    if (totalLOSVesselTicks > slowestLOSVesselTicks)
                    {
                        slowestLOSVesselTicks = totalLOSVesselTicks;
                        slowestLOSVesselName = vessel.vesselName;
                    }
                    #endif

                    log.Append("\n\t\t...failed LOS check");

                    log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.",
                        this.ToString(), potentialBestRelay);

                    log.AppendFormat("\n\t\t\tpotentialRelayRate: {0}", potentialRelayRate);
                    log.AppendFormat("\n\t\t\tcheapestOccludedRelayRate: {0}", cheapestOccludedRelayRate);

                    if (
                        (potentialRelayRate < cheapestRelayRate) &&
                        this.IsInRangeOf(potentialBestRelay) &&
                        potentialBestRelay.CanTransmit()
                    )
                    {
                        log.Append("\n\t\t...vessel is cheapest and in range and potentialBestRelay can transmit");
                        log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay);

                        this.bestOccludedRelay = potentialBestRelay;
                        bodyOccludingBestOccludedRelay = fob;
                        cheapestOccludedRelayRate = potentialRelayRate;
                    }
                    else
                    {
                        log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
                    }

                    continue;
                }
                #if BENCH
                else
                {
                    totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
                }

                if (totalLOSVesselTicks > slowestLOSVesselTicks)
                {
                    slowestLOSVesselTicks = totalLOSVesselTicks;
                    slowestLOSVesselName = vessel.vesselName;
                }
                #endif

                log.Append("\n\t\t...passed LOS check");

                /*
                 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
                 * */
                if (potentialRelayRate > cheapestRelayRate)
                {

                    log.AppendFormat(
                            "\n\t{0}: Relay {1} discarded because it is more expensive than the cheapest relay." +
                            "\n\t\t({2}, {3} > {4})",
                        this.ToString(),
                        potentialBestRelay,
                        this.nearestRelay == null ? "NULL" : this.nearestRelay.ToString(),
                        potentialRelayRate, cheapestRelayRate
                    );
                    continue;
                }

                log.Append("\n\t\t...passed distance check");

                if (potentialBestRelay.CanTransmit())
                {
                    #if BENCH
                    startCircularVesselTicks = performanceTimer.ElapsedTicks;
                    #endif

                    needle = potentialBestRelay;
                    isCircular = false;

                    iterCount = 0;
                    while (needle != null)
                    {
                        iterCount++;

                        if (needle.KerbinDirect)
                        {
                            break;
                        }

                        if (needle.targetRelay == null)
                        {
                            break;
                        }

                        if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
                        {
                            isCircular = true;
                            break;
                        }

                        // Avoid infinite loops when we're not catching things right.
                        if (iterCount > FlightGlobals.Vessels.Count)
                        {
                            this.LogError(
                                "iterCount exceeded while checking for circular network; assuming it is circular" +
                                "\n\tneedle={0}" +
                                "\n\tthis.moduleRef={1}",
                                needle == null ? "null" : string.Format(
                                    "{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
                                    needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
                                        "{0}\n\tneedle.targetRelay.vessel={1}",
                                        needle.targetRelay,
                                        needle.targetRelay.vessel == null ?
                                            "null" : needle.targetRelay.vessel.vesselName
                                    )
                                ),
                                this.moduleRef == null ? "null" : this.moduleRef.ToString()
                            );
                            isCircular = true;
                            break;
                        }

                        needle = needle.targetRelay;
                    }

                    if (!isCircular)
                    {
                        cheapestRelayRate = potentialRelayRate;
                        this.nearestRelay = potentialBestRelay;

                        log.AppendFormat("\n\t{0}: found new cheapest relay {1} ({2} EC/MiT)",
                            this.ToString(),
                            this.nearestRelay.ToString(),
                            cheapestRelayRate
                        );
                    }
                    else
                    {
                        log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping",
                            potentialBestRelay
                        );
                    }

                    #if BENCH
                    totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks;

                    if (totalCircularVesselTicks > slowestCircularVesselTicks)
                    {
                        slowestCircularVesselName = vessel.vesselName;
                        slowestCircularVesselTicks = totalCircularVesselTicks;
                    }

                    #endif
                }
            }

            #if BENCH
            totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks;
            #endif

            CelestialBody bodyOccludingKerbin = null;

            log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
            log.AppendFormat(
                    "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinRelayRate={5} EC/MiT)",
                this,
                this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                cheapestRelayRate,
                this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
                cheapestOccludedRelayRate,
                kerbinRelayRate
            );

            #if BENCH
            startKerbinLOSTicks = this.performanceTimer.ElapsedTicks;
            #endif

            // If we don't have LOS to Kerbin, focus on relays
            if (
                ARConfiguration.RequireLineOfSight &&
                !this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
            )
            {
                #if BENCH
                totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
                #endif
                log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);

                // If we're in range of the "nearest" (actually cheapest) relay, use it.
                if (this.IsInRangeOf(this.nearestRelay))
                {
                    log.AppendFormat("\n\t\tCan transmit to nearby relay {0}).",
                        this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                    );

                    this.KerbinDirect = false;
                    this.canTransmit = true;
                    this.targetRelay = this.nearestRelay;
                }
                // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
                else
                {
                    log.AppendFormat("\n\t\tCan't transmit to nearby relay {0}.",
                        this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                    );

                    this.canTransmit = false;

                    // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay.
                    // Since cheapestOccludedRelayRate is infinity if there are no occluded relays, this is safe
                    if (cheapestOccludedRelayRate < kerbinRelayRate)
                    {
                        log.AppendFormat("\n\t\t\tBest occluded relay is cheaper than Kerbin ({0} < {1})",
                            cheapestOccludedRelayRate, kerbinRelayRate);

                        this.KerbinDirect = false;

                        // If the nearest relay is cheaper than the best occluded relay, pick it.
                        // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                        if (cheapestRelayRate < cheapestOccludedRelayRate)
                        {
                            log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.",
                                cheapestRelayRate, cheapestOccludedRelayRate);

                            this.targetRelay = this.nearestRelay;
                            this.firstOccludingBody = null;
                        }
                        // Otherwise, target the best occluded relay.
                        else
                        {
                            log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                cheapestRelayRate, cheapestOccludedRelayRate);

                            this.targetRelay = bestOccludedRelay;
                            this.firstOccludingBody = bodyOccludingBestOccludedRelay;
                        }
                    }
                    // Otherwise, check Kerbin against the "nearest" (cheapest) relay.
                    else
                    {
                        log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})",
                            cheapestOccludedRelayRate, kerbinRelayRate);

                        // If the "nearest" (cheapest) relay is cheaper than Kerbin, pick it.
                        // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                        if (cheapestRelayRate < kerbinRelayRate)
                        {
                            log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.",
                                cheapestRelayRate, kerbinRelayRate);

                            // Since we have LOS, blank the first occluding body.
                            this.firstOccludingBody = null;

                            this.KerbinDirect = false;
                            this.targetRelay = this.nearestRelay;
                        }
                        // Otherwise, pick Kerbin.
                        else
                        {
                            log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                cheapestRelayRate, kerbinRelayRate);

                            this.KerbinDirect = true;
                            this.firstOccludingBody = bodyOccludingKerbin;
                            this.targetRelay = null;
                        }
                    }
                }
            }
            // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
            else
            {
                #if BENCH
                totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
                #endif

                log.AppendFormat("\n\tKerbin is in LOS.");

                // If the nearest relay is in range, we can transmit.
                if (this.IsInRangeOf(this.nearestRelay))
                {
                    log.AppendFormat("\n\t\tCan transmit to nearby relay {0} (in range).",
                        this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                    );

                    this.canTransmit = true;

                    // If the nearestRelay is closer than Kerbin, use it.
                    if (cheapestRelayRate < kerbinRelayRate)
                    {
                        log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
                            this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                            cheapestRelayRate, kerbinRelayRate);

                        this.KerbinDirect = false;
                        this.targetRelay = this.nearestRelay;
                    }
                    // Otherwise, Kerbin is closer, so use it.
                    else
                    {
                        log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
                            this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                            cheapestRelayRate, kerbinRelayRate);

                        this.KerbinDirect = true;
                        this.targetRelay = null;
                    }
                }
                // If the nearest relay is out of range, we still need to check on Kerbin.
                else
                {
                    log.AppendFormat("\n\t\tCheapest relay {0} is out of range.",
                        this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                    );

                    // If Kerbin is in range, use it.
                    if (this.IsInRangeOf(Kerbin))
                    {
                        log.AppendFormat("\n\t\t\tCan transmit to Kerbin (in range).");

                        this.canTransmit = true;
                        this.KerbinDirect = true;
                        this.targetRelay = null;
                    }
                    // If Kerbin is out of range and the nearest relay is out of range, pick a second best between
                    // Kerbin and bestOccludedRelay
                    else
                    {
                        log.AppendFormat("\n\t\t\tCan't transmit to Kerbin (out of range).");

                        this.canTransmit = false;

                        // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay.
                        // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
                        if (cheapestOccludedRelayRate < kerbinRelayRate)
                        {
                            log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
                                cheapestOccludedRelayRate, kerbinRelayRate);

                            this.KerbinDirect = false;

                            // If the nearest relay is closer than the best occluded relay, pick it.
                            // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                            if (cheapestRelayRate < cheapestOccludedRelayRate)
                            {
                                log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.",
                                    cheapestRelayRate, cheapestOccludedRelayRate);

                                this.targetRelay = this.nearestRelay;
                                this.firstOccludingBody = null;
                            }
                            // Otherwise, target the best occluded relay.
                            else
                            {
                                log.AppendFormat("\n\t\t\t\t...and cheaper than the cheapest relay ({0} >= {1}), so picking it.",
                                    cheapestRelayRate, cheapestOccludedRelayRate);

                                this.targetRelay = bestOccludedRelay;
                                this.firstOccludingBody = bodyOccludingBestOccludedRelay;
                            }
                        }
                        // Otherwise, check Kerbin against the nearest relay.
                        // Since we have LOS, blank the first occluding body.
                        else
                        {
                            log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})",
                                cheapestOccludedRelayRate, kerbinRelayRate);

                            this.firstOccludingBody = null;

                            // If the nearest relay is cheaper than Kerbin, pick it.
                            // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                            if (cheapestRelayRate < kerbinRelayRate)
                            {
                                log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.",
                                    cheapestRelayRate, kerbinRelayRate);

                                this.KerbinDirect = false;
                                this.targetRelay = this.nearestRelay;
                            }
                            // Otherwise, pick Kerbin.
                            else
                            {
                                log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                    cheapestRelayRate, kerbinRelayRate);

                                this.KerbinDirect = true;
                                this.targetRelay = null;
                            }
                        }
                    }
                }
            }

            if (ARConfiguration.UseAdditiveRanges)
            {
                if (this.KerbinDirect)
                {
                    this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange;
                    this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange;
                }
                else
                {
                    this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance;
                    this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance;
                }
            }
            else
            {
                this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
                this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
            }

            if (this.canTransmit)
            {
                if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance)
                {
                    this.LinkStatus = ConnectionStatus.Optimal;
                }
                else
                {
                    this.LinkStatus = ConnectionStatus.Suboptimal;
                }
            }
            else
            {
                this.LinkStatus = ConnectionStatus.None;
            }

            #if BENCH
            statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks;
            #endif

            log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());

            #if DEBUG
            } catch (Exception ex) {
                log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
            #if QUIT_ON_EXCEPTION
                UnityEngine.Application.Quit();
            #endif
            } finally {
            #endif
            log.Print(false);
            #if DEBUG
            }
            #endif

            #if BENCH
            AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks;
            AntennaRelay.searchCount++;
            this.performanceTimer.Stop();

            double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount;

            if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay)
            {
                AntennaRelay.searchCount = 0u;
                AntennaRelay.searchTimer = 0u;

                AntennaRelay.averager.AddItem(averageSearchTime);
                AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d);
            }

            if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime)
            {
                System.Text.StringBuilder sb = Tools.GetStringBuilder();

                sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" +
                    " took significantly longer than average ({1:S3}s vs {2:S3}s)",
                    this.ToString(),
                    (double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                    (double)AntennaRelay.averager.Average / (double)System.Diagnostics.Stopwatch.Frequency
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tVessel loop time: {0:S3}s",
                    (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tAverage vessel time for each of {1} vessels: {0:S3}s",
                    (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency /
                    (double)usefulVesselCount,
                    usefulVesselCount
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}",
                    (double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                    slowestLOSVesselName
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}",
                    (double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                    slowestCircularVesselName
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s",
                    (double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency
                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s",
                    (double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency
                );

                // sb.AppendFormat(Tools.SIFormatter, "", start)

                this.LogWarning(sb.ToString());

                Tools.PutStringBuilder(sb);
            }
            #endif
        }
        private void Update()
        {
            if (MapView.MapIsEnabled && this.mapRenderer == null)
            {
                this.mapRenderer = MapView.MapCamera.gameObject.AddComponent <ARMapRenderer>();
            }

            if (this.toolbarButton != null)
            {
                this.toolbarButton.Enabled = MapView.MapIsEnabled;
            }

            if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
            {
                this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication(
                    this.buttonToggle, this.buttonToggle,
                    ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW,
                    this.appLauncherTextures[ConnectionStatus.None]
                    );
            }

            if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > ARConfiguration.UpdateDelay)
            {
                this.updateTimer.Restart();
            }
            else
            {
                return;
            }

            this.log.Clear();

            this.log.Append("[ARFlightController]: Update");

            if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
            {
                Vessel                vessel;
                IAntennaRelay         relay;
                IAntennaRelay         bestActiveRelay = null;
                IList <IAntennaRelay> activeVesselRelays;

                usefulRelays.Clear();

                for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
                {
                    vessel = FlightGlobals.Vessels[vIdx];

                    if (vessel == null || vessel == FlightGlobals.ActiveVessel)
                    {
                        continue;
                    }

                    switch (vessel.vesselType)
                    {
                    case VesselType.Debris:
                    case VesselType.Flag:
                    case VesselType.Unknown:
                        continue;
                    }

                    log.AppendFormat("\nFetching best relay for vessel {0}", vessel);

                    relay = vessel.GetBestRelay();

                    if (relay != null)
                    {
                        log.AppendFormat("\n\tAdding useful relay {0}", relay);

                        usefulRelays.Add(relay);
                    }
                }

                activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];

                if (activeVesselRelays.Count > 0)
                {
                    bestActiveRelay = RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel);

                    log.AppendFormat("\n\tAdding best active vessel relay {0} to usefulRelays", bestActiveRelay);

                    usefulRelays.Add(bestActiveRelay);
                }

                log.AppendFormat("\n\tDoing target searches for {0} useful relays", usefulRelays.Count);

                for (int uIdx = 0; uIdx < usefulRelays.Count; uIdx++)
                {
                    relay = usefulRelays[uIdx];

                    if (relay == null)
                    {
                        continue;
                    }

                    log.AppendFormat("\n\tDoing target search for useful relay {0}", relay);

                    relay.FindNearestRelay();
                    relay.RecalculateTransmissionRates();
                }

                // Very last, find routes for the non-best relays on the active vessel.
                for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
                {
                    relay = activeVesselRelays[rIdx];

                    // The best active relay will get checked with the other useful relays later.
                    if (relay == null || relay == bestActiveRelay)
                    {
                        continue;
                    }

                    log.AppendFormat("\nFinding nearest relay for active vessel relay {0}", relay);

                    relay.FindNearestRelay();
                    relay.RecalculateTransmissionRates();
                }

                if (this.toolbarButton != null || this.appLauncherButton != null)
                {
                    log.Append("\nChecking active vessel relay status.");

                    this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();

                    log.AppendFormat("\n\tcurrentConnectionStatus: {0}, setting texture to {1}",
                                     this.currentConnectionStatus, this.currentConnectionTexture);

                    if (this.toolbarButton != null)
                    {
                        this.toolbarButton.TexturePath = this.currentConnectionTexture;

                        if (this.currentConnectionStatus == ConnectionStatus.None)
                        {
                            if (!this.toolbarButton.Important)
                            {
                                this.toolbarButton.Important = true;
                            }
                        }
                        else
                        {
                            if (this.toolbarButton.Important)
                            {
                                this.toolbarButton.Important = false;
                            }
                        }
                    }
                    if (this.appLauncherButton != null)
                    {
                        this.appLauncherButton.SetTexture(this.currentAppLauncherTexture);
                    }
                }
            }

            log.Print(false);
        }
		/// <summary>
		/// Returns the distance between two IAntennaRelays.
		/// </summary>
		/// <param name="relayOne">Relay one.</param>
		/// <param name="relayTwo">Relay two.</param>
		public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
		{
			return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
		}
		/// <summary>
		/// Determines if relayOne is in range of the specified relayTwo.
		/// </summary>
		/// <returns><c>true</c> if relayOne is in range of the specifie relayTwo; otherwise, <c>false</c>.</returns>
		/// <param name="relayOne">Relay one.</param>
		/// <param name="relayTwo">Relay two.</param>
		public static bool IsInRangeOf(this AntennaRelay relayOne, IAntennaRelay relayTwo)
		{
			if (relayOne == null || relayTwo == null)
			{
				return false;
			}

			return relayOne.SqrDistanceTo(relayTwo) <= relayOne.MaxLinkSqrDistanceTo(relayTwo);
		}
		/// <summary>
		/// Returns the square of the maximum link range between two relays.
		/// </summary>
		/// <returns>The maximum link range between two relays.</returns>
		/// <param name="relayOne">Relay one.</param>
		/// <param name="relayTwo">Relay two.</param>
		public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
		{
			if (ARConfiguration.UseAdditiveRanges)
			{
				return relayOne.maxTransmitDistance * relayTwo.maxTransmitDistance;
			}
			else
			{
				return relayOne.maxTransmitDistance * relayOne.maxTransmitDistance;
			}
		}
		private void SetRelayVertices(IAntennaRelay relay)
		{
			log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay);

			if (relay.vessel == null)
			{
				log.Append("\n\t\tvessel is null, bailing out");
				return;
			}

			LineRenderer renderer = this[relay.vessel.id];
			Vector3 start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());

			float lineWidth;
			float d = Screen.height / 2f + 0.01f;

			if (MapView.Draw3DLines)
			{
				lineWidth = 0.00833333333f * MapView.MapCamera.Distance;
			}
			else
			{
				lineWidth = 3f;

				// TODO: No idea if this substitution is right.
				// start = MapView.MapCamera.camera.WorldToScreenPoint(start);
				start = PlanetariumCamera.Camera.WorldToScreenPoint(start);

				start.z = start.z >= 0f ? d : -d;
			}

			renderer.SetWidth(lineWidth, lineWidth);

			renderer.SetPosition(0, start);

			int idx = 0;

			#if DEBUG
			relayStart = timer.ElapsedMilliseconds;
			#endif

			Vector3 nextPoint;

			renderer.enabled = true;

			if (!relay.CanTransmit())
			{
				thisColor = Color.red;
			}
			else
			{
				if (relay.LinkStatus == ConnectionStatus.Optimal)
				{
					thisColor = Color.green;
				}
				else
				{
					thisColor = Color.yellow;
				}
			}

			if (relay.KerbinDirect)
			{
				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
			}
			else
			{
				if (relay.targetRelay == null || relay.targetRelay.vessel == null)
				{
					this.LogError(
						"SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!",
						relay
					);

					renderer.enabled = false;
					return;
				}

				switch (relay.targetRelay.vessel.vesselType)
				{
					case VesselType.Debris:
					case VesselType.Flag:
					case VesselType.Unknown:
						renderer.enabled = false;
						return;
					default:
						break;
				}

				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
			}

			renderer.SetColors(thisColor, thisColor);

			if (!MapView.Draw3DLines)
			{
				// TODO: No idea if this substitution is right.
				// nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
				nextPoint = PlanetariumCamera.Camera.WorldToScreenPoint(nextPoint);
				nextPoint.z = nextPoint.z >= 0f ? d : -d;
			}

			idx++;

			renderer.SetVertexCount(idx + 1);
			renderer.SetPosition(idx, nextPoint);

			log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart);
		}
Example #13
0
        /// <summary>
        /// Returns the distance from this IAntennaRelay to the given CelestialBody
        /// </summary>
        /// <param name="relay">Relay.</param>
        /// <param name="body">Body.</param>
        public static double DistanceTo(this IAntennaRelay relay, CelestialBody body)
        {
            double range = relay.vessel.DistanceTo(body) - body.Radius;

            return(range);
        }
Example #14
0
 /// <summary>
 /// Returns the distance between two IAntennaRelays.
 /// </summary>
 /// <param name="relayOne">Relay one.</param>
 /// <param name="relayTwo">Relay two.</param>
 public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
 {
     return(relayOne.vessel.sqrDistanceTo(relayTwo.vessel));
 }
Example #15
0
        private void SetRelayVertices(IAntennaRelay relay)
        {
            log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay);

            if (relay.vessel == null)
            {
                log.Append("\n\t\tvessel is null, bailing out");
                return;
            }

            LineRenderer renderer = this[relay.vessel.id];
            Vector3      start    = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());

            float lineWidth;
            float d = Screen.height / 2f + 0.01f;

            if (MapView.Draw3DLines)
            {
                lineWidth = 0.00833333333f * MapView.MapCamera.Distance;
            }
            else
            {
                lineWidth = 3f;

                // TODO: No idea if this substitution is right.
                // start = MapView.MapCamera.camera.WorldToScreenPoint(start);
                start = PlanetariumCamera.Camera.WorldToScreenPoint(start);

                start.z = start.z >= 0f ? d : -d;
            }

            renderer.SetWidth(lineWidth, lineWidth);

            renderer.SetPosition(0, start);

            int idx = 0;

                        #if DEBUG
            relayStart = timer.ElapsedMilliseconds;
                        #endif

            Vector3 nextPoint;

            renderer.enabled = true;

            if (!relay.CanTransmit())
            {
                thisColor = Color.red;
            }
            else
            {
                if (relay.LinkStatus == ConnectionStatus.Optimal)
                {
                    thisColor = Color.green;
                }
                else
                {
                    thisColor = Color.yellow;
                }
            }

            if (relay.KerbinDirect)
            {
                nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
            }
            else
            {
                if (relay.targetRelay == null || relay.targetRelay.vessel == null)
                {
                    this.LogError(
                        "SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!",
                        relay
                        );

                    renderer.enabled = false;
                    return;
                }

                switch (relay.targetRelay.vessel.vesselType)
                {
                case VesselType.Debris:
                case VesselType.Flag:
                case VesselType.Unknown:
                    renderer.enabled = false;
                    return;

                default:
                    break;
                }

                nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
            }

            renderer.SetColors(thisColor, thisColor);

            if (!MapView.Draw3DLines)
            {
                // TODO: No idea if this substitution is right.
                // nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
                nextPoint   = PlanetariumCamera.Camera.WorldToScreenPoint(nextPoint);
                nextPoint.z = nextPoint.z >= 0f ? d : -d;
            }

            idx++;

            renderer.SetVertexCount(idx + 1);
            renderer.SetPosition(idx, nextPoint);

            log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart);
        }
        /// <summary>
        /// Gets the potential link cost, in EC/MiT.
        /// </summary>
        /// <returns>The potential link cost, in EC/MiT.</returns>
        /// <param name="potentialTarget">Potential target relay.</param>
        public RelayDataCost GetPotentialLinkCost(IAntennaRelay potentialTarget)
        {
            if (potentialTarget == null)
            {
                return RelayDataCost.Infinity;
            }

            double currentSqrDistance = this.SqrDistanceTo(potentialTarget);

            if (currentSqrDistance > this.MaxLinkSqrDistanceTo(potentialTarget))
            {
                return RelayDataCost.Infinity;
            }

            double nominalSqrDistance;
            if (ARConfiguration.UseAdditiveRanges)
            {
                nominalSqrDistance = this.nominalTransmitDistance * potentialTarget.nominalTransmitDistance;
            }
            else
            {
                nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
            }

            return GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance);
        }
Example #17
0
        // Produce a Part-hashed table of relays for the given vessel
        private void getVesselRelays(Vessel vessel, ref List <IAntennaRelay> relays)
        {
            // We're going to completely regen this table, so dump the current contents.
            relays.Clear();

            Logging.PostDebugMessage(string.Format(
                                         "{0}: Getting antenna relays from vessel {1}.",
                                         "IAntennaRelay",
                                         vessel.vesselName
                                         ));

            double        bestRelayRange = double.NegativeInfinity;
            IAntennaRelay bestRelay      = null;
            IAntennaRelay relay;

            // If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
            if (vessel.loaded)
            {
                Logging.PostDebugMessage(string.Format(
                                             "{0}: vessel {1} is loaded, searching for modules in loaded parts.",
                                             "IAntennaRelay",
                                             vessel.vesselName
                                             ));

                // Loop through the Parts in the Vessel...
                Part part;
                for (int partIdx = 0; partIdx < vessel.Parts.Count; partIdx++)
                {
                    part = vessel.Parts[partIdx];

                    // ...loop through the PartModules in the Part...
                    PartModule module;
                    for (int modIdx = 0; modIdx < part.Modules.Count; modIdx++)
                    {
                        module = part.Modules[modIdx];

                        // ...if the module is a relay...
                        if (module is IAntennaRelay)
                        {
                            relay = (module as IAntennaRelay);

                            if (relay.maxTransmitDistance > bestRelayRange)
                            {
                                bestRelayRange = relay.maxTransmitDistance;
                                bestRelay      = relay;
                            }

                            // ...add the module to the table
                            relays.Add(relay);
                            // ...neglect relay objects after the first in each part.
                            break;
                        }
                    }
                }
            }
            // If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots.
            else
            {
                Logging.PostDebugMessage(string.Format(
                                             "{0}: vessel {1} is not loaded, searching for modules in prototype parts.",
                                             this.GetType().Name,
                                             vessel.vesselName
                                             ));

                // Loop through the ProtoPartModuleSnapshots in the Vessel...
                ProtoPartSnapshot pps;
                for (int ppsIdx = 0; ppsIdx < vessel.protoVessel.protoPartSnapshots.Count; ppsIdx++)
                {
                    pps = vessel.protoVessel.protoPartSnapshots[ppsIdx];

                    Logging.PostDebugMessage(string.Format(
                                                 "{0}: Searching in protopartsnapshot {1}",
                                                 this.GetType().Name,
                                                 pps
                                                 ));

                    // ...Fetch the prefab, because it's more useful for what we're doing.
                    Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab;

                    Logging.PostDebugMessage(string.Format(
                                                 "{0}: Got partPrefab {1} in protopartsnapshot {2}",
                                                 this.GetType().Name,
                                                 partPrefab,
                                                 pps
                                                 ));

                    // ...loop through the PartModules in the prefab...
                    PartModule module;
                    for (int modIdx = 0; modIdx < partPrefab.Modules.Count; modIdx++)
                    {
                        module = partPrefab.Modules[modIdx];

                        Logging.PostDebugMessage(string.Format(
                                                     "{0}: Searching in partmodule {1}",
                                                     this.GetType().Name,
                                                     module
                                                     ));

                        // ...if the module is a relay...
                        if (module is IAntennaRelay)
                        {
                            Logging.PostDebugMessage(string.Format(
                                                         "{0}: partmodule {1} is antennarelay",
                                                         this.GetType().Name,
                                                         module
                                                         ));

                            relay = new ProtoAntennaRelay(module as IAntennaRelay, pps);

                            if (relay.maxTransmitDistance > bestRelayRange)
                            {
                                bestRelayRange = relay.maxTransmitDistance;
                                bestRelay      = relay;
                            }

                            // ...build a new ProtoAntennaRelay and add it to the table
                            relays.Add(relay);
                            // ...neglect relay objects after the first in each part.
                            break;
                        }
                    }
                }
            }

            this.bestRelayTable[vessel.id] = bestRelay;

            Logging.PostDebugMessage(string.Format(
                                         "{0}: vessel '{1}' ({2}) has {3} transmitters.",
                                         "IAntennaRelay",
                                         vessel.vesselName,
                                         vessel.id,
                                         relays.Count
                                         ));
        }
Example #18
0
        /// <summary>
        /// Finds the nearest relay.
        /// </summary>
        /// <returns>The nearest relay or null, if no relays in range.</returns>
        public void FindNearestRelay()
        {
            if (!FlightGlobals.ready)
            {
                return;
            }

            PooledDebugLogger log;

                        #if DEBUG
            log = PooledDebugLogger.New(this);
                        #endif

                        #if BENCH
            this.performanceTimer.Restart();

            long startVesselLoopTicks;
            long totalVesselLoopTicks;

            string slowestLOSVesselName  = string.Empty;
            long   slowestLOSVesselTicks = long.MinValue;
            long   startLOSVesselTicks;
            long   totalLOSVesselTicks;

            string slowestCircularVesselName  = string.Empty;
            long   slowestCircularVesselTicks = long.MinValue;
            long   startCircularVesselTicks;
            long   totalCircularVesselTicks;

            long startKerbinLOSTicks;
            long totalKerbinLOSTicks;
            long statusResolutionTicks;

            ushort usefulVesselCount = 0;
                        #endif

            log.AppendFormat("{0}: Target search started).", this.ToString());

                        #if DEBUG
            try {
                        #endif

            // Declare a bunch of variables we'll be using.
            CelestialBody bodyOccludingBestOccludedRelay = null;
            IAntennaRelay needle;

            RelayDataCost cheapestRelayRate         = RelayDataCost.Infinity;
            RelayDataCost cheapestOccludedRelayRate = RelayDataCost.Infinity;

            RelayDataCost potentialRelayRate;

            RelayDataCost kerbinRelayRate = this.GetPotentialLinkCost(Kerbin);

            bool isCircular;
            int iterCount;

            // Blank everything we're trying to find before the search.
            this.firstOccludingBody = null;
            this.bestOccludedRelay  = null;
            this.targetRelay        = null;
            this.nearestRelay       = null;

            // Default to KerbinDirect = true in case something in here doesn't work right.
            this.KerbinDirect = true;

            /*
             * Loop through the useful relays as determined by ARFlightController and check each for line of sight and
             * distance, searching for the relay with the best distance/maxRange ratio that is in sight, in range, and
             * can transmit, also stashing the "best" relay outside of line of sight for failure report.
             * */
            IAntennaRelay potentialBestRelay;
            CelestialBody fob;

                        #if BENCH
            startVesselLoopTicks = performanceTimer.ElapsedTicks;
                        #endif

            for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++)
            {
                potentialBestRelay = ARFlightController.UsefulRelays[rIdx];
                log.AppendFormat("\n\tgot useful relay {0}",
                                 potentialBestRelay == null ? "null" : potentialBestRelay.ToString());

                if (potentialBestRelay == null)
                {
                    log.Append("\n\t...skipping null relay");
                    continue;
                }

                if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel)
                {
                    log.AppendFormat(
                        "\n\t...skipping relay {0} because it or its vessel ({1}) is the same as ours" +
                        "\n\t\t(our vessel is {2})",
                        potentialBestRelay,
                        potentialBestRelay.vessel == null ? "null" : potentialBestRelay.vessel.vesselName,
                        this.vessel == null ? "null" : this.vessel.vesselName
                        );
                    continue;
                }

                                #if BENCH
                usefulVesselCount++;
                                #endif

                // Find the distance from here to the vessel...
                log.Append("\n\tgetting cost to potential vessel");
                potentialRelayRate = potentialBestRelay.CurrentNetworkLinkCost +
                                     this.GetPotentialLinkCost(potentialBestRelay);

                log.AppendFormat(
                    "\n\tpotentialRelayRate = {0} ({1} + {2})",
                    potentialRelayRate,
                    potentialBestRelay.CurrentNetworkLinkCost,
                    this.GetPotentialLinkCost(potentialBestRelay)
                    );

                                #if BENCH
                startLOSVesselTicks = performanceTimer.ElapsedTicks;
                                #endif

                log.Append("\n\t\tdoing LOS check");
                // Skip vessels to which we do not have line of sight.
                if (
                    ARConfiguration.RequireLineOfSight &&
                    !this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio)
                    )
                {
                                        #if BENCH
                    totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;

                    if (totalLOSVesselTicks > slowestLOSVesselTicks)
                    {
                        slowestLOSVesselTicks = totalLOSVesselTicks;
                        slowestLOSVesselName  = vessel.vesselName;
                    }
                                        #endif

                    log.Append("\n\t\t...failed LOS check");

                    log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.",
                                     this.ToString(), potentialBestRelay);

                    log.AppendFormat("\n\t\t\tpotentialRelayRate: {0}", potentialRelayRate);
                    log.AppendFormat("\n\t\t\tcheapestOccludedRelayRate: {0}", cheapestOccludedRelayRate);

                    if (
                        (potentialRelayRate < cheapestRelayRate) &&
                        this.IsInRangeOf(potentialBestRelay) &&
                        potentialBestRelay.CanTransmit()
                        )
                    {
                        log.Append("\n\t\t...vessel is cheapest and in range and potentialBestRelay can transmit");
                        log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay);

                        this.bestOccludedRelay         = potentialBestRelay;
                        bodyOccludingBestOccludedRelay = fob;
                        cheapestOccludedRelayRate      = potentialRelayRate;
                    }
                    else
                    {
                        log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
                    }

                    continue;
                }
                                #if BENCH
                else
                {
                    totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
                }

                if (totalLOSVesselTicks > slowestLOSVesselTicks)
                {
                    slowestLOSVesselTicks = totalLOSVesselTicks;
                    slowestLOSVesselName  = vessel.vesselName;
                }
                                #endif

                log.Append("\n\t\t...passed LOS check");

                /*
                 * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
                 * */
                if (potentialRelayRate > cheapestRelayRate)
                {
                    log.AppendFormat(
                        "\n\t{0}: Relay {1} discarded because it is more expensive than the cheapest relay." +
                        "\n\t\t({2}, {3} > {4})",
                        this.ToString(),
                        potentialBestRelay,
                        this.nearestRelay == null ? "NULL" : this.nearestRelay.ToString(),
                        potentialRelayRate, cheapestRelayRate
                        );
                    continue;
                }

                log.Append("\n\t\t...passed distance check");

                if (potentialBestRelay.CanTransmit())
                {
                                        #if BENCH
                    startCircularVesselTicks = performanceTimer.ElapsedTicks;
                                        #endif

                    needle     = potentialBestRelay;
                    isCircular = false;

                    iterCount = 0;
                    while (needle != null)
                    {
                        iterCount++;

                        if (needle.KerbinDirect)
                        {
                            break;
                        }

                        if (needle.targetRelay == null)
                        {
                            break;
                        }

                        if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
                        {
                            isCircular = true;
                            break;
                        }

                        // Avoid infinite loops when we're not catching things right.
                        if (iterCount > FlightGlobals.Vessels.Count)
                        {
                            this.LogError(
                                "iterCount exceeded while checking for circular network; assuming it is circular" +
                                "\n\tneedle={0}" +
                                "\n\tthis.moduleRef={1}",
                                needle == null ? "null" : string.Format(
                                    "{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
                                    needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
                                        "{0}\n\tneedle.targetRelay.vessel={1}",
                                        needle.targetRelay,
                                        needle.targetRelay.vessel == null ?
                                        "null" : needle.targetRelay.vessel.vesselName
                                        )
                                    ),
                                this.moduleRef == null ? "null" : this.moduleRef.ToString()
                                );
                            isCircular = true;
                            break;
                        }

                        needle = needle.targetRelay;
                    }

                    if (!isCircular)
                    {
                        cheapestRelayRate = potentialRelayRate;
                        this.nearestRelay = potentialBestRelay;

                        log.AppendFormat("\n\t{0}: found new cheapest relay {1} ({2} EC/MiT)",
                                         this.ToString(),
                                         this.nearestRelay.ToString(),
                                         cheapestRelayRate
                                         );
                    }
                    else
                    {
                        log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping",
                                         potentialBestRelay
                                         );
                    }

                                        #if BENCH
                    totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks;

                    if (totalCircularVesselTicks > slowestCircularVesselTicks)
                    {
                        slowestCircularVesselName  = vessel.vesselName;
                        slowestCircularVesselTicks = totalCircularVesselTicks;
                    }
                                        #endif
                }
            }

                        #if BENCH
            totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks;
                        #endif

            CelestialBody bodyOccludingKerbin = null;

            log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
            log.AppendFormat(
                "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinRelayRate={5} EC/MiT)",
                this,
                this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                cheapestRelayRate,
                this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
                cheapestOccludedRelayRate,
                kerbinRelayRate
                );

                        #if BENCH
            startKerbinLOSTicks = this.performanceTimer.ElapsedTicks;
                        #endif

            // If we don't have LOS to Kerbin, focus on relays
            if (
                ARConfiguration.RequireLineOfSight &&
                !this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
                )
            {
                                #if BENCH
                totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
                                #endif
                log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);

                // If we're in range of the "nearest" (actually cheapest) relay, use it.
                if (this.IsInRangeOf(this.nearestRelay))
                {
                    log.AppendFormat("\n\t\tCan transmit to nearby relay {0}).",
                                     this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                                     );

                    this.KerbinDirect = false;
                    this.canTransmit  = true;
                    this.targetRelay  = this.nearestRelay;
                }
                // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
                else
                {
                    log.AppendFormat("\n\t\tCan't transmit to nearby relay {0}.",
                                     this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                                     );

                    this.canTransmit = false;

                    // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay.
                    // Since cheapestOccludedRelayRate is infinity if there are no occluded relays, this is safe
                    if (cheapestOccludedRelayRate < kerbinRelayRate)
                    {
                        log.AppendFormat("\n\t\t\tBest occluded relay is cheaper than Kerbin ({0} < {1})",
                                         cheapestOccludedRelayRate, kerbinRelayRate);

                        this.KerbinDirect = false;

                        // If the nearest relay is cheaper than the best occluded relay, pick it.
                        // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                        if (cheapestRelayRate < cheapestOccludedRelayRate)
                        {
                            log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.",
                                             cheapestRelayRate, cheapestOccludedRelayRate);

                            this.targetRelay        = this.nearestRelay;
                            this.firstOccludingBody = null;
                        }
                        // Otherwise, target the best occluded relay.
                        else
                        {
                            log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                             cheapestRelayRate, cheapestOccludedRelayRate);

                            this.targetRelay        = bestOccludedRelay;
                            this.firstOccludingBody = bodyOccludingBestOccludedRelay;
                        }
                    }
                    // Otherwise, check Kerbin against the "nearest" (cheapest) relay.
                    else
                    {
                        log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})",
                                         cheapestOccludedRelayRate, kerbinRelayRate);

                        // If the "nearest" (cheapest) relay is cheaper than Kerbin, pick it.
                        // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                        if (cheapestRelayRate < kerbinRelayRate)
                        {
                            log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.",
                                             cheapestRelayRate, kerbinRelayRate);

                            // Since we have LOS, blank the first occluding body.
                            this.firstOccludingBody = null;

                            this.KerbinDirect = false;
                            this.targetRelay  = this.nearestRelay;
                        }
                        // Otherwise, pick Kerbin.
                        else
                        {
                            log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                             cheapestRelayRate, kerbinRelayRate);

                            this.KerbinDirect       = true;
                            this.firstOccludingBody = bodyOccludingKerbin;
                            this.targetRelay        = null;
                        }
                    }
                }
            }
            // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
            else
            {
                                #if BENCH
                totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
                                #endif

                log.AppendFormat("\n\tKerbin is in LOS.");

                // If the nearest relay is in range, we can transmit.
                if (this.IsInRangeOf(this.nearestRelay))
                {
                    log.AppendFormat("\n\t\tCan transmit to nearby relay {0} (in range).",
                                     this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                                     );

                    this.canTransmit = true;

                    // If the nearestRelay is closer than Kerbin, use it.
                    if (cheapestRelayRate < kerbinRelayRate)
                    {
                        log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
                                         this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                                         cheapestRelayRate, kerbinRelayRate);

                        this.KerbinDirect = false;
                        this.targetRelay  = this.nearestRelay;
                    }
                    // Otherwise, Kerbin is closer, so use it.
                    else
                    {
                        log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
                                         this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
                                         cheapestRelayRate, kerbinRelayRate);

                        this.KerbinDirect = true;
                        this.targetRelay  = null;
                    }
                }
                // If the nearest relay is out of range, we still need to check on Kerbin.
                else
                {
                    log.AppendFormat("\n\t\tCheapest relay {0} is out of range.",
                                     this.nearestRelay == null ? "null" : this.nearestRelay.ToString()
                                     );

                    // If Kerbin is in range, use it.
                    if (this.IsInRangeOf(Kerbin))
                    {
                        log.AppendFormat("\n\t\t\tCan transmit to Kerbin (in range).");

                        this.canTransmit  = true;
                        this.KerbinDirect = true;
                        this.targetRelay  = null;
                    }
                    // If Kerbin is out of range and the nearest relay is out of range, pick a second best between
                    // Kerbin and bestOccludedRelay
                    else
                    {
                        log.AppendFormat("\n\t\t\tCan't transmit to Kerbin (out of range).");

                        this.canTransmit = false;

                        // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay.
                        // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
                        if (cheapestOccludedRelayRate < kerbinRelayRate)
                        {
                            log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
                                             cheapestOccludedRelayRate, kerbinRelayRate);

                            this.KerbinDirect = false;

                            // If the nearest relay is closer than the best occluded relay, pick it.
                            // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                            if (cheapestRelayRate < cheapestOccludedRelayRate)
                            {
                                log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.",
                                                 cheapestRelayRate, cheapestOccludedRelayRate);

                                this.targetRelay        = this.nearestRelay;
                                this.firstOccludingBody = null;
                            }
                            // Otherwise, target the best occluded relay.
                            else
                            {
                                log.AppendFormat("\n\t\t\t\t...and cheaper than the cheapest relay ({0} >= {1}), so picking it.",
                                                 cheapestRelayRate, cheapestOccludedRelayRate);

                                this.targetRelay        = bestOccludedRelay;
                                this.firstOccludingBody = bodyOccludingBestOccludedRelay;
                            }
                        }
                        // Otherwise, check Kerbin against the nearest relay.
                        // Since we have LOS, blank the first occluding body.
                        else
                        {
                            log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})",
                                             cheapestOccludedRelayRate, kerbinRelayRate);

                            this.firstOccludingBody = null;

                            // If the nearest relay is cheaper than Kerbin, pick it.
                            // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe.
                            if (cheapestRelayRate < kerbinRelayRate)
                            {
                                log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.",
                                                 cheapestRelayRate, kerbinRelayRate);

                                this.KerbinDirect = false;
                                this.targetRelay  = this.nearestRelay;
                            }
                            // Otherwise, pick Kerbin.
                            else
                            {
                                log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.",
                                                 cheapestRelayRate, kerbinRelayRate);

                                this.KerbinDirect = true;
                                this.targetRelay  = null;
                            }
                        }
                    }
                }
            }

            if (ARConfiguration.UseAdditiveRanges)
            {
                if (this.KerbinDirect)
                {
                    this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange;
                    this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange;
                }
                else
                {
                    this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance;
                    this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance;
                }
            }
            else
            {
                this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
                this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
            }

            if (this.canTransmit)
            {
                if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance)
                {
                    this.LinkStatus = ConnectionStatus.Optimal;
                }
                else
                {
                    this.LinkStatus = ConnectionStatus.Suboptimal;
                }
            }
            else
            {
                this.LinkStatus = ConnectionStatus.None;
            }

                        #if BENCH
            statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks;
                        #endif

            log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());

                        #if DEBUG
        }

        catch (Exception ex) {
            log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
                        #if QUIT_ON_EXCEPTION
            UnityEngine.Application.Quit();
                        #endif
        } finally {
                        #endif
            log.Print(false);
                        #if DEBUG
        }
                        #endif

                        #if BENCH
            AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks;
            AntennaRelay.searchCount++;
            this.performanceTimer.Stop();

            double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount;

            if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay)
            {
                AntennaRelay.searchCount = 0u;
                AntennaRelay.searchTimer = 0u;

                AntennaRelay.averager.AddItem(averageSearchTime);
                AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d);
            }

            if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime)
            {
                System.Text.StringBuilder sb = Tools.GetStringBuilder();

                sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" +
                                " took significantly longer than average ({1:S3}s vs {2:S3}s)",
                                this.ToString(),
                                (double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                                (double)AntennaRelay.averager.Average / (double)System.Diagnostics.Stopwatch.Frequency
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tVessel loop time: {0:S3}s",
                                (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tAverage vessel time for each of {1} vessels: {0:S3}s",
                                (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency /
                                (double)usefulVesselCount,
                                usefulVesselCount
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}",
                                (double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                                slowestLOSVesselName
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}",
                                (double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
                                slowestCircularVesselName
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s",
                                (double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency
                                );

                sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s",
                                (double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency
                                );

                // sb.AppendFormat(Tools.SIFormatter, "", start)

                this.LogWarning(sb.ToString());

                Tools.PutStringBuilder(sb);
            }
                        #endif
        }