// 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 = '' + '
' + '' + '' + '' + '' + '
' + '' + '
' + '' + '' + '
' + '
' + '
Время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(){} }); }); })();