Merge pull request #676 from teyrana/509-fix-component-analysis
[fixes #509] Fix Component Analysis Dialog
This commit is contained in:
commit
8c129dcc10
@ -700,9 +700,10 @@ componentanalysisdlg.lbl.machnumber = Mach number:
|
||||
componentanalysisdlg.lbl.rollrate = Roll rate:
|
||||
componentanalysisdlg.lbl.activestages = Active stages:
|
||||
componentanalysisdlg.lbl.motorconf = Motor configuration:
|
||||
componentanalysisdlg.TabStability.Col = Component
|
||||
componentanalysisdlg.TabStability.Col.CG = CG
|
||||
componentanalysisdlg.TabStability.Col.Mass = Mass
|
||||
componentanalysisdlg.TabStability.Col = Component Name
|
||||
componentanalysisdlg.TabStability.Col.EachMass = Instance Mass
|
||||
componentanalysisdlg.TabStability.Col.AllMass = Aggregate Mass
|
||||
componentanalysisdlg.TabStability.Col.CG = Aggregate CG Location
|
||||
componentanalysisdlg.TabStability.Col.CP = CP
|
||||
componentanalysisdlg.TabStability = Stability
|
||||
componentanalysisdlg.TabStability.ttip = Stability information
|
||||
|
@ -2,14 +2,7 @@ package net.sf.openrocket.aerodynamics;
|
||||
|
||||
import static net.sf.openrocket.util.MathUtil.pow2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 java.util.*;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc;
|
||||
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.PolyInterpolator;
|
||||
import net.sf.openrocket.util.Reflection;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
|
||||
|
||||
/**
|
||||
* 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,
|
||||
WarningSet warnings) {
|
||||
checkCache(configuration);
|
||||
AerodynamicForces forces = calculateNonAxialForces(configuration, conditions, null, warnings);
|
||||
AerodynamicForces forces = calculateNonAxialForces(configuration, conditions, warnings);
|
||||
return forces.getCP();
|
||||
}
|
||||
|
||||
@ -72,60 +65,102 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
|
||||
@Override
|
||||
public Map<RocketComponent, AerodynamicForces> getForceAnalysis(FlightConfiguration configuration,
|
||||
FlightConditions conditions, WarningSet warnings) {
|
||||
checkCache(configuration);
|
||||
|
||||
AerodynamicForces f;
|
||||
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);
|
||||
FlightConditions conditions,
|
||||
WarningSet warnings) {
|
||||
if (calcMap == null){
|
||||
buildCalcMap(configuration);
|
||||
}
|
||||
|
||||
|
||||
|
||||
InstanceMap instMap = configuration.getActiveInstances();
|
||||
Map<RocketComponent, AerodynamicForces> eachMap = new LinkedHashMap<>();
|
||||
Map<RocketComponent, AerodynamicForces> assemblyMap = new LinkedHashMap<>();
|
||||
|
||||
// Calculate non-axial force data
|
||||
AerodynamicForces total = calculateNonAxialForces(configuration, conditions, map, warnings);
|
||||
|
||||
|
||||
calculateForceAnalysis(conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings);
|
||||
|
||||
// Calculate friction data
|
||||
total.setFrictionCD(calculateFrictionDrag(configuration, conditions, map, warnings));
|
||||
total.setPressureCD(calculatePressureDrag(configuration, conditions, map, warnings));
|
||||
total.setBaseCD(calculateBaseDrag(configuration, conditions, map, warnings));
|
||||
|
||||
total.setComponent(configuration.getRocket());
|
||||
map.put(total.getComponent(), total);
|
||||
|
||||
|
||||
for (RocketComponent c : map.keySet()) {
|
||||
f = map.get(c);
|
||||
if (Double.isNaN(f.getBaseCD()) && Double.isNaN(f.getPressureCD()) &&
|
||||
Double.isNaN(f.getFrictionCD()))
|
||||
AerodynamicForces rocketForces = assemblyMap.get(configuration.getRocket());
|
||||
rocketForces.setFrictionCD(calculateFrictionDrag(configuration, conditions, eachMap, warnings));
|
||||
rocketForces.setPressureCD(calculatePressureDrag(configuration, conditions, eachMap, warnings));
|
||||
rocketForces.setBaseCD(calculateBaseDrag(configuration, conditions, eachMap, warnings));
|
||||
|
||||
Map<RocketComponent, AerodynamicForces> finalMap = new LinkedHashMap<>();
|
||||
for(final RocketComponent comp : instMap.keySet()){
|
||||
|
||||
AerodynamicForces f;
|
||||
if(comp instanceof ComponentAssembly) {
|
||||
f = assemblyMap.get(comp);
|
||||
} else if(comp.isAerodynamic()){
|
||||
f = eachMap.get(comp);
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Double.isNaN(f.getCNa())){
|
||||
f.setCNa(0.0);
|
||||
}
|
||||
if (f.getCP().isNaN()) {
|
||||
f.setCP(Coordinate.ZERO);
|
||||
}
|
||||
|
||||
if (Double.isNaN(f.getBaseCD()))
|
||||
f.setBaseCD(0);
|
||||
|
||||
if (Double.isNaN(f.getPressureCD()))
|
||||
f.setPressureCD(0);
|
||||
|
||||
if (Double.isNaN(f.getFrictionCD()))
|
||||
f.setFrictionCD(0);
|
||||
|
||||
f.setCD(f.getBaseCD() + f.getPressureCD() + f.getFrictionCD());
|
||||
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
|
||||
public AerodynamicForces getAerodynamicForces(FlightConfiguration configuration,
|
||||
FlightConditions conditions, WarningSet warnings) {
|
||||
@ -135,7 +170,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
warnings = ignoreWarningSet;
|
||||
|
||||
// Calculate non-axial force data
|
||||
AerodynamicForces total = calculateNonAxialForces(configuration, conditions, null, warnings);
|
||||
AerodynamicForces total = calculateNonAxialForces(configuration, conditions, warnings);
|
||||
|
||||
// Calculate friction data
|
||||
total.setFrictionCD(calculateFrictionDrag(configuration, conditions, null, warnings));
|
||||
@ -150,77 +185,79 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
calculateDampingMoments(configuration, conditions, total);
|
||||
total.setCm(total.getCm() - total.getPitchDampingMoment());
|
||||
total.setCyaw(total.getCyaw() - total.getYawDampingMoment());
|
||||
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions,
|
||||
Map<RocketComponent, AerodynamicForces> calculators, WarningSet warnings) {
|
||||
|
||||
private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions, WarningSet warnings) {
|
||||
|
||||
checkCache(configuration);
|
||||
|
||||
|
||||
if (warnings == null)
|
||||
warnings = ignoreWarningSet;
|
||||
|
||||
|
||||
if (conditions.getAOA() > 17.5 * Math.PI / 180)
|
||||
warnings.add(new Warning.LargeAOA(conditions.getAOA()));
|
||||
|
||||
|
||||
if (calcMap == null)
|
||||
buildCalcMap(configuration);
|
||||
|
||||
|
||||
if( ! isContinuous( configuration.getRocket() ) ){
|
||||
warnings.add( Warning.DIAMETER_DISCONTINUITY);
|
||||
}
|
||||
|
||||
final InstanceMap imap = configuration.getActiveInstances();
|
||||
|
||||
|
||||
// across the _entire_ assembly -- like a rocket, or a stage
|
||||
final AerodynamicForces assemblyForces= new AerodynamicForces().zero();
|
||||
|
||||
// System.err.println("======================================");
|
||||
// System.err.println("==== Iterating through components ====");
|
||||
// iterate through all components
|
||||
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
|
||||
final RocketComponent comp = entry.getKey();
|
||||
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> mapEntry: imap.entrySet() ) {
|
||||
final RocketComponent comp = mapEntry.getKey();
|
||||
final List<InstanceContext> contextList = mapEntry.getValue();
|
||||
|
||||
RocketComponentCalc calcObj = calcMap.get(comp);
|
||||
|
||||
// System.err.println("comp=" + comp);
|
||||
if (null != calcObj) {
|
||||
// iterate across component instances
|
||||
final ArrayList<InstanceContext> contextList = entry.getValue();
|
||||
// calculated across all component instances
|
||||
final AerodynamicForces componentForces = calculateComponentNonAxialForces(conditions, comp, calcObj, contextList, warnings);
|
||||
|
||||
for(InstanceContext context: contextList ) {
|
||||
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);
|
||||
|
||||
}
|
||||
assemblyForces.merge(componentForces);
|
||||
}
|
||||
}
|
||||
|
||||
// System.err.println("____ cp:asbly: " + assemblyForces.getCP().toString());
|
||||
return assemblyForces;
|
||||
}
|
||||
|
||||
@ -230,7 +267,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
}
|
||||
|
||||
private boolean testIsContinuous( final RocketComponent treeRoot ){
|
||||
Queue<RocketComponent> queue = new LinkedList<RocketComponent>();
|
||||
Queue<RocketComponent> queue = new LinkedList<>();
|
||||
queue.addAll(treeRoot.getChildren());
|
||||
|
||||
boolean isContinuous = true;
|
||||
@ -279,7 +316,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
* @param conditions Flight conditions taken into account
|
||||
* @param map ?
|
||||
* @param set Set to handle
|
||||
* @return
|
||||
* @return friction drag for entire rocket
|
||||
*/
|
||||
private double calculateFrictionDrag(FlightConfiguration configuration, FlightConditions conditions,
|
||||
Map<RocketComponent, AerodynamicForces> map, WarningSet set) {
|
||||
@ -510,12 +547,12 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
*
|
||||
* @param configuration Rocket configuration
|
||||
* @param conditions Flight conditions taken into account
|
||||
* @param map ?
|
||||
* @param set Set to handle
|
||||
* @param forceMap
|
||||
* @param warningSet all current warnings
|
||||
* @return
|
||||
*/
|
||||
private double calculatePressureDrag(FlightConfiguration configuration, FlightConditions conditions,
|
||||
Map<RocketComponent, AerodynamicForces> map, WarningSet warnings) {
|
||||
Map<RocketComponent, AerodynamicForces> forceMap, WarningSet warningSet) {
|
||||
|
||||
double stagnation, base, total;
|
||||
|
||||
@ -538,11 +575,11 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
|
||||
// Pressure fore drag
|
||||
double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base,
|
||||
warnings);
|
||||
warningSet);
|
||||
total += cd;
|
||||
|
||||
if (map != null) {
|
||||
map.get(c).setPressureCD(cd);
|
||||
if (forceMap != null) {
|
||||
forceMap.get(c).setPressureCD(cd);
|
||||
}
|
||||
|
||||
if(c.isCDOverridden())
|
||||
@ -562,8 +599,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
cd = stagnation * area / conditions.getRefArea();
|
||||
total += cd;
|
||||
|
||||
if (map != null) {
|
||||
map.get(c).setPressureCD(map.get(c).getPressureCD() + cd);
|
||||
if (forceMap != null) {
|
||||
forceMap.get(c).setPressureCD(forceMap.get(c).getPressureCD() + cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -580,7 +617,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
* @param configuration Rocket configuration
|
||||
* @param conditions Flight conditions taken into account
|
||||
* @param map ?
|
||||
* @param set Set to handle
|
||||
* @param warnings all current warnings
|
||||
* @return
|
||||
*/
|
||||
private double calculateBaseDrag(FlightConfiguration configuration, FlightConditions conditions,
|
||||
@ -752,10 +789,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
total.setPitchDampingMoment(MathUtil.sign(pitchRate) * pitchDampingMomentMagnitude);
|
||||
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,
|
||||
double cgx) {
|
||||
if (cacheDiameter < 0) {
|
||||
@ -811,18 +845,15 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
|
||||
|
||||
private void buildCalcMap(FlightConfiguration configuration) {
|
||||
Iterator<RocketComponent> iterator;
|
||||
|
||||
calcMap = new HashMap<RocketComponent, RocketComponentCalc>();
|
||||
calcMap = new HashMap<>();
|
||||
|
||||
// because this is not a per-instance iteration... this usage of 'getActiveComponents' is probably fine.
|
||||
for (RocketComponent comp: configuration.getActiveComponents()) {
|
||||
if (!comp.isAerodynamic())
|
||||
continue;
|
||||
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
40
core/src/net/sf/openrocket/masscalc/CMAnalysisEntry.java
Normal file
40
core/src/net/sf/openrocket/masscalc/CMAnalysisEntry.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
package net.sf.openrocket.masscalc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.*;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
@ -73,8 +72,8 @@ public class MassCalculation {
|
||||
}
|
||||
}
|
||||
|
||||
public MassCalculation copy( final RocketComponent _root, final Transformation _transform ){
|
||||
return new MassCalculation( this.type, this.config, this.simulationTime, _root, _transform );
|
||||
public MassCalculation copy( final RocketComponent _root, final Transformation _transform){
|
||||
return new MassCalculation( this.type, this.config, this.simulationTime, _root, _transform, this.analysisMap);
|
||||
}
|
||||
|
||||
public Coordinate getCM() {
|
||||
@ -110,13 +109,17 @@ public class MassCalculation {
|
||||
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;
|
||||
config = _config;
|
||||
simulationTime = _time;
|
||||
root = _root;
|
||||
transform = _transform;
|
||||
|
||||
analysisMap = _map;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
@ -167,8 +170,10 @@ public class MassCalculation {
|
||||
|
||||
// center-of-mass AND moment-of-inertia data.
|
||||
final ArrayList<RigidBody> bodies = new ArrayList<RigidBody>();
|
||||
|
||||
String prefix = "";
|
||||
|
||||
String prefix = "";
|
||||
|
||||
Map<Integer, CMAnalysisEntry> analysisMap;
|
||||
|
||||
// =========== Private Instance Functions ========================
|
||||
|
||||
@ -183,8 +188,8 @@ public class MassCalculation {
|
||||
if( motorConfig.isEmpty() ){
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
final double mountXPosition = root.getPosition().x;
|
||||
|
||||
final int instanceCount = root.getInstanceCount();
|
||||
@ -210,10 +215,10 @@ public class MassCalculation {
|
||||
eachMass = eachMotorMass - eachCasingMass;
|
||||
eachCMx = (eachMotorCMx*eachMotorMass - eachCasingCMx*eachCasingMass)/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.
|
||||
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 );
|
||||
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 );
|
||||
addInertia( clusterMOI );
|
||||
|
||||
@ -273,7 +288,14 @@ public class MassCalculation {
|
||||
// if( this.config.isComponentActive(component) ){
|
||||
// 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.
|
||||
MassCalculation children = this.copy(component, parentTransform );
|
||||
for( int currentInstanceNumber = 0; currentInstanceNumber < instanceCount; ++currentInstanceNumber) {
|
||||
@ -288,14 +310,14 @@ public class MassCalculation {
|
||||
|
||||
for (RocketComponent child : component.getChildren()) {
|
||||
// child data, relative to rocket reference frame
|
||||
MassCalculation eachChild = copy( child, currentTransform);
|
||||
MassCalculation eachChild = copy(child, currentTransform);
|
||||
|
||||
eachChild.prefix = prefix + "....";
|
||||
eachChild.calculateStructure();
|
||||
|
||||
// accumulate children's data
|
||||
children.merge( eachChild );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( MIN_MASS < children.getMass() ) {
|
||||
@ -320,6 +342,20 @@ public class MassCalculation {
|
||||
// mass data for *this component only* in the rocket-frame
|
||||
compCM = parentTransform.transform( compCM.add(component.getPosition()) );
|
||||
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 compIt = component.getLongitudinalUnitInertia() * compCM.weight;
|
||||
@ -331,7 +367,7 @@ public class MassCalculation {
|
||||
// System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() ));
|
||||
// }
|
||||
|
||||
if (component.getOverrideSubcomponents()) {
|
||||
if (component.getOverrideSubcomponents()) {
|
||||
if (component.isMassOverridden()) {
|
||||
double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS);
|
||||
Coordinate newCM = this.getCM().setWeight( newMass );
|
||||
@ -355,29 +391,29 @@ public class MassCalculation {
|
||||
}
|
||||
|
||||
MassCalculation calculateMotors() {
|
||||
|
||||
final RocketComponent component = this.root;
|
||||
final Transformation parentTransform = this.transform;
|
||||
|
||||
final int instanceCount = component.getInstanceCount();
|
||||
Coordinate[] instanceLocations = component.getInstanceLocations();
|
||||
|
||||
|
||||
// // vvv DEBUG
|
||||
// if( this.config.isComponentActive(component) ){
|
||||
// System.err.println(String.format( "%s[%s]....", prefix, component.getName()));
|
||||
// }
|
||||
|
||||
|
||||
if (component.isMotorMount()) {
|
||||
MassCalculation propellant = this.copy(component, parentTransform);
|
||||
|
||||
propellant.calculateMountData();
|
||||
|
||||
|
||||
this.merge( propellant );
|
||||
|
||||
// // vvv DEBUG
|
||||
// // vvv DEBUG
|
||||
// if( 0 < propellant.getMass() ) {
|
||||
// System.err.println(String.format( "%s........++ propellantData: %s", prefix, propellant.toCMDebug()));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// iterate over the aggregated instances for the whole tree.
|
||||
@ -395,7 +431,7 @@ public class MassCalculation {
|
||||
|
||||
// accumulate children's data
|
||||
children.merge( eachChild );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( MIN_MASS < children.getMass() ) {
|
||||
@ -407,7 +443,7 @@ public class MassCalculation {
|
||||
// if( this.config.isComponentActive(component) && 0 < this.getMass() ) {
|
||||
// System.err.println(String.format( "%s....<< return assemblyData: %s (tree @%s)", prefix, this.toCMDebug(), component.getName() ));
|
||||
// }
|
||||
// ^^^ DEBUG
|
||||
// // ^^^ DEBUG
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -427,9 +463,8 @@ public class MassCalculation {
|
||||
It += eachGlobal.Iyy;
|
||||
}
|
||||
|
||||
return new RigidBody( centerOfMass, Ir, It, It );
|
||||
return new RigidBody( centerOfMass, Ir, It, It );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,7 @@ import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Monitorable;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
import net.sf.openrocket.util.*;
|
||||
|
||||
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 ){
|
||||
final FlightConfiguration config = status.getConfiguration();
|
||||
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();
|
||||
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
|
||||
public static RigidBody calculate( final MassCalculation.Type _type, final FlightConfiguration _config, double _time ){
|
||||
MassCalculation calculation = new MassCalculation( _type, _config, _time, _config.getRocket(), Transformation.IDENTITY);
|
||||
public static RigidBody calculate( final MassCalculation.Type _type, final FlightConfiguration _config, double _time){
|
||||
MassCalculation calculation = new MassCalculation( _type, _config, _time, _config.getRocket(), Transformation.IDENTITY, null);
|
||||
calculation.calculateAssembly();
|
||||
return calculation.calculateMomentOfInertia();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* 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
|
||||
* Rocket as the key.
|
||||
*
|
||||
*
|
||||
* 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 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.
|
||||
* [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 map from each rocket component to its corresponding CG.
|
||||
* @return a list of CG coordinates for every instance of this component
|
||||
*/
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
public static Map<Integer,CMAnalysisEntry> getCMAnalysis(FlightConfiguration config) {
|
||||
|
||||
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 ///////////////////
|
||||
@Override
|
||||
|
@ -202,23 +202,16 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
}
|
||||
|
||||
public Collection<RocketComponent> getAllComponents() {
|
||||
Queue<RocketComponent> toProcess = new ArrayDeque<RocketComponent>();
|
||||
toProcess.offer(this.rocket);
|
||||
|
||||
ArrayList<RocketComponent> toReturn = new ArrayList<>();
|
||||
|
||||
while (!toProcess.isEmpty()) {
|
||||
RocketComponent comp = toProcess.poll();
|
||||
|
||||
toReturn.add(comp);
|
||||
for (RocketComponent child : comp.getChildren()) {
|
||||
if (!(child instanceof AxialStage)) {
|
||||
toProcess.offer(child);
|
||||
}
|
||||
}
|
||||
List<RocketComponent> traversalOrder = new ArrayList<RocketComponent>();
|
||||
recurseAllComponentsDepthFirst(this.rocket,traversalOrder);
|
||||
return traversalOrder;
|
||||
}
|
||||
|
||||
private void recurseAllComponentsDepthFirst(RocketComponent comp, List<RocketComponent> traversalOrder){
|
||||
traversalOrder.add(comp);
|
||||
for( RocketComponent child : comp.getChildren()){
|
||||
recurseAllComponentsDepthFirst(child, traversalOrder);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/** Returns all the components on core stages (i.e. centerline)
|
||||
@ -401,10 +394,10 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
}
|
||||
|
||||
private void updateStages() {
|
||||
if (this.rocket.getStageCount() == this.stages.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.rocket.getStageCount() == this.stages.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.stages.clear();
|
||||
for (AxialStage curStage : this.rocket.getStageList()) {
|
||||
|
||||
|
@ -341,12 +341,9 @@ public class BarrowmanCalculatorTest {
|
||||
|
||||
final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods);
|
||||
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 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 CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import java.util.Vector;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
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.util.GUIUtil;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.masscalc.CMAnalysisEntry;
|
||||
import net.sf.openrocket.masscalc.MassCalculator;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
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.rocketcomponent.*;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.Unit;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.StateChangeListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ComponentAnalysisDialog extends JDialog implements StateChangeListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(ComponentAnalysisDialog.class);
|
||||
|
||||
private static final long serialVersionUID = 9131240570600307935L;
|
||||
private static ComponentAnalysisDialog singletonDialog = null;
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
@ -80,18 +80,16 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
private final JToggleButton worstToggle;
|
||||
private boolean fakeChange = false;
|
||||
private AerodynamicCalculator aerodynamicCalculator;
|
||||
private final MassCalculator massCalculator = new MassCalculator();
|
||||
|
||||
private final ColumnTableModel cpTableModel;
|
||||
private final ColumnTableModel longitudeStabilityTableModel;
|
||||
private final ColumnTableModel dragTableModel;
|
||||
private final ColumnTableModel rollTableModel;
|
||||
|
||||
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 double totalCD = 0;
|
||||
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");
|
||||
|
||||
|
||||
// Create the CP data table
|
||||
cpTableModel = new ColumnTableModel(
|
||||
// Create the Longitudinal Stability (CM vs CP) data table
|
||||
longitudeStabilityTableModel = new ColumnTableModel(
|
||||
|
||||
//// Component
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) {
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
Object c = cgData.get(row)[0];
|
||||
if (c instanceof Rocket) {
|
||||
return trans.get("componentanalysisdlg.TOTAL");
|
||||
}
|
||||
Object c = stabData.get(row).name;
|
||||
return c.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getDefaultWidth() {
|
||||
return 200;
|
||||
}
|
||||
},
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
|
||||
private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
|
||||
|
||||
// would be per-instance mass
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.EachMass") + " (" + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit() + ")") {
|
||||
final private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
Coordinate cg = (Coordinate) cgData.get(row)[1];
|
||||
if ( cg == null ) {
|
||||
return null;
|
||||
}
|
||||
return unit.toString(cg.x);
|
||||
return unit.toString(stabData.get(row).eachMass);
|
||||
}
|
||||
},
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.Mass") + " / " + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit()) {
|
||||
private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
|
||||
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.AllMass") + " (" + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit() + ")") {
|
||||
final private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
Coordinate cg = (Coordinate) cgData.get(row)[1];
|
||||
if ( cg == null ) {
|
||||
return null;
|
||||
}
|
||||
return unit.toString(cg.weight);
|
||||
return unit.toString(stabData.get(row).cm.weight);
|
||||
}
|
||||
},
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.CP") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
|
||||
private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " (" + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit() + ")") {
|
||||
final private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
AerodynamicForces forces = (AerodynamicForces) cgData.get(row)[2];
|
||||
if ( forces == null ) {
|
||||
return null;
|
||||
}
|
||||
return unit.toString(forces.getCP().x);
|
||||
return unit.toString(stabData.get(row).cm.x);
|
||||
}
|
||||
},
|
||||
new Column(trans.get("componentanalysisdlg.TabStability.Col.CP") + " (" + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit() + ")") {
|
||||
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>") {
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
AerodynamicForces forces = (AerodynamicForces) cgData.get(row)[2];
|
||||
if ( forces == null ) {
|
||||
return null;
|
||||
}
|
||||
return NOUNIT.toString(forces.getCP().weight);
|
||||
return NOUNIT.toString(stabData.get(row).cna);
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,15 +249,15 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return cgData.size();
|
||||
return stabData.size();
|
||||
}
|
||||
};
|
||||
|
||||
table = new ColumnTable(cpTableModel);
|
||||
table = new ColumnTable(longitudeStabilityTableModel);
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setSelectionBackground(Color.LIGHT_GRAY);
|
||||
table.setSelectionForeground(Color.BLACK);
|
||||
cpTableModel.setColumnWidths(table.getColumnModel());
|
||||
longitudeStabilityTableModel.setColumnWidths(table.getColumnModel());
|
||||
|
||||
table.setDefaultRenderer(Object.class, new CustomCellRenderer());
|
||||
// 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
|
||||
mach.addChangeListener(this);
|
||||
theta.addChangeListener(this);
|
||||
@ -534,80 +520,97 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
}
|
||||
}
|
||||
|
||||
Map<RocketComponent, AerodynamicForces> aeroData =
|
||||
aerodynamicCalculator.getForceAnalysis(configuration, conditions, set);
|
||||
Map<RocketComponent, Coordinate> massData =
|
||||
massCalculator.getCGAnalysis(configuration);
|
||||
// key is the comp.hashCode() or motor.getDesignation().hashCode()
|
||||
Map<Integer, CMAnalysisEntry> cmMap= MassCalculator.getCMAnalysis(configuration);
|
||||
|
||||
Map<RocketComponent, AerodynamicForces> aeroData = aerodynamicCalculator.getForceAnalysis(configuration, conditions, set);
|
||||
|
||||
cgData.clear();
|
||||
stabData.clear();
|
||||
dragData.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);
|
||||
data[1] = cg;
|
||||
|
||||
forces = aeroData.get(c);
|
||||
if (forces == null) {
|
||||
for(final RocketComponent comp: configuration.getAllComponents()) {
|
||||
// // this is actually redundant, because the analysis will not contain inactive stages.
|
||||
// if (!configuration.isComponentActive(comp)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
CMAnalysisEntry cmEntry = cmMap.get(comp.hashCode());
|
||||
if (null == cmEntry) {
|
||||
log.warn("Could not find massData entry for component: " + comp.getName());
|
||||
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())) {
|
||||
dragData.add(forces);
|
||||
}
|
||||
if (c instanceof FinSet) {
|
||||
|
||||
if (comp instanceof FinSet) {
|
||||
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()) {
|
||||
|
||||
Object [] data = new Object[3];
|
||||
cgData.add(data);
|
||||
|
||||
data[0] = motorConfig.getMotor().getDesignation();
|
||||
data[1] = motorConfig.getMotor().getLaunchMass();
|
||||
}
|
||||
|
||||
forces = aeroData.get(rkt);
|
||||
if (forces != null) {
|
||||
Object[] data = new Object[3];
|
||||
cgData.add(data);
|
||||
data[0] = rkt;
|
||||
data[1] = massData.get(rkt);
|
||||
data[2] = forces;
|
||||
dragData.add(forces);
|
||||
rollData.add(forces);
|
||||
totalCD = forces.getCD();
|
||||
} else {
|
||||
totalCD = 0;
|
||||
for(final MotorConfiguration config: configuration.getActiveMotors()) {
|
||||
CMAnalysisEntry cmEntry = cmMap.get(config.getMotor().getDesignation());
|
||||
if (null == cmEntry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LongitudinalStabilityRow row = new LongitudinalStabilityRow(cmEntry.name, cmEntry.source);
|
||||
stabData.add(row);
|
||||
|
||||
row.source = cmEntry.source;
|
||||
row.eachMass = cmEntry.eachMass;
|
||||
row.cm = cmEntry.totalCM;
|
||||
row.cpx = 0.0;
|
||||
row.cna = 0.0;
|
||||
}
|
||||
|
||||
// Set warnings
|
||||
if (set.isEmpty()) {
|
||||
warningList.setListData(new String[] {
|
||||
trans.get("componentanalysisdlg.noWarnings")
|
||||
warningList.setListData(new String[] {trans.get("componentanalysisdlg.noWarnings")
|
||||
});
|
||||
} else {
|
||||
warningList.setListData(new Vector<Warning>(set));
|
||||
}
|
||||
|
||||
cpTableModel.fireTableDataChanged();
|
||||
longitudeStabilityTableModel.fireTableDataChanged();
|
||||
dragTableModel.fireTableDataChanged();
|
||||
rollTableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
|
||||
if ((row < 0) || (row >= cgData.size()))
|
||||
if ((row < 0) || (row >= stabData.size()))
|
||||
return this;
|
||||
|
||||
if (cgData.get(row)[0] instanceof Rocket) {
|
||||
if ( 0 == row ) {
|
||||
this.setFont(boldFont);
|
||||
} else {
|
||||
this.setFont(normalFont);
|
||||
@ -662,6 +665,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
|
||||
if (value instanceof Double) {
|
||||
final double totalCD = dragData.get(0).getCD();
|
||||
|
||||
// A drag coefficient
|
||||
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
|
||||
|
||||
|
@ -682,19 +682,18 @@ public class BasicFrame extends JFrame {
|
||||
menubar.add(menu);
|
||||
|
||||
|
||||
// TODO: reimplement this
|
||||
// //// Component analysis
|
||||
// item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C);
|
||||
// //// Analyze the rocket components separately
|
||||
// item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc"));
|
||||
// item.addActionListener(new ActionListener() {
|
||||
// @Override
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// log.info(Markers.USER_MARKER, "Component analysis selected");
|
||||
// ComponentAnalysisDialog.showDialog(rocketpanel);
|
||||
// }
|
||||
// });
|
||||
// menu.add(item);
|
||||
//// Component analysis
|
||||
item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C);
|
||||
//// Analyze the rocket components separately
|
||||
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.info(Markers.USER_MARKER, "Component analysis selected");
|
||||
ComponentAnalysisDialog.showDialog(rocketpanel);
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
// TODO: reimplement this dialog
|
||||
// //// Optimize
|
||||
|
Loading…
x
Reference in New Issue
Block a user