// // // ROSEApplet.java // // 4/14/98 // // This program is licensed under the Gnu Public License. // Please modify it as you like. I would be interested in // the changes so I can post them at boilerbay.com/rockets: // I'm at rockets@boilerbay.com // Keep this header and add a change history below. // // Copyright (c) 1998 R. L. Deran // // Simulate rocket flight. // import java.io.IOException; import java.awt.*; import java.applet.*; import java.util.Vector; import java.util.Hashtable; public class ROSEApplet extends Applet { Button showButton=new Button("Run ROSE"); ROSE rf; public void init() { try { setLayout(new BorderLayout()); add("Center",showButton); invalidate(); validate(); // Button takes up the entire Applet. } catch (Exception e) { e.printStackTrace(); } } public void start() {} public void stop() { try { if (rf!=null) rf.hide(); } catch (Exception e) { e.printStackTrace(); } } public void destroy() {} public boolean action(Event evt,Object target) { try { if (rf!=null) rf.dispose(); rf=new ROSE(); } catch (Exception e) { e.printStackTrace(); } return true; } } class ROSE extends Frame { static final double G=-9.98; // gravitational acceleration at sea level, m/s^2 static double Mr=0.010; // Mass of airframe (empty rocket - no motor). static double R=.018/2; // Radius of rocket, m static double Cd=.6; // Coefficient of drag. static double It=10; // Total Impulse, Ns static double Ftave=6; // Average thrust, N // Specific impulse, m/s. White Lightning is 1925. // Black powder about 800? static double Isp=850; static double dt=0.1; // time increment for each simulation cycle static double rho=1.174; // density of air at sea level, Kg/m^3 static double maxX=30,maxY=2200; ChartFrame ch; TextField mrTextField,dTextField,cdTextField, itTextField,ftaveTextField,ispTextField, dtTextField,rhoTextField, maxXTextField,maxYTextField; Button goButton,closeButton; ROSE() { super("ROSE"); try { setLayout(new ColumnLayoutForApplet(0,10,10,10,25)); MenuBar mb=new MenuBar(); Menu m=new Menu("Help"); mb.add(m); m.add("About"); setMenuBar(mb); Label l=new Label("Rocket Optimization and"); l.setForeground(Color.red); l.setFont(new Font("Helvetica",Font.BOLD,14)); add(l); l=new Label("Simulation Environment."); l.setForeground(Color.red); l.setFont(new Font("Helvetica",Font.BOLD,14)); add(l); add(new Label("Enter flight parameters and press 'Go'")); Panel p=new Panel(); p.setLayout(new FlexibleGridLayoutForApplet(2,5,5)); p.add(new Label("Airframe mass, Mr (Kg)")); p.add(mrTextField=new TextField(5)); mrTextField.setText(""+Mr); p.add(new Label("Airframe diameter, d (mm)")); p.add(dTextField=new TextField(4)); dTextField.setText(""+R*2000); p.add(new Label("Coefficient of drag, Cd")); p.add(cdTextField=new TextField(3)); cdTextField.setText(""+Cd); p.add(new Label("Total impulse, It (Ns)")); p.add(itTextField=new TextField(5)); itTextField.setText(""+It); p.add(new Label("Average thrust, Ftave (N)")); p.add(ftaveTextField=new TextField(5)); ftaveTextField.setText(""+Ftave); p.add(new Label("Specific impulse Isp (m/s)")); p.add(ispTextField=new TextField(4)); ispTextField.setText(""+Isp); p.add(new Label("Time slice, dt (sec)")); p.add(dtTextField=new TextField(5)); dtTextField.setText(""+dt); p.add(new Label("Air density, rho (Kg/m^3) ")); p.add(rhoTextField=new TextField(5)); rhoTextField.setText(""+rho); p.add(new Label("Max x")); p.add(maxXTextField=new TextField(5)); maxXTextField.setText(""+maxX); p.add(new Label("Max y")); p.add(maxYTextField=new TextField(5)); maxYTextField.setText(""+maxY); p.add(goButton=new Button("Go")); p.add(closeButton=new Button("Close")); add(p); setBackground(Color.lightGray); pack(); show(); } catch (Throwable t) { t.printStackTrace(); try { System.in.read(); } catch (IOException e) {} } } void setParametersFromTextFields() { It=new Double(itTextField.getText()).doubleValue(); Ftave=new Double(ftaveTextField.getText()).doubleValue(); Isp=new Double(ispTextField.getText()).doubleValue(); Mr=new Double(mrTextField.getText()).doubleValue(); R=new Double(dTextField.getText()).doubleValue()/2000; Cd=new Double(cdTextField.getText()).doubleValue(); dt=new Double(dtTextField.getText()).doubleValue(); rho=new Double(rhoTextField.getText()).doubleValue(); maxX=new Double(maxXTextField.getText()).doubleValue(); maxY=new Double(maxYTextField.getText()).doubleValue(); } public boolean handleEvent(Event evt) { if (evt.id==Event.WINDOW_DESTROY) dispose(); return super.handleEvent(evt); } public boolean action(Event evt,Object arg) { if (evt.target==goButton) { go(); return true; } else if (arg.equals("Close")) dispose(); else if (arg.equals("About")) { new AboutDialog(this); } return false; } public static void main(String[] args) { System.out.println( "ROSE: Rocket Optimization and Simulation Environment"); new ROSE(); } void go() { setParametersFromTextFields(); switch (6) { case 0: { // Ftave test same It if (ch==null || !ch.isVisible()) ch= new ChartFrame("Apogee for Ftave=1..1000",100,maxX,1000,maxY); for (double Ftave=1; Ftave<1000; Ftave+=1) { SimulationResult r= simulation(Ftave,It,Cd,Mr,R,false/*noisyness*/,null); //println("Ft="+Ftave+" tb="+It/Ftave+" "+r); ch.plot(Ftave,r.apogee); } } break; case 1: { System.out.println("Mm(It)="+Mm(It)+ " mp(0,Ftave,It)="+mp(0,Ftave,It)); if (ch==null || !ch.isVisible()) ch= new ChartFrame("Apogee for r,Mr",.010,maxX,100,maxY); for (double r=0.005; r<=.03; r+=.002) { for (double Mr=0.001; Mr<=0.030; Mr+=0.001) { SimulationResult rslt= simulation(Ftave,It,Cd,Mr,r,false/*noisyness*/,null); println("r="+r+" Mr="+Mr+" "+rslt); ch.plot(Mr,rslt.apogee); } } } break; case 2: { // Tburn test. Longer and longer burns, doubling It. // Find optimum burn. double Cd=.5; double Mr=1; // mass of rocket without motor, Kg if (ch==null || !ch.isVisible()) ch= new ChartFrame("Tburn, doubling It",10,maxX,1000,maxY); for (double It=40; It<=1280; It*=2) { for (double Tburn=1; Tburn<100; Tburn+=1) { double Ftave=It/Tburn; SimulationResult r= simulation(Ftave,It,Cd,Mr,R,false/*noisyness*/,null); println("It="+It+" Tb="+Tburn+" "+r); ch.plot(Tburn,r.apogee*3.28); } } } break; case 3: { // Tburn test. // Longer and longer burns, doubling It. Find tApogeee double Cd=.5; double Mr=1; // mass of rocket without motor, Kg if (ch==null || !ch.isVisible()) ch=new ChartFrame( "Apogee for Tburn, doubling It",10,maxX,10,maxY); for (double It=10; It<=1280; It*=2) { for (double Tburn=1; Tburn<100; Tburn+=1) { double Ftave=It/Tburn; SimulationResult r=simulation(Ftave,It,Cd,Mr,R,false,null); println("It="+It+" Tb="+Tburn+" "+r); ch.plot(Tburn,r.tApogee); } } } break; case 4: // Ftave test. more and more thrust, same burn time { double Tburn=1.8; Cd=.0; Mr=1; // mass of rocket without motor, Kg if (ch==null || !ch.isVisible()) ch=new ChartFrame( "Apogee for Ftave",100,maxX,10000,maxY); for (double Ftave=10; Ftave<1000; Ftave+=10) { SimulationResult r= simulation(Ftave,Ftave*Tburn,Cd,Mr,R,false,null); println("It="+Ftave*Tburn+" Ft="+Ftave+" "+r); ch.plot(Ftave,r.apogee); } } break; case 5: // Mass test. more and more mass, variable burn time { if (ch==null || !ch.isVisible()) ch=new ChartFrame( "Apogee for Mr,Tburn",10,maxX,1000,maxY); for (double Mr=.1; Mr<=2; Mr+=.1) { for (double Tburn=.5; Tburn<50; Tburn+=.5) { double Ftave=It/Tburn; SimulationResult r= simulation(Ftave,It,Cd,Mr,R,false,null); println("Mr="+Mr+" Tb="+Tburn+" "+r); ch.plot(Tburn,r.tApogee); } } } case 6: { // Simple altitude versus time. if (ch==null || !ch.isVisible()) ch=new ChartFrame( "Altitude versus time",1,maxX,100,maxY); simulation(Ftave,It,Cd,Mr,R,false,ch); } break; } } static int lineCounter=0; static void println(String s) { System.out.println(s); //if (++lineCounter%10==0) waitForEnter(); } static void waitForEnter() { try { System.in.read(); } catch (IOException e) {} } static SimulationResult simulation( double Ftave, // Average thrust, N. double It, // Total impulse, Ns. double Cd, // Coefficient of drag. double Mr, // Mass of rocket. double r, // Radius of (circular) frontal view. boolean noisy, // Print results as computed. ChartFrame ch // If not null, plot on it. ) { double Tburn=It/Ftave; double Me=Mr+Mm(It)-mp(0,Ftave,It); // mass of rocket with burned-out motor (empty) double t=0; // time, s double h=0; // altitude, m double v=0; // velocity, m/s boolean hadApogee=false; boolean hadBurnout=false; boolean hadLanding=false; SimulationResult rslt=new SimulationResult(); // Loop until return to starting altitude. for (int count=1; !hadLanding; count++,t+=dt) { double m=Me+mp(t,Ftave,It); double f=m*G+fd(v,Cd,r)+ft(t,Ftave,It); if (noisy) println( "t="+t+ " h="+h+ " ("+h*3.28+"ft)"+ " v="+v+ " f="+f+ " fd="+fd(v,Cd,r)+ " m="+m); if (ch!=null) ch.plot(t,h*39.24/12); h+=v*dt; double a=f/m; // acceleration, m/s^2 v+=a*dt; if (t>=Tburn && !hadBurnout) { if (noisy) println("Burnout"); hadBurnout=true; rslt.hBurnout=h; rslt.vBurnout=v; } if (v<0 && !hadApogee) { if (noisy) println("Apogee"); hadApogee=true; rslt.tApogee=t; rslt.apogee=h; } if (h<0 && !hadLanding) { if (noisy) println("Landing"); hadLanding=true; rslt.tLanding=t; rslt.vLanding=v; } } return rslt; } // rectangular thrust curve, N static final double ft(double t,double Ftave,double It) { double Tburn=It/Ftave; return t0) f=-f; // drag going upwards is down return f; } // downward sloping triangular mass of propellant function of time, Kg // should integrate negative thrust curve with Mp as constant of integration static final double mp(double t,double Ftave,double It) { double Tburn=It/Ftave; double Mp=It/Isp; // initial mass of propellant, Ns/(Ns/Kg)=Kg if (t>Tburn) return 0; return Mp*(1-t/Tburn); } static final double Mm(double It) { return It/Isp*2; // approximation based on motor charts } private static void p(Object o) { System.out.println(o); } } class SimulationResult { double hBurnout, vBurnout, tApogee, apogee, tLanding, vLanding; public String toString() { return "hB="+hBurnout+ " vB="+vBurnout+ " tA"+tApogee+ " hA="+apogee+" ("+apogee*3.28+"ft)"+ " tL="+tLanding+ " vL="+vLanding; } } class ChartFrame extends Frame { private static final int WIDTH=600,HEIGHT=400; Chart ch; ChartFrame(String title,double xUnits,double xMax,double yUnits,double yMax) { super("ROSE chart - "+title); add("Center",ch=new Chart(xUnits,xMax,yUnits,yMax)); resize(WIDTH,HEIGHT); show(); } void plot(double x,double y) { ch.plot(x,y); } public boolean handleEvent(Event evt) { if (evt.id==Event.WINDOW_DESTROY) dispose(); return super.handleEvent(evt); } } class ChartPoint { double x,y; ChartPoint(double x,double y) { this.x=x; this.y=y; } } class Chart extends Canvas { private static final int MARGINX=25,MARGINY=25; double xUnits,xMax,yUnits,yMax; ChartPoint[] points=new ChartPoint[100000]; // plenty int nPoints=0; boolean hasGrid; Chart(double xUnits,double xMax,double yUnits,double yMax) { this.xUnits=xUnits; this.xMax=xMax; this.yUnits=yUnits; this.yMax=yMax; } void plot(double x,double y) { points[nPoints]=new ChartPoint(x,y); Graphics g=getGraphics(); if (!hasGrid) drawGrid(g); drawPoint(g,xScreen(x),yScreen(y)); nPoints++; } public void paint(Graphics g) { drawGrid(g); for (int i=0; iwidths[column]) widths[column]=d.width; if (d.height>heights[row]) heights[row]=d.height; //p("layoutSize: row="+row+" col="+column+ // " height="+heights[row]+" width="+widths[column]); } int width=(nColumns-1)*hGap; if (width<0) width=0; // No columns! Exception?.. for (int i=0; i