- Implemented a DampingMoment simulation listener example

- Added ability for simulation listeners to reserve their own data types. To support this:
  -- All data types can now be found from just the OpenRocketDocument (reduces some code duplication also).
  -- Custom expressions rebuilt after loading from file in case they use a listeners reserved type
- Fixed (possibly unrelated) issue where datatypes would be deleted and re-made each step if a customexpression used a range or index subexpression
This commit is contained in:
Richard Graham 2012-09-12 07:44:13 +00:00
parent 3fea838fab
commit c9d04f5e0a
12 changed files with 274 additions and 23 deletions

View File

@ -1,8 +1,12 @@
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;
@ -13,7 +17,10 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
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;
@ -121,6 +128,43 @@ public class OpenRocketDocument implements ComponentChangeListener {
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;

View File

@ -704,8 +704,6 @@ class OpenRocketContentHandler extends AbstractElementHandler {
}
}
class DatatypeHandler extends AbstractElementHandler {
private final DocumentLoadingContext context;
private final OpenRocketContentHandler contentHandler;
@ -1288,10 +1286,14 @@ class SimulationsHandler extends AbstractElementHandler {
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 {

View File

@ -269,8 +269,8 @@ public class FlightDataType implements Comparable<FlightDataType> {
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 ){
@ -279,14 +279,19 @@ public class FlightDataType implements Comparable<FlightDataType> {
// 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;

View File

@ -34,11 +34,12 @@ public class CustomExpression implements Cloneable{
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,
@ -171,6 +172,7 @@ public class CustomExpression implements Cloneable{
// 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());
@ -181,12 +183,22 @@ public class CustomExpression implements Cloneable{
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());
@ -196,6 +208,14 @@ public class CustomExpression implements Cloneable{
symbols.add(exp.getSymbol());
}
}
*/
for (FlightDataType type : doc.getFlightDataTypes()){
String symb = type.getSymbol();
if (!symb.equals( this.getSymbol() )){
symbols.add(symb);
}
}
return symbols;
}
@ -305,6 +325,7 @@ public class CustomExpression implements Cloneable{
//// Define the available variables as empty
// The built in data types
/*
for (FlightDataType type : FlightDataType.ALL_TYPES){
builder.withVariable(new Variable(type.getSymbol()));
}
@ -312,12 +333,16 @@ public class CustomExpression implements Cloneable{
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;
}
@ -341,14 +366,14 @@ public class CustomExpression implements Cloneable{
* 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;
}
@ -396,10 +421,13 @@ public class CustomExpression implements Cloneable{
*/
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);
@ -442,7 +470,7 @@ public class CustomExpression implements Cloneable{
@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

View File

@ -3,13 +3,16 @@ package net.sf.openrocket.simulation.customexpression;
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) {
@ -25,10 +28,17 @@ public class CustomExpressionSimulationListener extends AbstractSimulationListen
// 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;
}
}

View File

@ -23,7 +23,6 @@ public class IndexExpression extends CustomExpression {
setExpression(indexText);
this.setName("");
this.setSymbol(typeText);
}
@Override
@ -35,7 +34,10 @@ public class IndexExpression extends CustomExpression {
}
// 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);

View File

@ -40,7 +40,6 @@ public class RangeExpression extends CustomExpression {
this.expression = variableType+startTime+endTime; // this is used just for generating the hash
log.info("New range expression, "+startTime + " to "+endTime);
}
/*
@ -73,7 +72,9 @@ public class RangeExpression extends CustomExpression {
}
// 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);

View File

@ -1,5 +1,7 @@
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;
@ -8,6 +10,7 @@ import net.sf.openrocket.motor.MotorInstance;
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;
@ -67,7 +70,13 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
return false;
}
/**
* Return an array of any flight data types this listener creates.
*/
@Override
public FlightDataType[] getFlightDataTypes(){
return new FlightDataType[] {};
}
//// SimulationEventListener ////

View File

@ -1,9 +1,12 @@
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;
@ -64,4 +67,5 @@ public interface SimulationComputationListener extends SimulationListener {
public double postSimpleThrustCalculation(SimulationStatus status, double thrust) throws SimulationException;
public FlightDataType[] getFlightDataTypes();
}

View File

@ -1,9 +1,12 @@
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;
@ -56,6 +59,10 @@ public interface SimulationEventListener {
*/
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice)
throws SimulationException;
public FlightDataType[] getFlightDataTypes();
}

View File

@ -1,5 +1,8 @@
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;
@ -76,4 +79,11 @@ public interface SimulationListener {
*/
public boolean isSystemListener();
/**
* Return a list of any flight data types this listener creates.
*/
public FlightDataType[] getFlightDataTypes();
}

View File

@ -0,0 +1,129 @@
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;
}
}