(no commit message)
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / Burn.java
index c528333185343c2ab80ddf8ab910b8e5ec6a4142..e101d8154b7edb12858277f4d9533ac783a2fad9 100644 (file)
@@ -3,6 +3,8 @@ package com.billkuker.rocketry.motorsim;
 \r
 \r
 import java.util.Date;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
 import java.util.SortedMap;\r
 import java.util.TreeMap;\r
 \r
@@ -25,15 +27,29 @@ import org.apache.log4j.Logger;
 import org.jscience.physics.amount.Amount;\r
 import org.jscience.physics.amount.Constants;\r
 \r
+import com.billkuker.rocketry.motorsim.Validating.ValidationException;\r
+\r
 public class Burn {\r
        //Some constants to tune adaptive regression step\r
        private static final double regStepIncreaseFactor = 1.01;\r
        private static final double regStepDecreaseFactor = .5;\r
        private static final Amount<Pressure> chamberPressureMaxDelta = Amount.valueOf(.5, SI.MEGA(SI.PASCAL));\r
        \r
+       private static final Amount<Pressure> endPressure = Amount.valueOf(.1, RocketScience.PSI);\r
+       \r
        private static Logger log = Logger.getLogger(Burn.class);\r
        protected final Motor motor;\r
        \r
+       private boolean burning = false;\r
+       private boolean done = false;\r
+       \r
+       public interface BurnProgressListener{\r
+               public void setProgress(float p);\r
+               public void burnComplete();\r
+       }\r
+       \r
+       private Set<BurnProgressListener> bpls = new HashSet<Burn.BurnProgressListener>();\r
+       \r
        private static final Amount<Pressure> atmosphereicPressure = Amount.valueOf(101000, SI.PASCAL);\r
        \r
        public class Interval{\r
@@ -52,6 +68,8 @@ public class Burn {
        protected SortedMap<Amount<Duration>,Interval> data = new TreeMap<Amount<Duration>, Interval>();\r
        \r
        public SortedMap<Amount<Duration>,Interval> getData(){\r
+               if ( !done )\r
+                       throw new IllegalStateException("Burn not complete!");\r
                return data;\r
        }\r
        \r
@@ -60,16 +78,36 @@ public class Burn {
        }\r
 \r
        public Amount<Duration> burnTime(){\r
-               return data.lastKey();\r
+               return getData().lastKey();\r
        }\r
        \r
        public Burn(Motor m){\r
+               try {\r
+                       m.validate();\r
+               } catch (ValidationException e) {\r
+                       throw new IllegalArgumentException("Invalid Motor: " + e.getMessage());\r
+               }\r
                motor = m;\r
-               burn();\r
        }\r
        \r
-       private void burn(){\r
+       public void addBurnProgressListener( BurnProgressListener bpl ){\r
+               bpls.add(bpl);\r
+               if ( done )\r
+                       bpl.burnComplete();\r
+       }\r
+       \r
+       public void burn(){\r
+               synchronized(this){\r
+                       if ( burning )\r
+                               throw new IllegalStateException("Already burning!");\r
+                       burning = true;\r
+               }\r
                log.info("Starting burn...");\r
+               for (BurnProgressListener bpl : bpls ){\r
+                       bpl.setProgress(0);\r
+               }\r
+               \r
+               int endPressureSteps = 0;\r
                long start = new Date().getTime();\r
                \r
                Amount<Length> regStep = Amount.valueOf(0.01, SI.MILLIMETER);\r
@@ -112,6 +150,13 @@ public class Burn {
                        \r
                        log.debug("Regression: " + next.regression);\r
                        \r
+                       //Update BurnProgressListeners\r
+                       Amount<Dimensionless> a = next.regression.divide(motor.getGrain().webThickness()).to(Dimensionless.UNIT);\r
+                       for (BurnProgressListener bpl : bpls ){\r
+                               bpl.setProgress((float)a.doubleValue(Dimensionless.UNIT));\r
+                       }\r
+\r
+                       \r
                        next.time = prev.time.plus(dt);\r
                        \r
                        //log.debug("Vold: " + motor.getGrain().volume(prev.regression).to(SI.MILLIMETER.pow(3)));\r
@@ -129,7 +174,7 @@ public class Burn {
                        //log.debug("Mass Gen Rate: " + mGenRate);\r
                        \r
                        //Calculate specific gas constant\r
-                       Amount specificGasConstant = Constants.R.divide(motor.getFuel().getCombustionProduct().getEffectiveMolarWeight());\r
+                       Amount<?> specificGasConstant = Constants.R.divide(motor.getFuel().getCombustionProduct().getEffectiveMolarWeight());\r
                        //This unit conversion helps JScience to convert nozzle flow rate to\r
                        //kg/s a little later on I verified the conversion by hand and\r
                        //JScience checks it too.\r
@@ -145,7 +190,7 @@ public class Burn {
                                Amount<Area> aStar = motor.getNozzle().throatArea();\r
                                double k = motor.getFuel().getCombustionProduct().getRatioOfSpecificHeats();\r
                                double kSide = Math.sqrt(k) * Math.pow((2/(k+1)) , (((k+1)/2)/(k-1)));\r
-                               Amount sqrtPart = specificGasConstant.times(chamberTemp).sqrt();\r
+                               Amount<?> sqrtPart = specificGasConstant.times(chamberTemp).sqrt();\r
                                mNozzle = pDiff.times(aStar).times(kSide).divide(sqrtPart).to(MassFlowRate.UNIT);\r
                                //log.debug("Mass Exit Rate: " + mNozzle.to(MassFlowRate.UNIT));                \r
                        }\r
@@ -190,9 +235,11 @@ public class Burn {
                        next.thrust = motor.getNozzle().thrust(next.chamberPressure, atmosphereicPressure, atmosphereicPressure, motor.getFuel().getCombustionProduct().getRatioOfSpecificHeats2Phase());\r
                        assert(positive(next.thrust));\r
                        \r
-                       if ( i > 100 && next.chamberPressure.approximates(atmosphereicPressure)){\r
-                               log.info("Pressure at Patm on step " + i);\r
-                               break;\r
+                       if ( i > 100 && next.chamberPressure.minus(atmosphereicPressure).abs().isLessThan(endPressure)){\r
+                               log.info("Pressure at ~Patm on step " + i);\r
+                               endPressureSteps++;\r
+                               if ( endPressureSteps > 5 )\r
+                                       break;\r
                        }\r
                        \r
                        data.put(data.lastKey().plus(dt), next);\r
@@ -200,6 +247,10 @@ public class Burn {
 \r
                long time = new Date().getTime() - start;\r
                log.info("Burn took " + time + " millis.");\r
+               done = true;\r
+               for (BurnProgressListener bpl : bpls ){\r
+                       bpl.burnComplete();\r
+               }\r
        }\r
        \r
        @SuppressWarnings("unchecked")\r
@@ -214,11 +265,11 @@ public class Burn {
        }\r
        \r
        public Amount<Pressure> pressure(Amount<Duration> time){\r
-               return data.get(time).chamberPressure;\r
+               return getData().get(time).chamberPressure;\r
        }\r
        \r
        public Amount<Force> thrust(Amount<Duration> time){\r
-               return data.get(time).thrust;\r
+               return getData().get(time).thrust;\r
        }\r
        \r
        public Amount<Dimensionless> kn(Amount<Length> regression){\r