Merge pull request #676 from teyrana/509-fix-component-analysis

[fixes #509] Fix Component Analysis Dialog
This commit is contained in:
Daniel Williams 2020-07-04 12:28:12 -04:00 committed by GitHub
commit 8c129dcc10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 431 additions and 319 deletions

View File

@ -700,9 +700,10 @@ componentanalysisdlg.lbl.machnumber = Mach number:
componentanalysisdlg.lbl.rollrate = Roll rate: componentanalysisdlg.lbl.rollrate = Roll rate:
componentanalysisdlg.lbl.activestages = Active stages: componentanalysisdlg.lbl.activestages = Active stages:
componentanalysisdlg.lbl.motorconf = Motor configuration: componentanalysisdlg.lbl.motorconf = Motor configuration:
componentanalysisdlg.TabStability.Col = Component componentanalysisdlg.TabStability.Col = Component Name
componentanalysisdlg.TabStability.Col.CG = CG componentanalysisdlg.TabStability.Col.EachMass = Instance Mass
componentanalysisdlg.TabStability.Col.Mass = Mass componentanalysisdlg.TabStability.Col.AllMass = Aggregate Mass
componentanalysisdlg.TabStability.Col.CG = Aggregate CG Location
componentanalysisdlg.TabStability.Col.CP = CP componentanalysisdlg.TabStability.Col.CP = CP
componentanalysisdlg.TabStability = Stability componentanalysisdlg.TabStability = Stability
componentanalysisdlg.TabStability.ttip = Stability information componentanalysisdlg.TabStability.ttip = Stability information

View File

@ -2,14 +2,7 @@ package net.sf.openrocket.aerodynamics;
import static net.sf.openrocket.util.MathUtil.pow2; import static net.sf.openrocket.util.MathUtil.pow2;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc;
import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc; import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc;
@ -27,7 +20,7 @@ import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.PolyInterpolator; import net.sf.openrocket.util.PolyInterpolator;
import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.Reflection;
import net.sf.openrocket.util.Transformation;
/** /**
* An aerodynamic calculator that uses the extended Barrowman method to * An aerodynamic calculator that uses the extended Barrowman method to
@ -64,7 +57,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
public Coordinate getCP(FlightConfiguration configuration, FlightConditions conditions, public Coordinate getCP(FlightConfiguration configuration, FlightConditions conditions,
WarningSet warnings) { WarningSet warnings) {
checkCache(configuration); checkCache(configuration);
AerodynamicForces forces = calculateNonAxialForces(configuration, conditions, null, warnings); AerodynamicForces forces = calculateNonAxialForces(configuration, conditions, warnings);
return forces.getCP(); return forces.getCP();
} }
@ -72,60 +65,102 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
@Override @Override
public Map<RocketComponent, AerodynamicForces> getForceAnalysis(FlightConfiguration configuration, public Map<RocketComponent, AerodynamicForces> getForceAnalysis(FlightConfiguration configuration,
FlightConditions conditions, WarningSet warnings) { FlightConditions conditions,
checkCache(configuration); WarningSet warnings) {
if (calcMap == null){
AerodynamicForces f; buildCalcMap(configuration);
Map<RocketComponent, AerodynamicForces> map =
new LinkedHashMap<RocketComponent, AerodynamicForces>();
// Add all components to the map
for (RocketComponent component : configuration.getActiveComponents()) {
// Skip non-aerodynamic components
if (!component.isAerodynamic())
continue;
f = new AerodynamicForces();
f.setComponent(component);
map.put(component, f);
} }
InstanceMap instMap = configuration.getActiveInstances();
Map<RocketComponent, AerodynamicForces> eachMap = new LinkedHashMap<>();
Map<RocketComponent, AerodynamicForces> assemblyMap = new LinkedHashMap<>();
// Calculate non-axial force data // Calculate non-axial force data
AerodynamicForces total = calculateNonAxialForces(configuration, conditions, map, warnings); calculateForceAnalysis(conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings);
// Calculate friction data // Calculate friction data
total.setFrictionCD(calculateFrictionDrag(configuration, conditions, map, warnings)); AerodynamicForces rocketForces = assemblyMap.get(configuration.getRocket());
total.setPressureCD(calculatePressureDrag(configuration, conditions, map, warnings)); rocketForces.setFrictionCD(calculateFrictionDrag(configuration, conditions, eachMap, warnings));
total.setBaseCD(calculateBaseDrag(configuration, conditions, map, warnings)); rocketForces.setPressureCD(calculatePressureDrag(configuration, conditions, eachMap, warnings));
rocketForces.setBaseCD(calculateBaseDrag(configuration, conditions, eachMap, warnings));
total.setComponent(configuration.getRocket());
map.put(total.getComponent(), total); Map<RocketComponent, AerodynamicForces> finalMap = new LinkedHashMap<>();
for(final RocketComponent comp : instMap.keySet()){
for (RocketComponent c : map.keySet()) { AerodynamicForces f;
f = map.get(c); if(comp instanceof ComponentAssembly) {
if (Double.isNaN(f.getBaseCD()) && Double.isNaN(f.getPressureCD()) && f = assemblyMap.get(comp);
Double.isNaN(f.getFrictionCD())) } else if(comp.isAerodynamic()){
f = eachMap.get(comp);
}else{
continue; continue;
}
if (Double.isNaN(f.getCNa())){
f.setCNa(0.0);
}
if (f.getCP().isNaN()) {
f.setCP(Coordinate.ZERO);
}
if (Double.isNaN(f.getBaseCD())) if (Double.isNaN(f.getBaseCD()))
f.setBaseCD(0); f.setBaseCD(0);
if (Double.isNaN(f.getPressureCD())) if (Double.isNaN(f.getPressureCD()))
f.setPressureCD(0); f.setPressureCD(0);
if (Double.isNaN(f.getFrictionCD())) if (Double.isNaN(f.getFrictionCD()))
f.setFrictionCD(0); f.setFrictionCD(0);
f.setCD(f.getBaseCD() + f.getPressureCD() + f.getFrictionCD()); f.setCD(f.getBaseCD() + f.getPressureCD() + f.getFrictionCD());
f.setCaxial(calculateAxialDrag(conditions, f.getCD())); f.setCaxial(calculateAxialDrag(conditions, f.getCD()));
finalMap.put(comp, f);
} }
return map; return finalMap;
} }
private AerodynamicForces calculateForceAnalysis( FlightConditions conds,
RocketComponent comp,
InstanceMap instances,
Map<RocketComponent, AerodynamicForces> eachForces,
Map<RocketComponent, AerodynamicForces> assemblyForces,
WarningSet warnings)
{
// forces for this component, and all forces below it, in the rocket-tree
// => regardless `if(comp isinstance ComponentAssembly)`, or not.
AerodynamicForces aggregateForces = new AerodynamicForces().zero();
aggregateForces.setComponent(comp);
// forces for this component, _only_
if(comp.isAerodynamic()) {
RocketComponentCalc calcObj = calcMap.get(comp);
if (null == calcObj) {
throw new NullPointerException("Could not find a CalculationObject for aerodynamic Component!: " + comp.getComponentName());
} else {
List<InstanceContext> contextList = instances.get(comp);
// across every instance of this component:
AerodynamicForces compForces = calculateComponentNonAxialForces(conds, comp, calcObj, contextList, warnings);
eachForces.put(comp, compForces);
aggregateForces.merge(compForces);
}
}
for( RocketComponent child : comp.getChildren()) {
// forces particular to each component
AerodynamicForces childForces = calculateForceAnalysis(conds, child, instances, eachForces, assemblyForces, warnings);
if(null != childForces) {
aggregateForces.merge(childForces);
}
}
assemblyForces.put(comp, aggregateForces);
return eachForces.get(comp);
}
@Override @Override
public AerodynamicForces getAerodynamicForces(FlightConfiguration configuration, public AerodynamicForces getAerodynamicForces(FlightConfiguration configuration,
FlightConditions conditions, WarningSet warnings) { FlightConditions conditions, WarningSet warnings) {
@ -135,7 +170,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
warnings = ignoreWarningSet; warnings = ignoreWarningSet;
// Calculate non-axial force data // Calculate non-axial force data
AerodynamicForces total = calculateNonAxialForces(configuration, conditions, null, warnings); AerodynamicForces total = calculateNonAxialForces(configuration, conditions, warnings);
// Calculate friction data // Calculate friction data
total.setFrictionCD(calculateFrictionDrag(configuration, conditions, null, warnings)); total.setFrictionCD(calculateFrictionDrag(configuration, conditions, null, warnings));
@ -150,77 +185,79 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
calculateDampingMoments(configuration, conditions, total); calculateDampingMoments(configuration, conditions, total);
total.setCm(total.getCm() - total.getPitchDampingMoment()); total.setCm(total.getCm() - total.getPitchDampingMoment());
total.setCyaw(total.getCyaw() - total.getYawDampingMoment()); total.setCyaw(total.getCyaw() - total.getYawDampingMoment());
return total; return total;
} }
private AerodynamicForces calculateComponentNonAxialForces( FlightConditions conditions,
RocketComponent comp,
RocketComponentCalc calcObj,
List<InstanceContext> contextList,
WarningSet warnings)
{
// across every instance of this component:
final AerodynamicForces componentForces = new AerodynamicForces().zero();
// iterate across component instances
for(InstanceContext context: contextList ) {
// specific to this _instance_ of this component:
AerodynamicForces instanceForces = new AerodynamicForces().zero();
calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings);
Coordinate cp_inst = instanceForces.getCP();
Coordinate cp_abs = context.transform.transform(cp_inst);
cp_abs = cp_abs.setY(0.0).setZ(0.0);
instanceForces.setCP(cp_abs);
double CN_instanced = instanceForces.getCN();
instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength());
componentForces.merge(instanceForces);
}
componentForces.setComponent(comp);
return componentForces;
}
/** /**
* Perform the actual CP calculation. * Perform the actual CP calculation.
*/ */
private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions, private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions, WarningSet warnings) {
Map<RocketComponent, AerodynamicForces> calculators, WarningSet warnings) {
checkCache(configuration); checkCache(configuration);
if (warnings == null) if (warnings == null)
warnings = ignoreWarningSet; warnings = ignoreWarningSet;
if (conditions.getAOA() > 17.5 * Math.PI / 180) if (conditions.getAOA() > 17.5 * Math.PI / 180)
warnings.add(new Warning.LargeAOA(conditions.getAOA())); warnings.add(new Warning.LargeAOA(conditions.getAOA()));
if (calcMap == null) if (calcMap == null)
buildCalcMap(configuration); buildCalcMap(configuration);
if( ! isContinuous( configuration.getRocket() ) ){ if( ! isContinuous( configuration.getRocket() ) ){
warnings.add( Warning.DIAMETER_DISCONTINUITY); warnings.add( Warning.DIAMETER_DISCONTINUITY);
} }
final InstanceMap imap = configuration.getActiveInstances(); final InstanceMap imap = configuration.getActiveInstances();
// across the _entire_ assembly -- like a rocket, or a stage
final AerodynamicForces assemblyForces= new AerodynamicForces().zero(); final AerodynamicForces assemblyForces= new AerodynamicForces().zero();
// System.err.println("======================================"); for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> mapEntry: imap.entrySet() ) {
// System.err.println("==== Iterating through components ===="); final RocketComponent comp = mapEntry.getKey();
// iterate through all components final List<InstanceContext> contextList = mapEntry.getValue();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent comp = entry.getKey();
RocketComponentCalc calcObj = calcMap.get(comp); RocketComponentCalc calcObj = calcMap.get(comp);
// System.err.println("comp=" + comp);
if (null != calcObj) { if (null != calcObj) {
// iterate across component instances // calculated across all component instances
final ArrayList<InstanceContext> contextList = entry.getValue(); final AerodynamicForces componentForces = calculateComponentNonAxialForces(conditions, comp, calcObj, contextList, warnings);
for(InstanceContext context: contextList ) { assemblyForces.merge(componentForces);
AerodynamicForces instanceForces = new AerodynamicForces().zero();
// System.err.println(String.format("@ [%s]:[%d/%d]", comp.getName(), context.instanceNumber + 1, comp.getInstanceCount()));
// System.err.println("_________ inst/ctxt: xrotation: " + context.transform.getXrotation());
calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings);
Coordinate cp_inst = instanceForces.getCP();
Coordinate cp_abs = context.transform.transform(cp_inst);
cp_abs = cp_abs.setY(0.0).setZ(0.0);
// if( 1e-6 < cp_inst.weight) {
// System.err.println("_________ cp:inst: (rel): " + cp_inst.toString());
// System.err.println("_________ cp:inst: (abs): " + cp_abs.toString());
// }
instanceForces.setCP(cp_abs);
double CN_instanced = instanceForces.getCN();
instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength());
assemblyForces.merge(instanceForces);
}
} }
} }
// System.err.println("____ cp:asbly: " + assemblyForces.getCP().toString());
return assemblyForces; return assemblyForces;
} }
@ -230,7 +267,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
} }
private boolean testIsContinuous( final RocketComponent treeRoot ){ private boolean testIsContinuous( final RocketComponent treeRoot ){
Queue<RocketComponent> queue = new LinkedList<RocketComponent>(); Queue<RocketComponent> queue = new LinkedList<>();
queue.addAll(treeRoot.getChildren()); queue.addAll(treeRoot.getChildren());
boolean isContinuous = true; boolean isContinuous = true;
@ -279,7 +316,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
* @param conditions Flight conditions taken into account * @param conditions Flight conditions taken into account
* @param map ? * @param map ?
* @param set Set to handle * @param set Set to handle
* @return * @return friction drag for entire rocket
*/ */
private double calculateFrictionDrag(FlightConfiguration configuration, FlightConditions conditions, private double calculateFrictionDrag(FlightConfiguration configuration, FlightConditions conditions,
Map<RocketComponent, AerodynamicForces> map, WarningSet set) { Map<RocketComponent, AerodynamicForces> map, WarningSet set) {
@ -510,12 +547,12 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
* *
* @param configuration Rocket configuration * @param configuration Rocket configuration
* @param conditions Flight conditions taken into account * @param conditions Flight conditions taken into account
* @param map ? * @param forceMap
* @param set Set to handle * @param warningSet all current warnings
* @return * @return
*/ */
private double calculatePressureDrag(FlightConfiguration configuration, FlightConditions conditions, private double calculatePressureDrag(FlightConfiguration configuration, FlightConditions conditions,
Map<RocketComponent, AerodynamicForces> map, WarningSet warnings) { Map<RocketComponent, AerodynamicForces> forceMap, WarningSet warningSet) {
double stagnation, base, total; double stagnation, base, total;
@ -538,11 +575,11 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
// Pressure fore drag // Pressure fore drag
double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base, double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base,
warnings); warningSet);
total += cd; total += cd;
if (map != null) { if (forceMap != null) {
map.get(c).setPressureCD(cd); forceMap.get(c).setPressureCD(cd);
} }
if(c.isCDOverridden()) if(c.isCDOverridden())
@ -562,8 +599,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
cd = stagnation * area / conditions.getRefArea(); cd = stagnation * area / conditions.getRefArea();
total += cd; total += cd;
if (map != null) { if (forceMap != null) {
map.get(c).setPressureCD(map.get(c).getPressureCD() + cd); forceMap.get(c).setPressureCD(forceMap.get(c).getPressureCD() + cd);
} }
} }
} }
@ -580,7 +617,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
* @param configuration Rocket configuration * @param configuration Rocket configuration
* @param conditions Flight conditions taken into account * @param conditions Flight conditions taken into account
* @param map ? * @param map ?
* @param set Set to handle * @param warnings all current warnings
* @return * @return
*/ */
private double calculateBaseDrag(FlightConfiguration configuration, FlightConditions conditions, private double calculateBaseDrag(FlightConfiguration configuration, FlightConditions conditions,
@ -752,10 +789,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
total.setPitchDampingMoment(MathUtil.sign(pitchRate) * pitchDampingMomentMagnitude); total.setPitchDampingMoment(MathUtil.sign(pitchRate) * pitchDampingMomentMagnitude);
total.setYawDampingMoment(MathUtil.sign(yawRate) * yawDampingMomentMagnitude); total.setYawDampingMoment(MathUtil.sign(yawRate) * yawDampingMomentMagnitude);
} }
// TODO: MEDIUM: Are the rotation etc. being added correctly? sin/cos theta?
private double getDampingMultiplier(FlightConfiguration configuration, FlightConditions conditions, private double getDampingMultiplier(FlightConfiguration configuration, FlightConditions conditions,
double cgx) { double cgx) {
if (cacheDiameter < 0) { if (cacheDiameter < 0) {
@ -811,18 +845,15 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
private void buildCalcMap(FlightConfiguration configuration) { private void buildCalcMap(FlightConfiguration configuration) {
Iterator<RocketComponent> iterator; calcMap = new HashMap<>();
calcMap = new HashMap<RocketComponent, RocketComponentCalc>();
// because this is not a per-instance iteration... this usage of 'getActiveComponents' is probably fine.
for (RocketComponent comp: configuration.getActiveComponents()) { for (RocketComponent comp: configuration.getActiveComponents()) {
if (!comp.isAerodynamic()) if (!comp.isAerodynamic())
continue; continue;
RocketComponentCalc calcObj = (RocketComponentCalc) Reflection.construct(BARROWMAN_PACKAGE, comp, BARROWMAN_SUFFIX, comp); RocketComponentCalc calcObj = (RocketComponentCalc) Reflection.construct(BARROWMAN_PACKAGE, comp, BARROWMAN_SUFFIX, comp);
//String isNull = (null==calcObj?"null":"valid");
//System.err.println(" >> At component: "+c.getName() +"=="+c.getID()+". CalcObj is "+isNull);
calcMap.put(comp, calcObj ); calcMap.put(comp, calcObj );
} }
} }

View File

@ -0,0 +1,40 @@
package net.sf.openrocket.masscalc;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate;
public class CMAnalysisEntry {
public CMAnalysisEntry(final Object _src) {
source = _src;
if (source instanceof RocketComponent){
name = ((RocketComponent)source).getName();
}else if( source instanceof Motor ){
name = ((Motor) source).getDesignation();
}else {
name = null;
}
eachMass = Double.NaN;
totalCM = Coordinate.NaN;
}
public String name;
public Object source;
public double eachMass;
public Coordinate totalCM;
public void updateEachMass(final double newMass) {
if (Double.isNaN(eachMass)) {
eachMass = newMass;
}
}
public void updateAverageCM(final Coordinate newCM) {
if (this.totalCM.isNaN()) {
this.totalCM = newCM;
} else {
this.totalCM = totalCM.average(newCM);
}
}
}

View File

@ -1,12 +1,11 @@
package net.sf.openrocket.masscalc; package net.sf.openrocket.masscalc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.*;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation; import net.sf.openrocket.util.Transformation;
@ -73,8 +72,8 @@ public class MassCalculation {
} }
} }
public MassCalculation copy( final RocketComponent _root, final Transformation _transform ){ public MassCalculation copy( final RocketComponent _root, final Transformation _transform){
return new MassCalculation( this.type, this.config, this.simulationTime, _root, _transform ); return new MassCalculation( this.type, this.config, this.simulationTime, _root, _transform, this.analysisMap);
} }
public Coordinate getCM() { public Coordinate getCM() {
@ -110,13 +109,17 @@ public class MassCalculation {
return (int) (this.centerOfMass.hashCode()); return (int) (this.centerOfMass.hashCode());
} }
public MassCalculation( final Type _type, final FlightConfiguration _config, final double _time, final RocketComponent _root, final Transformation _transform) { public MassCalculation( final Type _type, final FlightConfiguration _config, final double _time,
final RocketComponent _root, final Transformation _transform,
Map<Integer, CMAnalysisEntry> _map)
{
type = _type; type = _type;
config = _config; config = _config;
simulationTime = _time; simulationTime = _time;
root = _root; root = _root;
transform = _transform; transform = _transform;
analysisMap = _map;
reset(); reset();
} }
@ -167,8 +170,10 @@ public class MassCalculation {
// center-of-mass AND moment-of-inertia data. // center-of-mass AND moment-of-inertia data.
final ArrayList<RigidBody> bodies = new ArrayList<RigidBody>(); final ArrayList<RigidBody> bodies = new ArrayList<RigidBody>();
String prefix = ""; String prefix = "";
Map<Integer, CMAnalysisEntry> analysisMap;
// =========== Private Instance Functions ======================== // =========== Private Instance Functions ========================
@ -183,8 +188,8 @@ public class MassCalculation {
if( motorConfig.isEmpty() ){ if( motorConfig.isEmpty() ){
return this; return this;
} }
final double mountXPosition = root.getPosition().x; final double mountXPosition = root.getPosition().x;
final int instanceCount = root.getInstanceCount(); final int instanceCount = root.getInstanceCount();
@ -210,10 +215,10 @@ public class MassCalculation {
eachMass = eachMotorMass - eachCasingMass; eachMass = eachMotorMass - eachCasingMass;
eachCMx = (eachMotorCMx*eachMotorMass - eachCasingCMx*eachCasingMass)/eachMass; eachCMx = (eachMotorCMx*eachMotorMass - eachCasingCMx*eachCasingMass)/eachMass;
} }
// System.err.println(String.format("%-40s|Motor: %s.... Mass @%f = %.6f", prefix, motorConfig.toDescription(), simulationTime, eachMass )); // System.err.println(String.format("%-40s|Motor: %s.... Mass @%f = %.6f", prefix, motorConfig.toDescription(), simulationTime, eachMass ));
// coordinates in rocket frame; Ir, It about CoM. // coordinates in rocket frame; Ir, It about CoM.
final Coordinate clusterLocalCM = new Coordinate( mountXPosition + motorXPosition + eachCMx, 0, 0, eachMass*instanceCount); final Coordinate clusterLocalCM = new Coordinate( mountXPosition + motorXPosition + eachCMx, 0, 0, eachMass*instanceCount);
@ -232,7 +237,17 @@ public class MassCalculation {
final Coordinate clusterCM = transform.transform( clusterLocalCM ); final Coordinate clusterCM = transform.transform( clusterLocalCM );
addMass( clusterCM ); addMass( clusterCM );
if(null != this.analysisMap) {
CMAnalysisEntry entry = analysisMap.get(motor.getDesignation());
if (null == entry){
entry = new CMAnalysisEntry(motor);
analysisMap.put(motor.getDesignation().hashCode(), entry);
}
entry.updateEachMass(eachMass);
entry.updateAverageCM(clusterCM);
}
RigidBody clusterMOI = new RigidBody( clusterCM, clusterIr, clusterIt, clusterIt ); RigidBody clusterMOI = new RigidBody( clusterCM, clusterIr, clusterIt, clusterIt );
addInertia( clusterMOI ); addInertia( clusterMOI );
@ -273,7 +288,14 @@ public class MassCalculation {
// if( this.config.isComponentActive(component) ){ // if( this.config.isComponentActive(component) ){
// System.err.println(String.format( "%s>>[%s]....", prefix, component.getName())); // System.err.println(String.format( "%s>>[%s]....", prefix, component.getName()));
// } // }
if(null != analysisMap) {
if (this.config.isComponentActive(component) && (! analysisMap.containsKey(component.hashCode()))){
CMAnalysisEntry entry = new CMAnalysisEntry(component);
analysisMap.put(component.hashCode(), entry);
}
}
// iterate over the aggregated instances for the whole tree. // iterate over the aggregated instances for the whole tree.
MassCalculation children = this.copy(component, parentTransform ); MassCalculation children = this.copy(component, parentTransform );
for( int currentInstanceNumber = 0; currentInstanceNumber < instanceCount; ++currentInstanceNumber) { for( int currentInstanceNumber = 0; currentInstanceNumber < instanceCount; ++currentInstanceNumber) {
@ -288,14 +310,14 @@ public class MassCalculation {
for (RocketComponent child : component.getChildren()) { for (RocketComponent child : component.getChildren()) {
// child data, relative to rocket reference frame // child data, relative to rocket reference frame
MassCalculation eachChild = copy( child, currentTransform); MassCalculation eachChild = copy(child, currentTransform);
eachChild.prefix = prefix + "...."; eachChild.prefix = prefix + "....";
eachChild.calculateStructure(); eachChild.calculateStructure();
// accumulate children's data // accumulate children's data
children.merge( eachChild ); children.merge( eachChild );
} }
} }
if( MIN_MASS < children.getMass() ) { if( MIN_MASS < children.getMass() ) {
@ -320,6 +342,20 @@ public class MassCalculation {
// mass data for *this component only* in the rocket-frame // mass data for *this component only* in the rocket-frame
compCM = parentTransform.transform( compCM.add(component.getPosition()) ); compCM = parentTransform.transform( compCM.add(component.getPosition()) );
this.addMass( compCM ); this.addMass( compCM );
if(null != analysisMap){
if( component instanceof ComponentAssembly) {
// For ComponentAssemblies, record the _assembly_ information
CMAnalysisEntry entry = analysisMap.get(component.hashCode());
entry.updateEachMass(children.getMass() / component.getInstanceCount());
entry.updateAverageCM(this.centerOfMass);
}else{
// For actual components, record the mass of the component, and disregard children
CMAnalysisEntry entry = analysisMap.get(component.hashCode());
entry.updateEachMass(compCM.weight);
entry.updateAverageCM(compCM);
}
}
double compIx = component.getRotationalUnitInertia() * compCM.weight; double compIx = component.getRotationalUnitInertia() * compCM.weight;
double compIt = component.getLongitudinalUnitInertia() * compCM.weight; double compIt = component.getLongitudinalUnitInertia() * compCM.weight;
@ -331,7 +367,7 @@ public class MassCalculation {
// System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() )); // System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() ));
// } // }
if (component.getOverrideSubcomponents()) { if (component.getOverrideSubcomponents()) {
if (component.isMassOverridden()) { if (component.isMassOverridden()) {
double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS); double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS);
Coordinate newCM = this.getCM().setWeight( newMass ); Coordinate newCM = this.getCM().setWeight( newMass );
@ -355,29 +391,29 @@ public class MassCalculation {
} }
MassCalculation calculateMotors() { MassCalculation calculateMotors() {
final RocketComponent component = this.root; final RocketComponent component = this.root;
final Transformation parentTransform = this.transform; final Transformation parentTransform = this.transform;
final int instanceCount = component.getInstanceCount(); final int instanceCount = component.getInstanceCount();
Coordinate[] instanceLocations = component.getInstanceLocations(); Coordinate[] instanceLocations = component.getInstanceLocations();
// // vvv DEBUG // // vvv DEBUG
// if( this.config.isComponentActive(component) ){ // if( this.config.isComponentActive(component) ){
// System.err.println(String.format( "%s[%s]....", prefix, component.getName())); // System.err.println(String.format( "%s[%s]....", prefix, component.getName()));
// } // }
if (component.isMotorMount()) { if (component.isMotorMount()) {
MassCalculation propellant = this.copy(component, parentTransform); MassCalculation propellant = this.copy(component, parentTransform);
propellant.calculateMountData(); propellant.calculateMountData();
this.merge( propellant ); this.merge( propellant );
// // vvv DEBUG // // vvv DEBUG
// if( 0 < propellant.getMass() ) { // if( 0 < propellant.getMass() ) {
// System.err.println(String.format( "%s........++ propellantData: %s", prefix, propellant.toCMDebug())); // System.err.println(String.format( "%s........++ propellantData: %s", prefix, propellant.toCMDebug()));
// } // }
} }
// iterate over the aggregated instances for the whole tree. // iterate over the aggregated instances for the whole tree.
@ -395,7 +431,7 @@ public class MassCalculation {
// accumulate children's data // accumulate children's data
children.merge( eachChild ); children.merge( eachChild );
} }
} }
if( MIN_MASS < children.getMass() ) { if( MIN_MASS < children.getMass() ) {
@ -407,7 +443,7 @@ public class MassCalculation {
// if( this.config.isComponentActive(component) && 0 < this.getMass() ) { // if( this.config.isComponentActive(component) && 0 < this.getMass() ) {
// System.err.println(String.format( "%s....<< return assemblyData: %s (tree @%s)", prefix, this.toCMDebug(), component.getName() )); // System.err.println(String.format( "%s....<< return assemblyData: %s (tree @%s)", prefix, this.toCMDebug(), component.getName() ));
// } // }
// ^^^ DEBUG // // ^^^ DEBUG
return this; return this;
} }
@ -427,9 +463,8 @@ public class MassCalculation {
It += eachGlobal.Iyy; It += eachGlobal.Iyy;
} }
return new RigidBody( centerOfMass, Ir, It, It ); return new RigidBody( centerOfMass, Ir, It, It );
} }
} }

View File

@ -7,10 +7,7 @@ import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.*;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Monitorable;
import net.sf.openrocket.util.Transformation;
public class MassCalculator implements Monitorable { public class MassCalculator implements Monitorable {
@ -104,7 +101,7 @@ public class MassCalculator implements Monitorable {
public static RigidBody calculate( final MassCalculation.Type _type, final SimulationStatus status ){ public static RigidBody calculate( final MassCalculation.Type _type, final SimulationStatus status ){
final FlightConfiguration config = status.getConfiguration(); final FlightConfiguration config = status.getConfiguration();
final double time = status.getSimulationTime(); final double time = status.getSimulationTime();
MassCalculation calculation= new MassCalculation( _type, config, time, config.getRocket(), Transformation.IDENTITY); MassCalculation calculation= new MassCalculation( _type, config, time, config.getRocket(), Transformation.IDENTITY, null);
calculation.calculateAssembly(); calculation.calculateAssembly();
RigidBody result = calculation.calculateMomentOfInertia(); RigidBody result = calculation.calculateMomentOfInertia();
@ -112,55 +109,51 @@ public class MassCalculator implements Monitorable {
} }
// convenience wrapper -- use this to implicitly create a plain MassCalculation object with common parameters // convenience wrapper -- use this to implicitly create a plain MassCalculation object with common parameters
public static RigidBody calculate( final MassCalculation.Type _type, final FlightConfiguration _config, double _time ){ public static RigidBody calculate( final MassCalculation.Type _type, final FlightConfiguration _config, double _time){
MassCalculation calculation = new MassCalculation( _type, _config, _time, _config.getRocket(), Transformation.IDENTITY); MassCalculation calculation = new MassCalculation( _type, _config, _time, _config.getRocket(), Transformation.IDENTITY, null);
calculation.calculateAssembly(); calculation.calculateAssembly();
return calculation.calculateMomentOfInertia(); return calculation.calculateMomentOfInertia();
} }
/** /**
* Compute an analysis of the per-component CG's of the provided configuration. * Compute an analysis of the per-component CG's of the provided configuration.
* The returned map will contain an entry for each physical rocket component (not stages) * The returned map will contain an entry for each physical rocket component (not stages)
* with its corresponding (best-effort) CG. Overriding of subcomponents is ignored. * with its corresponding (best-effort) CG. Overriding of subcomponents is ignored.
* The CG of the entire configuration with motors is stored in the entry with the corresponding * The CG of the entire configuration with motors is stored in the entry with the corresponding
* Rocket as the key. * Rocket as the key.
* *
* Deprecated: * Deprecated:
* This function is fundamentally broken, because it asks for a calculation which ignores instancing. * This function is fundamentally broken, because it asks for a calculation which ignores instancing.
* This function will work with simple rockets, but will be misleading or downright wrong for others. * This function will work with simple rockets, but will be misleading or downright wrong for others.
* *
* This is a problem with using a single-typed map: * This is a problem with using a single-typed map:
* [1] multiple instances of components are not allowed, and must be merged. * [1] multiple instances of components are not allowed, and must be merged.
* [2] propellant / motor data does not have a corresponding RocketComponent. * [2] propellant / motor data does not have a corresponding RocketComponent.
* ( or mount-data collides with motor-data ) * ( or mount-data collides with motor-data )
* *
* @param configuration the rocket configuration * @return a list of CG coordinates for every instance of this component
* @return a map from each rocket component to its corresponding CG.
*/ */
@Deprecated public static Map<Integer,CMAnalysisEntry> getCMAnalysis(FlightConfiguration config) {
public Map<RocketComponent, Coordinate> getCGAnalysis(FlightConfiguration configuration) {
// revalidateCache(configuration);
Map<RocketComponent, Coordinate> map = new HashMap<RocketComponent, Coordinate>();
Coordinate rocketCG = Coordinate.ZERO;
for (RocketComponent comp : configuration.getActiveComponents()) {
Coordinate[] cgs = comp.toAbsolute(comp.getCG());
Coordinate stageCG = Coordinate.NUL;
for (Coordinate cg : cgs) {
stageCG = stageCG.average(cg);
}
map.put(comp, stageCG);
rocketCG.average( stageCG);
}
map.put(configuration.getRocket(), rocketCG );
return map;
}
Map<Integer,CMAnalysisEntry> analysisMap = new HashMap<>();
MassCalculation calculation = new MassCalculation(
MassCalculation.Type.LAUNCH,
config,
Motor.PSEUDO_TIME_LAUNCH,
config.getRocket(),
Transformation.IDENTITY,
analysisMap);
calculation.calculateAssembly();
CMAnalysisEntry totals = new CMAnalysisEntry(config.getRocket());
totals.totalCM = calculation.centerOfMass;
totals.eachMass = calculation.centerOfMass.weight;
analysisMap.put(config.getRocket().hashCode(), totals);
return analysisMap;
}
////////////////// Mass property calculations /////////////////// ////////////////// Mass property calculations ///////////////////
@Override @Override

View File

@ -202,23 +202,16 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
} }
public Collection<RocketComponent> getAllComponents() { public Collection<RocketComponent> getAllComponents() {
Queue<RocketComponent> toProcess = new ArrayDeque<RocketComponent>(); List<RocketComponent> traversalOrder = new ArrayList<RocketComponent>();
toProcess.offer(this.rocket); recurseAllComponentsDepthFirst(this.rocket,traversalOrder);
return traversalOrder;
ArrayList<RocketComponent> toReturn = new ArrayList<>(); }
while (!toProcess.isEmpty()) { private void recurseAllComponentsDepthFirst(RocketComponent comp, List<RocketComponent> traversalOrder){
RocketComponent comp = toProcess.poll(); traversalOrder.add(comp);
for( RocketComponent child : comp.getChildren()){
toReturn.add(comp); recurseAllComponentsDepthFirst(child, traversalOrder);
for (RocketComponent child : comp.getChildren()) {
if (!(child instanceof AxialStage)) {
toProcess.offer(child);
}
}
} }
return toReturn;
} }
/** Returns all the components on core stages (i.e. centerline) /** Returns all the components on core stages (i.e. centerline)
@ -401,10 +394,10 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
} }
private void updateStages() { private void updateStages() {
if (this.rocket.getStageCount() == this.stages.size()) { if (this.rocket.getStageCount() == this.stages.size()) {
return; return;
} }
this.stages.clear(); this.stages.clear();
for (AxialStage curStage : this.rocket.getStageList()) { for (AxialStage curStage : this.rocket.getStageList()) {

View File

@ -341,12 +341,9 @@ public class BarrowmanCalculatorTest {
final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods); final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods);
final Coordinate cpPods = calcPods.getCP(configPods, conditionsPods, warningsPods); final Coordinate cpPods = calcPods.getCP(configPods, conditionsPods, warningsPods);
System.out.printf("with pods %s\n", cpPods.toString());
System.out.printf("without pods %s\n", cpNoPods.toString());
assertEquals(" Alpha III With Pods rocket cp x value is incorrect:", cpNoPods.x - 0.002788761352, cpPods.x, EPSILON); assertEquals(" Alpha III With Pods rocket cp x value is incorrect:", cpNoPods.x - 0.002788761352, cpPods.x, EPSILON);
assertEquals(" Alpha III With Pods rocket cp y value is incorrect:", cpNoPods.y, cpPods.y, EPSILON); assertEquals(" Alpha III With Pods rocket cp y value is incorrect:", cpNoPods.y, cpPods.y, EPSILON);
assertEquals(" Alpha III With Pods rocket cp z value is incorrect:", cpNoPods.z, cpPods.z, EPSILON); assertEquals(" Alpha III With Pods rocket cp z value is incorrect:", cpNoPods.z, cpPods.z, EPSILON);
assertEquals(" Alpha III With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON); assertEquals(" Alpha III With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON);
} }
} }

View File

@ -19,7 +19,6 @@ import java.util.Vector;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -54,21 +53,22 @@ import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.CMAnalysisEntry;
import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.*;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.StateChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ComponentAnalysisDialog extends JDialog implements StateChangeListener { public class ComponentAnalysisDialog extends JDialog implements StateChangeListener {
private static final Logger log = LoggerFactory.getLogger(ComponentAnalysisDialog.class);
private static final long serialVersionUID = 9131240570600307935L; private static final long serialVersionUID = 9131240570600307935L;
private static ComponentAnalysisDialog singletonDialog = null; private static ComponentAnalysisDialog singletonDialog = null;
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
@ -80,18 +80,16 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
private final JToggleButton worstToggle; private final JToggleButton worstToggle;
private boolean fakeChange = false; private boolean fakeChange = false;
private AerodynamicCalculator aerodynamicCalculator; private AerodynamicCalculator aerodynamicCalculator;
private final MassCalculator massCalculator = new MassCalculator();
private final ColumnTableModel cpTableModel; private final ColumnTableModel longitudeStabilityTableModel;
private final ColumnTableModel dragTableModel; private final ColumnTableModel dragTableModel;
private final ColumnTableModel rollTableModel; private final ColumnTableModel rollTableModel;
private final JList<Object> warningList; private final JList<Object> warningList;
private final List<Object[]> cgData = new ArrayList<Object[]>(); private final List<LongitudinalStabilityRow> stabData = new ArrayList<>();
private final List<AerodynamicForces> dragData = new ArrayList<AerodynamicForces>(); private final List<AerodynamicForces> dragData = new ArrayList<AerodynamicForces>();
private double totalCD = 0;
private final List<AerodynamicForces> rollData = new ArrayList<AerodynamicForces>(); private final List<AerodynamicForces> rollData = new ArrayList<AerodynamicForces>();
@ -186,69 +184,59 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
panel.add(tabbedPane, "spanx, growx, growy"); panel.add(tabbedPane, "spanx, growx, growy");
// Create the CP data table // Create the Longitudinal Stability (CM vs CP) data table
cpTableModel = new ColumnTableModel( longitudeStabilityTableModel = new ColumnTableModel(
//// Component //// Component
new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) { new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) {
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
Object c = cgData.get(row)[0]; Object c = stabData.get(row).name;
if (c instanceof Rocket) {
return trans.get("componentanalysisdlg.TOTAL");
}
return c.toString(); return c.toString();
} }
@Override @Override
public int getDefaultWidth() { public int getDefaultWidth() {
return 200; return 200;
} }
}, },
new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) { // would be per-instance mass
private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); new Column(trans.get("componentanalysisdlg.TabStability.Col.EachMass") + " (" + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit() + ")") {
final private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
Coordinate cg = (Coordinate) cgData.get(row)[1]; return unit.toString(stabData.get(row).eachMass);
if ( cg == null ) {
return null;
}
return unit.toString(cg.x);
} }
}, },
new Column(trans.get("componentanalysisdlg.TabStability.Col.Mass") + " / " + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit()) { new Column(trans.get("componentanalysisdlg.TabStability.Col.AllMass") + " (" + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit() + ")") {
private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit(); final private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
Coordinate cg = (Coordinate) cgData.get(row)[1]; return unit.toString(stabData.get(row).cm.weight);
if ( cg == null ) {
return null;
}
return unit.toString(cg.weight);
} }
}, },
new Column(trans.get("componentanalysisdlg.TabStability.Col.CP") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) { new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " (" + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit() + ")") {
private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); final private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
AerodynamicForces forces = (AerodynamicForces) cgData.get(row)[2]; return unit.toString(stabData.get(row).cm.x);
if ( forces == null ) { }
return null; },
} new Column(trans.get("componentanalysisdlg.TabStability.Col.CP") + " (" + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit() + ")") {
return unit.toString(forces.getCP().x); final private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
@Override
public Object getValueAt(int row) {
return NOUNIT.toString(stabData.get(row).cpx);
} }
}, },
new Column("<html>C<sub>N<sub>" + ALPHA + "</sub></sub>") { new Column("<html>C<sub>N<sub>" + ALPHA + "</sub></sub>") {
@Override @Override
public Object getValueAt(int row) { public Object getValueAt(int row) {
AerodynamicForces forces = (AerodynamicForces) cgData.get(row)[2]; return NOUNIT.toString(stabData.get(row).cna);
if ( forces == null ) {
return null;
}
return NOUNIT.toString(forces.getCP().weight);
} }
} }
@ -261,15 +249,15 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
@Override @Override
public int getRowCount() { public int getRowCount() {
return cgData.size(); return stabData.size();
} }
}; };
table = new ColumnTable(cpTableModel); table = new ColumnTable(longitudeStabilityTableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setSelectionBackground(Color.LIGHT_GRAY); table.setSelectionBackground(Color.LIGHT_GRAY);
table.setSelectionForeground(Color.BLACK); table.setSelectionForeground(Color.BLACK);
cpTableModel.setColumnWidths(table.getColumnModel()); longitudeStabilityTableModel.setColumnWidths(table.getColumnModel());
table.setDefaultRenderer(Object.class, new CustomCellRenderer()); table.setDefaultRenderer(Object.class, new CustomCellRenderer());
// table.setShowHorizontalLines(false); // table.setShowHorizontalLines(false);
@ -425,8 +413,6 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
// Add the data updater to listen to changes in aoa and theta // Add the data updater to listen to changes in aoa and theta
mach.addChangeListener(this); mach.addChangeListener(this);
theta.addChangeListener(this); theta.addChangeListener(this);
@ -534,80 +520,97 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
} }
} }
Map<RocketComponent, AerodynamicForces> aeroData = // key is the comp.hashCode() or motor.getDesignation().hashCode()
aerodynamicCalculator.getForceAnalysis(configuration, conditions, set); Map<Integer, CMAnalysisEntry> cmMap= MassCalculator.getCMAnalysis(configuration);
Map<RocketComponent, Coordinate> massData =
massCalculator.getCGAnalysis(configuration);
Map<RocketComponent, AerodynamicForces> aeroData = aerodynamicCalculator.getForceAnalysis(configuration, conditions, set);
cgData.clear(); stabData.clear();
dragData.clear(); dragData.clear();
rollData.clear(); rollData.clear();
for (RocketComponent c : configuration.getActiveComponents()) {
if ( c instanceof AxialStage ) {
continue;
}
Object[] data = new Object[3];
cgData.add(data);
data[0] = c;
Coordinate cg = massData.get(c); for(final RocketComponent comp: configuration.getAllComponents()) {
data[1] = cg; // // this is actually redundant, because the analysis will not contain inactive stages.
// if (!configuration.isComponentActive(comp)) {
forces = aeroData.get(c); // continue;
if (forces == null) { // }
CMAnalysisEntry cmEntry = cmMap.get(comp.hashCode());
if (null == cmEntry) {
log.warn("Could not find massData entry for component: " + comp.getName());
continue; continue;
} }
if (forces.getCP() != null) {
data[2] = forces; if ((comp instanceof ComponentAssembly) && !(comp instanceof Rocket)){
continue;
} }
LongitudinalStabilityRow row = new LongitudinalStabilityRow(cmEntry.name, cmEntry.source);
stabData.add(row);
row.source = cmEntry.source;
row.eachMass = cmEntry.eachMass;
row.cm = cmEntry.totalCM;
forces = aeroData.get(comp);
if (forces == null) {
// DEBUG / trace level
System.err.println("Could not find aeroData entry for component: " + comp.getName());
row.cpx = 0.0;
row.cna = 0.0;
continue;
}
// System.err.println(String.format(" .CP = %s", forces.getCP()));
if (forces.getCP() != null) {
row.cpx = forces.getCP().x;
row.cna = forces.getCNa();
}
// System.err.println(String.format(" .CD = %s", forces.getCD()));
if (!Double.isNaN(forces.getCD())) { if (!Double.isNaN(forces.getCD())) {
dragData.add(forces); dragData.add(forces);
} }
if (c instanceof FinSet) {
if (comp instanceof FinSet) {
rollData.add(forces); rollData.add(forces);
} }
// // We _would_ check this, except TubeFinSet doesn't implement cant angles... so they can't impart any roll torque
// // If this is ever implemented, uncomment this block:
// else if(comp instanceof TubeFinSet){
// rollData.add(forces)
// }
} }
for ( MotorConfiguration motorConfig : configuration.getActiveMotors()) { for(final MotorConfiguration config: configuration.getActiveMotors()) {
CMAnalysisEntry cmEntry = cmMap.get(config.getMotor().getDesignation());
Object [] data = new Object[3]; if (null == cmEntry) {
cgData.add(data); continue;
}
data[0] = motorConfig.getMotor().getDesignation();
data[1] = motorConfig.getMotor().getLaunchMass(); LongitudinalStabilityRow row = new LongitudinalStabilityRow(cmEntry.name, cmEntry.source);
} stabData.add(row);
forces = aeroData.get(rkt); row.source = cmEntry.source;
if (forces != null) { row.eachMass = cmEntry.eachMass;
Object[] data = new Object[3]; row.cm = cmEntry.totalCM;
cgData.add(data); row.cpx = 0.0;
data[0] = rkt; row.cna = 0.0;
data[1] = massData.get(rkt);
data[2] = forces;
dragData.add(forces);
rollData.add(forces);
totalCD = forces.getCD();
} else {
totalCD = 0;
} }
// Set warnings // Set warnings
if (set.isEmpty()) { if (set.isEmpty()) {
warningList.setListData(new String[] { warningList.setListData(new String[] {trans.get("componentanalysisdlg.noWarnings")
trans.get("componentanalysisdlg.noWarnings")
}); });
} else { } else {
warningList.setListData(new Vector<Warning>(set)); warningList.setListData(new Vector<Warning>(set));
} }
cpTableModel.fireTableDataChanged(); longitudeStabilityTableModel.fireTableDataChanged();
dragTableModel.fireTableDataChanged(); dragTableModel.fireTableDataChanged();
rollTableModel.fireTableDataChanged(); rollTableModel.fireTableDataChanged();
} }
private class CustomCellRenderer extends JLabel implements TableCellRenderer { private class CustomCellRenderer extends JLabel implements TableCellRenderer {
/** /**
* *
@ -628,10 +631,10 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
this.setText(value == null ? null : value.toString()); this.setText(value == null ? null : value.toString());
if ((row < 0) || (row >= cgData.size())) if ((row < 0) || (row >= stabData.size()))
return this; return this;
if (cgData.get(row)[0] instanceof Rocket) { if ( 0 == row ) {
this.setFont(boldFont); this.setFont(boldFont);
} else { } else {
this.setFont(normalFont); this.setFont(normalFont);
@ -662,6 +665,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
boolean isSelected, boolean hasFocus, int row, int column) { boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Double) { if (value instanceof Double) {
final double totalCD = dragData.get(0).getCD();
// A drag coefficient // A drag coefficient
double cd = (Double) value; double cd = (Double) value;
@ -698,6 +702,25 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
} }
} }
private class LongitudinalStabilityRow {
public String name;
public Object source;
public double eachMass;
public Coordinate cm;
public double cpx;
public double cna;
public LongitudinalStabilityRow(final String _name, final Object _source){
name = _name;
source = _source;
eachMass = Double.NaN;
cm = Coordinate.NaN;
cpx = Double.NaN;
cna = Double.NaN;
}
}
///////// Singleton implementation ///////// Singleton implementation

View File

@ -682,19 +682,18 @@ public class BasicFrame extends JFrame {
menubar.add(menu); menubar.add(menu);
// TODO: reimplement this //// Component analysis
// //// Component analysis item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C);
// item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C); //// Analyze the rocket components separately
// //// Analyze the rocket components separately item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc"));
// item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc")); item.addActionListener(new ActionListener() {
// item.addActionListener(new ActionListener() { @Override
// @Override public void actionPerformed(ActionEvent e) {
// public void actionPerformed(ActionEvent e) { log.info(Markers.USER_MARKER, "Component analysis selected");
// log.info(Markers.USER_MARKER, "Component analysis selected"); ComponentAnalysisDialog.showDialog(rocketpanel);
// ComponentAnalysisDialog.showDialog(rocketpanel); }
// } });
// }); menu.add(item);
// menu.add(item);
// TODO: reimplement this dialog // TODO: reimplement this dialog
// //// Optimize // //// Optimize