package net.sf.openrocket.document;
import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import net.sf.openrocket.document.events.DocumentChangeEvent;
import net.sf.openrocket.document.events.DocumentChangeListener;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.customexpression.CustomExpression;
+import net.sf.openrocket.simulation.exception.SimulationListenerException;
+import net.sf.openrocket.simulation.listeners.SimulationListener;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.ArrayList;
return customExpressions;
}
+ /*
+ * Returns a set of all the flight data types defined or available in any way in the rocket document
+ */
+ public Set<FlightDataType> getFlightDataTypes(){
+ Set<FlightDataType> allTypes = new LinkedHashSet<FlightDataType>();
+
+ // built in
+ Collections.addAll(allTypes, FlightDataType.ALL_TYPES);
+
+ // custom expressions
+ for (CustomExpression exp : customExpressions){
+ allTypes.add(exp.getType());
+ }
+
+ // simulation listeners
+ for (Simulation sim : simulations){
+ for (String className : sim.getSimulationListeners()) {
+ SimulationListener l = null;
+ try {
+ Class<?> c = Class.forName(className);
+ l = (SimulationListener) c.newInstance();
+
+ Collections.addAll(allTypes, l.getFlightDataTypes());
+ //System.out.println("This document has expression datatype from "+l.getName());
+ } catch (Exception e) {
+ log.error("Could not instantiate listener: " + className);
+ }
+ }
+ }
+
+ // imported data
+ /// not implemented yet
+
+
+ return allTypes;
+ }
+
public Rocket getRocket() {
return rocket;
}
}
-
-
class DatatypeHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final OpenRocketContentHandler contentHandler;
public void closeElement(String element, HashMap<String, String> attributes,
String content, WarningSet warnings) throws SAXException {
attributes.remove("status");
+
+ //Finished loading. Rebuilding custom expressions in case something has changed such as listener variable come available.
+ for (CustomExpression exp : doc.getCustomExpressions()){
+ exp.setExpression(exp.getExpressionString());
+ }
+
super.closeElement(element, attributes, content, warnings);
}
-
-
}
class SingleSimulationHandler extends AbstractElementHandler {
if (type != null) {
// found it from symbol
- // if name was not give (empty string), can use the one we found name
- if ( s.equals("") || s == null ){
+ // if name was not given (empty string), can use the one we found
+ if ( s == null || s.isEmpty()){
s = type.getName();
}
if ( u == null ){
// if something has changed, then we need to remove the old one
// otherwise, just return what we found
- if ( !u.equals(type.getUnitGroup()) ||
- !s.equals(type.getName())
- )
+ if ( !u.equals(type.getUnitGroup()) )
{
oldPriority = type.priority;
-
EXISTING_TYPES.remove(type);
- log.info("Something changed with the type "+type.getName()+", removed old version.");
+ log.info("Unitgroup of type "+type.getName() +
+ ", has changed from "+type.getUnitGroup().toString() +
+ " to "+u.toString() +
+ ". Removing old version.");
+ }
+ else if (!s.equals(type.getName())) {
+ oldPriority = type.priority;
+ EXISTING_TYPES.remove(type);
+ log.info("Name of type "+type.getName()+", has changed to "+s+". Removing old version.");
}
else{
return type;
private List<CustomExpression> subExpressions = new ArrayList<CustomExpression>();
public CustomExpression(OpenRocketDocument doc){
+ this.doc = doc;
+
setName("");
setSymbol("");
setUnit("");
setExpression("");
- this.doc = doc;
}
public CustomExpression(OpenRocketDocument doc,
// get a list of all the names of all the available variables
protected ArrayList<String> getAllNames(){
ArrayList<String> names = new ArrayList<String>();
+ /*
for (FlightDataType type : FlightDataType.ALL_TYPES)
names.add(type.getName());
names.add(exp.getName());
}
}
+ */
+ for (FlightDataType type : doc.getFlightDataTypes()){
+ String symb = type.getName();
+ if (name == null) continue;
+
+ if (!name.equals( this.getName() )){
+ names.add(symb);
+ }
+ }
return names;
}
// get a list of all the symbols of the available variables ignoring this one
protected ArrayList<String> getAllSymbols(){
ArrayList<String> symbols = new ArrayList<String>();
+ /*
for (FlightDataType type : FlightDataType.ALL_TYPES)
symbols.add(type.getSymbol());
symbols.add(exp.getSymbol());
}
}
+ */
+ for (FlightDataType type : doc.getFlightDataTypes()){
+ String symb = type.getSymbol();
+ if (!symb.equals( this.getSymbol() )){
+ symbols.add(symb);
+ }
+ }
+
return symbols;
}
//// Define the available variables as empty
// The built in data types
+ /*
for (FlightDataType type : FlightDataType.ALL_TYPES){
builder.withVariable(new Variable(type.getSymbol()));
}
for (String symb : getAllSymbols()){
builder.withVariable(new Variable(symb));
}
+ */
+ for (FlightDataType type : doc.getFlightDataTypes()){
+ builder.withVariable(new Variable(type.getSymbol()));
+ }
// Try to build
try {
builder.build();
} catch (Exception e) {
- log.user("Custom expression invalid : " + e.toString());
+ log.user("Custom expression " + this.toString() + " invalid : " + e.toString());
return false;
}
* Builds a specified expression, log any errors and returns null in case of error.
*/
protected Calculable buildExpression(ExpressionBuilder b){
- Calculable calc;
+ Calculable calc = null;
try {
calc = b.build();
} catch (UnknownFunctionException e1) {
- log.user("Unknown function. Could not build custom expression "+name);
+ log.user("Unknown function. Could not build custom expression "+this.toString());
return null;
} catch (UnparsableExpressionException e1) {
- log.user("Unparsable expression. Could not build custom expression "+name+". "+e1.getMessage());
+ log.user("Unparsable expression. Could not build custom expression "+this.toString()+". "+e1.getMessage());
return null;
}
*/
public FlightDataType getType(){
+
UnitGroup ug = UnitGroup.SIUNITS.get(unit);
if ( ug == null ){
+ log.debug("SI unit not found for "+unit+" in expression "+toString()+". Making a new fixed unit.");
ug = new FixedUnitGroup(unit);
}
+ //UnitGroup ug = new FixedUnitGroup(unit);
FlightDataType type = FlightDataType.getType(name, symbol, ug);
@Override
public String toString(){
- return "Custom expression : "+this.name.toString()+ " " + this.expression.toString();
+ return "[Expression name="+this.name.toString()+ " expression=" + this.expression+" unit="+this.unit+"]";
}
@Override
import java.util.ArrayList;
import java.util.List;
+import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
+import net.sf.openrocket.startup.Application;
public class CustomExpressionSimulationListener extends AbstractSimulationListener {
+ private static final LogHelper log = Application.getLogger();
private final List<CustomExpression> expressions;
public CustomExpressionSimulationListener(List<CustomExpression> expressions) {
// Calculate values for custom expressions
FlightDataBranch data = status.getFlightData();
for (CustomExpression expression : expressions ) {
- data.setValue(expression.getType(), expression.evaluateDouble(status));
+ double value = expression.evaluateDouble(status);
+ //log.debug("Setting value of custom expression "+expression.toString()+" = "+value);
+ data.setValue(expression.getType(), value);
}
}
+ @Override
+ public boolean isSystemListener(){
+ return true;
+ }
+
}
setExpression(indexText);
this.setName("");
this.setSymbol(typeText);
-
}
@Override
}
// From the given datatype, get the time and function values and make an interpolator
- FlightDataType type = getType();
+
+ //Note: must get in a way that flight data system will figure out units. Otherwise there will be a type conflict when we get the new data.
+ FlightDataType type = FlightDataType.getType(null, getSymbol(), null);
+
List<Double> data = status.getFlightData().get(type);
List<Double> time = status.getFlightData().get(FlightDataType.TYPE_TIME);
LinearInterpolator interp = new LinearInterpolator(time, data);
this.expression = variableType+startTime+endTime; // this is used just for generating the hash
log.info("New range expression, "+startTime + " to "+endTime);
-
}
/*
}
// From the given datatype, get the time and function values and make an interpolator
- FlightDataType type = getType();
+
+ //Note: must get in a way that flight data system will figure out units. Otherwise there will be a type conflict when we get the new data.
+ FlightDataType type = FlightDataType.getType(null, getSymbol(), null);
List<Double> data = status.getFlightData().get(type);
List<Double> time = status.getFlightData().get(FlightDataType.TYPE_TIME);
package net.sf.openrocket.simulation.listeners;
+import java.util.List;
+
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.AccelerationData;
+import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.MassData;
import net.sf.openrocket.simulation.SimulationStatus;
return false;
}
-
+ /**
+ * Return an array of any flight data types this listener creates.
+ */
+ @Override
+ public FlightDataType[] getFlightDataTypes(){
+ return new FlightDataType[] {};
+ }
//// SimulationEventListener ////
package net.sf.openrocket.simulation.listeners;
+import java.util.List;
+
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.simulation.AccelerationData;
+import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.MassData;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
public double postSimpleThrustCalculation(SimulationStatus status, double thrust) throws SimulationException;
+ public FlightDataType[] getFlightDataTypes();
}
package net.sf.openrocket.simulation.listeners;
+import java.util.List;
+
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
+import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
*/
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice)
throws SimulationException;
+
+
+
+ public FlightDataType[] getFlightDataTypes();
}
package net.sf.openrocket.simulation.listeners;
+import java.util.List;
+
+import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
*/
public boolean isSystemListener();
+
+ /**
+ * Return a list of any flight data types this listener creates.
+ */
+ public FlightDataType[] getFlightDataTypes();
+
+
}
--- /dev/null
+package net.sf.openrocket.simulation.listeners.example;
+
+import java.util.List;
+import java.util.Map;
+
+import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
+import net.sf.openrocket.aerodynamics.AerodynamicForces;
+import net.sf.openrocket.aerodynamics.FlightConditions;
+import net.sf.openrocket.motor.MotorId;
+import net.sf.openrocket.motor.MotorInstance;
+import net.sf.openrocket.motor.MotorInstanceConfiguration;
+import net.sf.openrocket.rocketcomponent.MotorMount;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.simulation.FlightDataBranch;
+import net.sf.openrocket.simulation.FlightDataType;
+import net.sf.openrocket.simulation.SimulationStatus;
+import net.sf.openrocket.simulation.exception.SimulationException;
+import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.ArrayList;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.PolyInterpolator;
+
+public class DampingMoment extends AbstractSimulationListener {
+
+ private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT);
+ private static final FlightDataType[] typeList = {type};
+
+ public String getName(){
+ return "Damping moment listener";
+ }
+
+ /**
+ * Return a list of any flight data types this listener creates.
+ */
+ public FlightDataType[] getFlightDataTypes(){
+ return typeList;
+ }
+
+ @Override
+ public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
+
+ // Save it as a flightdatatype
+
+ //status.getFlightData().setValue(type, aerodynamicPart + propulsivePart);
+ status.getFlightData().setValue(type, calculate(status, flightConditions));
+
+ return flightConditions;
+ }
+
+ private double calculate(SimulationStatus status, FlightConditions flightConditions){
+
+ // Work out the propulsive/jet damping part of the moment.
+
+ // dm/dt = (thrust - ma)/v
+ FlightDataBranch data = status.getFlightData();
+
+ List<Double> mpAll = data.get(FlightDataType.TYPE_PROPELLANT_MASS);
+ List<Double> time = data.get(FlightDataType.TYPE_TIME);
+ if (mpAll == null || time == null){
+ return Double.NaN;
+ }
+
+ int len = mpAll.size();
+
+ // This isn't as accurate as I would like
+ double mdot=Double.NaN;
+ if (len > 2){
+ // Using polynomial interpolator for derivative. Doesn't help much
+ //double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) };
+ //double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) };
+ //PolyInterpolator interp = new PolyInterpolator(x);
+ //double[] coeff = interp.interpolator(y);
+ //double dt = .01;
+ //mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt;
+
+ mdot = (mpAll.get(len-1) - mpAll.get(len-2)) / (time.get(len-1) - time.get(len-2));
+ }
+
+ double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION);
+
+ // find the maximum distance from nose to nozzle.
+ double nozzleDistance = 0;
+ for (MotorId id: status.getMotorConfiguration().getMotorIDs()){
+ MotorInstanceConfiguration config = status.getMotorConfiguration();
+ Coordinate position = config.getMotorPosition(id);
+
+ double x = position.x + config.getMotorInstance(id).getParentMotor().getLength();
+ if (x > nozzleDistance){
+ nozzleDistance = x;
+ }
+ }
+
+ // now can get the propulsive part
+ double propulsivePart = mdot * Math.pow(nozzleDistance - cg, 2);
+
+ // Work out the aerodynamic part of the moment.
+ double aerodynamicPart = 0;
+
+ AerodynamicCalculator aerocalc = status.getSimulationConditions().getAerodynamicCalculator();
+
+ // Must go through each component ...
+ Map<RocketComponent, AerodynamicForces> forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, null);
+ for (Map.Entry<RocketComponent, AerodynamicForces> entry : forces.entrySet()){
+
+ RocketComponent comp = entry.getKey();
+
+ if (!comp.isAerodynamic()) continue;
+
+ //System.out.println(comp.toString());
+
+ double CNa = entry.getValue().getCNa(); //?
+ double Cp = entry.getValue().getCP().length();
+ double z = comp.getPositionValue(); //?
+
+ aerodynamicPart += CNa*Math.pow(z-Cp, 2);
+ }
+
+ double v = flightConditions.getVelocity();
+ double rho = flightConditions.getAtmosphericConditions().getDensity();
+ double ar = flightConditions.getRefArea();
+
+ aerodynamicPart = aerodynamicPart * .5 * rho * v * ar;
+
+ return aerodynamicPart + propulsivePart;
+
+ }
+
+}