public static Vector3d WorldPositionToGeoCoords(Vector3d worldPosition, CelestialBody body) { if(!body) { return Vector3d.zero; } double lat = body.GetLatitude(worldPosition); double longi = body.GetLongitude(worldPosition); double alt = body.GetAltitude(worldPosition); return new Vector3d(lat,longi,alt); }
public static DMPRaycastPair RaycastGround(double latitude, double longitude, CelestialBody body) { //We can only find the ground on bodies that actually *have* ground and if we are in flight near the origin if (!HighLogic.LoadedSceneIsFlight || FlightGlobals.fetch.activeVessel == null || body.pqsController == null) { return new DMPRaycastPair(-1f, Vector3.up); } //Math functions take radians. double latRadians = latitude * Mathf.Deg2Rad; double longRadians = longitude * Mathf.Deg2Rad; //Radial vector Vector3d surfaceRadial = new Vector3d(Math.Cos(latRadians) * Math.Cos(longRadians), Math.Sin(latRadians), Math.Cos(latRadians) * Math.Sin(longRadians)); double surfaceHeight = body.pqsController.GetSurfaceHeight(surfaceRadial) - body.pqsController.radius; Vector3d origin = body.GetWorldSurfacePosition(latitude, longitude, surfaceHeight + 500); //Only return the surface if it's really close. double highestHit = double.NegativeInfinity; Vector3 rotatedVector = Vector3.up; if (Vector3d.Distance(FlightGlobals.fetch.activeVessel.GetWorldPos3D(), origin) < 2500) { //Down vector Vector3d downVector = -body.GetSurfaceNVector(latitude, longitude); //Magic numbers! LayerMask groundMask = 33792; RaycastHit[] raycastHits = Physics.RaycastAll(origin, downVector, 1000f, groundMask); foreach (RaycastHit raycastHit in raycastHits) { if (raycastHit.collider == null) { //I don't think this is technically possible, but unity's weird enough that we should probably check this anyway. continue; } if (raycastHit.collider.name == body.name) { continue; } double hitAltitude = body.GetAltitude(raycastHit.point); if ((hitAltitude > highestHit) && (!body.ocean || hitAltitude > 0)) { highestHit = hitAltitude; rotatedVector = Quaternion.Inverse(body.rotation) * raycastHit.normal; } } } if (highestHit == double.NegativeInfinity) { return new DMPRaycastPair(-1f, Vector3.up); } else { return new DMPRaycastPair(highestHit, rotatedVector); } }
public static string traceTrans(string prev, Transform tr, CelestialBody body = null) { string tmp; if (body != null) { tmp = prev + "." + tr.name + " - (" + body.GetLatitude(tr.position) + ", " + body.GetLongitude(tr.position) + ", " + body.GetAltitude(tr.position) + ")\n"; } else { tmp = prev + "." + tr.name + " - (" + tr.position + ", " + tr.rotation + ")\n"; } Component[] comps = tr.gameObject.GetComponents<Component>(); foreach (Component comp in comps) { tmp += "\t" + comp.GetType().Name + " - " + comp.name + "\n"; } for (int i = 0; i < tr.childCount; i++) { tmp += traceTrans(prev + "." + tr.name, tr.GetChild(i), body); } return tmp; }
/// <summary> /// Converts world position to Lat,Long,Alt form. /// </summary> /// <returns>The position in geo coords.</returns> /// <param name="worldPosition">World position.</param> /// <param name="body">Body.</param> public static Vector3d WorldPositionToGeoCoords(Vector3d worldPosition, CelestialBody body) { if(!body) { //Debug.Log ("BahaTurret.VectorUtils.WorldPositionToGeoCoords body is null"); return Vector3d.zero; } double lat = body.GetLatitude(worldPosition); double longi = body.GetLongitude(worldPosition); double alt = body.GetAltitude(worldPosition); return new Vector3d(lat,longi,alt); }
Vector3d WorldPositionToGeoCoords(Vector3d worldPosition, CelestialBody body) { if(!body) { Debug.LogWarning ("WorldPositionToGeoCoords body is null"); return Vector3d.zero; } double lat = body.GetLatitude(worldPosition); double longi = body.GetLongitude(worldPosition); double alt = body.GetAltitude(worldPosition); return new Vector3d(lat,longi,alt); }
private KSP.UI.Screens.Mapview.MapNode MakePoolNode() { var new_node = KSP.UI.Screens.Mapview.MapNode.Create( "apsis", // If we see this colour, something has gone wrong. XKCDColors.Pale, pixelSize: 32, hoverable: true, pinnable: true, blocksInput: true); new_node.OnClick += (KSP.UI.Screens.Mapview.MapNode node, Mouse.Buttons buttons) => { if (buttons == Mouse.Buttons.Left) { var properties = properties_[node]; if (PlanetariumCamera.fetch.target != properties_[node].associated_map_object) { PlanetariumCamera.fetch.SetTarget( properties_[node].associated_map_object); } } }; new_node.OnUpdateVisible += (KSP.UI.Screens.Mapview.MapNode node, KSP.UI.Screens.Mapview.MapNode.IconData icon) => { icon.visible = properties_[node].visible; icon.color = properties_[node].colour; }; new_node.OnUpdateType += (KSP.UI.Screens.Mapview.MapNode node, KSP.UI.Screens.Mapview.MapNode.TypeData type) => { var properties = properties_[node]; type.oType = properties.object_type; if (properties.object_type == MapObject.ObjectType.PatchTransition) { type.pType = KSP.UI.Screens.Mapview.MapNode.PatchTransitionNodeType.Impact; } else if (properties.object_type == MapObject.ObjectType.ApproachIntersect) { type.aType = KSP.UI.Screens.Mapview.MapNode.ApproachNodeType .CloseApproachOwn; } }; new_node.OnUpdateCaption += (KSP.UI.Screens.Mapview.MapNode node, KSP.UI.Screens.Mapview.MapNode.CaptionData caption) => { var properties = properties_[node]; string source; switch (properties.source) { case NodeSource.FLIGHT_PLAN: source = "Planned"; break; case NodeSource.PREDICTION: source = "Predicted"; break; default: throw Log.Fatal($"Unexpected node source {properties.source}"); } switch (properties.object_type) { case MapObject.ObjectType.Periapsis: case MapObject.ObjectType.Apoapsis: { string apsis_name = properties.object_type == MapObject.ObjectType.Periapsis ? "Periapsis" : "Apoapsis"; CelestialBody celestial = properties.reference_frame.selected_celestial; Vector3d position = properties.world_position; double speed = properties.velocity.magnitude; caption.Header = $@"{source} {celestial.name} {apsis_name} :\n{ celestial.GetAltitude(position):N0} m".ToString( Culture.culture); caption.captionLine2 = $"{speed:N0} m/s".ToString(Culture.culture); break; } case MapObject.ObjectType.AscendingNode: case MapObject.ObjectType.DescendingNode: { string node_name = properties.object_type == MapObject.ObjectType.AscendingNode ? "Ascending Node" : "Descending Node"; string plane = properties.reference_frame.ReferencePlaneDescription(); caption.Header = $"{source} {node_name} :\n{plane}"; caption.captionLine2 = $"{properties.velocity.z:N0} m/s".ToString(Culture.culture); break; } case MapObject.ObjectType.ApproachIntersect: { Vessel target_vessel = properties.reference_frame.target_override; double separation = (target_vessel.GetWorldPos3D() - properties.world_position).magnitude; double speed = properties.velocity.magnitude; caption.Header = $@"{source} Target Approach : {separation:N0} m".ToString( Culture.culture); caption.captionLine2 = $"{speed:N0} m/s".ToString(Culture.culture); break; } case MapObject.ObjectType.PatchTransition: { CelestialBody celestial = properties.reference_frame.selected_celestial; caption.Header = $"{source} {celestial.name} Impact"; caption.captionLine1 = ""; caption.captionLine2 = ""; break; } } if (properties.object_type != MapObject.ObjectType.PatchTransition) { caption.captionLine1 = "T" + FlightPlanner.FormatTimeSpan(TimeSpan.FromSeconds( Planetarium.GetUniversalTime() - properties.time)); } }; new_node.OnUpdatePosition += (KSP.UI.Screens.Mapview.MapNode node) => ScaledSpace.LocalToScaledSpace(properties_[node].world_position); return(new_node); }
//This method is called periodically by the main thread, //then it decides if it is time to start a new thread or not public static void update() { now = Time.time; //double lc = lastCharge; //float lt = lastTime; //lastTime = now; double dt = TimeWarp.fixedDeltaTime; //Log.Info("Starting simulation at " + Math.Round(now, 3)); isReadable = false; //Reset some stuff ship = FlightGlobals.ActiveVessel; body = ship.mainBody; _gravity = FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; _electricCharge = 0; _electricMax = 0; _evamaxfuel = 0; _evafuel = 0; _liquidFuel = 0; _liquidMax = 0; _monoFuel = 0; _monoMax = 0; _airin = 0; _airreq = 0; _mass = 0; _max_thrust = 0; _cur_thrust = 0; _isp = 0; _engineaccel = 0; _enginedecel = 0; _max_temp_percent = 0; _max_temp_actual = 0; double _best_temp = 0; double _best_ablation = 0; _ablation = 1; _tarpos = new Vector3(); // int cur_stage = ship.currentStage; //Log.Info("##########################"); //Log.Info("Current Stage: "+cur_stage); //Part loop - look at all the parts and modules for stuff. foreach (Part P in ship.parts) { /*Log.Info("-------------------------------"); * Log.Info(P.name); * Log.Info("inStageIndex: " + P.inStageIndex); * Log.Info("inv Stage: " + P.inverseStage); * Log.Info("Manual Stage Offset: " + P.manualStageOffset); * Log.Info("Computed Stage: " + (P.inverseStage + P.manualStageOffset)); * Log.Info("Original Stage: " + P.originalStage); * //Log.Info("Stage After: " + P.stageAfter); * //Log.Info("Stage Before: " + P.stageBefore); * Log.Info("Stage Offset: " + P.stageOffset); */ //Temperature //First, look at internal temps _best_temp = P.temperature / P.maxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.temperature; } //Now look at skin temps _best_temp = P.skinTemperature / P.skinMaxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.skinTemperature; } //if (P.temperature/P.maxTemp > .5) Log.Info(P.partInfo.title + " Temp: " + (int)P.temperature + "⁰/" + (int)P.maxTemp + "⁰"); //Vessel Mass if (P.physicalSignificance == Part.PhysicalSignificance.FULL) { _mass += P.mass; _mass += P.GetResourceMass(); } //Resource loop foreach (PartResource pr in P.Resources) { if (pr.resourceName.Equals("ElectricCharge")) { _electricCharge += pr.amount; _electricMax += pr.maxAmount; } else if (pr.resourceName.Equals("LiquidFuel")) { _liquidFuel += pr.amount; _liquidMax += pr.maxAmount; } else if (pr.resourceName.Equals("MonoPropellant")) { _monoFuel += pr.amount; _monoMax += pr.maxAmount; } else if (pr.resourceName.Equals("Ablator")) { _best_ablation = pr.amount / pr.maxAmount; if (_best_ablation < _ablation) { _ablation = _best_ablation; } } } //Module loop foreach (PartModule pm in P.Modules) { if (pm is KerbalEVA) { KerbalEVA eva = pm as KerbalEVA; _evafuel = eva.Fuel; _evamaxfuel = eva.FuelCapacity; } else if (pm is ModuleWheelDeployment) { _gearstate = 0; //Gear up ModuleWheelDeployment mlg = pm as ModuleWheelDeployment; if (mlg.stateString.Equals("Deployed")) { _gearstate = 3; } if (mlg.stateString.StartsWith("Deploying")) { _gearstate = 2; } if (mlg.stateString.StartsWith("Retracting")) { _gearstate = 1; } } else if (pm is ModuleEngines) { ModuleEngines ME = pm as ModuleEngines; if (ME.engineShutdown || !ME.EngineIgnited) //We don't care about engines that aren't ready to fire { continue; } if (1 / ME.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / ME.engineAccelerationSpeed; } if (1 / ME.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / ME.engineDecelerationSpeed; } //Thrust/ISP float thrust = ME.maxThrust * ME.thrustPercentage * 0.01f; _isp += thrust / ME.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += ME.finalThrust; //I think this is the actual thrust being produced by each engine //Intake Air foreach (Propellant Pro in ME.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } //Overheat stuff //if (!ME.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} } else if (pm is ModuleEnginesFX) //The newer engines all use this instead of ModuleEngines { ModuleEnginesFX MFX = pm as ModuleEnginesFX; if (MFX.engineShutdown || !MFX.EngineIgnited) { continue; } //Thrust/ISP float thrust = MFX.maxThrust * MFX.thrustPercentage * 0.01f; _isp += thrust / MFX.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += MFX.finalThrust; //Overheat stuff //if (!MFX.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} if (1 / MFX.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / MFX.engineAccelerationSpeed; } if (1 / MFX.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / MFX.engineDecelerationSpeed; } foreach (Propellant Pro in MFX.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } } else if (pm is ModuleResourceIntake) { ModuleResourceIntake MRI = pm as ModuleResourceIntake; if (MRI.intakeEnabled && MRI.resourceName.Equals("IntakeAir")) { _airin += MRI.airFlow * dt; //Using dt courtesy of FAR } } } } _isp = _max_thrust / _isp; //Complete the average isp if (_engineaccel == 0) { _engineaccel = 2; //Default in case the engines don't define it } //Log.Info("Engine Acceleration: " + Math.Round(_engineaccel, 2)); //Electric charge rate if (now - lastTime > 0.1) { _chargeRate = (_electricCharge - lastCharge) / (now - lastTime); lastCharge = _electricCharge; lastTime = now; } //Clamp to +30/-30 if (_chargeRate > 30) { _chargeRate = 30; } if (_chargeRate < -30) { _chargeRate = -30; } //Maneuver node burn calculations //dv = Isp * ln (m0 / m1) //e^(dv/ISP) = m0/m1 //m1 = m0/e^(dv/ISP) //mass flow = thrust/isp try { ManeuverNode myNode = FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.ToArray()[0]; double deltaVRem = myNode.GetBurnVector(ship.orbit).magnitude; //Remaining ΔV in the burn, per maneuver node //Log.Info("dV: " + Math.Round(deltaVRem, 1)); //Log.Info("Mass: " + Math.Round(_mass, 1) + " ISP: " + Math.Round(_isp, 1)); _final_mass = _mass / Math.Pow(Math.E, (deltaVRem / (_isp * 9.82))); //Mass after burn Changed from 9.821 //Log.Info("Final Mass: " + Math.Round(_final_mass, 1)); _mass_rate = _max_thrust / (_isp * 9.82); //Mass flow rate //Log.Info("Mass Rate: " + Math.Round(_mass_rate, 3)); //Log.Info("Burn Mass: " + Math.Round(_mass - _final_mass, 1)); _burn_time = (_mass - _final_mass);// / _mass_rate; //Time it takes for this burn //Log.Info("Burn Mass: " + _burn_time); _burn_time = _burn_time / _mass_rate; //Log.Info("Burn Time: " + _burn_time); _burn_time += 2d; //_burn_time += _engineaccel + _enginedecel; //Factor in slow throttles } catch { _burn_time = 0; _final_mass = 0; _mass_rate = 0; } //Heading, courtesy of MechJeb /*Vector3d CoM, up, north, east, forward, rootPartPos; * Quaternion rotationSurface, rotationVesselSurface; * CoM = ship.findWorldCenterOfMass(); * up = (CoM - body.position).normalized; * Rigidbody rigidBody = ship.rootPart.rigidbody; * if (rigidBody != null) rootPartPos = rigidBody.position; * north = Vector3d.Exclude(up, (body.position + body.transform.up * (float)body.Radius) - CoM).normalized; * east = body.getRFrmVel(CoM).normalized; * forward = ship.GetTransform().up; * rotationSurface = Quaternion.LookRotation(north, up); * rotationVesselSurface = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(ship.GetTransform().rotation) * rotationSurface); * _heading = rotationVesselSurface.eulerAngles.y; */ _heading = FlightGlobals.ship_heading; //Log.Info("FlightGlobals Heading: " + FlightGlobals.ship_heading + " MechJeb Heading: " + _heading); //good //Log.Info("Ship temp: " + FlightGlobals.ship_temp); //not useful //Log.Info("Get dV: " + FlightGlobals.ActiveVessel.GetDeltaV()); //nothing //Log.Info("Surface Height: " + FlightGlobals.ActiveVessel.GetHeightFromSurface() + " Terrain Height: "+FlightGlobals.ActiveVessel.GetHeightFromTerrain()); //not useful //Log.Info("GetMass(): " + FlightGlobals.ActiveVessel.GetTotalMass()+" myMass: "+_mass); //good //Log.Info("IAS: " + FlightGlobals.ActiveVessel.indicatedAirSpeed + " M: " + FlightGlobals.ActiveVessel.mach); //good //Waypoint distance code //Uses the Spherical Law of Cosines from http://www.movable-type.co.uk/scripts/latlong.html NavWaypoint nW = NavWaypoint.fetch; //The active nav waypoint FinePrint.WaypointManager WM; WM = FinePrint.WaypointManager.Instance(); if (nW != null && nW.IsActive) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = nW.Latitude * Math.PI / 180; double Bo = nW.Longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; //used to be 600000 double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } else if (_nav_waypoint != 0) { //defaults _nav_heading = -1; _nav_dist = -1; //now try actually calculating things int count = 0; foreach (FinePrint.Waypoint W in WM.Waypoints) { //we clearly only care about waypoints on this planet if (W.celestialName.Equals(body.name)) { count++; if (count == _nav_waypoint) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = W.latitude * Math.PI / 180; double Bo = W.longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } } } } else { _nav_dist = -1; //Both of these at -1 implies no nav data _nav_heading = -1f; } //Pitch, yaw, roll /*NavBall ball = FlightUIController.fetch.GetComponentInChildren<NavBall>(); * Quaternion vesselRot = Quaternion.Inverse(ball.relativeGymbal); * pitch = (vesselRot.eulerAngles.x > 180) ? (360 - vesselRot.eulerAngles.x) : -vesselRot.eulerAngles.x; * roll = (vesselRot.eulerAngles.z > 180) ? (360 - vesselRot.eulerAngles.z) : -vesselRot.eulerAngles.z; * yaw = vesselRot.eulerAngles.y; //Heading, more or less * _rel_yaw = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.forward); * _rel_pitch = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.right); * * //Polars * if (pitch > 0f) * { * Quaternion polarRot = Quaternion.Euler(90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * } * else * { * Quaternion polarRot = Quaternion.Euler(-90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * }*/ //Airspeed stuff getAirspeedInfo(ship, out _mach, out _tas, out _eas); //Max Altitude if (ship.Landed) { max_altitude = ship.altitude; //Reset max altitude upon landing/takeoff } if (ship.altitude > max_altitude) { max_altitude = ship.altitude; } //RA double terrain = ship.terrainAltitude; if (terrain < 0) { terrain = 0; } _ra = (long)(body.GetAltitude(ship.CoM) - terrain); //In orbit? inOrbit = false; //Minimum periapsis of 5km if (ship.orbit.PeA > 5000) { //Make sure our orbit is out of the atmosphere, if there is one. if (body.atmosphere) { if (ship.orbit.PeA > body.atmosphereDepth) //is this the same as max altitude? { inOrbit = true; } } else { inOrbit = true; } } //Closest approach and time, target distance and speed if (FlightGlobals.fetch.VesselTarget != null) { Orbit TO = FlightGlobals.fetch.VesselTarget.GetOrbit(); Vector3d aPos = ship.ReferenceTransform.position; //Control source's position Vector3d tPos = _tarpos = FlightGlobals.fetch.VesselTarget.GetTransform().position; //Rough distance if (FlightGlobals.fetch.VesselTarget is ModuleDockingNode) //Use more precise distance { ModuleDockingNode targetDockingPort = FlightGlobals.fetch.VesselTarget as ModuleDockingNode; tPos = _tarpos = targetDockingPort.controlTransform.position; } // MechJeb guidance targets _don't have an orbit_! else if (TO != null) { // This is in the wrong coordinate system for _tarpos, and only usable for distance aPos = ship.GetOrbit().pos; tPos = TO.pos; } _target_dist = Vector3d.Distance(aPos, tPos); } else { _target_dist = 0; } var target = FlightGlobals.fetch.VesselTarget; // MechJeb guidance targets _don't have an orbit! // Also, landed targets often have invalid orbit that causes error spam and lag. if (target != null && target.GetOrbit() != null) { Orbit O = ship.GetOrbit(); Orbit TO = target.GetOrbit(); double t = Planetarium.GetUniversalTime(); Func <double, Vector3d> targetPosAt = (d => TO.getPositionAtUT(t + d)); // For landed targets the orbit doesn't make sense, so calculate planet rotation if (target.GetVessel() != null && target.GetVessel().LandedOrSplashed) { Vessel ves = target.GetVessel(); CelestialBody vbody = ves.mainBody; Vector3d pos = vbody.GetRelSurfacePosition(ves.latitude, ves.longitude, ves.altitude); targetPosAt = delegate(double d) { double angle = vbody.rotates ? d * 360.0 / vbody.rotationPeriod : 0; return(vbody.position + QuaternionD.AngleAxis(angle, Vector3d.down) * pos); }; } //I chunck my orbit into 100 pieces, and find between which two chuncks the minimum occurs... double period = O.period; double bestDist = double.MaxValue; double bestTime = 0; for (double d = 0; d < period; d += period / 100) //Look at 100 places around the orbit { if (d == 0) { continue; //skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //Now, do it again, but over a small section of the orbit centered on the above double start = bestTime - (period / 100); double end = bestTime + (period / 100); for (double d = start; d < end; d += period / 1000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //Skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //And one last time, which is probably overkill start = bestTime - (period / 1000); end = bestTime + (period / 1000); for (double d = start; d < end; d += period / 10000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //For ease of computation } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; //previous time is my start time } } _closest_time = bestTime; _closest_approach = bestDist; } else { _closest_approach = 0; _closest_time = 0; } //Time to Impact calculations double vertspeed = FlightGlobals.ActiveVessel.verticalSpeed; if (!ship.Landed && vertspeed < 0 && ship.orbit.PeA <= 0 && !ship.mainBody.atmosphere) { double Vf = Math.Sqrt((vertspeed * vertspeed) + 2 * _ra * _gravity); //t = 2d/(Vi+Vf) _tti = (Math.Abs(Vf) - Math.Abs(vertspeed)) / _gravity; _tti++; //So you hit the ground at 0, not 1 second after 0 } else { _tti = 0; } //Suicide Altitude calculations if (ship.Landed || ship.orbit.PeA > 0 || body.atmosphere) { _sa = -1; //Not calculating this } else { _sa = 0; double avgG = body.gravParameter / ((body.Radius + ship.terrainAltitude) * (body.Radius + ship.terrainAltitude)); avgG += FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; avgG /= 2; //Log.Info("Average G: " + Math.Round(avgG, 2)); double vdv = Math.Sqrt((2 * avgG * _ra) + (ship.verticalSpeed * ship.verticalSpeed)); //Log.Info("Vertical Delta V: " + Math.Round(vdv, 1)); //Altitude Fraction = (Vertical dv ^2) / (2 * 1000 * Thrust (kN)) double altFrac = (vdv * vdv) / (2 * 1000 * _max_thrust); //Log.Info("Altitude Fraction: " + Math.Round(altFrac, 2)); //m-avg = (m0 + (m0 / e ^ (dv / (Isp * 9.82)))) / 2 double avgMass = (_mass / Math.Pow(Math.E, (vdv / (_isp * 9.82)))); avgMass = (_mass + avgMass) / 2; //Log.Info("Average mass: " + Math.Round(avgMass, 1)); _sa = (long)Math.Round(altFrac * avgMass * 1000); //Log.Info("Suicide Altitude: " + Math.Round(_sa)); } //Cleanup isReadable = true; //Log.Info("Thread simulation complete at " + Math.Round(Time.time, 3)); }
public static string traceTrans(string prev, Transform tr, CelestialBody body = null) { string tmp; if (body != null) { tmp = prev + "." + tr.name + " - (" + body.GetLatitude(tr.position) + ", " + body.GetLongitude(tr.position) + ", " + body.GetAltitude(tr.position) + ")\n"; } else { tmp = prev + "." + tr.name + " - (" + tr.position + ", " + tr.rotation + ")\n"; } Component[] comps = tr.gameObject.GetComponents <Component>(); foreach (Component comp in comps) { tmp += "\t" + comp.GetType().Name + " - " + comp.name + "\n"; } for (int i = 0; i < tr.childCount; i++) { tmp += traceTrans(prev + "." + tr.name, tr.GetChild(i), body); } return(tmp); }