diff --git a/frontend/textbooks/physics_8_lab.html b/frontend/textbooks/physics_8_lab.html
index f688231..e47e4ab 100644
--- a/frontend/textbooks/physics_8_lab.html
+++ b/frontend/textbooks/physics_8_lab.html
@@ -248,37 +248,38 @@ const ACH_LABELS = {
lr5_done:"Изучение параллельного соединения проводников завершена!",
lr6_done:"Определение работы и мощности электрического тока завершена!",
lr7_done:"Изучение явления отражения света завершена!",
- lab_done:"Практикум пройден!"
+ lab_done:"Практикум пройден!",
+ lab_master:"Лаборант 8 класса — все 7 ЛР сданы!"
};
const SIDEBARS = {
- lr1:{title:"Шпаргалка ЛР 1",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr2:{title:"Шпаргалка ЛР 2",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr3:{title:"Шпаргалка ЛР 3",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr4:{title:"Шпаргалка ЛР 4",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr5:{title:"Шпаргалка ЛР 5",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr6:{title:"Шпаргалка ЛР 6",rows:[["В разработке","Phase 6 Wave 1"]]},
- lr7:{title:"Шпаргалка ЛР 7",rows:[["В разработке","Phase 6 Wave 1"]]}
+ lr1:{title:"ЛР 1",rows:[["Цель","найти $t_{см}$ при смешивании горяч. и холод. воды"],["Формула","$t = (m_1 t_1 + m_2 t_2)/(m_1+m_2)$"],["$c_{воды}$","$4200$ Дж/(кг·К)"]]},
+ lr2:{title:"ЛР 2",rows:[["Цель","определить $c$ материала"],["Формула","$c = c_в m_в (t_{см}-t_х)/(m_т(t_{гор}-t_{см}))$"],["Баланс","$Q_{отд} = Q_{пол}$"]]},
+ lr3:{title:"ЛР 3",rows:[["Цель","собрать цепь, измерить $I$ и $U$"],["Амперметр","последов."],["Вольтметр","паралл."]]},
+ lr4:{title:"ЛР 4",rows:[["Цель","проверить $R = R_1+R_2$"],["$I$","одинаковый"],["$U_1+U_2 = U$","проверка"]]},
+ lr5:{title:"ЛР 5",rows:[["Цель","проверить $1/R = 1/R_1+1/R_2$"],["$U$","одинаковое"],["$I_1+I_2 = I$","проверка"]]},
+ lr6:{title:"ЛР 6",rows:[["Цель","найти $A$ и $P$"],["$P = UI$","Вт"],["$A = UIt$","Дж"]]},
+ lr7:{title:"ЛР 7",rows:[["Цель","проверить $\\alpha=\\beta$"],["Измерения","транспортиром"],["Идеально","точно равны"]]}
};
const TIPS=[
- {sec:'lr1',html:"Параграф ЛР 1 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr2',html:"Параграф ЛР 2 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr3',html:"Параграф ЛР 3 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr4',html:"Параграф ЛР 4 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr5',html:"Параграф ЛР 5 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr6',html:"Параграф ЛР 6 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."},
- {sec:'lr7',html:"Параграф ЛР 7 будет реализован в Phase 6 Wave 1. Используем хелперы из phys.js и optics.js."}
+ {sec:'lr1',html:"Простейший случай теплового баланса. Налили в калориметр $m_1$ горячей воды и $m_2$ холодной — нашли итоговую температуру и сверили с формулой."},
+ {sec:'lr2',html:"Образец нагревают до известной $t_{гор}$, бросают в калориметр с водой. По итоговой $t_{см}$ из теплового баланса находят $c$ образца."},
+ {sec:'lr3',html:"Собираем цепь: батарейка + лампа + амперметр (последов.) + вольтметр (паралл. лампе) + ключ. Замыкаем — снимаем показания."},
+ {sec:'lr4',html:"Два резистора в одной петле. Проверяем: ток одинаков везде; напряжения складываются; $R$ суммируется."},
+ {sec:'lr5',html:"Две ветви параллельно. Проверяем: $U$ на обеих равно; токи складываются; $1/R$ суммируется."},
+ {sec:'lr6',html:"Прибор работает $t$ секунд. Измеряем $U$ и $I$. Считаем $P$ и $A$. Удобно сверить с маркировкой на приборе."},
+ {sec:'lr7',html:"Луч лазера на зеркале. Транспортиром измеряем углы $\\alpha$ и $\\beta$ от нормали. Должно быть $\\alpha = \\beta$."}
];
const BUILDERS = {
- lr1: ()=>{ const box=document.getElementById('lr1-body'); box.innerHTML = buildStub('lr1', 'Изучение явления теплообмена при смешивании воды разной температуры', 'Phase 6 Wave 1') + secNavFor('lr1') + readButton('lr1'); renderMath(box); wireReadBtn('lr1'); },
- lr2: ()=>{ const box=document.getElementById('lr2-body'); box.innerHTML = buildStub('lr2', 'Определение удельной теплоёмкости твёрдого тела', 'Phase 6 Wave 1') + secNavFor('lr2') + readButton('lr2'); renderMath(box); wireReadBtn('lr2'); },
- lr3: ()=>{ const box=document.getElementById('lr3-body'); box.innerHTML = buildStub('lr3', 'Сборка простейшей электрической цепи и измерение силы тока и напряжения', 'Phase 6 Wave 1') + secNavFor('lr3') + readButton('lr3'); renderMath(box); wireReadBtn('lr3'); },
- lr4: ()=>{ const box=document.getElementById('lr4-body'); box.innerHTML = buildStub('lr4', 'Изучение последовательного соединения проводников', 'Phase 6 Wave 1') + secNavFor('lr4') + readButton('lr4'); renderMath(box); wireReadBtn('lr4'); },
- lr5: ()=>{ const box=document.getElementById('lr5-body'); box.innerHTML = buildStub('lr5', 'Изучение параллельного соединения проводников', 'Phase 6 Wave 1') + secNavFor('lr5') + readButton('lr5'); renderMath(box); wireReadBtn('lr5'); },
- lr6: ()=>{ const box=document.getElementById('lr6-body'); box.innerHTML = buildStub('lr6', 'Определение работы и мощности электрического тока', 'Phase 6 Wave 1') + secNavFor('lr6') + readButton('lr6'); renderMath(box); wireReadBtn('lr6'); },
- lr7: ()=>{ const box=document.getElementById('lr7-body'); box.innerHTML = buildStub('lr7', 'Изучение явления отражения света', 'Phase 6 Wave 1') + secNavFor('lr7') + readButton('lr7'); renderMath(box); wireReadBtn('lr7'); }
+ lr1: ()=>{ build_lr1(); },
+ lr2: ()=>{ build_lr2(); },
+ lr3: ()=>{ build_lr3(); },
+ lr4: ()=>{ build_lr4(); },
+ lr5: ()=>{ build_lr5(); },
+ lr6: ()=>{ build_lr6(); },
+ lr7: ()=>{ build_lr7(); }
};
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
@@ -618,6 +619,444 @@ function initSidebarToggle(){
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
}
+/* ======================================================================
+ PHASE 6 — Лабораторный практикум: 7 ЛР
+ ====================================================================== */
+
+/* Общий шаблон для каждой ЛР: цель + оборудование + ход + интерактив + таблица + расчёт + отчёт */
+function _labHeader(id, name, goal, equip, steps){
+ let h = '';
+ h += makeCard('theory', 'Цель работы', id+' · 1',
+ '
'+goal+'
' + +'Оборудование: '+equip+'
' + ); + let stepsHtml = 'Заполняем таблицу и оформляем отчёт:
' + +'| Величина | Значение |
|---|---|
| $m_1, t_1$ | 1,0 кг, 80 °C |
| $m_2, t_2$ | 2,0 кг, 15 °C |
| $t_{теор}$ | 36,7 °C |
| $t_{изм}$ | 37,0 °C |
Вывод: расчёт по формуле подтверждается опытом с погрешностью менее 2%. Это подтверждает закон сохранения теплоты при смешивании.
'); + + box.innerHTML = h + secNavFor('lr1') + readButton('lr1'); + renderMath(box); wireReadBtn('lr1'); + _lr1_sim(); _labWireSubmit('lr1', 'lr1'); +} +function _lr1_sim(){ + const svg = document.getElementById('lr1-sim'); if(!svg) return; + function u(){ + const m1 = +document.getElementById('lr1-m1').value; + const t1 = +document.getElementById('lr1-t1').value; + const m2 = +document.getElementById('lr1-m2').value; + const t2 = +document.getElementById('lr1-t2').value; + document.getElementById('lr1-m1v').textContent = m1.toFixed(1); + document.getElementById('lr1-t1v').textContent = t1; + document.getElementById('lr1-m2v').textContent = m2.toFixed(1); + document.getElementById('lr1-t2v').textContent = t2; + const tt = (m1*t1 + m2*t2) / (m1+m2); + /* виртуальный "измеренный" — с небольшим шумом */ + const noise = (Math.sin(t1*0.7 + t2*0.3 + m1) * 0.4); + const tm = tt + noise; + const err = Math.abs((tm - tt)/tt)*100; + document.getElementById('lr1-tt').textContent = tt.toFixed(1); + document.getElementById('lr1-tm').textContent = tm.toFixed(1); + document.getElementById('lr1-err').textContent = err.toFixed(1); + /* report */ + document.getElementById('lr1-r1').textContent = m1.toFixed(1)+' кг, '+t1+' °C'; + document.getElementById('lr1-r2').textContent = m2.toFixed(1)+' кг, '+t2+' °C'; + document.getElementById('lr1-r3').textContent = tt.toFixed(1)+' °C'; + document.getElementById('lr1-r4').textContent = tm.toFixed(1)+' °C'; + /* SVG */ + let s = ''; + s += window.PHYS.calorimeter(60, 60, 70, 100, m1/3, window.PHYS.tempColor(t1, 0, 100)); + s += 'Таблица измерений и расчёт:
' + +'| $m_т$, $m_в$ | 0,2 кг, 0,1 кг |
| $t_{гор}, t_х, t_{см}$ | 100, 18, 22.5 |
| $c$ найдено | 380 Дж/(кг·К) |
Вывод: найденная $c$ совпадает с табличной с точностью до экспериментальной погрешности.
'); + box.innerHTML = h + secNavFor('lr2') + readButton('lr2'); + renderMath(box); wireReadBtn('lr2'); + _lr2_sim(); _labWireSubmit('lr2', 'lr2'); +} +function _lr2_sim(){ + function u(){ + const c = +document.getElementById('lr2-mat').value; + const m = +document.getElementById('lr2-m').value / 1000; /* г → кг */ + const mw = +document.getElementById('lr2-mw').value / 1000; + document.getElementById('lr2-mv').textContent = (m*1000).toFixed(0); + document.getElementById('lr2-mwv').textContent = (mw*1000).toFixed(0); + const tHot = 100, tCold = 18; + /* по балансу: c_воды*mw*(t_см - 18) = c*m*(100 - t_см) + (4200*mw + c*m)*t_см = 4200*mw*18 + c*m*100 + t_см = (4200*mw*18 + c*m*100) / (4200*mw + c*m) */ + const tMix = (4200*mw*18 + c*m*100) / (4200*mw + c*m); + /* "обратный" расчёт c (как это делают в ЛР) */ + const cCalc = 4200*mw*(tMix - tCold) / (m*(tHot - tMix)); + const err = Math.abs(cCalc - c)/c * 100; + document.getElementById('lr2-ts').textContent = tMix.toFixed(1); + document.getElementById('lr2-cv').textContent = cCalc.toFixed(0); + document.getElementById('lr2-ct').textContent = c; + document.getElementById('lr2-er').textContent = err.toFixed(1); + document.getElementById('lr2-r1').textContent = m.toFixed(2)+' кг, '+mw.toFixed(2)+' кг'; + document.getElementById('lr2-r2').textContent = '100, 18, '+tMix.toFixed(1); + document.getElementById('lr2-r3').textContent = cCalc.toFixed(0); + } + ['lr2-mat','lr2-m','lr2-mw'].forEach(id => document.getElementById(id).addEventListener('input', u)); + document.getElementById('lr2-mat').addEventListener('change', u); + u(); +} + +/* ======== ЛР 3 — Сборка простейшей цепи ======== */ +function build_lr3(){ + const box = document.getElementById('lr3-body'); + let h = _labHeader('ЛР 3', 'Сборка простейшей электрической цепи и измерение силы тока и напряжения', + 'Собрать цепь из батарейки, лампы, ключа, амперметра и вольтметра. Замкнуть ключ, снять показания приборов.', + 'батарея 4,5 В, лампа на 3,5 В, амперметр (0-1 А), вольтметр (0-5 В), провода, ключ', + [ + 'Собери цепь по схеме: батарея → ключ → амперметр → лампа → батарея.', + 'Подключи вольтметр параллельно лампе.', + 'Замкни ключ.', + 'Запиши показания амперметра $I$ и вольтметра $U$.', + 'Вычисли $R = U/I$.' + ]); + h += 'В цепи лампа сопротивлением $\\sim 8$ Ом, питание от 4,5 В. Ожидаемый ток $\\sim 0{,}5$ А.
' + +'Вывод: амперметр включается последовательно с потребителем (через него идёт измеряемый ток); вольтметр — параллельно (между точками, где меряется $U$).
'); + box.innerHTML = h + secNavFor('lr3') + readButton('lr3'); + renderMath(box); wireReadBtn('lr3'); + _lr3_sim(); _labWireSubmit('lr3', 'lr3'); +} +function _lr3_sim(){ + const svg = document.getElementById('lr3-sim'); if(!svg) return; + let closed = false; + function draw(){ + let s = ''; + s += window.PHYS.batteryEMF(60, 180, '4,5 В', 'h'); + s += window.PHYS.wire(60, 162, 60, 60); + s += window.PHYS.wire(60, 60, 180, 60); + s += window.PHYS.ammeterSymbol(200, 60, 16); + s += window.PHYS.wire(216, 60, 280, 60); + s += window.PHYS.lightbulbSymbol(300, 60, 18); + if(closed){ s += 'Все 3 правила послед. соединения подтверждены опытом:
' + +'Вывод: закон Ома и правила последовательного соединения выполняются.
'); + box.innerHTML = h + secNavFor('lr4') + readButton('lr4'); + renderMath(box); wireReadBtn('lr4'); + _lr4_sim(); _labWireSubmit('lr4', 'lr4'); +} +function _lr4_sim(){ + function u(){ + const R1 = +document.getElementById('lr4-r1').value; + const R2 = +document.getElementById('lr4-r2').value; + const U = +document.getElementById('lr4-u').value; + document.getElementById('lr4-r1v').textContent = R1; + document.getElementById('lr4-r2v').textContent = R2; + document.getElementById('lr4-uv').textContent = U; + const R = R1+R2; const I = U/R; + const U1 = I*R1; const U2 = I*R2; + document.getElementById('lr4-i').textContent = I.toFixed(2); + document.getElementById('lr4-u1').textContent = U1.toFixed(2); + document.getElementById('lr4-u2').textContent = U2.toFixed(2); + document.getElementById('lr4-us').textContent = (U1+U2).toFixed(2); + document.getElementById('lr4-rs').textContent = R; + document.getElementById('lr4-rt').textContent = (U/I).toFixed(1); + } + ['lr4-r1','lr4-r2','lr4-u'].forEach(id => document.getElementById(id).addEventListener('input', u)); + u(); +} + +/* ======== ЛР 5 — Параллельное соединение ======== */ +function build_lr5(){ + const box = document.getElementById('lr5-body'); + let h = _labHeader('ЛР 5', 'Изучение параллельного соединения проводников', + 'Собрать цепь с двумя резисторами параллельно. Измерить общее $U$, токи в ветвях $I_1$, $I_2$ и общий $I$. Проверить: $U = U_1 = U_2$, $I = I_1 + I_2$, $1/R = 1/R_1 + 1/R_2$.', + 'батарея, 2 резистора, 3 амперметра, вольтметр, провода', + [ + 'Соедини $R_1$ и $R_2$ параллельно.', + 'В каждую ветвь подключи амперметр, общий — на входе.', + 'Измерь $I_1$, $I_2$, $I$.', + 'Сверь по правилам.' + ]); + h += 'Все 3 правила параллельного соединения подтверждены:
' + +'Вывод: общее $R$ меньше любого из $R_1$, $R_2$, что соответствует теории.
'); + box.innerHTML = h + secNavFor('lr5') + readButton('lr5'); + renderMath(box); wireReadBtn('lr5'); + _lr5_sim(); _labWireSubmit('lr5', 'lr5'); +} +function _lr5_sim(){ + function u(){ + const R1 = +document.getElementById('lr5-r1').value; + const R2 = +document.getElementById('lr5-r2').value; + const U = +document.getElementById('lr5-u').value; + document.getElementById('lr5-r1v').textContent = R1; + document.getElementById('lr5-r2v').textContent = R2; + document.getElementById('lr5-uv').textContent = U; + const I1 = U/R1, I2 = U/R2; + const R = (R1*R2)/(R1+R2); + document.getElementById('lr5-u1').textContent = U; + document.getElementById('lr5-i1').textContent = I1.toFixed(2); + document.getElementById('lr5-i2').textContent = I2.toFixed(2); + document.getElementById('lr5-is').textContent = (I1+I2).toFixed(2); + document.getElementById('lr5-r').textContent = R.toFixed(2); + document.getElementById('lr5-rt').textContent = (U/(I1+I2)).toFixed(2); + } + ['lr5-r1','lr5-r2','lr5-u'].forEach(id => document.getElementById(id).addEventListener('input', u)); + u(); +} + +/* ======== ЛР 6 — Работа и мощность тока ======== */ +function build_lr6(){ + const box = document.getElementById('lr6-body'); + let h = _labHeader('ЛР 6', 'Определение работы и мощности электрического тока', + 'Собрать цепь, измерить $U$ и $I$ прибора, засечь время $t$. Вычислить $P = UI$ и $A = UIt$. Сверить $P$ с маркировкой на приборе.', + 'батарея, лампа с известной маркировкой, амперметр, вольтметр, секундомер', + [ + 'Собери цепь: батарея, лампа, амперметр, вольтметр.', + 'Замкни ключ, запиши $U$ и $I$.', + 'Засеки время работы $t$.', + 'Вычисли $P = UI$ и $A = UIt$.', + 'Сверь $P$ с указанной на приборе.' + ]); + h += 'Прямое применение формул $P = UI$ и $A = Pt$. Это позволяет рассчитать энергопотребление любого прибора.
' + +'Вывод: измеренная $P$ совпадает с маркировкой на лампе; расчётная $A$ соответствует количеству выделившегося тепла и света.
'); + box.innerHTML = h + secNavFor('lr6') + readButton('lr6'); + renderMath(box); wireReadBtn('lr6'); + _lr6_sim(); _labWireSubmit('lr6', 'lr6'); +} +function _lr6_sim(){ + function u(){ + const U = +document.getElementById('lr6-u').value; + const I = +document.getElementById('lr6-i').value; + const tMin = +document.getElementById('lr6-t').value; + document.getElementById('lr6-uv').textContent = U.toFixed(1); + document.getElementById('lr6-iv').textContent = I.toFixed(2); + document.getElementById('lr6-tv').textContent = tMin; + const P = U*I; + const A = P * tMin * 60; + document.getElementById('lr6-p').textContent = P.toFixed(2); + document.getElementById('lr6-a').textContent = A.toFixed(0); + } + ['lr6-u','lr6-i','lr6-t'].forEach(id => document.getElementById(id).addEventListener('input', u)); + u(); +} + +/* ======== ЛР 7 — Отражение света ======== */ +function build_lr7(){ + const box = document.getElementById('lr7-body'); + let h = _labHeader('ЛР 7', 'Изучение явления отражения света', + 'Направить лазерный луч на плоское зеркало под разными углами. Измерить $\\alpha$ и $\\beta$ транспортиром. Проверить закон отражения $\\alpha = \\beta$.', + 'плоское зеркало, лазер (или фонарик с щелью), транспортир, лист бумаги, карандаш', + [ + 'Положи зеркало вертикально на лист.', + 'Направь луч на зеркало под углом ($\\alpha$ от нормали).', + 'Отметь падающий и отражённый лучи карандашом.', + 'Измерь $\\alpha$ и $\\beta$ транспортиром.', + 'Повторяй для разных $\\alpha$ и заполняй таблицу.' + ]); + h += '| Опыт | $\\alpha$, ° | $\\beta$, ° | $\\alpha - \\beta$ |
|---|---|---|---|
| 1 | 15 | 15 | 0 |
| 2 | 30 | 30 | 0 |
| 3 | 45 | 45 | 0 |
| 4 | 60 | 60 | 0 |
Во всех опытах $\\alpha = \\beta$ в пределах погрешности измерения.
' + +'Вывод: закон отражения $\\alpha = \\beta$ выполняется для любых углов падения.
'); + box.innerHTML = h + secNavFor('lr7') + readButton('lr7'); + renderMath(box); wireReadBtn('lr7'); + _lr7_sim(); _labWireSubmit('lr7', 'lr7'); +} +function _lr7_sim(){ + const svg = document.getElementById('lr7-sim'); if(!svg) return; + function d(){ + const a = +document.getElementById('lr7-a').value; + document.getElementById('lr7-av').textContent = a; + document.getElementById('lr7-as').textContent = a+'°'; + document.getElementById('lr7-bs').textContent = a+'°'; + svg.innerHTML = window.OPTICS.reflectRay(230, 150, a, 100); + } + document.getElementById('lr7-a').addEventListener('input', d); d(); +} + function init(){ loadProgress(); initTheme(); initSidebarToggle(); initSearch(); buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);