public static string ToStringDecimal(double latitude, double longitude, bool newline = false, int precision = 3) { double clampedLongitude = MuUtils.ClampDegrees180(longitude); double latitudeAbs = Math.Abs(latitude); double longitudeAbs = Math.Abs(clampedLongitude); return(latitudeAbs.ToString("F" + precision) + "° " + (latitude > 0 ? "N" : "S") + (newline ? "\n" : ", ") + longitudeAbs.ToString("F" + precision) + "° " + (clampedLongitude > 0 ? "E" : "W")); }
public string NextManeuverNodeDeltaV() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { return("N/A"); } return(MuUtils.ToSI(vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(orbit).magnitude, -1) + "m/s"); }
private double computeYaw() { // probably not perfect, especially when the aircraft is turning double path = vesselState.HeadingFromDirection(vesselState.surfaceVelocity); double nose = vesselState.HeadingFromDirection(vesselState.forward); double angle = MuUtils.ClampDegrees180(nose - path); return(angle); }
public override void OnFixedUpdate() { if (markUT == 0) { Mark(); } timeSinceMark = vesselState.time - markUT; if (vessel.situation == Vessel.Situations.PRELAUNCH) { Mark(); //keep resetting stats until we launch return; } if (vesselState.currentThrustAccel > 0) { gravityLosses += vesselState.deltaT * Vector3d.Dot(-vesselState.orbitalVelocity.normalized, vesselState.gravityForce); } dragLosses += vesselState.deltaT * vesselState.drag; deltaVExpended += vesselState.deltaT * vesselState.currentThrustAccel; steeringLosses += vesselState.deltaT * vesselState.currentThrustAccel * (1 - Vector3d.Dot(vesselState.orbitalVelocity.normalized, vesselState.forward)); maxDragGees = Math.Max(maxDragGees, vesselState.drag / 9.81); double circularPeriod = 2 * Math.PI * vesselState.radius / OrbitalManeuverCalculator.CircularOrbitSpeed(mainBody, vesselState.radius); double angleTraversed = (vesselState.longitude - markLongitude) + 360 * (vesselState.time - markUT) / part.vessel.mainBody.rotationPeriod; phaseAngleFromMark = MuUtils.ClampDegrees360(360 * (vesselState.time - markUT) / circularPeriod - angleTraversed); if (paused) { return; } //int oldHistoryIdx = historyIdx; //historyIdx = Mathf.Min(Mathf.FloorToInt((float)(timeSinceMark / precision)), history.Length - 1); if (vesselState.time >= (lastRecordTime + precision) && historyIdx < history.Length - 1) { lastRecordTime = vesselState.time; historyIdx++; Record(historyIdx); #if DEBUG if (TimeWarp.WarpMode == TimeWarp.Modes.HIGH) { Log.dbg("WRP {0} {1:0} {2:0.00}", historyIdx, history[historyIdx].downRange, history[historyIdx].AoA); } else { Log.dbg("STD {0} {1:0} {2:0.00}", historyIdx, history[historyIdx].downRange, history[historyIdx].AoA); } #endif } }
protected override void WindowGUI(int windowID) { GUILayout.BeginVertical(); GUIStyle s = new GUIStyle(GUI.skin.label); s.alignment = TextAnchor.MiddleCenter; List <Runway> availableRunways = MechJebModuleSpaceplaneAutopilot.runways.Where(p => p.body == mainBody).ToList(); if (runwayIndex > availableRunways.Count) { runwayIndex = 0; } if (availableRunways.Any()) { GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label1"), s);//Landing runwayIndex = GuiUtils.ComboBox.Box(runwayIndex, availableRunways.Select(p => p.name).ToArray(), this); autoland.runway = availableRunways[runwayIndex]; GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label2") + MuUtils.ToSI(Vector3d.Distance(vesselState.CoM, autoland.runway.Start()), 0) + "m"); //Distance to runway: showLandingTarget = GUILayout.Toggle(showLandingTarget, Localizer.Format("#MechJeb_ApproAndLand_label3")); //Show landing navball guidance if (GUILayout.Button(Localizer.Format("#MechJeb_ApproAndLan_button1"))) //Autoland { autoland.Autoland(this); } if (autoland.enabled && GUILayout.Button(Localizer.Format("#MechJeb_ApproAndLan_button2")))//Abort { autoland.AutopilotOff(); } GuiUtils.SimpleTextBox(Localizer.Format("#MechJeb_ApproAndLand_label14"), autoland.glideslope, "°"); //Autoland glideslope: GuiUtils.SimpleTextBox(Localizer.Format("#MechJeb_ApproAndLand_label4"), autoland.approachSpeed, "m/s"); //Approach speed: GuiUtils.SimpleTextBox(Localizer.Format("#MechJeb_ApproAndLand_label5"), autoland.touchdownSpeed, "m/s"); //Touchdown speed: autoland.bEngageReverseIfAvailable = GUILayout.Toggle(autoland.bEngageReverseIfAvailable, Localizer.Format("#MechJeb_ApproAndLand_label6")); //Reverse thrust upon touchdown autoland.bBreakAsSoonAsLanded = GUILayout.Toggle(autoland.bBreakAsSoonAsLanded, Localizer.Format("#MechJeb_ApproAndLand_label7")); //Brake as soon as landed if (autoland.enabled) { GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label8") + autoland.AutolandApproachStateToHumanReadableDescription()); //State: GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label9", Math.Round(autoland.GetAutolandLateralDistanceToNextWaypoint(), 0))); //Distance to waypoint: {0}m GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label10", Math.Round(autoland.Autopilot.SpeedTarget, 1))); //Target speed: {0} m/s GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label11", Math.Round(autoland.GetAutolandTargetAltitude(autoland.GetAutolandTargetVector()), 0))); //Target altitude: {0} m GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label12", Math.Round(autoland.Autopilot.VertSpeedTarget, 1))); //Target vertical speed: {0} m/s GUILayout.Label(Localizer.Format("#MechJeb_ApproAndLand_label13", Math.Round(autoland.Autopilot.HeadingTarget, 0))); //Target heading: {0}º } } GUILayout.EndVertical(); base.WindowGUI(windowID); }
//The mean anomaly of the orbit. //For elliptical orbits, the value return is always between 0 and 2pi //For hyperbolic orbits, the value can be any number. public static double MeanAnomalyAtUT(this Orbit o, double UT) { double ret = o.meanAnomalyAtEpoch + o.MeanMotion() * (UT - o.epoch); if (o.eccentricity < 1) { ret = MuUtils.ClampRadiansTwoPi(ret); } return(ret); }
protected override void WindowGUI(int windowID) { GUILayout.BeginVertical(); GUIStyle s = new GUIStyle(GUI.skin.label); s.alignment = TextAnchor.MiddleCenter; List <Runway> availableRunways = MechJebModuleSpaceplaneAutopilot.runways.Where(p => p.body == mainBody).ToList(); if (runwayIndex > availableRunways.Count) { runwayIndex = 0; } if (availableRunways.Any()) { GUILayout.Label("Landing", s); runwayIndex = GuiUtils.ComboBox.Box(runwayIndex, availableRunways.Select(p => p.name).ToArray(), this); autoland.runway = availableRunways[runwayIndex]; GUILayout.Label("Distance to runway: " + MuUtils.ToSI(Vector3d.Distance(vesselState.CoM, autoland.runway.Start()), 0) + "m"); showLandingTarget = GUILayout.Toggle(showLandingTarget, "Show landing navball guidance"); if (GUILayout.Button("Autoland")) { autoland.Autoland(this); } if (autoland.enabled && GUILayout.Button("Abort")) { autoland.AutopilotOff(); } GuiUtils.SimpleTextBox("Autoland glideslope:", autoland.glideslope); GuiUtils.SimpleTextBox("Approach speed:", autoland.approachSpeed); GuiUtils.SimpleTextBox("Touchdown speed:", autoland.touchdownSpeed); autoland.bEngageReverseIfAvailable = GUILayout.Toggle(autoland.bEngageReverseIfAvailable, "Reverse thrust upon touchdown"); autoland.bBreakAsSoonAsLanded = GUILayout.Toggle(autoland.bBreakAsSoonAsLanded, "Break As Soon As Landed"); if (autoland.enabled) { GUILayout.Label("State: " + autoland.AutolandApproachStateToHumanReadableDescription()); GUILayout.Label(string.Format("Distance to waypoint: {0} m", Math.Round(autoland.GetAutolandLateralDistanceToNextWaypoint(), 0))); GUILayout.Label(string.Format("Target speed: {0} m/s", Math.Round(autoland.Autopilot.SpeedTarget, 1))); GUILayout.Label(string.Format("Target altitude: {0} m", Math.Round(autoland.GetAutolandTargetAltitude(autoland.GetAutolandTargetVector()), 0))); GUILayout.Label(string.Format("Target vertical speed: {0} m/s", Math.Round(autoland.Autopilot.VertSpeedTarget, 1))); GUILayout.Label(string.Format("Target heading: {0}º", Math.Round(autoland.Autopilot.HeadingTarget, 0))); } } GUILayout.EndVertical(); base.WindowGUI(windowID); }
// provides AoA limiting and ground track steering to pitch controllers (possibly should be moved into the attitude controller, but // right now it collaborates too heavily with the ascent autopilot) // protected void attitudeTo(double desiredPitch) { double desiredHeading = OrbitalManeuverCalculator.HeadingForLaunchInclination(vessel, vesselState, autopilot.desiredInclination); Vector3d desiredHeadingVector = Math.Sin(desiredHeading * UtilMath.Deg2Rad) * vesselState.east + Math.Cos(desiredHeading * UtilMath.Deg2Rad) * vesselState.north; Vector3d desiredThrustVector = Math.Cos(desiredPitch * UtilMath.Deg2Rad) * desiredHeadingVector + Math.Sin(desiredPitch * UtilMath.Deg2Rad) * vesselState.up; thrustVectorForNavball = desiredThrustVector; desiredThrustVector = desiredThrustVector.normalized; if (autopilot.limitAoA) { float fade = vesselState.dynamicPressure < autopilot.aoALimitFadeoutPressure ? (float)(autopilot.aoALimitFadeoutPressure / vesselState.dynamicPressure) : 1; autopilot.currentMaxAoA = Math.Min(fade * autopilot.maxAoA, 180d); autopilot.limitingAoA = vessel.altitude <mainBody.atmosphereDepth && Vector3.Angle(vesselState.surfaceVelocity, desiredThrustVector)> autopilot.currentMaxAoA; if (autopilot.limitingAoA) { desiredThrustVector = Vector3.RotateTowards(vesselState.surfaceVelocity, desiredThrustVector, (float)(autopilot.currentMaxAoA * Mathf.Deg2Rad), 1).normalized; } } double pitch = 90 - Vector3d.Angle(desiredThrustVector, vesselState.up); double hdg; if (pitch > 89.9) { hdg = desiredHeading; } else { hdg = MuUtils.ClampDegrees360(UtilMath.Rad2Deg * Math.Atan2(Vector3d.Dot(desiredThrustVector, vesselState.east), Vector3d.Dot(desiredThrustVector, vesselState.north))); } if (autopilot.forceRoll) { core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (desiredPitch == 90.0) { core.attitude.attitudeTo(hdg, pitch, autopilot.turnRoll, this); } else { core.attitude.attitudeTo(hdg, pitch, autopilot.verticalRoll, this); } } else { core.attitude.attitudeTo(desiredThrustVector, AttitudeReference.INERTIAL, this); } }
protected override void WindowGUI(int windowID) { GUILayout.BeginVertical(); GUIStyle s = new GUIStyle(GUI.skin.label); s.alignment = TextAnchor.MiddleCenter; GUILayout.Label("Landing", s); Runway[] runways = MechJebModuleSpaceplaneAutopilot.runways; int runwayIndex = Array.IndexOf(runways, autopilot.runway); runwayIndex = GuiUtils.ArrowSelector(runwayIndex, runways.Length, autopilot.runway.name); autopilot.runway = runways[runwayIndex]; GUILayout.Label("Distance to runway: " + MuUtils.ToSI(Vector3d.Distance(vesselState.CoM, autopilot.runway.Start(vesselState.CoM)), 0) + "m"); showLandingTarget = GUILayout.Toggle(showLandingTarget, "Show landing navball guidance"); if (GUILayout.Button("Autoland")) { autopilot.Autoland(this); } if (autopilot.enabled && autopilot.mode == MechJebModuleSpaceplaneAutopilot.Mode.AUTOLAND && GUILayout.Button("Abort")) { autopilot.AutopilotOff(); } GuiUtils.SimpleTextBox("Autoland glideslope:", autopilot.glideslope, "º"); GUILayout.Label("Hold", s); GUILayout.BeginHorizontal(); if (GUILayout.Button("Initiate hold:")) { autopilot.HoldHeadingAndAltitude(this); } GUILayout.Label("Heading:"); autopilot.targetHeading.text = GUILayout.TextField(autopilot.targetHeading.text, GUILayout.Width(40)); GUILayout.Label("º Altitude:"); autopilot.targetAltitude.text = GUILayout.TextField(autopilot.targetAltitude.text, GUILayout.Width(40)); GUILayout.Label("m"); GUILayout.EndHorizontal(); if (autopilot.enabled && autopilot.mode == MechJebModuleSpaceplaneAutopilot.Mode.HOLD && GUILayout.Button("Abort")) { autopilot.AutopilotOff(); } GUILayout.EndVertical(); base.WindowGUI(windowID); }
public string OrbitSummary(Orbit o) { if (o.eccentricity > 1) { return("hyperbolic, Pe = " + MuUtils.ToSI(o.PeA, 2) + "m"); } else { return(MuUtils.ToSI(o.PeA, 2) + "m x " + MuUtils.ToSI(o.ApA, 2) + "m"); } }
//The next time at which the orbiting object will reach the given mean anomaly. //For elliptical orbits, this will be a time between UT and UT + o.period //For hyperbolic orbits, this can be any time, including a time in the past, if //the given mean anomaly occurred in the past public static double UTAtMeanAnomaly(this Orbit o, double meanAnomaly, double UT) { double currentMeanAnomaly = o.MeanAnomalyAtUT(UT); double meanDifference = meanAnomaly - currentMeanAnomaly; if (o.eccentricity < 1) { meanDifference = MuUtils.ClampRadiansTwoPi(meanDifference); } return(UT + meanDifference / o.MeanMotion()); }
public string TargetClosestApproachDistance() { if (!core.target.NormalTargetExists) { return("N/A"); } if (core.target.Orbit.referenceBody != orbit.referenceBody) { return("N/A"); } return(MuUtils.ToSI(orbit.NextClosestApproachDistance(core.target.Orbit, vesselState.time), -1) + "m"); }
//return the mass of the simulated FuelNode. This is not the same as the mass of the Part, //because the simulated node may have lost resources, and thus mass, during the simulation. public float Mass(int simStage) { //print("\n(" + simStage + ") " + partName.PadRight(25) + " dryMass " + dryMass.ToString("F3") // + " ResMass " + (resources.Keys.Sum(id => resources[id] * MuUtils.ResourceDensity(id))).ToString("F3") // + " Fairing Mass " + (inverseStage < simStage ? fairingMass : 0).ToString("F3") // + " (" + fairingMass.ToString("F3") + ")" // + " ModuleMass " + moduleMass.ToString("F3") // ); return(dryMass + resources.Keys.Sum(id => resources[id] * MuUtils.ResourceDensity(id)) + (inverseStage < simStage ? fairingMass : 0)); }
public EditableAngle(double angle) { angle = MuUtils.ClampDegrees180(angle); negative = (angle < 0); angle = Math.Abs(angle); degrees = (int)angle; angle -= degrees; minutes = (int)(60 * angle); angle -= minutes / 60; seconds = Math.Round(3600 * angle); }
//The mean anomaly of the orbit. //For elliptical orbits, the value return is always between 0 and 2pi //For hyperbolic orbits, the value can be any number. public static double MeanAnomalyAtUT(this Orbit o, double UT) { // We use ObtAtEpoch and not meanAnomalyAtEpoch because somehow meanAnomalyAtEpoch // can be wrong when using the RealSolarSystem mod. ObtAtEpoch is always correct. double ret = (o.ObTAtEpoch + (UT - o.epoch)) * o.MeanMotion(); if (o.eccentricity < 1) { ret = MuUtils.ClampRadiansTwoPi(ret); } return(ret); }
public override ManeuverParameters MakeNodeImpl(Orbit o, double universalTime, MechJebModuleTargetController target) { double UT = timeSelector.ComputeManeuverTime(o, universalTime, target); if (o.referenceBody.Radius + newApA < o.Radius(UT)) { string burnAltitude = MuUtils.ToSI(o.Radius(UT) - o.referenceBody.Radius) + "m"; throw new OperationException(Localizer.Format("#MechJeb_Ap_Exception", burnAltitude));//new apoapsis cannot be lower than the altitude of the burn (<<1>>) } return(new ManeuverParameters(OrbitalManeuverCalculator.DeltaVToChangeApoapsis(o, UT, newApA + o.referenceBody.Radius), UT)); }
//Originally by Zool, revised by The_Duck //Converts an eccentric anomaly into a mean anomaly. //For an elliptical orbit, the returned value is between 0 and 2pi //For a hyperbolic orbit, the returned value is any number public static double GetMeanAnomalyAtEccentricAnomaly(this Orbit o, double E) { double e = o.eccentricity; if (e < 1) //elliptical orbits { return(MuUtils.ClampRadiansTwoPi(E - (e * Math.Sin(E)))); } else //hyperbolic orbits { return((e * Math.Sinh(E)) - E); } }
/// <summary> /// Find the time to a target plane defined by the LAN and inc for a rocket on the ground. /// </summary> /// /// <param name="rotationPeriod">Rotation period of the central body (seconds).</param> /// <param name="latitude">Latitude of the launchite (degrees).</param> /// <param name="celestialLongitude">Celestial longitude of the current position of the launchsite.</param> /// <param name="LAN">Longitude of the Ascending Node of the target plane (degrees).</param> /// <param name="inc">Inclination of the target plane (degrees).</param> /// public static double TimeToPlane(double rotationPeriod, double latitude, double celestialLongitude, double LAN, double inc) { // alpha is the 90 degree angle between the line of longitude and the equator and omitted double beta = OrbitalManeuverCalculator.HeadingForInclination(inc, latitude) * UtilMath.Deg2Rad; double c = Math.Abs(latitude) * UtilMath.Deg2Rad; // Abs for south hemisphere launch sites // b is how many radians to the west of the launch site that the LAN is (east in south hemisphere) double b = Math.Atan2(2 * Math.Sin(beta), Math.Cos(beta) / Math.Tan(c / 2) + Math.Tan(c / 2) * Math.Cos(beta)); // napier's analogies // LAN if we launched now double LANnow = celestialLongitude - Math.Sign(latitude) * b * UtilMath.Rad2Deg; return(MuUtils.ClampDegrees360(LAN - LANnow) / 360 * rotationPeriod); }
// Compute an angular heading from point a to point b on a unit sphere public static double Heading(double lat_a, double long_a, double lat_b, double long_b) { // Using Great-Circle Navigation formula for initial heading from http://en.wikipedia.org/wiki/Great-circle_navigation // Note the switch from degrees to radians and back // Original equation returns 0 for due south, increasing clockwise. We add 180 and clamp to 0-360 degrees to map to compass-type headings double lat_a_rad = Math.PI / 180 * lat_a; double lat_b_rad = Math.PI / 180 * lat_b; double long_diff_rad = Math.PI / 180 * (long_b - long_a); return(MuUtils.ClampDegrees360(180.0 / Math.PI * Math.Atan2( Math.Sin(long_diff_rad), Math.Cos(lat_a_rad) * Math.Tan(lat_b_rad) - Math.Sin(lat_a_rad) * Math.Cos(long_diff_rad)))); }
//Computes the delta-V of the burn required to attain a given periapsis, starting from //a given orbit and burning at a given UT. Throws an ArgumentException if given an impossible periapsis. //The computed burn is always horizontal, though this may not be strictly optimal. public static Vector3d DeltaVToChangePeriapsis(Orbit o, double UT, double newPeR) { double radius = o.Radius(UT); //sanitize input newPeR = MuUtils.Clamp(newPeR, 0 + 1, radius - 1); //are we raising or lowering the periapsis? bool raising = (newPeR > o.PeR); Vector3d burnDirection = (raising ? 1 : -1) * o.Horizontal(UT); double minDeltaV = 0; double maxDeltaV; if (raising) { //put an upper bound on the required deltaV: maxDeltaV = 0.25; while (o.PerturbedOrbit(UT, maxDeltaV * burnDirection).PeR < newPeR) { maxDeltaV *= 2; if (maxDeltaV > 100000) { break; //a safety precaution } } } else { //when lowering periapsis, we burn horizontally, and max possible deltaV is the deltaV required to kill all horizontal velocity maxDeltaV = Math.Abs(Vector3d.Dot(o.SwappedOrbitalVelocityAtUT(UT), burnDirection)); } //now do a binary search to find the needed delta-v while (maxDeltaV - minDeltaV > 0.01) { double testDeltaV = (maxDeltaV + minDeltaV) / 2.0; double testPeriapsis = o.PerturbedOrbit(UT, testDeltaV * burnDirection).PeR; if ((testPeriapsis > newPeR && raising) || (testPeriapsis < newPeR && !raising)) { maxDeltaV = testDeltaV; } else { minDeltaV = testDeltaV; } } return(((maxDeltaV + minDeltaV) / 2) * burnDirection); }
//Computes the deltaV of the burn needed to set a given LAN at a given UT. public static Vector3d DeltaVToShiftLAN(Orbit o, double UT, double newLAN) { Vector3d pos = o.SwappedAbsolutePositionAtUT(UT); // Burn position in the same reference frame as LAN double burn_latitude = o.referenceBody.GetLatitude(pos); double burn_longitude = o.referenceBody.GetLongitude(pos) + o.referenceBody.rotationAngle; const double target_latitude = 0; // Equator double target_longitude = 0; // Prime Meridian // Select the location of either the descending or ascending node. // If the descending node is closer than the ascending node, or there is no ascending node, target the reverse of the newLAN // Otherwise target the newLAN if (o.AscendingNodeEquatorialExists() && o.DescendingNodeEquatorialExists()) { if (o.TimeOfDescendingNodeEquatorial(UT) < o.TimeOfAscendingNodeEquatorial(UT)) { // DN is closer than AN // Burning for the AN would entail flipping the orbit around, and would be very expensive // therefore, burn for the corresponding Longitude of the Descending Node target_longitude = MuUtils.ClampDegrees360(newLAN + 180.0); } else { // DN is closer than AN target_longitude = MuUtils.ClampDegrees360(newLAN); } } else if (o.AscendingNodeEquatorialExists() && !o.DescendingNodeEquatorialExists()) { // No DN target_longitude = MuUtils.ClampDegrees360(newLAN); } else if (!o.AscendingNodeEquatorialExists() && o.DescendingNodeEquatorialExists()) { // No AN target_longitude = MuUtils.ClampDegrees360(newLAN + 180.0); } else { throw new ArgumentException("OrbitalManeuverCalculator.DeltaVToShiftLAN: No Equatorial Nodes"); } double desiredHeading = MuUtils.ClampDegrees360(Heading(burn_latitude, burn_longitude, target_latitude, target_longitude)); Vector3d actualHorizontalVelocity = Vector3d.Exclude(o.Up(UT), o.SwappedOrbitalVelocityAtUT(UT)); Vector3d eastComponent = actualHorizontalVelocity.magnitude * Math.Sin(Math.PI / 180 * desiredHeading) * o.East(UT); Vector3d northComponent = actualHorizontalVelocity.magnitude * Math.Cos(Math.PI / 180 * desiredHeading) * o.North(UT); Vector3d desiredHorizontalVelocity = eastComponent + northComponent; return(desiredHorizontalVelocity - actualHorizontalVelocity); }
void DriveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (autopilot.autoThrottle && orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) { mode = AscentMode.VERTICAL_ASCENT; return; } if (autopilot.autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, autopilot.desiredOrbitAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { // follow surface velocity to reduce flipping attitudeTo(srfvelPitch()); status = "Fine tuning apoapsis"; return; } } double desiredFlightPathAngle = FlightPathAngle(vesselState.altitudeASL, vesselState.speedSurface); if (autopilot.correctiveSteering) { double actualFlightPathAngle = Math.Atan2(vesselState.speedVertical, vesselState.speedSurfaceHorizontal) * UtilMath.Rad2Deg; /* form an isosceles triangle with unit vectors pointing in the desired and actual flight path angle directions and find the length of the base */ double velocityError = 2 * Math.Sin(UtilMath.Deg2Rad * (desiredFlightPathAngle - actualFlightPathAngle) / 2); double difficulty = vesselState.surfaceVelocity.magnitude * 0.02 / vesselState.ThrustAccel(core.thrust.targetThrottle); difficulty = MuUtils.Clamp(difficulty, 0.1, 1.0); double steerOffset = autopilot.correctiveSteeringGain * difficulty * velocityError; double steerAngle = MuUtils.Clamp(Math.Asin(steerOffset) * UtilMath.Rad2Deg, -30, 30); desiredFlightPathAngle = MuUtils.Clamp(desiredFlightPathAngle + steerAngle, -90, 90); } attitudeTo(desiredFlightPathAngle); status = "Gravity turn"; }
//Computes the time and dV of a Hohmann transfer injection burn such that at apoapsis the transfer //orbit passes as close as possible to the target. //The output burnUT will be the first transfer window found after the given UT. //Assumes o and target are in approximately the same plane, and orbiting in the same direction. //Also assumes that o is a perfectly circular orbit (though result should be OK for small eccentricity). public static Vector3d DeltaVAndTimeForHohmannTransfer(Orbit o, Orbit target, double UT, out double burnUT) { double synodicPeriod = o.SynodicPeriod(target); Vector3d burnDV = Vector3d.zero; burnUT = UT; double bestApproachDistance = Double.MaxValue; double minTime = UT; double maxTime = UT + 1.1 * synodicPeriod; int numDivisions = 20; for (int iter = 0; iter < 8; iter++) { double dt = (maxTime - minTime) / numDivisions; for (int i = 0; i < numDivisions; i++) { double t = minTime + i * dt; Vector3d apsisDirection = -o.SwappedRelativePositionAtUT(t); double desiredApsis = target.RadiusAtTrueAnomaly(target.TrueAnomalyFromVector(apsisDirection)); double approachDistance; Vector3d burn; if (desiredApsis > o.ApR) { burn = DeltaVToChangeApoapsis(o, t, desiredApsis); Orbit transferOrbit = o.PerturbedOrbit(t, burn); approachDistance = transferOrbit.Separation(target, transferOrbit.NextApoapsisTime(t)); } else { burn = DeltaVToChangePeriapsis(o, t, desiredApsis); Orbit transferOrbit = o.PerturbedOrbit(t, burn); approachDistance = transferOrbit.Separation(target, transferOrbit.NextPeriapsisTime(t)); } if (approachDistance < bestApproachDistance) { bestApproachDistance = approachDistance; burnUT = t; burnDV = burn; } } minTime = MuUtils.Clamp(burnUT - dt, UT, UT + synodicPeriod); maxTime = MuUtils.Clamp(burnUT + dt, UT, UT + synodicPeriod); } return(burnDV); }
public string TargetClosestApproachRelativeVelocity() { if (!core.target.NormalTargetExists) { return("N/A"); } if (core.target.Orbit.referenceBody != orbit.referenceBody) { return("N/A"); } double UT = orbit.NextClosestApproachTime(core.target.Orbit, vesselState.time); double relVel = (orbit.SwappedOrbitalVelocityAtUT(UT) - core.target.Orbit.SwappedOrbitalVelocityAtUT(UT)).magnitude; return(MuUtils.ToSI(relVel, -1) + "m/s"); }
//return the mass of the simulated FuelNode. This is not the same as the mass of the Part, //because the simulated node may have lost resources, and thus mass, during the simulation. public double Mass(int simStage) { //print("\n(" + simStage + ") " + partName.PadRight(25) + " dryMass " + dryMass.ToString("F3") // + " ResMass " + (resources.Keys.Sum(id => resources[id] * MuUtils.ResourceDensity(id))).ToString("F3") // + " Fairing Mass " + (inverseStage < simStage ? fairingMass : 0).ToString("F3") // + " (" + fairingMass.ToString("F3") + ")" // + " ModuleMass " + moduleMass.ToString("F3") // ); //return dryMass + resources.Keys.Sum(id => resources[id] * MuUtils.ResourceDensity(id)) + double resMass = resources.KeysList.Slinq().Select((r, rs) => rs[r] * MuUtils.ResourceDensity(r), resources).Sum(); return(dryMass + resMass + (inverseStage < simStage ? modulesUnstagedMass : modulesStagedMass)); }
public override void OnFixedUpdate() { if (markUT == 0) { Mark(); } timeSinceMark = vesselState.time - markUT; if (vessel.situation == Vessel.Situations.PRELAUNCH) { Mark(); //keep resetting stats until we launch return; } gravityLosses += vesselState.deltaT * Vector3d.Dot(-vesselState.surfaceVelocity.normalized, vesselState.gravityForce); gravityLosses -= vesselState.deltaT * Vector3d.Dot(vesselState.surfaceVelocity.normalized, vesselState.up * vesselState.radius * Math.Pow(2 * Math.PI / part.vessel.mainBody.rotationPeriod, 2)); dragLosses += vesselState.deltaT * vesselState.drag; maxDragGees = Math.Max(maxDragGees, vesselState.drag / 9.81); double circularPeriod = 2 * Math.PI * vesselState.radius / OrbitalManeuverCalculator.CircularOrbitSpeed(mainBody, vesselState.radius); double angleTraversed = (vesselState.longitude - markLongitude) + 360 * (vesselState.time - markUT) / part.vessel.mainBody.rotationPeriod; phaseAngleFromMark = MuUtils.ClampDegrees360(360 * (vesselState.time - markUT) / circularPeriod - angleTraversed); if (paused) { return; } //int oldHistoryIdx = historyIdx; //historyIdx = Mathf.Min(Mathf.FloorToInt((float)(timeSinceMark / precision)), history.Length - 1); if (vesselState.time >= (lastRecordTime + precision) && historyIdx < history.Length - 1) { lastRecordTime = vesselState.time; historyIdx++; Record(historyIdx); //if (TimeWarp.WarpMode == TimeWarp.Modes.HIGH) // print("WRP " + historyIdx + " " + history[historyIdx].downRange.ToString("F0") + " " + history[historyIdx].AoA.ToString("F2")); //else //{ // print("STD " + historyIdx + " " + history[historyIdx].downRange.ToString("F0") + " " + history[historyIdx].AoA.ToString("F2")); //} } }
public override ManeuverParameters MakeNodeImpl(Orbit o, double universalTime, MechJebModuleTargetController target) { double UT = timeSelector.ComputeManeuverTime(o, universalTime, target); if (o.referenceBody.Radius + newPeA > o.Radius(UT)) { string burnAltitude = MuUtils.ToSI(o.Radius(UT) - o.referenceBody.Radius) + "m"; throw new Exception("new periapsis cannot be higher than the altitude of the burn (" + burnAltitude + ")"); } else if (newPeA < -o.referenceBody.Radius) { throw new Exception("new periapsis cannot be lower than minus the radius of " + o.referenceBody.theName + "(-" + MuUtils.ToSI(o.referenceBody.Radius, 3) + "m)"); } return(new ManeuverParameters(OrbitalManeuverCalculator.DeltaVToChangePeriapsis(o, UT, newPeA + o.referenceBody.Radius), UT)); }
public static Coordinates GetMouseCoordinates(CelestialBody body) { Ray mouseRay = PlanetariumCamera.Camera.ScreenPointToRay(Input.mousePosition); mouseRay.origin = ScaledSpace.ScaledToLocalSpace(mouseRay.origin); Vector3d relOrigin = mouseRay.origin - body.position; Vector3d relSurfacePosition; double curRadius = body.pqsController.radiusMax; double lastRadius = 0; double error = 0; int loops = 0; float st = Time.time; while (loops < 50) { if (PQS.LineSphereIntersection(relOrigin, mouseRay.direction, curRadius, out relSurfacePosition)) { Vector3d surfacePoint = body.position + relSurfacePosition; double alt = body.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(body.GetLongitude(surfacePoint), Vector3d.down) * QuaternionD.AngleAxis(body.GetLatitude(surfacePoint), Vector3d.forward) * Vector3d.right); error = Math.Abs(curRadius - alt); if (error < (body.pqsController.radiusMax - body.pqsController.radiusMin) / 100) { return(new Coordinates(body.GetLatitude(surfacePoint), MuUtils.ClampDegrees180(body.GetLongitude(surfacePoint)))); } else { lastRadius = curRadius; curRadius = alt; loops++; } } else { if (loops == 0) { break; } else { // Went too low, needs to try higher curRadius = (lastRadius * 9 + curRadius) / 10; loops++; } } } return(null); }
public void WarpToUT(double UT, double maxRate = 100000) { double desiredRate = 1.0 * (UT - (vesselState.time + Time.fixedDeltaTime * (float)TimeWarp.CurrentRateIndex)); desiredRate = MuUtils.Clamp(desiredRate, 1, maxRate); if (!vessel.LandedOrSplashed && vesselState.altitudeASL < TimeWarp.fetch.GetAltitudeLimit(1, mainBody)) { //too low to use any regular warp rates. Use physics warp at a max of x2: WarpPhysicsAtRate((float)Math.Min(desiredRate, 2)); } else { WarpRegularAtRate((float)desiredRate); } }
//Vector3d must be either a position RELATIVE to referenceBody, or a velocity public AbsoluteVector ToAbsolute(Vector3d vector3d, double UT) { AbsoluteVector absolute = new AbsoluteVector(); absolute.latitude = 180 / Math.PI * Math.Asin(Vector3d.Dot(vector3d.normalized, lat90AtStart)); double longitude = 180 / Math.PI * Math.Atan2(Vector3d.Dot(vector3d.normalized, lat0lon90AtStart), Vector3d.Dot(vector3d.normalized, lat0lon0AtStart)); longitude -= 360 * (UT - epoch) / referenceBody.rotationPeriod; absolute.longitude = MuUtils.ClampDegrees180(longitude); absolute.radius = vector3d.magnitude; absolute.UT = UT; return(absolute); }