public void SimulateAeroProperties(out Vector3 aeroForce, out Vector3 aeroTorque, Vector3 velocityWorldVector, double altitude) { FARCenterQuery center = new FARCenterQuery(); float pressure; float density; float temperature; float speedOfSound; CelestialBody body = _vessel.mainBody; //Calculate main gas properties pressure = (float)body.GetPressure(altitude); temperature = (float)body.GetTemperature(altitude); density = (float)body.GetDensity(pressure, temperature); speedOfSound = (float)body.GetSpeedOfSound(pressure, density); float velocityMag = velocityWorldVector.magnitude; float machNumber = velocityMag / speedOfSound; float reynoldsNumber = (float)FARAeroUtil.CalculateReynoldsNumber(density, Length, velocityMag, machNumber, temperature, body.atmosphereAdiabaticIndex); float reynoldsPerLength = reynoldsNumber / (float)Length; float skinFriction = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); for(int i = 0; i < _currentAeroSections.Count; i++) _currentAeroSections[i].PredictionCalculateAeroForces(density, machNumber, reynoldsPerLength, skinFriction, velocityWorldVector, center); for (int i = 0; i < _legacyWingModels.Count; i++) _legacyWingModels[i].PrecomputeCenterOfLift(velocityWorldVector, machNumber, density, center); aeroForce = center.force; aeroTorque = center.TorqueAt(_vessel.CoM); }
void UpdateAerodynamicCenter() { FARCenterQuery aeroSection, dummy; aeroSection = new FARCenterQuery(); dummy = new FARCenterQuery(); if((object)EditorLogic.RootPart == null) return; Vector3 vel_base, vel_fuzz; Transform rootPartTrans = EditorLogic.RootPart.partTransform; if (EditorDriver.editorFacility == EditorFacility.SPH) { vel_base = Vector3.forward; vel_fuzz = 0.02f * Vector3.up; } else { vel_base = Vector3.up; vel_fuzz = -0.02f * Vector3.forward; } Vector3 vel = (vel_base - vel_fuzz).normalized; for(int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection section = _currentAeroSections[i]; section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0.005f, vel, aeroSection); } FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel, 1); Vector3 pos = Vector3.zero;//rootPartTrans.position; float mass = 0; for(int i = 0; i < EditorLogic.SortedShipList.Count; i++) { Part p = EditorLogic.SortedShipList[i]; float tmpMass = p.mass + p.GetResourceMass(); mass += tmpMass; pos += p.partTransform.position * tmpMass; } pos /= mass; Vector3 avgForcePos = Vector3.zero; Vector3 force0, moment0; force0 = aeroSection.force; moment0 = aeroSection.TorqueAt(pos); avgForcePos += aeroSection.GetPos(); //aeroSection.force = -aeroSection.force; //aeroSection.torque = -aeroSection.torque; aeroSection.ClearAll(); vel = (vel_base + vel_fuzz).normalized; for (int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection section = _currentAeroSections[i]; section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0.005f, vel, aeroSection); } FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel, 1); Vector3 force1, moment1; force1 = aeroSection.force; moment1 = aeroSection.TorqueAt(pos); avgForcePos += aeroSection.GetPos(); aeroSection.ClearAll(); avgForcePos *= 0.5f; Vector3 deltaForce = force1 - force0; Vector3 deltaMoment = moment1 - moment0; Vector3 deltaForcePerp = Vector3.ProjectOnPlane(deltaForce, vel_base); float deltaForcePerpMag = deltaForcePerp.magnitude; Vector3 deltaForcePerpNorm = deltaForcePerp / deltaForcePerpMag; Vector3 deltaMomentPerp = deltaMoment - Vector3.Dot(deltaMoment, deltaForcePerpNorm) * deltaForcePerpNorm - Vector3.Project(deltaMoment, vel_base); //float dist = deltaMomentPerp.magnitude / deltaForcePerpMag; //vesselRootLocalAeroCenter = vel_base * dist; vesselRootLocalAeroCenter = deltaMomentPerp.magnitude / deltaForcePerpMag * vel_base * Math.Sign(Vector3.Dot(Vector3.Cross(deltaForce, deltaMoment), vel_base)); //Debug.Log(dist + " " + deltaMomentPerp.magnitude + " " + deltaForcePerpMag); //vesselRootLocalAeroCenter += avgForcePos; //avgForcePos = rootPartTrans.worldToLocalMatrix.MultiplyPoint3x4(avgForcePos); //vesselRootLocalAeroCenter += Vector3.ProjectOnPlane(avgForcePos, Vector3.up); //vesselRootLocalAeroCenter = aeroSection.GetPos(); vesselRootLocalAeroCenter += pos;//Vector3.ProjectOnPlane(avgForcePos - pos, vesselRootLocalAeroCenter) + pos; vesselRootLocalAeroCenter = rootPartTrans.worldToLocalMatrix.MultiplyPoint3x4(vesselRootLocalAeroCenter); }
public void GetClCdCmSteady(InstantConditionSimInput input, out InstantConditionSimOutput output, bool clear, bool reset_stall = false) { output = new InstantConditionSimOutput(); double area = 0; double MAC = 0; double b_2 = 0; Vector3d forward = Vector3.forward; Vector3d up = Vector3.up; Vector3d right = Vector3.right; Vector3d CoM = Vector3d.zero; double mass = 0; List<Part> partsList = EditorLogic.SortedShipList; for (int i = 0; i < partsList.Count; i++) { Part p = partsList[i]; if (FARAeroUtil.IsNonphysical(p)) continue; double partMass = p.mass; if (p.Resources.Count > 0) partMass += p.GetResourceMass(); //partMass += p.GetModuleMass(p.mass); // If you want to use GetModuleMass, you need to start from p.partInfo.mass, not p.mass CoM += partMass * (Vector3d)p.transform.TransformPoint(p.CoMOffset); mass += partMass; } CoM /= mass; if (EditorDriver.editorFacility == EditorFacility.VAB) { forward = Vector3.up; up = -Vector3.forward; } double sinAlpha = Math.Sin(input.alpha * Math.PI / 180); double cosAlpha = Math.Sqrt(Math.Max(1 - sinAlpha * sinAlpha, 0)); double sinBeta = Math.Sin(input.beta * Math.PI / 180); double cosBeta = Math.Sqrt(Math.Max(1 - sinBeta * sinBeta, 0)); double sinPhi = Math.Sin(input.phi * Math.PI / 180); double cosPhi = Math.Sqrt(Math.Max(1 - sinPhi * sinPhi, 0)); double alphaDot = input.alphaDot * Math.PI / 180; double betaDot = input.betaDot * Math.PI / 180; double phiDot = input.phiDot * Math.PI / 180; Vector3d AngVel = (phiDot - sinAlpha * betaDot) * forward; AngVel += (cosPhi * alphaDot + cosAlpha * sinPhi * betaDot) * right; AngVel += (sinPhi * alphaDot - cosAlpha * cosPhi * betaDot) * up; Vector3d velocity = forward * cosAlpha * cosBeta; velocity += right * (sinPhi * cosAlpha * cosBeta + cosPhi * sinBeta); velocity += -up * cosPhi * (sinAlpha * cosBeta + sinBeta); velocity.Normalize(); //this is negative wrt the ground Vector3d liftVector = -forward * sinAlpha + right * sinPhi * cosAlpha - up * cosPhi * cosAlpha; Vector3d sideways = Vector3.Cross(velocity, liftVector).normalized; for (int i = 0; i < _wingAerodynamicModel.Count; i++) { FARWingAerodynamicModel w = _wingAerodynamicModel[i]; if (!(w && w.part)) continue; w.ComputeForceEditor(velocity.normalized, input.machNumber, 2); if (clear) w.EditorClClear(reset_stall); Vector3d relPos = w.GetAerodynamicCenter() - CoM; Vector3d vel = velocity + Vector3d.Cross(AngVel, relPos); if (w is FARControllableSurface) (w as FARControllableSurface).SetControlStateEditor(CoM, vel, (float)input.pitchValue, 0, 0, input.flaps, input.spoilers); else if (w.isShielded) continue; //w.ComputeForceEditor(velocity, input.machNumber); //do this just to get the AC right Vector3d force = w.ComputeForceEditor(vel.normalized, input.machNumber, 2) * 1000; output.Cl += -Vector3d.Dot(force, liftVector); output.Cy += Vector3d.Dot(force, sideways); output.Cd += -Vector3d.Dot(force, velocity); Vector3d moment = -Vector3d.Cross(relPos, force); output.Cm += Vector3d.Dot(moment, sideways); output.Cn += Vector3d.Dot(moment, liftVector); output.C_roll += Vector3d.Dot(moment, velocity); //w.ComputeClCdEditor(vel.normalized, input.machNumber); /*double tmpCl = w.GetCl() * w.S; output.Cl += tmpCl * -Vector3d.Dot(w.GetLiftDirection(), liftVector); output.Cy += tmpCl * -Vector3d.Dot(w.GetLiftDirection(), sideways); double tmpCd = w.GetCd() * w.S; output.Cd += tmpCd; output.Cm += tmpCl * Vector3d.Dot((relPos), velocity) * -Vector3d.Dot(w.GetLiftDirection(), liftVector) + tmpCd * -Vector3d.Dot((relPos), liftVector); output.Cn += tmpCd * Vector3d.Dot((relPos), sideways) + tmpCl * Vector3d.Dot((relPos), velocity) * -Vector3d.Dot(w.GetLiftDirection(), sideways); output.C_roll += tmpCl * Vector3d.Dot((relPos), sideways) * -Vector3d.Dot(w.GetLiftDirection(), liftVector);*/ area += w.S; MAC += w.GetMAC() * w.S; b_2 += w.Getb_2() * w.S; } FARCenterQuery center = new FARCenterQuery(); for (int i = 0; i < _currentAeroSections.Count; i++) { _currentAeroSections[i].PredictionCalculateAeroForces(2, (float)input.machNumber, 10000, 0.005f, velocity.normalized, center); } Vector3d centerForce = center.force * 1000; output.Cl += -Vector3d.Dot(centerForce, liftVector); output.Cy += Vector3d.Dot(centerForce, sideways); output.Cd += -Vector3d.Dot(centerForce, velocity); Vector3d centerMoment = -center.TorqueAt(CoM) * 1000; output.Cm += Vector3d.Dot(centerMoment, sideways); output.Cn += Vector3d.Dot(centerMoment, liftVector); output.C_roll += Vector3d.Dot(centerMoment, velocity); /*for (int i = 0; i < FARAeroUtil.CurEditorParts.Count; i++) { Part p = FARAeroUtil.CurEditorParts[i]; if (FARAeroUtil.IsNonphysical(p)) continue; Vector3 part_pos = p.transform.TransformPoint(p.CoMOffset) - CoM; double partMass = p.mass; if (p.Resources.Count > 0) partMass += p.GetResourceMass(); double stock_drag = partMass * p.maximum_drag * FlightGlobals.DragMultiplier * 1000; output.Cd += stock_drag; output.Cm += stock_drag * -Vector3d.Dot(part_pos, liftVector); output.Cn += stock_drag * Vector3d.Dot(part_pos, sideways); }*/ if (area == 0) { area = _maxCrossSectionFromBody; b_2 = 1; MAC = _bodyLength; } double recipArea = 1 / area; MAC *= recipArea; b_2 *= recipArea; output.Cl *= recipArea; output.Cd *= recipArea; output.Cm *= recipArea / MAC; output.Cy *= recipArea; output.Cn *= recipArea / b_2; output.C_roll *= recipArea / b_2; }