use of com.chrisali.javaflightsim.simulation.propulsion.Engine in project j6dof-flight-sim by chris-ali.
the class AccelAndMoments method calculateLinearAccelerations.
/**
* Calculates the total linear acceleration experienced by the aircraft (ft/sec^2)
*
* @param windParameters
* @param angularRates
* @param environmentParameters
* @param controls
* @param alphaDot
* @param engineList
* @param aircraft
* @param groundReaction
* @param heightAGL
* @return linearAccelerations
*/
public static double[] calculateLinearAccelerations(double[] windParameters, double[] angularRates, Map<EnvironmentParameters, Double> environmentParameters, Map<FlightControl, Double> controls, double alphaDot, Set<Engine> engineList, Aircraft aircraft, IntegrateGroundReaction groundReaction, double heightAGL) {
Vector3D aeroForceVector = new Vector3D(aero.calculateBodyForces(windParameters, angularRates, environmentParameters, controls, alphaDot, heightAGL));
Vector3D groundForceVector = new Vector3D(groundReaction.getTotalGroundForces());
// Create a vector of engine force, iterate through engineList and add the thrust of each engine in list
Vector3D engineForceVector = Vector3D.ZERO;
for (Engine engine : engineList) engineForceVector = engineForceVector.add(new Vector3D(engine.getEngineThrust()));
linearAccelerations = aeroForceVector.add(engineForceVector).add(groundForceVector).scalarMultiply(1 / aircraft.getMassProperty(MassProperties.TOTAL_MASS)).toArray();
return SaturationUtilities.limitLinearAccelerations(linearAccelerations);
}
use of com.chrisali.javaflightsim.simulation.propulsion.Engine in project j6dof-flight-sim by chris-ali.
the class Integrate6DOFEquations method updateDataMembers.
/**
* Runs various helper methods to update data members in {@link Integrate6DOFEquations}. It updates the 6DOF states, environment parameters, controls, engine state, and finally
* calculates accelerations and moments to be used in {@link Integrate6DOFEquations#updateDerivatives(double[])}
*/
private void updateDataMembers() {
// Assign indices in yTemp array to 6DOF state arrays
for (int i = 0; i < linearVelocities.length; i++) {
linearVelocities[i] = y[i];
NEDPosition[i] = y[i + 3];
eulerAngles[i] = y[i + 6];
angularRates[i] = y[i + 9];
}
// Implement saturation and (2)pi bounding to keep states within realistic limits
linearVelocities = SaturationUtilities.limitLinearVelocities(linearVelocities);
NEDPosition = SaturationUtilities.limitNEDPosition(NEDPosition, terrainHeight);
eulerAngles = SaturationUtilities.piBounding(eulerAngles, angularRates);
angularRates = SaturationUtilities.limitAngularRates(angularRates);
// Update wind parameters
windParameters = SixDOFUtilities.calculateWindParameters(linearVelocities);
// Update environment
environmentParameters = Environment.getAndUpdateEnvironmentParams(NEDPosition);
// Update all engines in engine list
for (Engine engine : engineList) engine.updateEngineState(controlsMap, environmentParameters, windParameters);
// Update alphaDot
alphaDot = SixDOFUtilities.calculateAlphaDot(linearVelocities, sixDOFDerivatives);
// Update mach
mach = SixDOFUtilities.calculateMach(windParameters, environmentParameters);
// Integrate another step of ground reaction only if within 100 ft of ground
double heightAGL = NEDPosition[2] - terrainHeight;
if (heightAGL < 100)
groundReaction.integrateStep(terrainHeight);
// System.out.println(groundReaction);
// Update accelerations
linearAccelerations = AccelAndMoments.calculateLinearAccelerations(windParameters, angularRates, environmentParameters, controlsMap, alphaDot, engineList, aircraft, groundReaction, heightAGL);
// Update moments
totalMoments = AccelAndMoments.calculateTotalMoments(windParameters, angularRates, environmentParameters, controlsMap, alphaDot, engineList, aircraft, groundReaction, heightAGL);
// Recalculates derivatives for next step
updateDerivatives(y);
}
use of com.chrisali.javaflightsim.simulation.propulsion.Engine in project j6dof-flight-sim by chris-ali.
the class Integrate6DOFEquations method logData.
/**
* Adds simulation data to the ArrayList {@link Integrate6DOFEquations#getLogsOut()} after each successful step of integration
* for plotting and outputs to the console, if set in {@link Integrate6DOFEquations#options}.
* The data calculated in each step of integration is available in the EnumMap {@link Integrate6DOFEquations#getSimOut()}. All
* collections are synchronized to mitigate data access problems from threading
*/
private void logData() {
// Need to initialize within logData(), else plots won't display correctly
simOut = Collections.synchronizedMap(new EnumMap<SimOuts, Double>(SimOuts.class));
synchronized (simOut) {
// Assign EnumMap with data members from integration
simOut.put(SimOuts.TIME, t);
// 6DOF States
simOut.put(SimOuts.U, linearVelocities[0]);
simOut.put(SimOuts.V, linearVelocities[1]);
simOut.put(SimOuts.W, linearVelocities[2]);
simOut.put(SimOuts.NORTH, NEDPosition[0]);
simOut.put(SimOuts.EAST, NEDPosition[1]);
simOut.put(SimOuts.ALT, NEDPosition[2]);
simOut.put(SimOuts.PHI, eulerAngles[0]);
simOut.put(SimOuts.THETA, eulerAngles[1]);
simOut.put(SimOuts.PSI, eulerAngles[2]);
simOut.put(SimOuts.P, angularRates[0]);
simOut.put(SimOuts.Q, angularRates[1]);
simOut.put(SimOuts.R, angularRates[2]);
// Earth Position/Velocity
simOut.put(SimOuts.LAT, y[12]);
simOut.put(SimOuts.LAT_DOT, sixDOFDerivatives[12]);
simOut.put(SimOuts.LON, y[13]);
simOut.put(SimOuts.LON_DOT, sixDOFDerivatives[13]);
// Wind Parameters
simOut.put(SimOuts.TAS, windParameters[0]);
simOut.put(SimOuts.BETA, windParameters[1]);
simOut.put(SimOuts.ALPHA, windParameters[2] * -1);
simOut.put(SimOuts.ALPHA_DOT, alphaDot);
simOut.put(SimOuts.MACH, mach);
// Accelerations
simOut.put(SimOuts.A_X, linearAccelerations[0]);
simOut.put(SimOuts.A_Y, linearAccelerations[1]);
simOut.put(SimOuts.A_Z, linearAccelerations[2]);
simOut.put(SimOuts.AN_X, (sixDOFDerivatives[0] / gravity));
simOut.put(SimOuts.AN_Y, (sixDOFDerivatives[1] / gravity));
simOut.put(SimOuts.AN_Z, ((sixDOFDerivatives[2] / gravity) + 1.0));
// Moments
simOut.put(SimOuts.L, totalMoments[0]);
simOut.put(SimOuts.M, totalMoments[1]);
simOut.put(SimOuts.N, totalMoments[2]);
// 6DOF Derivatives
simOut.put(SimOuts.U_DOT, sixDOFDerivatives[0]);
simOut.put(SimOuts.V_DOT, sixDOFDerivatives[1]);
simOut.put(SimOuts.W_DOT, sixDOFDerivatives[2]);
simOut.put(SimOuts.NORTH_DOT, sixDOFDerivatives[3]);
simOut.put(SimOuts.EAST_DOT, sixDOFDerivatives[4]);
simOut.put(SimOuts.ALT_DOT, (sixDOFDerivatives[5] * 60));
simOut.put(SimOuts.PHI_DOT, sixDOFDerivatives[6]);
simOut.put(SimOuts.THETA_DOT, sixDOFDerivatives[7]);
simOut.put(SimOuts.PSI_DOT, sixDOFDerivatives[8]);
simOut.put(SimOuts.P_DOT, sixDOFDerivatives[9]);
simOut.put(SimOuts.Q_DOT, sixDOFDerivatives[10]);
simOut.put(SimOuts.R_DOT, sixDOFDerivatives[11]);
// Engine(s)
simOut.put(SimOuts.THRUST_1, 0.0);
simOut.put(SimOuts.RPM_1, 0.0);
simOut.put(SimOuts.FUEL_FLOW_1, 0.0);
simOut.put(SimOuts.THRUST_2, 0.0);
simOut.put(SimOuts.RPM_2, 0.0);
simOut.put(SimOuts.FUEL_FLOW_2, 0.0);
simOut.put(SimOuts.THRUST_3, 0.0);
simOut.put(SimOuts.RPM_3, 0.0);
simOut.put(SimOuts.FUEL_FLOW_3, 0.0);
simOut.put(SimOuts.THRUST_4, 0.0);
simOut.put(SimOuts.RPM_4, 0.0);
simOut.put(SimOuts.FUEL_FLOW_4, 0.0);
for (Engine engine : engineList) {
int engineNumber = engine.getEngineNumber();
simOut.put(Enum.valueOf(SimOuts.class, "THRUST_" + engineNumber), engine.getEngineThrust()[0]);
simOut.put(Enum.valueOf(SimOuts.class, "RPM_" + engineNumber), engine.getRPM());
simOut.put(Enum.valueOf(SimOuts.class, "FUEL_FLOW_" + engineNumber), engine.getFuelFlow());
}
// Controls
simOut.put(SimOuts.ELEVATOR, controlsMap.get(FlightControl.ELEVATOR));
simOut.put(SimOuts.AILERON, controlsMap.get(FlightControl.AILERON));
simOut.put(SimOuts.RUDDER, controlsMap.get(FlightControl.RUDDER));
simOut.put(SimOuts.THROTTLE_1, controlsMap.get(FlightControl.THROTTLE_1));
simOut.put(SimOuts.THROTTLE_2, controlsMap.get(FlightControl.THROTTLE_2));
simOut.put(SimOuts.THROTTLE_3, controlsMap.get(FlightControl.THROTTLE_3));
simOut.put(SimOuts.THROTTLE_4, controlsMap.get(FlightControl.THROTTLE_4));
simOut.put(SimOuts.PROPELLER_1, controlsMap.get(FlightControl.PROPELLER_1));
simOut.put(SimOuts.PROPELLER_2, controlsMap.get(FlightControl.PROPELLER_2));
simOut.put(SimOuts.PROPELLER_3, controlsMap.get(FlightControl.PROPELLER_3));
simOut.put(SimOuts.PROPELLER_4, controlsMap.get(FlightControl.PROPELLER_4));
simOut.put(SimOuts.MIXTURE_1, controlsMap.get(FlightControl.MIXTURE_1));
simOut.put(SimOuts.MIXTURE_2, controlsMap.get(FlightControl.MIXTURE_2));
simOut.put(SimOuts.MIXTURE_3, controlsMap.get(FlightControl.MIXTURE_3));
simOut.put(SimOuts.MIXTURE_4, controlsMap.get(FlightControl.MIXTURE_4));
simOut.put(SimOuts.FLAPS, controlsMap.get(FlightControl.FLAPS));
simOut.put(SimOuts.GEAR, controlsMap.get(FlightControl.GEAR));
}
synchronized (logsOut) {
// Removes the first entry in logsOut to keep a maximum of 100 sec of flight data in UNLIMITED_FLIGHT
if (options.contains(Options.UNLIMITED_FLIGHT) & t >= 100 & logsOut.size() > 0)
logsOut.remove(0);
// Add output step to logging arrayList
logsOut.add(simOut);
}
}
use of com.chrisali.javaflightsim.simulation.propulsion.Engine in project j6dof-flight-sim by chris-ali.
the class Trimming method trimSim.
/**
* Trims an aircraft longitudinally for a forward velocity and altitude specified in
*
* <p> SimConfig/InitialConditions.txt </p>
*
* by setting the elevator, throttle and pitch attitude. These values are calculated by statically equating forces and moments.
* If unable to reach a given trim condition, the method will return the maximum values for each control and pitch attitude.
* These values are then saved to
*
* <p> SimConfig/InitialConditions.txt </p>
* and
* <p> SimConfig/InitialControls.txt </p>
*
* as long as the test mode boolean flag is false; otherwise the results will be displayed in the console
*
* @param configuration
* @param testMode
*/
public static void trimSim(SimulationConfiguration configuration, boolean testMode) {
Aircraft aircraft = FileUtilities.readAircraftConfiguration(configuration.getSelectedAircraft());
aero = new Aerodynamics(aircraft);
initialConditions = configuration.getInitialConditions();
initialControls = configuration.getInitialControls();
environmentParams = Environment.getAndUpdateEnvironmentParams(new double[] { 0, 0, initialConditions.get(InitialConditions.INITD) });
double alphaMin = -0.18, alphaMax = 0.18, throttleMin = 0.0, throttleMax = 1.0, alphaTrim = 0.0, thetaTrim = 0.0, elevTrim = 0.0, throttleTrim = 0.0, wVelocityTrim = 0.0, lift = 0.0, drag = 0.0, totalThrust = 0.0, zForce = 100.0;
double trueAirspeed = Math.sqrt(Math.pow(initialConditions.get(InitialConditions.INITU), 2) + Math.pow(initialConditions.get(InitialConditions.INITW), 2));
double weight = aircraft.getMassProperty(MassProperties.TOTAL_MASS) * Environment.getGravity();
double q = environmentParams.get(EnvironmentParameters.RHO) * Math.pow(trueAirspeed, 2) / 2;
double s = aircraft.getWingGeometry(WingGeometry.S_WING);
int counter = 0;
logger.debug("Finding trim pitch and elevator...");
do {
alphaTrim = (alphaMin + alphaMax) / 2;
// Break out of loop if trim condition not satisfied after 100 attempts
if (counter == 100) {
logger.error("Unable to trim elevator and pitch for given conditions!");
break;
}
// =============================================== Pitch ================================================================
// Calculate trim pitch by using (theta = alpha + FPA)
// FPA is zero in level flight
thetaTrim = alphaTrim + 0;
double[] windParameters = new double[] { trueAirspeed, 0, alphaTrim };
double CL_alpha = aero.calculateInterpStabDer(windParameters, initialControls, StabilityDerivatives.CL_ALPHA);
double CL_0 = aero.calculateInterpStabDer(windParameters, initialControls, StabilityDerivatives.CL_0);
lift = q * s * ((CL_alpha * alphaTrim) + CL_0);
double CD_alpha = aero.calculateInterpStabDer(windParameters, initialControls, StabilityDerivatives.CD_ALPHA);
double CD_0 = aero.calculateInterpStabDer(windParameters, initialControls, StabilityDerivatives.CD_0);
drag = q * s * ((CD_alpha * alphaTrim) + CD_0);
// Calculate zForce to find trim alpha to zero forces
zForce = (-lift * Math.cos(alphaTrim)) - (drag * Math.sin(alphaTrim)) + (weight * Math.cos(thetaTrim));
if (zForce > 0)
alphaMin = alphaTrim;
else
alphaMax = alphaTrim;
// Recalculate w velocity for new angle of attack
wVelocityTrim = windParameters[0] * Math.sin(alphaTrim);
// ==================================================== Elevator ========================================================
// Calculate trim elevator, limiting if necessary
double CM_alpha = aero.calculateInterpStabDer(windParameters, initialControls, StabilityDerivatives.CM_ALPHA);
double CM_d_elev = aircraft.getStabilityDerivative(StabilityDerivatives.CM_D_ELEV).getValue();
double CM_0 = aircraft.getStabilityDerivative(StabilityDerivatives.CM_0).getValue();
elevTrim = (CM_0 + (CM_alpha * alphaTrim)) / CM_d_elev;
elevTrim = elevTrim > FlightControl.ELEVATOR.getMaximum() ? FlightControl.ELEVATOR.getMaximum() : elevTrim < FlightControl.ELEVATOR.getMinimum() ? FlightControl.ELEVATOR.getMinimum() : elevTrim;
counter++;
} while (Math.abs(zForce) > 1);
logger.debug("...done!");
// ==================================================== Throttle ============================================================
Set<Engine> engines = aircraft.getEngines();
drag = (drag * Math.cos(alphaTrim)) - (lift * Math.sin(alphaTrim)) + (weight * Math.sin(thetaTrim));
counter = 0;
logger.debug("Finding trim throttle...");
do {
throttleTrim = (throttleMin + throttleMax) / 2;
// Break out of loop if trim condition not satisfied after 100 attempts
if (counter == 100) {
logger.error("Unable to trim throttle for given conditions!");
break;
}
initialControls.put(FlightControl.THROTTLE_1, throttleTrim);
initialControls.put(FlightControl.THROTTLE_2, throttleTrim);
initialControls.put(FlightControl.THROTTLE_3, throttleTrim);
initialControls.put(FlightControl.THROTTLE_4, throttleTrim);
// Get total thrust, equate it with drag of aircraft to find trim throttle
totalThrust = 0.0;
for (Engine engine : engines) {
engine.updateEngineState(initialControls, environmentParams, new double[] { trueAirspeed, 0, 0 });
totalThrust += engine.getEngineThrust()[0];
}
if (totalThrust < drag)
throttleMin = throttleTrim;
else
throttleMax = throttleTrim;
counter++;
} while (Math.abs(totalThrust - drag) > 1);
logger.debug("...done!");
// Update initialControls and initialConditions
initialConditions.put(InitialConditions.INITTHETA, thetaTrim);
initialConditions.put(InitialConditions.INITW, wVelocityTrim);
initialControls.put(FlightControl.ELEVATOR, -elevTrim);
initialControls.put(FlightControl.AILERON, 0.0);
initialControls.put(FlightControl.RUDDER, 0.0);
logger.debug("Finished trimming aircraft!");
logger.debug(String.format("Trim controls are: \nElevator: %.4f rad\nThrottle: %.4f", elevTrim, throttleTrim));
logger.debug(String.format("Trim states are: \nW Velocity: %3.4f ft/sec\nTheta: %.4f rad\nAlpha: %.4f rad", wVelocityTrim, thetaTrim, alphaTrim));
// In test mode do not write any config settings to files
if (!testMode) {
logger.debug("Updating initial conditions and initial flight controls...");
configuration.setInitialConditions(initialConditions);
configuration.setInitialControls(initialControls);
configuration.save();
} else {
logger.debug(Trimming.outputTrimValues());
}
}
use of com.chrisali.javaflightsim.simulation.propulsion.Engine in project j6dof-flight-sim by chris-ali.
the class AccelAndMoments method calculateTotalMoments.
/**
* Calculates the total moment experienced by the aircraft (lb ft)
*
* @param windParameters
* @param angularRates
* @param environmentParameters
* @param controls
* @param alphaDot
* @param engineList
* @param aircraft
* @param groundReaction
* @param heightAGL
* @return totalMoments
*/
public static double[] calculateTotalMoments(double[] windParameters, double[] angularRates, Map<EnvironmentParameters, Double> environmentParameters, Map<FlightControl, Double> controls, double alphaDot, Set<Engine> engineList, Aircraft aircraft, IntegrateGroundReaction groundReaction, double heightAGL) {
Vector3D aeroForceVector = new Vector3D(aero.calculateBodyForces(windParameters, angularRates, environmentParameters, controls, alphaDot, heightAGL));
// Apache Commons vector methods only accept primitive double[] arrays
Vector3D acVector = new Vector3D(aircraft.getAerodynamicCenter());
Vector3D cgVector = new Vector3D(aircraft.getCenterOfGravity());
Vector3D aeroForceCrossProd = Vector3D.crossProduct(aeroForceVector, acVector.subtract(cgVector));
Vector3D aeroMomentVector = new Vector3D(aero.calculateAeroMoments(windParameters, angularRates, environmentParameters, controls, alphaDot));
Vector3D groundMomentVector = new Vector3D(groundReaction.getTotalGroundMoments());
// Create a vector of engine moment, iterate through engineList and add the moment of each engine in list
Vector3D engineMoment = Vector3D.ZERO;
for (Engine engine : engineList) engineMoment = engineMoment.add(new Vector3D(engine.getEngineMoment()));
totalMoments = aeroMomentVector.add(engineMoment).add(aeroForceCrossProd).add(groundMomentVector).toArray();
return SaturationUtilities.limitTotalMoments(totalMoments);
}
Aggregations