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.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
|
||||||
|
@ -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,59 +65,101 @@ 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<RocketComponent, AerodynamicForces> finalMap = new LinkedHashMap<>();
|
||||||
map.put(total.getComponent(), total);
|
for(final RocketComponent comp : instMap.keySet()){
|
||||||
|
|
||||||
|
AerodynamicForces f;
|
||||||
for (RocketComponent c : map.keySet()) {
|
if(comp instanceof ComponentAssembly) {
|
||||||
f = map.get(c);
|
f = assemblyMap.get(comp);
|
||||||
if (Double.isNaN(f.getBaseCD()) && Double.isNaN(f.getPressureCD()) &&
|
} else if(comp.isAerodynamic()){
|
||||||
Double.isNaN(f.getFrictionCD()))
|
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,
|
||||||
@ -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));
|
||||||
@ -151,17 +186,44 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
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);
|
||||||
|
|
||||||
@ -174,53 +236,28 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
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 ) {
|
|
||||||
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;
|
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,
|
||||||
@ -753,9 +790,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
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,17 +845,14 @@ 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 );
|
||||||
}
|
}
|
||||||
|
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;
|
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;
|
||||||
@ -74,7 +73,7 @@ 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,12 +109,16 @@ 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();
|
||||||
}
|
}
|
||||||
@ -170,6 +173,8 @@ public class MassCalculation {
|
|||||||
|
|
||||||
String prefix = "";
|
String prefix = "";
|
||||||
|
|
||||||
|
Map<Integer, CMAnalysisEntry> analysisMap;
|
||||||
|
|
||||||
// =========== Private Instance Functions ========================
|
// =========== Private Instance Functions ========================
|
||||||
|
|
||||||
private MassCalculation calculateMountData(){
|
private MassCalculation calculateMountData(){
|
||||||
@ -233,6 +238,16 @@ 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 );
|
||||||
|
|
||||||
@ -274,6 +289,13 @@ public class MassCalculation {
|
|||||||
// 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) {
|
||||||
@ -321,6 +343,20 @@ public class MassCalculation {
|
|||||||
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;
|
||||||
RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt );
|
RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt );
|
||||||
@ -355,7 +391,6 @@ 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;
|
||||||
|
|
||||||
@ -378,6 +413,7 @@ public class MassCalculation {
|
|||||||
// 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.
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -430,6 +466,5 @@ public class MassCalculation {
|
|||||||
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.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();
|
||||||
@ -113,12 +110,11 @@ 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)
|
||||||
@ -135,32 +131,29 @@ public class MassCalculator implements Monitorable {
|
|||||||
* [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>();
|
Map<Integer,CMAnalysisEntry> analysisMap = new HashMap<>();
|
||||||
|
|
||||||
Coordinate rocketCG = Coordinate.ZERO;
|
MassCalculation calculation = new MassCalculation(
|
||||||
for (RocketComponent comp : configuration.getActiveComponents()) {
|
MassCalculation.Type.LAUNCH,
|
||||||
Coordinate[] cgs = comp.toAbsolute(comp.getCG());
|
config,
|
||||||
Coordinate stageCG = Coordinate.NUL;
|
Motor.PSEUDO_TIME_LAUNCH,
|
||||||
for (Coordinate cg : cgs) {
|
config.getRocket(),
|
||||||
stageCG = stageCG.average(cg);
|
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;
|
||||||
}
|
}
|
||||||
map.put(comp, stageCG);
|
|
||||||
|
|
||||||
rocketCG.average( stageCG);
|
|
||||||
}
|
|
||||||
|
|
||||||
map.put(configuration.getRocket(), rocketCG );
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////// Mass property calculations ///////////////////
|
////////////////// Mass property calculations ///////////////////
|
||||||
@Override
|
@Override
|
||||||
|
@ -202,24 +202,17 @@ 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<>();
|
private void recurseAllComponentsDepthFirst(RocketComponent comp, List<RocketComponent> traversalOrder){
|
||||||
|
traversalOrder.add(comp);
|
||||||
while (!toProcess.isEmpty()) {
|
|
||||||
RocketComponent comp = toProcess.poll();
|
|
||||||
|
|
||||||
toReturn.add(comp);
|
|
||||||
for( RocketComponent child : comp.getChildren()){
|
for( RocketComponent child : comp.getChildren()){
|
||||||
if (!(child instanceof AxialStage)) {
|
recurseAllComponentsDepthFirst(child, traversalOrder);
|
||||||
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)
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,17 +184,14 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,50 +200,43 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
|||||||
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;
|
|
||||||
}
|
}
|
||||||
return unit.toString(forces.getCP().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>") {
|
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 ) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
Object[] data = new Object[3];
|
|
||||||
cgData.add(data);
|
|
||||||
data[0] = c;
|
|
||||||
|
|
||||||
Coordinate cg = massData.get(c);
|
if ((comp instanceof ComponentAssembly) && !(comp instanceof Rocket)){
|
||||||
data[1] = cg;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
forces = aeroData.get(c);
|
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) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// System.err.println(String.format(" .CP = %s", forces.getCP()));
|
||||||
if (forces.getCP() != null) {
|
if (forces.getCP() != null) {
|
||||||
data[2] = forces;
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
forces = aeroData.get(rkt);
|
LongitudinalStabilityRow row = new LongitudinalStabilityRow(cmEntry.name, cmEntry.source);
|
||||||
if (forces != null) {
|
stabData.add(row);
|
||||||
Object[] data = new Object[3];
|
|
||||||
cgData.add(data);
|
row.source = cmEntry.source;
|
||||||
data[0] = rkt;
|
row.eachMass = cmEntry.eachMass;
|
||||||
data[1] = massData.get(rkt);
|
row.cm = cmEntry.totalCM;
|
||||||
data[2] = forces;
|
row.cpx = 0.0;
|
||||||
dragData.add(forces);
|
row.cna = 0.0;
|
||||||
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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user