vikram-physics/orbital-ascension.html

593 lines
22 KiB
HTML
Raw Normal View History

2026-07-18 21:42:30 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Orbital Ascension Simulator | Anthony Figueroa</title>
<style>
:root {
--void: #0a0a0f;
--nebula: #1a1a2e;
--plasma: #ff6b35;
--ice: #00d9ff;
--lattice: #4a4a5a;
--text: #e8e8f0;
--accent: #ffd700;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'SF Mono', 'Menlo', monospace;
background: var(--void);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
}
header {
padding: 2rem;
border-bottom: 1px solid var(--lattice);
position: relative;
}
h1 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
letter-spacing: -0.02em;
margin-bottom: 0.5rem;
}
.subtitle {
color: var(--plasma);
font-size: 0.9rem;
opacity: 0.8;
}
nav {
display: flex;
gap: 2rem;
margin-top: 1.5rem;
flex-wrap: wrap;
}
nav a {
color: var(--text);
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid var(--lattice);
border-radius: 4px;
transition: all 0.2s ease;
font-size: 0.85rem;
}
nav a:hover {
border-color: var(--plasma);
background: rgba(255, 107, 53, 0.1);
}
.canvas-container {
width: 100%;
height: 70vh;
position: relative;
background: radial-gradient(ellipse at center, var(--nebula) 0%, var(--void) 100%);
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
.hud {
position: absolute;
bottom: 2rem;
left: 2rem;
right: 2rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
z-index: 10;
}
.telemetry-panel {
background: rgba(10, 10, 15, 0.9);
border: 1px solid var(--lattice);
border-radius: 8px;
padding: 1rem;
backdrop-filter: blur(10px);
}
.telemetry-panel h3 {
color: var(--plasma);
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 0.75rem;
border-bottom: 1px solid var(--lattice);
padding-bottom: 0.5rem;
}
.metric {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
font-size: 0.85rem;
border-bottom: 1px dashed var(--lattice);
}
.metric:last-child {
border-bottom: none;
}
.metric-value {
color: var(--ice);
font-weight: 600;
}
.controls {
position: absolute;
top: 2rem;
right: 2rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
z-index: 10;
}
button {
background: transparent;
color: var(--text);
border: 1px solid var(--plasma);
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: 0.85rem;
transition: all 0.2s ease;
text-align: left;
}
button:hover {
background: var(--plasma);
color: var(--void);
}
button.active {
background: var(--plasma);
color: var(--void);
}
.phase-indicator {
position: absolute;
top: 2rem;
left: 50%;
transform: translateX(-50%);
background: rgba(10, 10, 15, 0.95);
border: 1px solid var(--ice);
padding: 0.75rem 1.5rem;
border-radius: 50px;
font-size: 0.8rem;
color: var(--ice);
z-index: 10;
white-space: nowrap;
}
footer {
padding: 2rem;
border-top: 1px solid var(--lattice);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.credits {
font-size: 0.8rem;
opacity: 0.7;
}
.citation-link {
color: var(--plasma);
text-decoration: none;
border-bottom: 1px dotted var(--plasma);
}
.data-link {
color: var(--ice);
text-decoration: none;
}
@media (max-width: 768px) {
.hud {
grid-template-columns: 1fr 1fr;
}
.controls {
position: fixed;
bottom: 2rem;
right: 2rem;
top: auto;
}
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>Orbital Ascension Simulator</h1>
<div class="subtitle">From Duluth to LEO: A Solid-Propellant Trajectory Model</div>
<nav>
<a href="/index.html">Home</a>
<a href="/monte-carlo-winter-load.html">Monte Carlo Winter Load</a>
<a href="/orbital-ascension.json" class="data-link">Physics Schema ↗</a>
<a href="https://skyroot.in/vks.html" target="_blank" rel="noopener">Vikram-S Specs ↗</a>
</nav>
</header>
<main class="canvas-container" id="simulation">
<canvas id="trajectoryCanvas"></canvas>
<div class="phase-indicator" id="phaseDisplay">PHASE: PRE-LIGHT</div>
<div class="controls">
<button id="btnLiftoff" onclick="initiateSequence('liftoff')">IGNITE FIRST STAGE</button>
<button id="btnStage" onclick="initiateSequence('stage')" disabled>STAGE SEPARATION</button>
<button id="btnInjection" onclick="initiateSequence('inject')" disabled>ORBIT INJECTION</button>
<button onclick="resetSimulation()">ABORT & RESET</button>
</div>
<div class="hud">
<div class="telemetry-panel">
<h3>Vehicle State</h3>
<div class="metric"><span>ALTITUDE</span><span class="metric-value" id="altValue">0.0 km</span></div>
<div class="metric"><span>VELOCITY</span><span class="metric-value" id="velValue">0.0 m/s</span></div>
<div class="metric"><span>MASS</span><span class="metric-value" id="massValue">0.0 kg</span></div>
<div class="metric"><span>G-LOAD</span><span class="metric-value" id="gValue">0.0 g</span></div>
</div>
<div class="telemetry-panel">
<h3>Atmospheric Profile</h3>
<div class="metric"><span>DENSITY</span><span class="metric-value" id="densityValue">1.225 kg/m³</span></div>
<div class="metric"><span>TEMP</span><span class="metric-value" id="tempValue">15.0 °C</span></div>
<div class="metric"><span>PRESSURE</span><span class="metric-value" id="pressureValue">101.3 kPa</span></div>
<div class="metric"><span>DRAG COEF</span><span class="metric-value" id="dragValue">0.00</span></div>
</div>
<div class="telemetry-panel">
<h3>Engine Telemetry</h3>
<div class="metric"><span>THRUST</span><span class="metric-value" id="thrustValue">0.0 kN</span></div>
<div class="metric"><span>ISP</span><span class="metric-value" id="ispValue">0 s</span></div>
<div class="metric"><span>FLOW RATE</span><span class="metric-value" id="flowValue">0.0 kg/s</span></div>
<div class="metric"><span>TIME ELAPSED</span><span class="metric-value" id="timeValue">00:00</span></div>
</div>
</div>
</main>
<footer>
<div class="credits">
Grounded in <a href="https://4ort.xyz/entity/vikram-s" class="citation-link" target="_blank" rel="noopener">Vikram-S (Q115269876)</a> • Skyroot Aerospace • Service Entry: 2022-11-18
</div>
<div class="credits">
<a href="/orbital-ascension.json" class="data-link">Agent-Legible Physics Twin ↗</a>
</div>
</footer>
<script>
// PHYSICS CONSTANTS FROM VIKRAM-S MISSION PROFILE
const PHYSICS = {
// Initial vehicle state (solid propellant sounding rocket baseline)
dryMass: 1200, // kg - estimated dry mass
propellantMass: 8500, // kg - solid propellant load
totalInitialMass: 9700, // kg
// Engine performance (typical solid motor values)
ispVacuum: 265, // seconds - vacuum specific impulse
ispSeaLevel: 235, // seconds - sea level ISP
thrustSeaLevel: 1800, // kN - approximate liftoff thrust
thrustVacuum: 2100, // kN - vacuum thrust
burnDuration: 62, // seconds - first stage burn time
// Aerodynamic properties
diameter: 2.4, // meters - vehicle diameter
length: 28, // meters - total length
dragCoefficient: 0.35, // dimensionless
finenessRatio: 11.67, // length/diameter
// Atmospheric model (U.S. Standard Atmosphere 1976 simplified)
seaLevelDensity: 1.225, // kg/m³
scaleHeight: 8500, // meters - atmospheric scale height
seaLevelTemp: 288.15, // Kelvin
lapseRate: -0.0065, // K/m - troposphere lapse rate
// Gravity
g0: 9.80665, // m/s² - standard gravity
earthRadius: 6371000, // meters
mu: 3.986004418e14, // m³/s² - gravitational parameter
// Mission profile targets
targetAltitude: 300000, // meters - 300km orbit
targetVelocity: 7700, // m/s - circular orbit velocity at 300km
stagingAltitude: 85000, // meters - approximate staging point
stagingVelocity: 2400, // m/s - velocity at staging
};
// Simulation state
let simState = {
t: 0, // elapsed time (seconds)
alt: 0, // altitude (meters)
vel: 0, // velocity (m/s)
mass: PHYSICS.totalInitialMass,
thrust: 0, // current thrust (Newtons)
isp: 0, // current ISP
flowRate: 0, // propellant mass flow (kg/s)
gLoad: 0, // acceleration in g's
phase: 'PRELIGHT', // PRELIGHT -> ASCENT -> STAGING -> INJECTION -> ORBIT
running: false
};
// Canvas setup
const canvas = document.getElementById('trajectoryCanvas');
const ctx = canvas.getContext('2d');
let animationId;
function resizeCanvas() {
canvas.width = canvas.parentElement.clientWidth;
canvas.height = canvas.parentElement.clientHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Atmospheric model
function getAtmosphere(altitude) {
const density = PHYSICS.seaLevelDensity * Math.exp(-altitude / PHYSICS.scaleHeight);
const temp = Math.max(180, PHYSICS.seaLevelTemp + PHYSICS.lapseRate * altitude);
const pressure = 101325 * Math.exp(-altitude / PHYSICS.scaleHeight);
return { density, temp: temp - 273.15, pressure: pressure / 1000 };
}
// Rocket equation integration
function computeThrustCycle(dt) {
const atm = getAtmosphere(simState.alt);
const altFraction = Math.min(1, simState.alt / PHYSICS.stagingAltitude);
// Linear ISP interpolation (sea level -> vacuum)
simState.isp = PHYSICS.ispSeaLevel +
(PHYSICS.ispVacuum - PHYSICS.ispSeaLevel) * altFraction;
// Thrust scales slightly with ambient pressure
const pressureFactor = 1 + 0.1 * (1 - atm.pressure / 101.3);
simState.thrust = PHYSICS.thrustSeaLevel * 1000 * pressureFactor;
// Mass flow from T = ṁ * Isp * g₀
simState.flowRate = simState.thrust / (simState.isp * PHYSICS.g0);
}
// Euler integration step
function integrateStep(dt) {
if (!simState.running || simState.phase === 'PRELIGHT') return;
computeThrustCycle(dt);
// Mass depletion
const dm = simState.flowRate * dt;
simState.mass = Math.max(PHYSICS.dryMass, simState.mass - dm);
// Forces
const weight = simState.mass * PHYSICS.mu / Math.pow(PHYSICS.earthRadius + simState.alt, 2);
const area = Math.PI * Math.pow(PHYSICS.diameter / 2, 2);
const atm = getAtmosphere(simState.alt);
const drag = 0.5 * atm.density * simState.vel * simState.vel *
PHYSICS.dragCoefficient * area;
// Net acceleration
const netForce = simState.thrust - weight - drag;
const accel = netForce / simState.mass;
// Update kinematics
simState.vel += accel * dt;
simState.alt += simState.vel * dt;
simState.t += dt;
// G-load
simState.gLoad = accel / PHYSICS.g0;
// Phase transitions
if (simState.phase === 'ASCENT' &&
simState.alt >= PHYSICS.stagingAltitude * 0.95 &&
simState.t >= PHYSICS.burnDuration * 0.9) {
triggerStaging();
}
if (simState.phase === 'INJECTION' &&
simState.vel >= PHYSICS.targetVelocity * 0.98 &&
simState.alt >= PHYSICS.targetAltitude * 0.95) {
achieveOrbit();
}
}
function triggerStaging() {
simState.phase = 'STAGING';
document.getElementById('phaseDisplay').textContent = 'PHASE: STAGE SEPARATION';
document.getElementById('btnStage').classList.add('active');
// Simulate staging event (dry mass reduction)
setTimeout(() => {
simState.mass = PHYSICS.dryMass; // Second stage dry mass
simState.phase = 'INJECTION';
document.getElementById('phaseDisplay').textContent = 'PHASE: ORBIT INJECTION';
document.getElementById('btnStage').classList.remove('active');
document.getElementById('btnInjection').classList.add('active');
}, 2000);
}
function achieveOrbit() {
simState.phase = 'ORBIT';
document.getElementById('phaseDisplay').textContent = 'PHASE: ORBIT ACHIEVED';
document.getElementById('btnInjection').classList.remove('active');
simState.running = false;
}
// Visualization
function drawTrajectory() {
const w = canvas.width;
const h = canvas.height;
// Clear
ctx.fillStyle = '#0a0a0f';
ctx.fillRect(0, 0, w, h);
// Draw Earth curvature
const earthCenterY = h + PHYSICS.earthRadius * (h / PHYSICS.targetAltitude);
ctx.beginPath();
ctx.arc(w / 2, earthCenterY, PHYSICS.earthRadius * (h / PHYSICS.targetAltitude),
Math.PI, 0, false);
ctx.strokeStyle = '#00d9ff';
ctx.lineWidth = 2;
ctx.stroke();
// Draw trajectory arc (parabolic approximation)
if (simState.alt > 0) {
const xScale = w / (PHYSICS.targetVelocity * PHYSICS.burnDuration * 2);
const yScale = h / PHYSICS.targetAltitude;
ctx.beginPath();
ctx.moveTo(0, h);
// Quadratic Bezier approximating the ascent curve
const controlX = simState.vel * xScale * 0.5;
const controlY = h - simState.alt * yScale * 0.5;
const endX = simState.vel * xScale;
const endY = h - simState.alt * yScale;
ctx.quadraticCurveTo(controlX, controlY, endX, endY);
ctx.strokeStyle = '#ff6b35';
ctx.lineWidth = 3;
ctx.stroke();
// Vehicle marker
ctx.beginPath();
ctx.arc(endX, endY, 6, 0, Math.PI * 2);
ctx.fillStyle = '#ffd700';
ctx.fill();
}
// Grid overlay
ctx.strokeStyle = 'rgba(74, 74, 90, 0.3)';
ctx.lineWidth = 1;
for (let i = 0; i < 10; i++) {
const y = h - (i * h / 10);
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
}
// HUD updates
function updateHUD() {
document.getElementById('altValue').textContent = (simState.alt / 1000).toFixed(1) + ' km';
document.getElementById('velValue').textContent = simState.vel.toFixed(0) + ' m/s';
document.getElementById('massValue').textContent = (simState.mass).toFixed(0) + ' kg';
document.getElementById('gValue').textContent = simState.gLoad.toFixed(2) + ' g';
const atm = getAtmosphere(simState.alt);
document.getElementById('densityValue').textContent = atm.density.toFixed(3) + ' kg/m³';
document.getElementById('tempValue').textContent = atm.temp.toFixed(1) + ' °C';
document.getElementById('pressureValue').textContent = atm.pressure.toFixed(1) + ' kPa';
document.getElementById('thrustValue').textContent = (simState.thrust / 1000).toFixed(0) + ' kN';
document.getElementById('ispValue').textContent = simState.isp.toFixed(0) + ' s';
document.getElementById('flowValue').textContent = simState.flowRate.toFixed(1) + ' kg/s';
const minutes = Math.floor(simState.t / 60);
const seconds = Math.floor(simState.t % 60);
document.getElementById('timeValue').textContent =
`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}
// Main loop
function gameLoop() {
if (simState.running) {
const dt = 0.05; // 20 Hz update rate
for (let i = 0; i < 4; i++) {
integrateStep(dt);
}
}
drawTrajectory();
updateHUD();
animationId = requestAnimationFrame(gameLoop);
}
// Control sequences
function initiateSequence(stage) {
switch(stage) {
case 'liftoff':
simState.phase = 'ASCENT';
simState.running = true;
document.getElementById('phaseDisplay').textContent = 'PHASE: MAIN ENGINE ASCENT';
document.getElementById('btnLiftoff').classList.add('active');
document.getElementById('btnStage').disabled = false;
break;
case 'stage':
if (simState.phase === 'ASCENT') {
triggerStaging();
}
break;
case 'inject':
if (simState.phase === 'INJECTION') {
// Boost to orbital velocity
const boostDuration = 180; // 3 minutes upper stage burn
const dvNeeded = PHYSICS.targetVelocity - simState.vel;
simState.thrust *= 0.3; // Upper stage lower thrust
simState.isp = PHYSICS.ispVacuum;
setTimeout(() => {
achieveOrbit();
}, boostDuration * 1000);
}
break;
}
}
function resetSimulation() {
cancelAnimationFrame(animationId);
simState = {
t: 0,
alt: 0,
vel: 0,
mass: PHYSICS.totalInitialMass,
thrust: 0,
isp: 0,
flowRate: 0,
gLoad: 0,
phase: 'PRELIGHT',
running: false
};
document.getElementById('phaseDisplay').textContent = 'PHASE: PRE-LIGHT';
document.getElementById('btnLiftoff').classList.remove('active');
document.getElementById('btnStage').classList.remove('active');
document.getElementById('btnStage').disabled = true;
document.getElementById('btnInjection').classList.remove('active');
document.getElementById('btnInjection').disabled = true;
gameLoop();
}
// Initialize
gameLoop();
</script>
</body>
</html>