| Author | djarvis <email> |
|---|---|
| Date | 2024-12-03 23:52:42 GMT-0800 |
| Commit | 3cb38eb279a9a927de5de812b72c0bf54de87c8c |
| Parent | e89468d |
| ``` | ||
| +Transform the variables: | ||
| + | ||
| +``` bash | ||
| +java -jar $HOME/archive/saxon/saxon-he-12.5.jar launch.xsl ../../../index.xsl | ||
| +``` | ||
| class Rocket { | ||
| + /** | ||
| + * Initializes the Rocket object with wet mass and payload mass. | ||
| + * Calculates dry mass and sets initial azimuth and altitude. | ||
| + * | ||
| + * @param {number} wetMass - Total mass including fuel (kilograms). | ||
| + * @param {number} payloadMass - Mass of the payload (kilograms). | ||
| + */ | ||
| constructor(wetMass, payloadMass) { | ||
| - this.wetMass = wetMass; | ||
| this.payloadMass = payloadMass; | ||
| - this.dryMass = this.wetMass * 0.1 + this.payloadMass; | ||
| - this.mass = this.wetMass; | ||
| + this.dryMass = wetMass * 0.1 + this.payloadMass; | ||
| + this.mass = wetMass; | ||
| this.azimuth = 0.0; | ||
| + this.altitude = 0.0; | ||
| } | ||
| + /** | ||
| + * Associates the rocket with a specific planet. | ||
| + * | ||
| + * @param {object} planet - An object representing the planet. | ||
| + */ | ||
| on(planet) { | ||
| this.planet = planet; | ||
| } | ||
| + /** | ||
| + * Sets the fuselage properties including diameter, drag coefficient, | ||
| + * and specific impulse. | ||
| + * | ||
| + * @param {number} diameter - Diameter of the rocket (meters). | ||
| + * @param {number} dragCoefficient - Drag coefficient (dimensionless). | ||
| + * @param {number} specificImpulse - Specific impulse (seconds). | ||
| + */ | ||
| fuselage(diameter, dragCoefficient, specificImpulse) { | ||
| const crossSectionalArea = Math.PI * (diameter / 2) ** 2; | ||
| this.ballisticCoefficient = dragCoefficient * crossSectionalArea; | ||
| this.specificImpulse = specificImpulse; | ||
| } | ||
| + /** | ||
| + * Sets the rocket's latitude and altitude for the launch. | ||
| + * | ||
| + * @param {number} latitude - Latitude of the launch site (degrees). | ||
| + * @param {number} altitude - Altitude of the launch site (meters). | ||
| + */ | ||
| translocate(latitude, altitude) { | ||
| this.latitude = latitude; | ||
| this.altitude = altitude; | ||
| } | ||
| + /** | ||
| + * Sets the target apogee (maximum altitude) for the rocket's flight. | ||
| + * | ||
| + * @param {number} targetAltitude - Target altitude (meters). | ||
| + */ | ||
| apogee(targetAltitude) { | ||
| this.targetAltitude = targetAltitude; | ||
| } | ||
| + /** | ||
| + * Calculates the gravitational acceleration at the current position. | ||
| + * | ||
| + * @returns {number} Gravitational acceleration (meters per second squared). | ||
| + */ | ||
| gravity() { | ||
| return this.planet.gravity(this.latitude, this.altitude); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Calculates the rotational speed of the planet at the current latitude | ||
| + * and altitude. | ||
| + * | ||
| + * @returns {number} Rotational speed (meters per second). | ||
| + */ | ||
| + rotational() { | ||
| + return this.planet.rotationalSpeed(this.latitude, this.altitude); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Calculates the orbital speed required at the current latitude and | ||
| + * target altitude. | ||
| + * | ||
| + * @returns {number} Orbital speed (meters per second). | ||
| + */ | ||
| + orbital() { | ||
| + return this.planet.orbitalSpeed(this.latitude, this.targetAltitude); | ||
| } | ||
| + /** | ||
| + * Launches the rocket with an initial velocity. | ||
| + * | ||
| + * @param {number} initialVelocity - Initial velocity as a fraction of the | ||
| + * speed of sound (dimensionless). | ||
| + */ | ||
| launch(initialVelocity) { | ||
| this.exhaustVelocity = this.specificImpulse * this.gravity(); | ||
| this.maximumAcceleration = 5.0 * this.gravity(); | ||
| this.vVelocity = initialVelocity * this.planet.soundSpeed(this.altitude); | ||
| - this.hVelocity = this.planet.rotationalSpeed(this.latitude, this.altitude); | ||
| - this.hVelocityTarget = this.planet.orbitalSpeed(this.latitude, this.targetAltitude); | ||
| - this.massFlowRate = Math.abs(this.drag()[1]) / this.exhaustVelocity * 1.001; | ||
| + this.hVelocity = this.rotational(); | ||
| + this.hVelocityTarget = this.orbital(); | ||
| + this.massFlowRate = Math.abs(this.drag()[1]) / | ||
| + this.exhaustVelocity * 1.001; | ||
| } | ||
| + /** | ||
| + * Attaches a flight recorder to the rocket. | ||
| + * | ||
| + * @param {object} blackBox - An object representing the flight recorder. | ||
| + */ | ||
| recorder(blackBox) { | ||
| this.flightRecorder = blackBox; | ||
| } | ||
| + /** | ||
| + * Calculates the drag force experienced by the rocket. | ||
| + * | ||
| + * @returns {Array<number>} Drag force components [horizontal, vertical] | ||
| + * (newtons). | ||
| + */ | ||
| drag() { | ||
| const rho = this.planet.airDensity(this.altitude); | ||
| - const vHorNet = this.hVelocity - this.planet.rotationalSpeed(this.latitude, this.altitude); | ||
| - const totalSpeed = Math.sqrt(vHorNet ** 2 + this.vVelocity ** 2); | ||
| - const magnitude = 0.5 * rho * this.ballisticCoefficient * totalSpeed ** 2; | ||
| - const ratio = [-vHorNet / totalSpeed, -this.vVelocity / totalSpeed]; | ||
| + const vHorNet = this.hVelocity - this.rotational(); | ||
| + const speedTotal = this.magnitude(vHorNet, this.vVelocity); | ||
| + const dragForce = 0.5 * rho * this.ballisticCoefficient * | ||
| + speedTotal ** 2; | ||
| + const ratio = [-vHorNet / speedTotal, -this.vVelocity / speedTotal]; | ||
| - return [ratio[0] * magnitude, ratio[1] * magnitude]; | ||
| + return [ratio[0] * dragForce, ratio[1] * dragForce]; | ||
| } | ||
| + /** | ||
| + * Checks if the rocket is still flying, i.e., if it has fuel and has not | ||
| + * reached the target altitude. | ||
| + * | ||
| + * @returns {boolean} True if the rocket is flying. | ||
| + */ | ||
| flying() { | ||
| return this.mass > this.dryMass && this.altitude < this.targetAltitude; | ||
| } | ||
| + /** | ||
| + * Simulates the flight of the rocket for a given time step. | ||
| + * | ||
| + * @param {number} step - Time step for the simulation (seconds). | ||
| + */ | ||
| fly(step) { | ||
| const drag = this.drag(); | ||
| if (this.hVelocity < this.hVelocityTarget) { | ||
| - const accMag = Math.sqrt(this.hAcceleration ** 2 + this.vAcceleration ** 2); | ||
| + const accMag = this.magnitude(this.hAcceleration, this.vAcceleration); | ||
| const absDrag = Math.abs(drag[1]); | ||
| - if (accMag > this.maximumAcceleration && this.exhaustVelocity * this.massFlowRate * 0.98 > absDrag) { | ||
| + if (accMag > this.maximumAcceleration && | ||
| + this.exhaustVelocity * this.massFlowRate * 0.98 > absDrag) { | ||
| this.massFlowRate *= 0.99; | ||
| } | ||
| + | ||
| if (this.exhaustVelocity * this.massFlowRate < absDrag) { | ||
| this.massFlowRate = absDrag / this.exhaustVelocity * 1.1; | ||
| } | ||
| } else { | ||
| this.massFlowRate = 0.0; | ||
| } | ||
| - this.totalThrust = this.exhaustVelocity * this.massFlowRate; | ||
| + // Compute the total thrust. | ||
| + this.tThrust = this.exhaustVelocity * this.massFlowRate; | ||
| - const thrusting = this.totalThrust > 0; | ||
| + const thrusting = this.tThrust > 0; | ||
| const vThrust = thrusting ? -drag[1] : 0; | ||
| - const hThrust = thrusting ? Math.sqrt(this.totalThrust ** 2 - vThrust ** 2) : 0; | ||
| + const hThrust = thrusting ? Math.sqrt(this.tThrust ** 2 - vThrust ** 2) : 0; | ||
| this.hAcceleration = drag[0] / this.mass + hThrust / this.mass; | ||
| } | ||
| + /** | ||
| + * Records the rocket's flight data at a given time. | ||
| + * | ||
| + * @param {number} time - The current simulation time (seconds). | ||
| + */ | ||
| record(time) { | ||
| if (this.flightRecorder) { | ||
| this.flightRecorder.azimuth(time, this.azimuth); | ||
| this.flightRecorder.horizontalVelocity(time, this.hVelocity); | ||
| this.flightRecorder.horizontalAcceleration(time, this.hAcceleration); | ||
| this.flightRecorder.altitude(time, this.altitude); | ||
| this.flightRecorder.verticalVelocity(time, this.vVelocity); | ||
| this.flightRecorder.verticalAcceleration(time, this.vAcceleration); | ||
| - this.flightRecorder.thrustAcceleration(time, this.totalThrust / this.mass); | ||
| + this.flightRecorder.thrustAcceleration(time, this.tThrust / this.mass); | ||
| this.flightRecorder.mass(time, this.mass); | ||
| } | ||
| + } | ||
| + | ||
| + /** | ||
| + * Calculates the magnitude of two values. | ||
| + * | ||
| + * @param {number} x - First value. | ||
| + * @param {number} y - Second value. | ||
| + * @returns {number} Magnitude. | ||
| + */ | ||
| + magnitude(x, y) { | ||
| + return Math.sqrt(x ** 2 + y ** 2); | ||
| } | ||
| } | ||
| import FlightRecorder from './FlightRecorder.js'; | ||
| import Rocket from './Rocket.js'; | ||
| +import $ from './Selector.js'; | ||
| document.querySelectorAll('.submit').forEach(button => { | ||
| button.addEventListener('click', function (event) { | ||
| event.preventDefault(); | ||
| - 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 INITIAL_VELOCITY = $('#initial_velocity').val(); // Mach | ||
| + const INITIAL_LATITUDE = $('#initial_latitude').val(); // degrees | ||
| + const INITIAL_ALTITUDE = $('#initial_altitude').val(); // meters | ||
| + const TARGET_ALTITUDE = $('#target_altitude').val() * 1000.0; // meters | ||
| - const ROCKET_DIAMETER = 0.6; | ||
| - const WET_MASS = 250.0; | ||
| - const PAYLOAD_MASS = 25.0; | ||
| - const DRAG_COEFFICIENT = 0.29; | ||
| - const SPECIFIC_IMPULSE = 1400.0; // seconds | ||
| + const ROCKET_DIAMETER = $('#diameter').val(); | ||
| + const WET_MASS = $('#wet_mass').val(); | ||
| + const PAYLOAD_MASS = $('#payload_mass').val(); | ||
| + const DRAG_COEFFICIENT = $('#drag_coefficient').val(); | ||
| + const SPECIFIC_IMPULSE = $('#specific_impulse').val(); // seconds | ||
| const blackBox = new FlightRecorder(); |
| -<!DOCTYPE html><!DOCTYPE HTML><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1user-scalable=yes"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="description" content="A hard sci-fi novel about automated food production, sentient machines, and surveillance societies."><link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"><style media="screen" type="text/css"> | ||
| +<!DOCTYPE HTML><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1user-scalable=yes"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="description" content="A hard sci-fi novel about automated food production, sentient machines, and surveillance societies."><link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"><style media="screen" type="text/css"> | ||
| /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}/*# sourceMappingURL=normalize.min.css.map */ | ||
| </style><link rel="stylesheet" type="text/css" media="screen" href="../../../themes/simple.css"><link rel="stylesheet" type="text/css" media="screen" href="../../../themes/form.css"><link rel="stylesheet" type="text/css" media="screen" href="../../../themes/calculator.css"><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css" integrity="sha512-fHwaWebuwA7NSF5Qg/af4UeDx9XqUpYpOGgubo3yWu+b2IQR4UeQwbb42Ti7gVAjNtVoI/I9TEoYeu9omwcC6g==" crossorigin="anonymous" referrerpolicy="no-referrer"><link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:200|Libre+Baskerville" rel="stylesheet"></head><body><div class="page"><header class="section header"><h1>Orbital Launch Insertion</h1></header><nav class="section menu"></nav><main class="section content"><p> | ||
| Helps determine whether a payload can be inserted into a stable orbit. | ||
| Form field values are explained in the <a href="#equations">Equations</a> | ||
| section, below. | ||
| - </p><form id="calculator"><button class="submit">Simulate</button><fieldset id="environment"><legend>Environment</legend><label for="surface_temperature">Surface temperature (℃)</label><input tabindex="1" class="variable" type="number" step="1" min="-80" value="25" id="surface_temperature" name="surface_temperature"><label for="relative_humidity">Relative humidity</label><input tabindex="2" class="variable" type="number" step="0.01" min="0" value="86.34" id="surface_temperature" name="surface_temperature"></fieldset><fieldset id="mission"><legend>Mission</legend><label for="initial_velocity">Initial velocity (Mach)</label><input tabindex="3" class="variable" type="number" step="1" min="0" value="8" id="initial_velocity" name="initial_velocity"><label for="initial_latitude">Initial latitude (°)</label><input tabindex="4" class="variable" type="number" step="any" min="0" value="1.469167" id="initial_latitude" name="initial_latitude"><label for="initial_altitude">Initial altitude (m)</label><input tabindex="5" class="variable" type="number" step="1" min="0" value="6212" id="initial_altitude" name="initial_altitude"><label for="target_altitude">Target altitude (km)</label><input tabindex="6" class="variable" type="number" step="1" min="1" value="400" id="target_altitude" name="target_altitude"></fieldset><fieldset id="rocket"><legend>Rocket</legend><label for="diameter">Diameter (m)</label><input tabindex="7" class="variable" type="number" step="any" min="0" value="0.6" id="diameter" name="diameter"><label for="wet_mass">Wet mass (kg)</label><input tabindex="8" class="variable" type="number" step="1" min="1" value="250" id="wet_mass" name="wet_mass" oninput="this.value = Math.abs(this.value)"><label for="payload">Payload mass (kg)</label><input tabindex="9" class="variable" type="number" step="1" min="1" value="25" id="payload_mass" name="payload_mass" oninput="this.value = Math.abs(this.value)"><label for="drag_coefficient">Drag coefficient</label><input tabindex="10" class="variable" type="number" step="any" min="0" value="0.219" id="drag_coefficient" name="drag_coefficient"><label for="specific_impulse">Specific impulse (s)</label><input tabindex="11" class="variable" type="number" step="1" min="1" value="1700" id="specific_impulse" name="specific_impulse"></fieldset><button class="submit">Simulate</button><fieldset id="result"><legend>Result</legend><div id="plots"></div></fieldset></form><a name="equations"></a><h1>Equations</h1><p> | ||
| + </p><form id="calculator"><button class="submit">Simulate</button><fieldset id="mission"><legend>Mission</legend><label for="initial_velocity">Initial velocity (Mach)</label><input tabindex="3" class="variable" type="number" step="1" min="0" value="8" id="initial_velocity" name="initial_velocity"><label for="initial_latitude">Initial latitude (°)</label><input tabindex="4" class="variable" type="number" step="any" min="0" value="1.469167" id="initial_latitude" name="initial_latitude"><label for="initial_altitude">Initial altitude (m)</label><input tabindex="5" class="variable" type="number" step="1" min="0" value="6212" id="initial_altitude" name="initial_altitude"><label for="target_altitude">Target altitude (km)</label><input tabindex="6" class="variable" type="number" step="1" min="1" value="400" id="target_altitude" name="target_altitude"></fieldset><fieldset id="rocket"><legend>Rocket</legend><label for="diameter">Diameter (m)</label><input tabindex="7" class="variable" type="number" step="any" min="0" value="0.6" id="diameter" name="diameter"><label for="wet_mass">Wet mass (kg)</label><input tabindex="8" class="variable" type="number" step="1" min="1" value="250" id="wet_mass" name="wet_mass" oninput="this.value = Math.abs(this.value)"><label for="payload">Payload mass (kg)</label><input tabindex="9" class="variable" type="number" step="1" min="1" value="25" id="payload_mass" name="payload_mass" oninput="this.value = Math.abs(this.value)"><label for="drag_coefficient">Drag coefficient</label><input tabindex="10" class="variable" type="number" step="any" min="0" value="0.219" id="drag_coefficient" name="drag_coefficient"><label for="specific_impulse">Specific impulse (s)</label><input tabindex="11" class="variable" type="number" step="1" min="1" value="1700" id="specific_impulse" name="specific_impulse"></fieldset><button class="submit">Simulate</button><fieldset id="result"><legend>Result</legend><div id="plots"></div></fieldset></form><a name="equations"></a><h1>Equations</h1><p> | ||
| This section describes equation inputs and outputs. | ||
| </p><h2>Cross-section area</h2><p> |
| <button class="submit">Simulate</button> | ||
| - <fieldset id="environment"> | ||
| - <legend>Environment</legend> | ||
| - <label for="surface_temperature">Surface temperature (℃)</label> | ||
| - <input tabindex="1" | ||
| - class="variable" type="number" step="1" min="-80" value="25" | ||
| - id="surface_temperature" name="surface_temperature" /> | ||
| - | ||
| - <label for="relative_humidity">Relative humidity</label> | ||
| - <input tabindex="2" | ||
| - class="variable" type="number" step="0.01" min="0" value="86.34" | ||
| - id="surface_temperature" name="surface_temperature" /> | ||
| - </fieldset> | ||
| - | ||
| <fieldset id="mission"> | ||
| <legend>Mission</legend> |
| <rocket> | ||
| <sound>thoom</sound> | ||
| - <fuel>metallic hydrogen</fuel> | ||
| + <fuel> | ||
| + <name>metallic hydrogen</name> | ||
| + <impulse> | ||
| + <value>1400</value> | ||
| + <unit>s</unit> | ||
| + </impulse> | ||
| + </fuel> | ||
| <dilute>water</dilute> | ||
| <combat>basilisk</combat> | ||
| - <speed> | ||
| - <value>29000</value> | ||
| - <unit>kilometers per hour</unit> | ||
| - </speed> | ||
| + <mass> | ||
| + <wet> | ||
| + <value>250</value> | ||
| + <unit>kg</unit> | ||
| + </wet> | ||
| + <dry> | ||
| + <value>10</value> | ||
| + <unit>%</unit> | ||
| + </dry> | ||
| + <payload> | ||
| + <value>25</value> | ||
| + <unit>kg</unit> | ||
| + </payload> | ||
| + </mass> | ||
| + <properties> | ||
| + <diameter> | ||
| + <value>0.5</value> | ||
| + <unit>m</unit> | ||
| + </diameter> | ||
| + <length> | ||
| + <value>4</value> | ||
| + <unit>m</unit> | ||
| + </length> | ||
| + <drag> | ||
| + <value>0.219</value> | ||
| + </drag> | ||
| + </properties> | ||
| + <launch> | ||
| + <temperature> | ||
| + <surface> | ||
| + <value>25</value> | ||
| + <unit>Celsius</unit> | ||
| + </surface> | ||
| + </temperature> | ||
| + <velocity> | ||
| + <value>8</value> | ||
| + <unit>Mach</unit> | ||
| + </velocity> | ||
| + <latitude> | ||
| + <value>1.469167</value> | ||
| + <unit>degrees</unit> | ||
| + </latitude> | ||
| + <altitude> | ||
| + <value>6212</value> | ||
| + <unit>m</unit> | ||
| + </altitude> | ||
| + </launch> | ||
| </rocket> | ||
| <satellite> |
| <xsl:template match="/"> | ||
| - <xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text> | ||
| <html lang="en"> | ||
| <head> |
| Delta | 193 lines added, 47 lines removed, 146-line increase |
|---|