// F19. Полёт ракеты (финал курса в ch4) — Циолковский + гравитация + сопротивление.
(function(){
'use strict';
const B = () => window.PHYS9_FLAG_BASE;
const C = () => window.PHYS9_COLORS || {};
function init(secId){
if (!B()) return false;
const body = ''
+ '
'
+ 'Сухая масса $m_0$, т: 5 '
+ 'Топливо $m_f$, т: 95 '
+ '$v_{газов}$, м/с: 3000 '
+ 'Расход топлива, т/с: 2 '
+ '
'
+ ' '
+ ''
+ 'ЗАПУСК! '
+ 'Сброс '
+ '
'
+ ''
+ '
Время 0 с
'
+ '
Высота 0 м
'
+ '
Скорость 0 м/с
'
+ '
Топливо 100%
'
+ '
Перегрузка 0 g
'
+ '
'
+ '
';
const card = B().makeCard(secId,
'F19. Полёт ракеты МАГИСТР ',
'Запусти ракету с Земли на орбиту 400 км (МКС). Физика: тяга по Циолковскому, сила тяжести g(h), сопротивление атмосферы. Финальный босс курса!',
body);
if (!card) return false;
const cv = document.getElementById('F19-cv');
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height;
let st = { t: 0, h: 0, v: 0, m: 0, mf: 0, running: false, ended: false, history: [] };
function readSliders(){
document.getElementById('F19-m0v').textContent = (+document.getElementById('F19-m0').value).toFixed(1);
document.getElementById('F19-mfv').textContent = (+document.getElementById('F19-mf').value).toFixed(0);
document.getElementById('F19-uv').textContent = (+document.getElementById('F19-u').value).toFixed(0);
document.getElementById('F19-qv').textContent = (+document.getElementById('F19-q').value).toFixed(1);
}
function reset(){
const m0 = +document.getElementById('F19-m0').value * 1000; /* кг */
const mf = +document.getElementById('F19-mf').value * 1000;
st = { t: 0, h: 0, v: 0, m: m0 + mf, mf: mf, m0: m0, mf0: mf, running: false, ended: false, history: [] };
document.getElementById('F19-go').textContent = 'ЗАПУСК!';
document.getElementById('F19-fb').className = 'flag-feedback';
draw();
}
function tick(dt){
if (!st.running || st.ended) { draw(); return; }
const u = +document.getElementById('F19-u').value;
const q = +document.getElementById('F19-q').value * 1000; /* кг/с */
const G = 6.674e-11, M = 5.972e24, R = 6.371e6;
const N = 4;
const ddt = dt / N;
for (let i = 0; i < N; i++){
const dm = Math.min(st.mf, q * ddt);
const thrust = (dm > 1e-3) ? q * u : 0;
const g = G * M / Math.pow(R + st.h, 2);
/* атм сопротивление: плотность падает с высотой, упрощённо */
const rho = st.h < 80000 ? 1.225 * Math.exp(-st.h/8000) : 0;
const drag = 0.3 * rho * st.v * Math.abs(st.v) * 5; /* k*ρ*v²*A */
const F_net = thrust - st.m * g - Math.sign(st.v) * drag;
const a = F_net / st.m;
st.v += a * ddt;
st.h += st.v * ddt;
st.mf = Math.max(0, st.mf - dm);
st.m = st.m0 + st.mf;
st.t += ddt;
st.history.push({ t: st.t, h: st.h, v: st.v });
if (st.history.length > 1000) st.history.shift();
/* окончание */
if (st.h < 0){ st.ended = true; st.running = false; document.getElementById('F19-go').textContent='ЗАПУСК!'; break; }
if (st.t > 600){ st.ended = true; st.running = false; document.getElementById('F19-go').textContent='ЗАПУСК!'; break; }
}
/* stats */
document.getElementById('F19-t').textContent = st.t.toFixed(1) + ' с';
document.getElementById('F19-h').textContent = (st.h/1000).toFixed(1) + ' км';
document.getElementById('F19-v').textContent = st.v.toFixed(0) + ' м/с';
document.getElementById('F19-fuel').textContent = (st.mf/st.mf0*100).toFixed(0) + '%';
/* перегрузка во время тяги */
const u2 = +document.getElementById('F19-u').value;
const q2 = +document.getElementById('F19-q').value * 1000;
const thrust2 = (st.mf > 1e-3) ? q2 * u2 : 0;
const G2 = 6.674e-11, M2 = 5.972e24, R2 = 6.371e6;
const g2 = G2 * M2 / Math.pow(R2 + st.h, 2);
const a2 = (thrust2 - st.m * g2) / st.m;
document.getElementById('F19-g').textContent = ((a2/9.8) + 1).toFixed(2) + ' g';
/* feedback */
const fb = document.getElementById('F19-fb');
if (st.ended && st.h < 1){
fb.className = 'flag-feedback fail show';
fb.innerHTML = '✗ КРАШ. Ракета упала на Землю — не хватило топлива/тяги.';
} else if (st.h >= 400000 && Math.abs(st.v) < 100 && st.ended){
fb.className = 'flag-feedback ok show';
fb.innerHTML = '✓ УСПЕХ! Ракета на орбите МКС (400 км). Магистр Физики 9 — Вы!';
try { localStorage.setItem('phys9_F19_success', '1'); if(window.addXp) window.addXp(150, 'F19-magistr'); } catch(e){}
} else if (st.h > 400000){
fb.className = 'flag-feedback warn show';
fb.innerHTML = 'Высоко! Но нужна ещё горизонтальная скорость для орбиты ($\\sim 7.7$ км/с). Это упрощённая 1D модель.';
}
draw();
}
function draw(){
const col = C();
ctx.fillStyle = '#0f172a'; /* космос */
ctx.fillRect(0, 0, W, H);
/* звёзды */
ctx.fillStyle = '#fff';
for (let i = 0; i < 80; i++) ctx.fillRect((i*37+11)%W, (i*73+19)%H, 1, 1);
/* Земля внизу */
const earthY = H - 30;
ctx.fillStyle = '#16a34a';
ctx.fillRect(0, earthY, W, 30);
ctx.fillStyle = col.forceGravity || '#2563eb';
ctx.beginPath(); ctx.arc(W/2, earthY + 100, 80, 0, Math.PI*2); ctx.fill();
/* шкала высоты по правому краю */
ctx.fillStyle = '#fff';
ctx.font = '10px Inter,sans-serif';
const maxH = Math.max(450000, st.h * 1.1);
for (let k = 0; k <= 4; k++){
const h = maxH * k/4;
const py = earthY - (h/maxH) * (earthY - 40);
ctx.fillText((h/1000).toFixed(0) + ' км', W - 60, py + 3);
ctx.strokeStyle = 'rgba(255,255,255,0.2)';
ctx.beginPath(); ctx.moveTo(40, py); ctx.lineTo(W - 65, py); ctx.stroke();
}
/* 400 км — целевая орбита */
const targetY = earthY - (400000/maxH) * (earthY - 40);
ctx.strokeStyle = '#fbbf24';
ctx.setLineDash([8, 5]);
ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(0, targetY); ctx.lineTo(W, targetY); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = '#fbbf24';
ctx.font = 'bold 11px Inter,sans-serif';
ctx.fillText('★ цель: 400 км (МКС)', 10, targetY - 4);
/* Ракета */
const rocketX = 100;
const rocketY = earthY - (st.h/maxH) * (earthY - 40);
ctx.save();
ctx.translate(rocketX, rocketY);
/* корпус */
ctx.fillStyle = '#e5e7eb';
ctx.beginPath();
ctx.moveTo(0, -22);
ctx.lineTo(7, -16);
ctx.lineTo(7, 14);
ctx.lineTo(-7, 14);
ctx.lineTo(-7, -16);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#0f172a';
ctx.stroke();
/* окошко */
ctx.fillStyle = '#0891b2';
ctx.beginPath(); ctx.arc(0, -8, 3, 0, Math.PI*2); ctx.fill();
/* плавники */
ctx.fillStyle = '#dc2626';
ctx.beginPath();
ctx.moveTo(-7, 14); ctx.lineTo(-12, 22); ctx.lineTo(-7, 18); ctx.fill();
ctx.beginPath();
ctx.moveTo(7, 14); ctx.lineTo(12, 22); ctx.lineTo(7, 18); ctx.fill();
/* выхлоп */
if (st.running && st.mf > 0){
ctx.fillStyle = '#fbbf24';
ctx.beginPath();
ctx.moveTo(-5, 18);
ctx.lineTo(0, 18 + 10 + Math.random()*8);
ctx.lineTo(5, 18);
ctx.fill();
ctx.fillStyle = '#dc2626';
ctx.beginPath();
ctx.moveTo(-3, 18);
ctx.lineTo(0, 18 + 6 + Math.random()*4);
ctx.lineTo(3, 18);
ctx.fill();
}
ctx.restore();
/* график высота(t) справа */
if (st.history.length > 1){
ctx.strokeStyle = '#fbbf24';
ctx.lineWidth = 2;
ctx.beginPath();
for (let i = 0; i < st.history.length; i++){
const p = st.history[i];
const py = earthY - (p.h/maxH) * (earthY - 40);
const px = 200 + (p.t/Math.max(60, st.t)) * (W - 290);
if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
}
ctx.stroke();
}
}
document.getElementById('F19-go').addEventListener('click', ()=>{
if (st.ended) reset();
st.running = !st.running;
document.getElementById('F19-go').textContent = st.running ? 'ПАУЗА' : 'ЗАПУСК!';
});
document.getElementById('F19-reset').addEventListener('click', reset);
['F19-m0','F19-mf','F19-u','F19-q'].forEach(id => document.getElementById(id).addEventListener('input', ()=>{
readSliders();
if (!st.running) reset();
}));
readSliders();
reset();
B().startLoop('F19', cv, tick);
return true;
}
if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F19', { init: init, cleanup: function(){} });
else document.addEventListener('DOMContentLoaded', ()=>{
if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F19', { init: init, cleanup: function(){} });
});
})();