/// <summary>
        /// Calculates the fuel units required for the transfer.
        /// </summary>
        public float CalculateFuelUnits()
        {
            // Fuel required is cached at launch, so just return the cached value
            if (Status != DeliveryStatus.PreLaunch)
            {
                return(_fuelUnits);
            }

            CelestialBody mainBody = Origin.mainBody;

            var sourceProtoVessel = Origin.protoVessel;
            var targetProtoVessel = Destination.protoVessel;

            var sourceSituation = sourceProtoVessel.situation;
            var targetSituation = targetProtoVessel.situation;

            double fuelUnits = double.PositiveInfinity;

            // Calculate fuel requirements based on source/target situation (i.e. landed or orbiting)
            // Both vessels on the ground
            if (sourceSituation == (sourceSituation & SURFACE) && targetSituation == (targetSituation & SURFACE))
            {
                // Determine central angle between the vessels' coordinates
                double centralAngle = GetCentralAngle(
                    sourceProtoVessel.latitude, sourceProtoVessel.longitude,
                    targetProtoVessel.latitude, targetProtoVessel.longitude
                    );

                // Determine the percentage of 360 degrees the central angle represents
                double anglePercent = centralAngle / 360;

                // Determine the velocity of a low, circular orbit around the body
                double orbitV = OrbitalLogisticsExtensions.OrbitalVelocity(mainBody, mainBody.minOrbitalDistance + mainBody.Radius);

                // Determine dV required to reach the target (i.e. a percentage of the dV required to achieve full orbit)
                double dV = orbitV * anglePercent * GRAV_LOSS_MODIFIER;

                fuelUnits = CalculateFuelNeededFromDeltaV(dV, mainBody.atmosphere);
            }
            // One or both vessels in orbit
            else
            {
                // One vessel on the ground, one in orbit
                if ((sourceSituation == (sourceSituation & SURFACE) && targetSituation == Vessel.Situations.ORBITING) ||
                    (sourceSituation == Vessel.Situations.ORBITING && targetSituation == (targetSituation & SURFACE))
                    )
                {
                    // Determine average orbital velocity
                    double velocity = sourceSituation == Vessel.Situations.ORBITING
                        ? Origin.AverageOrbitalVelocity()
                        : Destination.AverageOrbitalVelocity();

                    // Determine dV required to land/launch
                    double dV = velocity * INC_CHANGE_MODIFIER * GRAV_LOSS_MODIFIER;

                    fuelUnits = CalculateFuelNeededFromDeltaV(dV, mainBody.atmosphere);
                }
                // Both vessels in orbit
                else if (sourceSituation == Vessel.Situations.ORBITING && targetSituation == Vessel.Situations.ORBITING)
                {
                    // Determine which vessel has the higher semi major axis and which is lower
                    double highOrbitSMA;
                    double lowOrbitSMA;
                    if (sourceProtoVessel.orbitSnapShot.semiMajorAxis >= targetProtoVessel.orbitSnapShot.semiMajorAxis)
                    {
                        highOrbitSMA = sourceProtoVessel.orbitSnapShot.semiMajorAxis;
                        lowOrbitSMA  = targetProtoVessel.orbitSnapShot.semiMajorAxis;
                    }
                    else
                    {
                        highOrbitSMA = targetProtoVessel.orbitSnapShot.semiMajorAxis;
                        lowOrbitSMA  = sourceProtoVessel.orbitSnapShot.semiMajorAxis;
                    }

                    // Determine orbit velocity of origin and destination vessels
                    double highOrbitV = OrbitalLogisticsExtensions.OrbitalVelocity(mainBody, highOrbitSMA);
                    double lowOrbitV  = OrbitalLogisticsExtensions.OrbitalVelocity(mainBody, lowOrbitSMA);

                    // Determine difference in sma
                    double dSMA = highOrbitSMA - lowOrbitSMA;

                    // If the orbits are similar, we need to spend some extra fuel to get into a transfer orbit
                    double transferV = 0;
                    if (dSMA < 10000)
                    {
                        // Determine the dV needed to get into a higher orbit
                        transferV = highOrbitV - OrbitalLogisticsExtensions.OrbitalVelocity(mainBody, highOrbitSMA + 30000);
                    }

                    // Total dV is the difference between the orbital velocities plus any transfer dV needed
                    // Note: Remember, the lower orbit has the higher velocity!
                    double dV = lowOrbitV - highOrbitV + transferV;

                    // Factor in potential inclination changes to calculate the total cost
                    fuelUnits = CalculateFuelNeededFromDeltaV(dV * INC_CHANGE_MODIFIER);
                }
            }

            return((float)fuelUnits * FUEL_MULTIPLIER);
        }
        /// <summary>
        /// Calculates delivery time and updates <see cref="Duration"/>.
        /// </summary>
        public void CalculateDuration()
        {
            CelestialBody mainBody = Origin.mainBody;

            var sourceProtoVessel = Origin.protoVessel;
            var targetProtoVessel = Destination.protoVessel;

            var sourceSituation = sourceProtoVessel.situation;
            var targetSituation = targetProtoVessel.situation;

            // Set base duration (i.e. docking maneuver, refueling and loading/unloading)
            double baseDuration = DOCKING_TIME * 2 + REFUELING_TIME * 2 + PER_TONNE_LOAD_RATE * TotalMass() * 2;

            // Both vessels on the ground
            if (sourceSituation == (sourceSituation & SURFACE) && targetSituation == (targetSituation & SURFACE))
            {
                // Determine central angle between the vessels' coordinates
                double centralAngle = GetCentralAngle(
                    sourceProtoVessel.latitude, sourceProtoVessel.longitude,
                    targetProtoVessel.latitude, targetProtoVessel.longitude
                    );

                // Determine the percentage of 360 degrees the central angle represents
                double anglePercent = centralAngle / 360;

                // Determine the orbital period of a low, circular orbit around the body
                double velocity = OrbitalLogisticsExtensions.OrbitalVelocity(mainBody, mainBody.minOrbitalDistance + mainBody.Radius);
                double period   = OrbitalLogisticsExtensions.OrbitalPeriod(mainBody, mainBody.minOrbitalDistance + mainBody.Radius);

                // Determine the time spent in the air (i.e. a percentage of the orbital period)
                double inFlight = period * anglePercent;

                Duration = baseDuration + inFlight * 2;
            }
            // One vessel on the ground, one in orbit
            else if ((sourceSituation == (sourceSituation & SURFACE) && targetSituation == Vessel.Situations.ORBITING) ||
                     (sourceSituation == Vessel.Situations.ORBITING && targetSituation == (targetSituation & SURFACE))
                     )
            {
                // Determine the orbital period of the vessel in orbit
                double period = sourceSituation == Vessel.Situations.ORBITING
                    ? OrbitalLogisticsExtensions.OrbitalPeriod(mainBody, sourceProtoVessel.orbitSnapShot.semiMajorAxis)
                    : OrbitalLogisticsExtensions.OrbitalPeriod(mainBody, targetProtoVessel.orbitSnapShot.semiMajorAxis);

                // We'll guess on average it will take 1.5 orbits for the vessel in orbit to be in the right position
                //   for de-orbiting and rendezvous.
                Duration = baseDuration + period * 1.5;
            }
            // Both vessels in orbit
            else if (sourceSituation == Vessel.Situations.ORBITING && targetSituation == Vessel.Situations.ORBITING)
            {
                // Determine which vessel has the higher semi major axis and which is lower
                double highOrbitSMA;
                double lowOrbitSMA;
                if (sourceProtoVessel.orbitSnapShot.semiMajorAxis >= targetProtoVessel.orbitSnapShot.semiMajorAxis)
                {
                    highOrbitSMA = sourceProtoVessel.orbitSnapShot.semiMajorAxis;
                    lowOrbitSMA  = targetProtoVessel.orbitSnapShot.semiMajorAxis;
                }
                else
                {
                    highOrbitSMA = targetProtoVessel.orbitSnapShot.semiMajorAxis;
                    lowOrbitSMA  = sourceProtoVessel.orbitSnapShot.semiMajorAxis;
                }

                // Determine difference in sma
                double dSMA = highOrbitSMA - lowOrbitSMA;

                // Determine orbital periods of both vessels
                double highOrbitPeriod = OrbitalLogisticsExtensions.OrbitalPeriod(mainBody, highOrbitSMA);
                double lowOrbitPeriod  = OrbitalLogisticsExtensions.OrbitalPeriod(mainBody, lowOrbitSMA);

                // If the orbits are similar, we need spend some extra time to get into a transfer orbit
                double transferPeriod = 0;
                if (dSMA < 10000)
                {
                    // Say we'll spend an extra orbit from each vessel to get into a transfer orbit
                    transferPeriod = highOrbitPeriod + lowOrbitPeriod;
                }

                // We'll guess on average it will take 4 total orbits to rendezvous, plus any time spent in transfer orbits
                Duration = baseDuration + highOrbitPeriod * 2 + lowOrbitPeriod * 2 + transferPeriod;
            }
            else
            {
                Duration = double.PositiveInfinity;
            }
        }