@Override public FlightData simulate(SimulationConditions simulationConditions) throws SimulationException { Set<MotorId> motorBurntOut = new HashSet<MotorId>(); // Set up flight data FlightData flightData = new FlightData(); // Set up rocket configuration Configuration configuration = setupConfiguration(simulationConditions); MotorInstanceConfiguration motorConfiguration = setupMotorConfiguration(configuration); if (motorConfiguration.getMotorIDs().isEmpty()) { throw new MotorIgnitionException("No motors defined in the simulation."); } // Initialize the simulation currentStepper = flightStepper; status = initialStatus(configuration, motorConfiguration, simulationConditions, flightData); status = currentStepper.initialize(status); SimulationListenerHelper.fireStartSimulation(status); // Get originating position (in case listener has modified launch position) Coordinate origin = status.getRocketPosition(); Coordinate originVelocity = status.getRocketVelocity(); try { double maxAlt = Double.NEGATIVE_INFINITY; // Start the simulation while (handleEvents()) { // Take the step double oldAlt = status.getRocketPosition().z; if (SimulationListenerHelper.firePreStep(status)) { // Step at most to the next event double maxStepTime = Double.MAX_VALUE; FlightEvent nextEvent = status.getEventQueue().peek(); if (nextEvent != null) { maxStepTime = MathUtil.max(nextEvent.getTime() - status.getSimulationTime(), 0.001); } log.verbose( "BasicEventSimulationEngine: Taking simulation step at t=" + status.getSimulationTime()); currentStepper.step(status, maxStepTime); } SimulationListenerHelper.firePostStep(status); // Calculate values for custom expressions FlightDataBranch data = status.getFlightData(); ArrayList<CustomExpression> allExpressions = status.getSimulationConditions().getSimulation().getCustomExpressions(); for (CustomExpression expression : allExpressions) { data.setValue(expression.getType(), expression.evaluate(status)); } // Check for NaN values in the simulation status checkNaN(); // Add altitude event addEvent( new FlightEvent( FlightEvent.Type.ALTITUDE, status.getSimulationTime(), status.getConfiguration().getRocket(), new Pair<Double, Double>(oldAlt, status.getRocketPosition().z))); if (status.getRocketPosition().z > maxAlt) { maxAlt = status.getRocketPosition().z; } // Position relative to start location Coordinate relativePosition = status.getRocketPosition().sub(origin); // Add appropriate events if (!status.isLiftoff()) { // Avoid sinking into ground before liftoff if (relativePosition.z < 0) { status.setRocketPosition(origin); status.setRocketVelocity(originVelocity); } // Detect lift-off if (relativePosition.z > 0.02) { addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime())); } } else { // Check ground hit after liftoff if (status.getRocketPosition().z < 0) { status.setRocketPosition(status.getRocketPosition().setZ(0)); addEvent(new FlightEvent(FlightEvent.Type.GROUND_HIT, status.getSimulationTime())); addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime())); } } // Check for launch guide clearance if (!status.isLaunchRodCleared() && relativePosition.length() > status.getSimulationConditions().getLaunchRodLength()) { addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null)); } // Check for apogee if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) { addEvent( new FlightEvent( FlightEvent.Type.APOGEE, status.getSimulationTime(), status.getConfiguration().getRocket())); } // Check for burnt out motors for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) { MotorInstance motor = status.getMotorConfiguration().getMotorInstance(motorId); if (!motor.isActive() && motorBurntOut.add(motorId)) { addEvent( new FlightEvent( FlightEvent.Type.BURNOUT, status.getSimulationTime(), (RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId)); } } } } catch (SimulationException e) { SimulationListenerHelper.fireEndSimulation(status, e); throw e; } SimulationListenerHelper.fireEndSimulation(status, null); flightData.addBranch(status.getFlightData()); if (!flightData.getWarningSet().isEmpty()) { log.info("Warnings at the end of simulation: " + flightData.getWarningSet()); } // TODO: HIGH: Simulate branches return flightData; }
private FlightDataBranch simulateLoop() { // Initialize the simulation currentStepper = flightStepper; currentStatus = currentStepper.initialize(currentStatus); // Get originating position (in case listener has modified launch position) Coordinate origin = currentStatus.getRocketPosition(); Coordinate originVelocity = currentStatus.getRocketVelocity(); try { // Start the simulation while (handleEvents()) { // Take the step double oldAlt = currentStatus.getRocketPosition().z; if (SimulationListenerHelper.firePreStep(currentStatus)) { // Step at most to the next event double maxStepTime = Double.MAX_VALUE; FlightEvent nextEvent = currentStatus.getEventQueue().peek(); if (nextEvent != null) { maxStepTime = MathUtil.max(nextEvent.getTime() - currentStatus.getSimulationTime(), 0.001); } log.trace( "BasicEventSimulationEngine: Taking simulation step at t=" + currentStatus.getSimulationTime()); currentStepper.step(currentStatus, maxStepTime); } SimulationListenerHelper.firePostStep(currentStatus); // Check for NaN values in the simulation status checkNaN(); // Add altitude event addEvent( new FlightEvent( FlightEvent.Type.ALTITUDE, currentStatus.getSimulationTime(), currentStatus.getConfiguration().getRocket(), new Pair<Double, Double>(oldAlt, currentStatus.getRocketPosition().z))); if (currentStatus.getRocketPosition().z > currentStatus.getMaxAlt()) { currentStatus.setMaxAlt(currentStatus.getRocketPosition().z); } // Position relative to start location Coordinate relativePosition = currentStatus.getRocketPosition().sub(origin); // Add appropriate events if (!currentStatus.isLiftoff()) { // Avoid sinking into ground before liftoff if (relativePosition.z < 0) { currentStatus.setRocketPosition(origin); currentStatus.setRocketVelocity(originVelocity); } // Detect lift-off if (relativePosition.z > 0.02) { addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, currentStatus.getSimulationTime())); } } else { // Check ground hit after liftoff if (currentStatus.getRocketPosition().z < 0) { currentStatus.setRocketPosition(currentStatus.getRocketPosition().setZ(0)); addEvent( new FlightEvent(FlightEvent.Type.GROUND_HIT, currentStatus.getSimulationTime())); addEvent( new FlightEvent( FlightEvent.Type.SIMULATION_END, currentStatus.getSimulationTime())); } } // Check for launch guide clearance if (!currentStatus.isLaunchRodCleared() && relativePosition.length() > currentStatus.getSimulationConditions().getLaunchRodLength()) { addEvent( new FlightEvent(FlightEvent.Type.LAUNCHROD, currentStatus.getSimulationTime(), null)); } // Check for apogee if (!currentStatus.isApogeeReached() && currentStatus.getRocketPosition().z < currentStatus.getMaxAlt() - 0.01) { currentStatus.setMaxAltTime(currentStatus.getSimulationTime()); addEvent( new FlightEvent( FlightEvent.Type.APOGEE, currentStatus.getSimulationTime(), currentStatus.getConfiguration().getRocket())); } // //@Obsolete // //@Redundant // // Check for burnt out motors // for( MotorClusterState state : currentStatus.getActiveMotors()){ // if ( state.isSpent()){ // addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, // currentStatus.getSimulationTime(), // (RocketComponent) state.getMount(), state)); // } // } // Check for Tumbling // Conditions for transision are: // apogee reached (if sustainer stage) // and is not already tumbling // and not stable (cg > cp) // and aoa > AOA_TUMBLE_CONDITION threshold // and thrust < THRUST_TUMBLE_CONDITION threshold if (!currentStatus.isTumbling()) { final double t = currentStatus.getFlightData().getLast(FlightDataType.TYPE_THRUST_FORCE); final double cp = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION); final double cg = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION); final double aoa = currentStatus.getFlightData().getLast(FlightDataType.TYPE_AOA); final boolean wantToTumble = (cg > cp && aoa > AOA_TUMBLE_CONDITION); if (wantToTumble) { final boolean tooMuchThrust = t > THRUST_TUMBLE_CONDITION; // final boolean isSustainer = status.getConfiguration().isStageActive(0); final boolean isApogee = currentStatus.isApogeeReached(); if (tooMuchThrust) { currentStatus.getWarnings().add(Warning.TUMBLE_UNDER_THRUST); } else if (isApogee) { addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime())); currentStatus.setTumbling(true); } } } } } catch (SimulationException e) { SimulationListenerHelper.fireEndSimulation(currentStatus, e); // Add FlightEvent for Abort. currentStatus .getFlightData() .addEvent( new FlightEvent( FlightEvent.Type.EXCEPTION, currentStatus.getSimulationTime(), currentStatus.getConfiguration().getRocket(), e.getLocalizedMessage())); currentStatus.getWarnings().add(e.getLocalizedMessage()); } return currentStatus.getFlightData(); }
private SimulationStatus initialStatus( Configuration configuration, MotorInstanceConfiguration motorConfiguration, SimulationConditions simulationConditions, FlightData flightData) { SimulationStatus init = new SimulationStatus(); init.setSimulationConditions(simulationConditions); init.setConfiguration(configuration); init.setMotorConfiguration(motorConfiguration); init.setSimulationTime(0); init.setPreviousTimeStep(simulationConditions.getTimeStep()); init.setRocketPosition(Coordinate.NUL); init.setRocketVelocity(Coordinate.NUL); init.setRocketWorldPosition(simulationConditions.getLaunchSite()); // Initialize to roll angle with least stability w.r.t. the wind Quaternion o; FlightConditions cond = new FlightConditions(configuration); simulationConditions.getAerodynamicCalculator().getWorstCP(configuration, cond, null); double angle = -cond.getTheta() - simulationConditions.getLaunchRodDirection(); o = Quaternion.rotation(new Coordinate(0, 0, angle)); // Launch rod angle and direction o = o.multiplyLeft( Quaternion.rotation(new Coordinate(0, simulationConditions.getLaunchRodAngle(), 0))); o = o.multiplyLeft( Quaternion.rotation( new Coordinate(0, 0, simulationConditions.getLaunchRodDirection()))); init.setRocketOrientationQuaternion(o); init.setRocketRotationVelocity(Coordinate.NUL); /* * Calculate the effective launch rod length taking into account launch lugs. * If no lugs are found, assume a tower launcher of full length. */ double length = simulationConditions.getLaunchRodLength(); double lugPosition = Double.NaN; for (RocketComponent c : configuration) { if (c instanceof LaunchLug) { double pos = c.toAbsolute(new Coordinate(c.getLength()))[0].x; if (Double.isNaN(lugPosition) || pos > lugPosition) { lugPosition = pos; } } } if (!Double.isNaN(lugPosition)) { double maxX = 0; for (Coordinate c : configuration.getBounds()) { if (c.x > maxX) maxX = c.x; } if (maxX >= lugPosition) { length = Math.max(0, length - (maxX - lugPosition)); } } init.setEffectiveLaunchRodLength(length); init.setSimulationStartWallTime(System.nanoTime()); init.setMotorIgnited(false); init.setLiftoff(false); init.setLaunchRodCleared(false); init.setApogeeReached(false); init.getEventQueue() .add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket())); init.setFlightData(new FlightDataBranch("MAIN", FlightDataType.TYPE_TIME)); init.setWarnings(flightData.getWarningSet()); return init; }