1 package com.billkuker.rocketry.motorsim.visual;
\r
3 import java.awt.BorderLayout;
\r
4 import java.awt.Dimension;
\r
5 import java.awt.GridLayout;
\r
6 import java.text.NumberFormat;
\r
8 import javax.measure.quantity.Duration;
\r
9 import javax.measure.quantity.Force;
\r
10 import javax.measure.quantity.Pressure;
\r
11 import javax.measure.quantity.Velocity;
\r
12 import javax.measure.unit.SI;
\r
13 import javax.swing.JFrame;
\r
14 import javax.swing.JLabel;
\r
15 import javax.swing.JPanel;
\r
16 import javax.swing.JSlider;
\r
17 import javax.swing.JSplitPane;
\r
18 import javax.swing.WindowConstants;
\r
19 import javax.swing.event.ChangeEvent;
\r
20 import javax.swing.event.ChangeListener;
\r
22 import org.jscience.physics.amount.Amount;
\r
24 import com.billkuker.rocketry.motorsim.Burn;
\r
25 import com.billkuker.rocketry.motorsim.Burn.Interval;
\r
26 import com.billkuker.rocketry.motorsim.RocketScience;
\r
28 public class BurnPanel extends JPanel {
\r
29 private static final long serialVersionUID = 1L;
\r
31 Chart<Duration, Pressure> pressure;
\r
32 Chart<Duration, Force> thrust;
\r
33 Chart<Pressure, Velocity> burnRate;
\r
35 Amount<Duration> displayedTime = Amount.valueOf(0, SI.SECOND);
\r
37 public BurnPanel(Burn b){
\r
38 super( new BorderLayout() );
\r
42 pressure = new Chart<Duration, Pressure>(
\r
47 pressure.setDomain(burn.getData().keySet());
\r
49 thrust = new Chart<Duration, Force>(
\r
54 thrust.setDomain(burn.getData().keySet());
\r
56 burnRate = new Chart<Pressure, Velocity>(
\r
58 SI.METERS_PER_SECOND,
\r
59 burn.getMotor().getFuel(),
\r
62 burnRate.new IntervalDomain(
\r
63 Amount.valueOf(0, SI.MEGA(SI.PASCAL)),
\r
64 Amount.valueOf(11, SI.MEGA(SI.PASCAL)),
\r
69 JSplitPane tp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, thrust, pressure);
\r
70 tp.setDividerLocation(.5);
\r
71 tp.setResizeWeight(.5);
\r
73 grain = new GrainPanel(burn.getMotor().getGrain()){
\r
74 private static final long serialVersionUID = 1L;
\r
75 @Override protected void addComponents(java.awt.Component crossSection, java.awt.Component slider, java.awt.Component label, java.awt.Component area, java.awt.Component volume) {
\r
76 JSplitPane h = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, crossSection, area);
\r
77 add(h, BorderLayout.CENTER);
\r
78 h.resetToPreferredSizes();
\r
82 JSplitPane grains = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, grain, burnRate);
\r
83 grains.setDividerLocation(.5);
\r
84 grains.setResizeWeight(.5);
\r
86 JSplitPane main = new JSplitPane(JSplitPane.VERTICAL_SPLIT, grains, tp);
\r
87 Dimension minimumSize = new Dimension(800, 200);
\r
88 grains.setMinimumSize(minimumSize);
\r
89 tp.setMinimumSize(minimumSize);
\r
90 main.setDividerLocation(.5);
\r
91 main.setResizeWeight(.5);
\r
93 add( main, BorderLayout.CENTER );
\r
95 add( new SL(), BorderLayout.SOUTH);
\r
99 Amount<RocketScience.Impulse> ns = Amount.valueOf(0, RocketScience.NEWTON_SECOND);
\r
101 Amount<Duration> thrustTime = Amount.valueOf(0, SI.SECOND);
\r
102 Amount<Force> maxThrust = Amount.valueOf(0, SI.NEWTON);
\r
103 Amount<Pressure> maxPressure = Amount.valueOf(0, SI.MEGA(SI.PASCAL));
\r
105 for( Interval i: burn.getData().values() ){
\r
106 ns = ns.plus(i.dt.times(i.thrust));
\r
107 if ( i.thrust.isGreaterThan(Amount.valueOf(0.01, SI.NEWTON))){
\r
108 thrustTime = thrustTime.plus(i.dt);
\r
110 if ( i.thrust.isGreaterThan(maxThrust))
\r
111 maxThrust = i.thrust;
\r
112 if ( i.chamberPressure.isGreaterThan(maxPressure))
\r
113 maxPressure = i.chamberPressure;
\r
116 Amount<Force> averageThrust = Amount.valueOf(0, SI.NEWTON);
\r
117 if ( thrustTime.isGreaterThan(Amount.valueOf(0, SI.SECOND)))
\r
118 averageThrust = ns.divide(thrustTime).to(SI.NEWTON);
\r
120 float cnf = (float)(Math.log(ns.doubleValue(RocketScience.NEWTON_SECOND)/1.25) / Math.log(2));
\r
122 float fraction = cnf - cn;
\r
123 int percent = (int)(100 * fraction);
\r
124 char cl = (char)((int)'A' + cn);
\r
127 Amount<Duration> isp = ns.divide(
\r
128 b.getMotor().getGrain().volume(Amount.valueOf(0, SI.MILLIMETER))
\r
129 .times(b.getMotor().getFuel().getIdealDensity().times(b.getMotor().getFuel().getDensityRatio()))
\r
130 ).to(SI.METERS_PER_SECOND).divide(Amount.valueOf(9.81, SI.METERS_PER_SQUARE_SECOND)).to(SI.SECOND);
\r
132 JPanel text = new JPanel(new GridLayout(2,5));
\r
134 text.add(new JLabel("Rating"));
\r
135 text.add(new JLabel("Total Impulse"));
\r
136 text.add(new JLabel("ISP"));
\r
137 text.add(new JLabel("Max Thrust"));
\r
138 text.add(new JLabel("Average Thust"));
\r
139 text.add(new JLabel("Max Pressure"));
\r
141 text.add(new JLabel(percent + "% " + new String(new char[]{cl}) + "-" +Math.round(averageThrust.doubleValue(SI.NEWTON))));
\r
142 text.add(new JLabel(RocketScience.approx(ns)));
\r
143 text.add(new JLabel(RocketScience.approx(isp)));
\r
144 text.add(new JLabel(RocketScience.approx(maxThrust)));
\r
145 text.add(new JLabel(RocketScience.approx(averageThrust)));
\r
146 text.add(new JLabel(RocketScience.approx(maxPressure)));
\r
148 add(text, BorderLayout.NORTH);
\r
151 } catch (NoSuchMethodException e){
\r
152 throw new Error(e);
\r
158 private class SL extends JSlider implements ChangeListener{
\r
159 private static final long serialVersionUID = 1L;
\r
160 private static final int STEPS = 80;
\r
162 addChangeListener(this);
\r
168 public void stateChanged(ChangeEvent e) {
\r
169 double t = ((SL)e.getSource()).getValue();
\r
170 displayedTime = burn.burnTime().divide(STEPS).times(t);
\r
172 //Find the nearest key in the data set
\r
173 displayedTime =burn.getData().tailMap(displayedTime).firstKey();
\r
175 NumberFormat nf = NumberFormat.getInstance();
\r
176 nf.setMaximumFractionDigits(2);
\r
178 pressure.mark(displayedTime);
\r
179 thrust.mark(displayedTime);
\r
181 grain.setDisplayedRegression(burn.getData().get(displayedTime).regression);
\r
183 burnRate.mark(burn.getData().get(displayedTime).chamberPressure);
\r
187 double r = ((SL)e.getSource()).getValue();
\r
188 displayedRegression = grain.webThickness().divide(STEPS).times(r);
\r
189 NumberFormat nf = NumberFormat.getInstance();
\r
190 nf.setMaximumFractionDigits(2);
\r
191 l.setText("Regression: " + nf.format(displayedRegression.doubleValue(SI.MILLIMETER)) + "mm");
\r
192 area.mark(displayedRegression);
\r
193 volume.mark(displayedRegression);
\r
200 public void showAsWindow(){
\r
201 JFrame f = new JFrame();
\r
202 f.setTitle(burn.getMotor().getName());
\r
203 f.setSize(1280,720);
\r
204 f.setLocation(0, 0);
\r
205 f.setContentPane(this);
\r
206 f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
\r
207 f.setVisible(true);
\r