| +class Layer { | ||
| + static TEMPERATURES = { | ||
| + TROPOSPHERE: [{ | ||
| + range: [0, 11], | ||
| + temperature: (altitude) => 288.15 - 6.5 * altitude | ||
| + }], | ||
| + STRATOSPHERE: [{ | ||
| + range: [11, 20], | ||
| + temperature: (altitude) => 216.65 | ||
| + }, { | ||
| + range: [20, 32], | ||
| + temperature: (altitude) => 196.65 + altitude | ||
| + }, { | ||
| + range: [32, 47], | ||
| + temperature: (altitude) => 139.05 + 2.8 * altitude | ||
| + }], | ||
| + MESOSPHERE: [{ | ||
| + range: [47, 51], | ||
| + temperature: (altitude) => 270.65 | ||
| + }, { | ||
| + range: [51, 71], | ||
| + temperature: (altitude) => 413.45 - 2.8 * altitude | ||
| + }, { | ||
| + range: [71, 84.852], | ||
| + temperature: (altitude) => 356.65 - 2.0 * altitude | ||
| + }], | ||
| + THERMOSPHERE: [{ | ||
| + range: [84.852, 91], | ||
| + temperature: (altitude) => 186.8673 | ||
| + }, { | ||
| + range: [91, 110], | ||
| + temperature: (altitude) => 263.1905 - 76.3232 * Math.sqrt(1 - Math.pow((altitude - 91) / -19.9429, 2)) | ||
| + }, { | ||
| + range: [110, 120], | ||
| + temperature: (altitude) => 240 + 12 * (altitude - 110) | ||
| + }, { | ||
| + range: [120, 1000], | ||
| + temperature: (altitude) => { | ||
| + const ξ = (altitude - 120) * (6356.766 + 120) / (6356.766 + altitude); | ||
| + return 1000 - 640 * Math.exp(-0.01875 * ξ); | ||
| + } | ||
| + }, { | ||
| + range: [1000, Infinity], | ||
| + temperature: (altitude) => 1000 | ||
| + }] | ||
| + }; | ||
| + | ||
| + constructor(name, range, pressure, density) { | ||
| + this.name = name; | ||
| + this.range = range; | ||
| + this.pressure = pressure; | ||
| + this.density = density; | ||
| + } | ||
| + | ||
| + within(altitude) { | ||
| + return altitude >= this.range[0] && altitude <= this.range[1]; | ||
| + } | ||
| + | ||
| + temperature(altitude) { | ||
| + let result; | ||
| + const temperatures = Layer.TEMPERATURES[this.name]; | ||
| + | ||
| + for (let t of temperatures) { | ||
| + if (altitude >= t.range[0] && altitude <= t.range[1]) { | ||
| + result = t.temperature(altitude); | ||
| + break; | ||
| + } | ||
| + } | ||
| + | ||
| + console.assert(result !== null && !isNaN(result)); | ||
| + return result; | ||
| + } | ||
| +} | ||
| + | ||
| +/** | ||
| + * See http://www.braeunig.us/space/atmmodel.htm | ||
| + */ | ||
| +class Atmosphere { | ||
| + static EQUATORIAL_RADIUS = 6378137.0; // m | ||
| + static FLATTENING = 1.0 / 298.25722356; | ||
| + static SPECIFIC_GAS_CONSTANT = 287.053; // J/kg-K | ||
| + | ||
| + static LAYERS = [ | ||
| + new Layer( | ||
| + "TROPOSPHERE", | ||
| + [0, 11], | ||
| + (altitude) => 101325.0 * Math.pow((288.15 / (288.15 - 6.5 * altitude)), (34.1632 / -6.5)), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "STRATOSPHERE", | ||
| + [11, 20], | ||
| + (altitude) => 22632.06 * Math.exp(-34.1632 * (altitude - 11) / 216.65), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "STRATOSPHERE", | ||
| + [20, 32], | ||
| + (altitude) => 5474.889 * Math.pow(216.65 / (216.65 + (altitude - 20)), 34.1632), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "STRATOSPHERE", | ||
| + [32, 47], | ||
| + (altitude) => 868.0187 * Math.pow(228.65 / (228.65 + 2.8 * (altitude - 32)), 34.1632 / 2.8), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "MESOSPHERE", | ||
| + [47, 51], | ||
| + (altitude) => 110.9063 * Math.exp(-34.1632 * (altitude - 47) / 270.65), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "MESOSPHERE", | ||
| + [51, 71], | ||
| + (altitude) => 66.93887 * Math.pow(270.65 / (270.65 - 2.8 * (altitude - 51)), 34.1632 / -2.8), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "MESOSPHERE", | ||
| + [71, 84.852], | ||
| + (altitude) => 3.956420 * Math.pow(214.65 / (214.65 - 2 * (altitude - 71)), 34.1632 / -2), | ||
| + (pressure, gas, temperature) => pressure / (gas * temperature) | ||
| + ), | ||
| + new Layer( | ||
| + "THERMOSPHERE", | ||
| + [84.852, Infinity], | ||
| + (altitude) => | ||
| + Math.exp(-0.0000000422012 * | ||
| + Math.pow(altitude, 5) + 0.0000213489 * | ||
| + Math.pow(altitude, 4) - 0.00426388 * | ||
| + Math.pow(altitude, 3) + 0.421404 * | ||
| + Math.pow(altitude, 2) - 20.8270 * altitude + 416.225), | ||
| + (altitude) => Math.exp( | ||
| + 0.000000075691 * | ||
| + Math.pow(altitude, 5) - 0.0000376113 * | ||
| + Math.pow(altitude, 4) + 0.0074765 * | ||
| + Math.pow(altitude, 3) - 0.743012 * | ||
| + Math.pow(altitude, 2) + 36.7280 * altitude - 729.346) | ||
| + ) | ||
| + ]; | ||
| + | ||
| + constructor(latitude) { | ||
| + this.latitude = latitude; | ||
| + } | ||
| + | ||
| + radius(latitude) { | ||
| + const phi = latitude * (Math.PI / 180); | ||
| + return Atmosphere.EQUATORIAL_RADIUS * (1 - Atmosphere.FLATTENING * Math.sin(phi) ** 2); | ||
| + } | ||
| + | ||
| + geopotential(altitude) { | ||
| + const radius = this.radius(this.latitude); | ||
| + return (altitude * radius) / (radius + altitude); | ||
| + } | ||
| + | ||
| + layerValue(altitude, callback) { | ||
| + let result; | ||
| + | ||
| + const gpAltitude = this.geopotential(altitude / 1000); | ||
| + | ||
| + for (let layer of Atmosphere.LAYERS) { | ||
| + if (layer.within(gpAltitude)) { | ||
| + result = callback(layer, gpAltitude); | ||
| + break; | ||
| + } | ||
| + } | ||
| + | ||
| + this.invariant(result); | ||
| + | ||
| + return result; | ||
| + } | ||
| + | ||
| + temperature(altitude) { | ||
| + return this.layerValue(altitude, (layer, gpAltitude) => layer.temperature(gpAltitude)); | ||
| + } | ||
| + | ||
| + airPressure(altitude) { | ||
| + return this.layerValue(altitude, (layer, gpAltitude) => layer.pressure(gpAltitude)); | ||
| + } | ||
| + | ||
| + airDensity(altitude) { | ||
| + return this.layerValue(altitude, (layer, gpAltitude) => { | ||
| + const temperature = layer.temperature(gpAltitude); | ||
| + const pressure = layer.pressure(gpAltitude); | ||
| + | ||
| + return layer.density(pressure, this.constructor.SPECIFIC_GAS_CONSTANT, temperature); | ||
| + }); | ||
| + } | ||
| + | ||
| + invariant(result) { | ||
| + console.assert(result !== null && !isNaN(result)); | ||
| + } | ||
| +} | ||
| + | ||
| +export default Atmosphere; | ||
| +import Atmosphere from './Atmosphere.js'; | ||
| + | ||
| class Earth { | ||
| static KELVIN = 273.15; | ||
| - static STATIC_PRESSURE = 101325; | ||
| - static TEMPERATURE_LAPSE_RATE = -0.0065; | ||
| - static GAS_CONSTANT = 8.31432; | ||
| - static GRAVITY = 9.80665; | ||
| - static MOLAR_MASS_AIR = 0.0289644; | ||
| - static SPECIFIC_GAS_CONSTANT_DRY_AIR = 287.85; | ||
| - static SPECIFIC_GAS_CONSTANT_WATER_VAPOUR = 461.495; | ||
| - static RADIUS_EQUATOR = 6378137.0; | ||
| - static RADIUS_POLAR = 6356752.0; | ||
| static MASS = 5.9722e24; | ||
| static SURFACE_GRAVITY = 9.780327; | ||
| static SIDEREAL_TIME = 86164.0905; | ||
| static SPEED_SOUND_0C = 331.3; | ||
| static GRAVITATIONAL_CONST = 6.67430e-11; | ||
| static STANDARD_GRAVITY = Earth.GRAVITATIONAL_CONST * Earth.MASS; | ||
| - | ||
| - constructor(temperature, humidity) { | ||
| - this.surface = temperature; | ||
| - this.humidity = humidity / 100; | ||
| - } | ||
| - kelvins(celsius) { | ||
| - return celsius + Earth.KELVIN; | ||
| + constructor(latitude) { | ||
| + this.atmosphere = new Atmosphere(latitude); | ||
| } | ||
| radians(degrees) { | ||
| return degrees * (Math.PI / 180); | ||
| } | ||
| radius(latitude) { | ||
| - return ( | ||
| - Earth.RADIUS_EQUATOR * | ||
| - (1 - | ||
| - ((Earth.RADIUS_EQUATOR - Earth.RADIUS_POLAR) / | ||
| - Earth.RADIUS_EQUATOR) * | ||
| - Math.pow(Math.sin(this.radians(latitude)), 2)) | ||
| - ); | ||
| + return this.atmosphere.radius(latitude); | ||
| } | ||
| soundSpeed(altitude) { | ||
| - const temperature = this.surface - (6 * altitude) / 1000; | ||
| - | ||
| - return Earth.SPEED_SOUND_0C * Math.sqrt(1 + temperature / Earth.KELVIN); | ||
| + const temperature = this.atmosphere.temperature(altitude); | ||
| + return Earth.SPEED_SOUND_0C * Math.sqrt((1 + temperature) / Earth.KELVIN); | ||
| } | ||
| airPressure(altitude) { | ||
| - const T = this.kelvins(this.surface); | ||
| - const P = Earth.STATIC_PRESSURE; | ||
| - const g = Earth.GRAVITY; | ||
| - const M = Earth.MOLAR_MASS_AIR; | ||
| - const R = Earth.GAS_CONSTANT; | ||
| - const L = Earth.TEMPERATURE_LAPSE_RATE; | ||
| - | ||
| - let pressure; | ||
| - | ||
| - if (altitude <= 11000) { | ||
| - pressure = P * Math.pow((1 + (L / T) * altitude), -(g * M) / (R * L)); | ||
| - } else { | ||
| - const T1 = 216.65; | ||
| - const P1 = P * Math.pow((1 + (L / T) * 11000), -(g * M) / (R * L)); | ||
| - | ||
| - pressure = P1 * Math.exp(-(g * M) * (altitude - 11000) / (R * T1)); | ||
| - } | ||
| - | ||
| - return pressure; | ||
| - } | ||
| - | ||
| - saturationVaporPressure(temperature) { | ||
| - const BASE_PRESSURE = 6.1078; | ||
| - const COEFFICIENTS = [ | ||
| - 0.99999683, | ||
| - -0.90826951e-2, | ||
| - 0.78736169e-4, | ||
| - -0.61117958e-6, | ||
| - 0.43884187e-8, | ||
| - -0.29883885e-10, | ||
| - 0.21874425e-12, | ||
| - -0.17892321e-14, | ||
| - 0.11112018e-16, | ||
| - -0.30994571e-19 | ||
| - ]; | ||
| - | ||
| - let polynomial = 0; | ||
| - | ||
| - for (let i = 0; i < COEFFICIENTS.length; i++) { | ||
| - polynomial += COEFFICIENTS[i] * Math.pow(temperature, i); | ||
| - } | ||
| - | ||
| - return (BASE_PRESSURE / Math.pow(polynomial, 8)) * 100; | ||
| - } | ||
| - | ||
| - waterVaporPressure(humidity, saturationVaporPressure) { | ||
| - return humidity * saturationVaporPressure; | ||
| - } | ||
| - | ||
| - dryAirPressure(totalPressure, waterVaporPressure) { | ||
| - return totalPressure - waterVaporPressure; | ||
| + return this.atmosphere.airPressure(altitude); | ||
| } | ||
| airDensity(altitude) { | ||
| - const pressure = this.airPressure(altitude); | ||
| - const kelvins = this.kelvins(this.surface); | ||
| - const T_DRY_AIR = Earth.SPECIFIC_GAS_CONSTANT_DRY_AIR * kelvins; | ||
| - const T_WET_AIR = Earth.SPECIFIC_GAS_CONSTANT_WATER_VAPOUR * kelvins; | ||
| - const vapourSaturated = this.saturationVaporPressure(this.surface); | ||
| - const vapourWater = this.waterVaporPressure(this.humidity, vapourSaturated); | ||
| - const dryAir = this.dryAirPressure(pressure, vapourWater); | ||
| - const airDensity = dryAir / T_DRY_AIR + vapourWater / T_WET_AIR; | ||
| + const density = this.atmosphere.airDensity(altitude); | ||
| - return Math.max(0, airDensity); | ||
| + return Math.max(0, density); | ||
| } | ||
| } | ||
| console.assert(specificImpulse > 1); | ||
| + this.wet = wet; | ||
| this.dry = wet * 0.1; | ||
| - this.mass = wet; | ||
| this.targetMass = this.dry + payload; | ||
| this.radius = diameter / 2.0; | ||
| this.specificImpulse = specificImpulse; | ||
| this.totalThrust = 0.0; | ||
| this.targetAltitude = 0.0; | ||
| - const crossSectionArea = Math.PI * Math.pow(this.radius, 2); | ||
| + const crossSectionArea = Math.PI * this.radius ** 2; | ||
| this.ballisticCoefficient = dragCoefficient * crossSectionArea; | ||
| this.flightRecorder = false; | ||
| this.launched = false; | ||
| this.relocated = false; | ||
| this.world = false; | ||
| - this.massFlowRate = NaN; | ||
| + this.massFlowRate = 5.0; | ||
| } | ||
| this.maxAcceleration = 5.0 * LOCAL_GRAVITY; | ||
| this.rotationalSpeed = this.planet.rotationalSpeed(latitude, altitude); | ||
| - this.exhaustVelocity = this.specificImpulse * LOCAL_GRAVITY; | ||
| + this.vExhaust = this.specificImpulse * LOCAL_GRAVITY; | ||
| this.relocated = true; | ||
| } | ||
| launch(hVelocity, vVelocity) { | ||
| console.assert(!this.launched); | ||
| + | ||
| + this.launched = true; | ||
| this.hVelocity = hVelocity + this.rotationalSpeed; | ||
| this.vVelocity = vVelocity; | ||
| - this.launched = true; | ||
| + | ||
| + const dragForce = this.drag(); | ||
| + const absDragForce = Math.abs(dragForce[1]); | ||
| + | ||
| + this.massFlowRate = absDragForce / this.vExhaust * 1.001; | ||
| + | ||
| + const vOrb = this.planet.orbitalSpeed(this.latitude, this.altitude); | ||
| + const vReq = vOrb - this.rotationalSpeed; | ||
| + const vRocket = this.vExhaust * Math.log(this.wet / this.targetMass); | ||
| + | ||
| + console.info(`orbital and horizontal = ${vOrb} ${this.hVelocity}`); | ||
| + console.info(`dry + payload = ${this.targetMass}`); | ||
| + console.info(`Δv = ${vReq}; Δv rocket = ${vRocket}`); | ||
| + console.info(`mass flow rate = ${this.massFlowRate}`); | ||
| + console.info(`drag force = ${dragForce}`); | ||
| + console.info(`hVelocity = ${hVelocity}`); | ||
| + console.info(`vVelocity = ${vVelocity}`); | ||
| } | ||
| const rho = this.planet.airDensity(this.altitude); | ||
| - const netHorizontalVelocity = this.hVelocity - this.rotationalSpeed; | ||
| - const totalSpeed = Math.sqrt(Math.pow(netHorizontalVelocity, 2) + Math.pow(this.vVelocity, 2)); | ||
| - const magnitude = 0.5 * rho * this.ballisticCoefficient * Math.pow(totalSpeed, 2); | ||
| - const forceRatio = [-netHorizontalVelocity / totalSpeed, -this.vVelocity / totalSpeed]; | ||
| + const hVelocityNet = this.hVelocity - this.rotationalSpeed; | ||
| + const totalSpeed = Math.sqrt(hVelocityNet ** 2 + this.vVelocity ** 2); | ||
| + const magnitude = 0.5 * rho * this.ballisticCoefficient * totalSpeed ** 2; | ||
| + const forceRatio = [-hVelocityNet / totalSpeed, -this.vVelocity / totalSpeed]; | ||
| + | ||
| + console.info(`rho = ${rho}`); | ||
| + console.info(`hV (net) = ${hVelocityNet}`); | ||
| + console.info(`vV = ${this.vVelocity}`); | ||
| + console.info(`totalSpeed = ${totalSpeed}`); | ||
| + console.info(`bC = ${this.ballisticCoefficient}`); | ||
| + console.info(`magnitude = ${magnitude}`); | ||
| + console.info(`ratio = [${forceRatio}]`); | ||
| return [forceRatio[0] * magnitude, forceRatio[1] * magnitude]; | ||
| } | ||
| flying() { | ||
| - console.assert(this.mass > 0); | ||
| + console.assert(this.wet > 0); | ||
| console.assert(this.targetMass > 0); | ||
| console.assert(this.targetAltitude > 0); | ||
| return ( | ||
| - this.mass > this.targetMass && | ||
| - this.mass > this.dry && | ||
| + this.wet > this.targetMass && | ||
| + this.wet > this.dry && | ||
| this.altitude < this.targetAltitude | ||
| ); | ||
| } | ||
| fly(time) { | ||
| + console.assert(this.launched); | ||
| console.assert(time > 0); | ||
| const dragForce = this.drag(); | ||
| - | ||
| - if (isNaN(this.massFlowRate)) { | ||
| - this.massFlowRate = Math.abs(dragForce[1]) / this.exhaustVelocity * 1.001; | ||
| - } | ||
| - | ||
| - console.debug(`mass flow rate = ${this.massFlowRate}`); | ||
| + const absDragForce = Math.abs(dragForce[1]); | ||
| - this.totalThrust = this.exhaustVelocity * this.massFlowRate; | ||
| + this.totalThrust = this.vExhaust * this.massFlowRate; | ||
| const thrusting = this.totalThrust > 0; | ||
| const vThrust = thrusting ? -dragForce[1] : 0.0; | ||
| - const hThrust = thrusting ? Math.sqrt(Math.pow(this.totalThrust, 2) - Math.pow(vThrust, 2)) : 0.0; | ||
| + const hThrust = thrusting ? Math.sqrt(this.totalThrust ** 2 - vThrust ** 2) : 0.0; | ||
| - console.assert(this.mass > 0); | ||
| + console.assert(this.wet > 0); | ||
| - this.hAcceleration = dragForce[0] / this.mass + hThrust / this.mass; | ||
| - this.vAcceleration = dragForce[1] / this.mass + this.planet.gravity(this.latitude, this.altitude) + vThrust / this.mass; | ||
| + this.hAcceleration = dragForce[0] / this.wet + hThrust / this.wet; | ||
| + this.vAcceleration = dragForce[1] / this.wet + this.planet.gravity(this.latitude, this.altitude) + vThrust / this.wet; | ||
| // Records the initial values and all subsequent deltas. | ||
| this.record(time); | ||
| this.hVelocity += this.hAcceleration * time; | ||
| this.vVelocity += this.vAcceleration * time; | ||
| - this.azimuth += this.hVelocity * time + 0.5 * this.hAcceleration * time * time; | ||
| - this.altitude += this.vVelocity * time + 0.5 * this.vAcceleration * time * time; | ||
| + this.azimuth += this.hVelocity * time + 0.5 * this.hAcceleration * time ** 2; | ||
| + this.altitude += this.vVelocity * time + 0.5 * this.vAcceleration * time ** 2; | ||
| - this.mass -= this.massFlowRate * time; | ||
| + this.wet -= this.massFlowRate * time; | ||
| + | ||
| + console.log(`Time: ${time}, Altitude: ${this.altitude}, hVelocity: ${this.hVelocity}, vVelocity: ${this.vVelocity}`); | ||
| + console.log(`Drag: ${dragForce}, hAcc: ${this.hAcceleration}, vAcc: ${this.vAcceleration}, Mass: ${this.wet}, Mass Flow Rate: ${this.massFlowRate}`); | ||
| if (this.hVelocity < this.targetVelocity) { | ||
| const accelerationMagnitude = Math.sqrt( | ||
| - Math.pow(this.hAcceleration, 2) + | ||
| - Math.pow(this.vAcceleration, 2) | ||
| + this.hAcceleration ** 2 + this.vAcceleration ** 2 | ||
| ); | ||
| - | ||
| - const excessiveAcceleration = accelerationMagnitude > this.maxAcceleration; | ||
| - const reducibleFlowRate = this.exhaustVelocity * this.massFlowRate * 0.98 > Math.abs(dragForce[1]); | ||
| - if (excessiveAcceleration && reducibleFlowRate) { | ||
| + if (accelerationMagnitude > this.maxAcceleration && | ||
| + this.vExhaust * this.massFlowRate * 0.98 > absDragForce) { | ||
| this.massFlowRate *= 0.99; | ||
| + console.log('Limiting Mfr :', this.massFlowRate); | ||
| } | ||
| - if (this.exhaustVelocity * this.massFlowRate < Math.abs(dragForce[1])) { | ||
| - this.massFlowRate = Math.abs(dragForce[1]) / this.exhaustVelocity * 1.1; | ||
| + if (this.vExhaust * this.massFlowRate < absDragForce) { | ||
| + this.massFlowRate = absDragForce / this.vExhaust * 1.1; | ||
| } | ||
| } else { | ||
| this.blackBox.verticalVelocity(time, this.vVelocity); | ||
| this.blackBox.verticalAcceleration(time, this.vAcceleration); | ||
| - this.blackBox.thrustAcceleration(time, this.totalThrust / this.mass); | ||
| - this.blackBox.mass(time, this.mass); | ||
| + this.blackBox.thrustAcceleration(time, this.totalThrust / this.wet); | ||
| + this.blackBox.mass(time, this.wet); | ||
| + | ||
| + const magnitude = Math.sqrt(this.hAcceleration ** 2 + this.vAcceleration ** 2) / 9.8; | ||
| + const percentSpeed = this.hVelocity / this.targetVelocity * 100.0; | ||
| + | ||
| + console.info('time, alt, hVel :', | ||
| + time, | ||
| + this.altitude, | ||
| + this.hVelocity, percentSpeed, this.wet, magnitude) | ||
| } | ||
| } | ||
| // Inputs | ||
| - const SURFACE_TEMPERATURE = 25; // Celsius | ||
| - const RELATIVE_HUMIDITY = 86.34; // per cent | ||
| - const INITIAL_VELOCITY = 8; // Mach | ||
| - const INITIAL_LATITUDE = 1.469167; // meters | ||
| - const INITIAL_ALTITUDE = 6212; // meters | ||
| + const INITIAL_VELOCITY = 8.0; // Mach | ||
| + const INITIAL_LATITUDE = 1.469167; // degrees | ||
| + const INITIAL_ALTITUDE = 6212.0; // meters | ||
| const TARGET_ALTITUDE = 400.0 * 1000.0; // meters | ||
| const ROCKET_DIAMETER = 0.6; | ||
| - const WET_MASS = 250; | ||
| - const PAYLOAD_MASS = 25; | ||
| - const DRAG_COEFFICIENT = 0.219; | ||
| - const SPECIFIC_IMPULSE = 1400.0; // seconds | ||
| + const WET_MASS = 250.0; | ||
| + const PAYLOAD_MASS = 25.0; | ||
| + const DRAG_COEFFICIENT = 0.29; | ||
| + const SPECIFIC_IMPULSE = 1700.0; // seconds | ||
| - const earth = new Earth(SURFACE_TEMPERATURE, RELATIVE_HUMIDITY); | ||
| + const earth = new Earth(INITIAL_LATITUDE); | ||
| const SPEED_OF_SOUND = earth.soundSpeed(INITIAL_ALTITUDE); | ||
| + console.info( `sound speed = ${SPEED_OF_SOUND}` ); | ||
| + | ||
| const INITIAL_HORIZONTAL_VELOCITY = 0; | ||
| const INITIAL_VERTICAL_VELOCITY = INITIAL_VELOCITY * SPEED_OF_SOUND; | ||
| while (rocket.flying()) { | ||
| - time += 0.01; | ||
| - time = parseFloat(time.toFixed(2)); | ||
| + time = parseFloat((time + 0.01).toFixed(2)); | ||
| rocket.fly(time); | ||
| } | ||
| Author | djarvis <email> |
|---|---|
| Date | 2024-12-02 01:45:14 GMT-0800 |
| Commit | 1ed186144c7d72934fb472519145403798ca1082 |
| Parent | 881e556 |
| Delta | 285 lines added, 134 lines removed, 151-line increase |