Procházet zdrojové kódy

Ионосфера

ypv před 1 dnem
rodič
revize
43f5cbdf0c
3 změnil soubory, kde provedl 383 přidání a 229 odebrání
  1. 1 1
      tzi.md
  2. 371 223
      ПМ.03.01/3.1.100/002/index.html
  3. 11 5
      ПМ.03.01/3.1.100/002/specification.md

+ 1 - 1
tzi.md

@@ -224,7 +224,7 @@
 
 | № | Название модели (ссылка на Gogs) | Автор (Студент) | Демо (Steelfeet) | Тип |
 |:--|:---------------------------------|:----------------|:-----------------|:---:|
-|   |                                  |                 |                  |     |
+| 1 | [Электромагнитный канал утечки информации, ч.2, гальваническя наводка](ПМ.03.01/3.1.400/001/) | ЯПВ | [Демо](https://steelfeet.ru/zaslon/ПМ.03.01/3.1.400/001/) | 🟢 |
 
 **3.1.600 Номенклатура и характеристика аппаратуры для измерения параметров ПЭМИН, фоновых шумов и физических полей.**
 

+ 371 - 223
ПМ.03.01/3.1.100/002/index.html

@@ -303,7 +303,7 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
   </div>
   <div class="readout" id="r-mod"></div>
   <div class="feedback" id="fb-mod"></div>
-  <p class="legend">Верхний график — модулирующий сигнал F_м (зелёный). Средний — несущая f_c (синяя). Нижний — АМ-сигнал: <b style="color:#b22222">красная огибающая точно повторяет верхний график (F_м)</b>, <b style="color:#1a4a7a">заполнение под ней — несущая f_c, то же число осцилляций, что на среднем</b>. При движении F_м меняется только огибающая. При движении f_c — только число осцилляций несущей. Масштаб осцилляций условный (реальное 200:1 нечитаемо), реальные λ — в readout.</p>
+  <p class="legend">Верхний график — модулирующий сигнал F_м (зелёный). Средний — несущая f_c (синяя). Нижний — АМ-сигнал: <b style="color:#b22222">красная огибающая точно повторяет верхний график (F_м)</b>, <b style="color:#1a4a7a">заполнение под ней — несущая f_c, то же число осцилляций, что на среднем</b>. При движении F_м меняется только огибающая. При движении f_c — только число осцилляций несущей.</p>
   <div class="nav"><button class="btn" onclick="goTo(3)">← Назад</button><button class="btn" onclick="goTo(5)">Далее →</button></div>
 </section>
 
@@ -311,7 +311,7 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
 <section class="scene" data-scene="5">
   <h2>5. Частотная модуляция (ЧМ)</h2>
   <p>В АМ меняется амплитуда несущей, а частота постоянна. В ЧМ наоборот: <b>амплитуда постоянна, а частота несущей меняется по закону модулирующего сигнала</b>. Информация заложена в отклонении частоты от среднего значения — в девиации.</p>
-  <p><b>Девиация Δf</b> — максимальное отклонение мгновенной частоты от несущей. <b>Индекс модуляции β = Δf / F<sub>м</sub></b> — насколько «глубоко» модулируем. Полоса по Карсону: <b>B ≈ 2(Δf + F<sub>м</sub>)</b>.</p>
+  <p><b>Девиация Δf</b> — максимальное отклонение мгновенной частоты от несущей. <b>Индекс модуляции β = Δf / F<sub>м</sub></b>. Полоса по Карсону: <b>B ≈ 2(Δf + F<sub>м</sub>)</b>.</p>
   <canvas id="c-fm"></canvas>
   <div class="controls">
     <div class="control-row">
@@ -332,14 +332,14 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
   </div>
   <div class="readout" id="r-fm"></div>
   <div class="feedback" id="fb-fm"></div>
-  <p class="legend">Верхний — модулирующий F_м. Средний — несущая f_c без модуляции (для сравнения). Нижний — ЧМ-сигнал: <b style="color:#6a3a8a">амплитуда постоянна</b>, но осцилляции <b>сгущаются</b> (частота выше) при положительной полуволне модулирующего и <b>разрежаются</b> (частота ниже) при отрицательной. Это и есть модуляция частоты. Масштаб осцилляций условный.</p>
+  <p class="legend">Верхний — модулирующий F_м. Средний — несущая f_c без модуляции (для сравнения). Нижний — ЧМ-сигнал: <b style="color:#6a3a8a">амплитуда постоянна</b>, но осцилляции <b>сгущаются</b> при положительной полуволне модулирующего и <b>разрежаются</b> при отрицательной.</p>
   <div class="nav"><button class="btn" onclick="goTo(4)">← Назад</button><button class="btn" onclick="goTo(6)">Далее →</button></div>
 </section>
 
 <!-- 6: ФМ -->
 <section class="scene" data-scene="6">
   <h2>6. Фазовая модуляция (ФМ)</h2>
-  <p>В ФМ <b>амплитуда тоже постоянна</b>, но меняется не частота, а <b>фаза несущей</b> — она сдвигается вперёд-назад по закону модулирующего сигнала. Внешне сигнал похож на ЧМ, но есть принципиальное отличие: в ЧМ мгновенная частота пропорциональна модулирующему (sin), а в ФМ сама фаза пропорциональна модулирующему. Значит, мгновенная частота в ФМ меняется по производной — по cos, со сдвигом π/2 относительно огибающей.</p>
+  <p>В ФМ <b>амплитуда тоже постоянна</b>, но меняется не частота, а <b>фаза несущей</b> — она сдвигается вперёд-назад по закону модулирующего сигнала. Внешне похоже на ЧМ, но отличие принципиальное: в ЧМ мгновенная частота пропорциональна модулирующему (sin), а в ФМ сама фаза пропорциональна модулирующему. Значит, мгновенная частота в ФМ меняется по производной — по cos, со сдвигом π/2.</p>
   <p><b>Индекс модуляции β</b> — максимальный сдвиг фазы в радианах. Полоса: <b>B ≈ 2F<sub>м</sub>(β + 1)</b>.</p>
   <canvas id="c-pm"></canvas>
   <div class="controls">
@@ -361,56 +361,74 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
   </div>
   <div class="readout" id="r-pm"></div>
   <div class="feedback" id="fb-pm"></div>
-  <p class="legend">Верхний — модулирующий F_м. Средний — несущая f_c без модуляции. Нижний — ФМ-сигнал: амплитуда постоянна, <b style="color:#6a3a8a">фаза смещается вперёд-назад</b> — видно, как осцилляции сдвигаются вправо-влево в такт с модулирующим, но частота меняется не в фазе с ним, а со сдвигом π/2. Это отличие от ЧМ. Масштаб условный.</p>
+  <p class="legend">Верхний — модулирующий F_м. Средний — несущая f_c без модуляции. Нижний — ФМ-сигнал: амплитуда постоянна, <b style="color:#6a3a8a">фаза смещается вперёд-назад</b> — видно, как осцилляции сдвигаются вправо-влево в такт с модулирующим, но частота меняется не в фазе с ним, а со сдвигом π/2.</p>
   <div class="nav"><button class="btn" onclick="goTo(5)">← Назад</button><button class="btn" onclick="goTo(7)">Далее →</button></div>
 </section>
 
-<!-- 7 -->
+<!-- 7: дифракция -->
 <section class="scene" data-scene="7">
   <h2>7. Распространение: дифракция и прямая видимость</h2>
   <p>Волна огибает препятствие, если её длина соизмерима с препятствием. Если λ много меньше — за препятствием тень.</p>
-  <p>Геометрия: передатчик и приёмник на земле, по 10 км от препятствия. Препятствие — ножевой край на 200 м выше линии прямой видимости. Меняйте λ и смотрите, что доходит до приёмника.</p>
+  <p>Передатчик и приёмник на земле, по 10 км от препятствия. Меняйте λ и высоту препятствия h — смотрите, как меняется волновое поле и что доходит до приёмника.</p>
   <canvas id="c-diff"></canvas>
   <div class="controls">
     <div class="control-row">
       <label>Длина волны λ</label>
-      <input type="range" id="s-lam" min="-1" max="5" step="0.01" value="1">
-      <span class="value-box" id="v-lam">10 м</span>
+      <input type="range" id="s-lam" min="0" max="4" step="0.01" value="2">
+      <span class="value-box" id="v-lam">100 м</span>
+    </div>
+    <div class="control-row">
+      <label>Высота препятствия h</label>
+      <input type="range" id="s-h" min="0" max="500" step="10" value="200">
+      <span class="value-box" id="v-h">200 м</span>
     </div>
   </div>
   <div class="readout" id="r-diff"></div>
   <div class="feedback" id="fb-diff"></div>
-  <p class="legend">Вертикальный масштаб увеличен. Модель ножевого края (ITU-R P.526); реальный холм даёт меньше потерь, но качественно то же.</p>
+  <p class="legend">Шаг волновых фронтов = λ в масштабе экрана: при λ много меньше h фронты мелкие и за препятствием гаснут, при λ ~ h фронты крупные и огибают. Модель ножевого края (ITU-R P.526).</p>
   <div class="nav"><button class="btn" onclick="goTo(6)">← Назад</button><button class="btn" onclick="goTo(8)">Далее →</button></div>
 </section>
 
-<!-- 8 -->
+<!-- 8: ионосфера -->
 <section class="scene" data-scene="8">
   <h2>8. Ионосфера: отражение и поглощение</h2>
-  <p>Почему ДВ/СВ/КВ идут за горизонт, а УКВ — нет. Меняйте частоту и время суток.</p>
+  <p>Ионосфера — ионизированные слои атмосферы. Радиоволна может отразиться от них, пройти сквозь или поглотиться — это зависит от частоты и от солнечной активности (чем она выше, тем ионизация сильнее).</p>
+
+  <div class="notebook" style="margin-bottom:12px">
+    <div class="nb-title">СЛОИ ИОНОСФЕРЫ</div>
+    <dl>
+      <dt>D (60–90 км)</dt>
+      <dd>Существует только днём. Поглощает ДВ/СВ и нижние КВ. Ночью исчезает.</dd>
+      <dt>E (90–120 км)</dt>
+      <dd>Отражает СВ и нижние КВ. Днём ионизация выше — слой плотнее и чуть выше.</dd>
+      <dt>F (150–400 км)</dt>
+      <dd>Основной слой для КВ. Ночью ниже (~250 км), днём выше (~400 км). Выше слой — дальше точка отражения и приёма.</dd>
+    </dl>
+  </div>
+
   <canvas id="c-iono"></canvas>
   <div class="controls">
     <div class="control-row">
       <label>Частота f</label>
-      <input type="range" id="s-iono-f" min="5" max="10" step="0.01" value="6.5">
-      <span class="value-box" id="v-iono-f">3 МГц</span>
+      <input type="range" id="s-iono-f" min="5" max="10" step="0.01" value="7">
+      <span class="value-box" id="v-iono-f">10 МГц</span>
     </div>
     <div class="control-row">
-      <label>Время суток</label>
-      <button class="btn small active" id="b-day">День</button>
-      <button class="btn small" id="b-night">Ночь</button>
+      <label>Солнечная активность</label>
+      <input type="range" id="s-iono-s" min="0" max="100" step="5" value="50">
+      <span class="value-box" id="v-iono-s">50%</span>
     </div>
   </div>
   <div class="readout" id="r-iono"></div>
   <div class="feedback" id="fb-iono"></div>
-  <p class="legend">Модель пороговая: 3 МГц и 30 МГц — качественные границы, не формула. Для инженерного расчёта — МПЧ, закон секанса, солнечная активность.</p>
+  <p class="legend">Солнечная активность: 0% — ночь, 100% — полдень. Угол выхода луча фиксирован (50° от вертикали). При росте активности слой F поднимается — точка отражения на слое и точка приёма на Земле сдвигаются вправо. Поверхность Земли изогнута; вертикальный масштаб увеличен для наглядности.</p>
   <div class="nav"><button class="btn" onclick="goTo(7)">← Назад</button><button class="btn" onclick="goTo(9)">Далее →</button></div>
 </section>
 
 <!-- 9 -->
 <section class="scene" data-scene="9">
   <h2>9. Тренажёр диапазонов</h2>
-  <p>Выберите карточку, затем нажмите на корзину. Верно — карточка встанет на место с пояснением. Неверно — подсказка, попробуйте снова.</p>
+  <p>Выберите карточку, затем нажмите на корзину. Верно — карточка встанет на место с пояснением.</p>
   <div id="trainer"></div>
   <div class="feedback" id="fb-trainer" style="display:none"></div>
   <div class="nav"><button class="btn" onclick="goTo(8)">← Назад</button><button class="btn" onclick="goTo(10)">Далее →</button></div>
@@ -443,11 +461,11 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
     <div class="nb-title">ЧЕК-ЛИСТ КОНСПЕКТА</div>
     <ul>
       <li>λ = c / f, c = 3·10⁸ м/с</li>
-      <li>АМ: меняется амплитуда. Огибающая = модулирующий сигнал F_м, заполнение = несущая f_c. B = 2F_м.</li>
+      <li>АМ: меняется амплитуда. Огибающая = модулирующий F_м, заполнение = несущая f_c. B = 2F_м.</li>
       <li>ЧМ: меняется частота, амплитуда постоянна. Девиация Δf, индекс β = Δf/F_м. B ≈ 2(Δf + F_м).</li>
       <li>ФМ: меняется фаза, амплитуда постоянна. Индекс β. B ≈ 2F_м(β + 1).</li>
-      <li>Дифракция: λ соизмерима с препятствием → огибает</li>
-      <li>Ионосфера отражает КВ, прозрачна для УКВ</li>
+      <li>Дифракция: λ соизмерима с h → огибает; λ ≪ h → тень</li>
+      <li>Ионосфера: D поглощает днём, E отражает СВ, F — основной слой КВ. Чем выше солнечная активность, тем выше F и дальше приём.</li>
       <li>Радиогоризонт: d ≈ 3.57·(√h₁ + √h₂) км</li>
       <li>Направленная антенна сужает зону перехвата, ППРЧ защищает от узкополосной помехи, экранирование снижает утечку</li>
     </ul>
@@ -651,7 +669,6 @@ function initMod() {
     const x0 = 55, x1 = W - 20;
     const midX = (x0 + x1) / 2;
 
-    // верхний: модулирующий сигнал
     const mY = 22, mH = 80;
     const mMid = mY + mH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -687,7 +704,6 @@ function initMod() {
     ctx.textAlign = 'right';
     ctx.fillText('циклов: ' + nMod.toFixed(0), x1, mY - 6);
 
-    // средний: несущая
     const cY = 145, cH = 80;
     const cMid = cY + cH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -723,7 +739,6 @@ function initMod() {
     ctx.textAlign = 'right';
     ctx.fillText('осцилляций: ' + nOscDisplay.toFixed(0), x1, cY - 6);
 
-    // нижний: АМ
     const tY = 265, tH = 140;
     const tMid = tY + tH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -780,7 +795,6 @@ function initMod() {
       ctx.fillText('ПЕРЕМОДУЛЯЦИЯ: огибающая пересекает ноль', x0, tY + tH + 14);
     }
 
-    // спектр
     const sY = 440, sH = 80;
     const sBase = sY + sH;
     ctx.fillStyle = '#1a2a4a';
@@ -829,7 +843,6 @@ function initMod() {
       ctx.fillText('B = 2F_м = ' + (2 * Fm).toFixed(1) + ' кГц', fcX, sBase - 66);
     }
 
-    // readout
     const lamC = 300 / Fc;
     const lamMod = 3e8 / (Fm * 1e3);
     const lamModStr = lamMod >= 1000 ? (lamMod / 1000).toFixed(1) + ' км' : lamMod.toFixed(0) + ' м';
@@ -859,13 +872,7 @@ function initMod() {
 }
 
 /* ============================================================
-   SCENE 5 — ЧМ (FM)
-   Логика:
-   - Мгновенная частота: f(t) = Fc + Δf·sin(2π·Fm·t)
-   - Фаза: φ(t) = 2π·Fc·t - β·cos(2π·Fm·t), β = Δf/Fm
-   - Амплитуда постоянна.
-   Нормализованно: φ(t) = 2π·nOsc·t - β·cos(2π·nMod·t)
-   При положительной полуволне модулирующего — частота max, сгущение.
+   SCENE 5 — ЧМ
    ============================================================ */
 function initFM() {
   const canvas = document.getElementById('c-fm');
@@ -887,7 +894,6 @@ function initFM() {
 
     const x0 = 55, x1 = W - 20;
 
-    // верхний: модулирующий
     const mY = 22, mH = 80;
     const mMid = mY + mH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -918,7 +924,6 @@ function initFM() {
     ctx.stroke();
     ctx.lineWidth = 1;
 
-    // средний: эталонная несущая (без модуляции) для сравнения
     const cY = 145, cH = 80;
     const cMid = cY + cH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -949,7 +954,6 @@ function initFM() {
     ctx.stroke();
     ctx.lineWidth = 1;
 
-    // нижний: ЧМ-сигнал
     const tY = 265, tH = 130;
     const tMid = tY + tH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -976,7 +980,6 @@ function initFM() {
     for (let i = 0; i <= tSamples; i++) {
       const x = x0 + (x1 - x0) * i / tSamples;
       const tNorm = i / tSamples;
-      // φ(t) = 2π·nOsc·t − β·cos(2π·nMod·t)
       const phase = 2 * Math.PI * nOscDisplay * tNorm - beta * Math.cos(2 * Math.PI * nMod * tNorm);
       const y = tMid - ampBase * Math.sin(phase);
       if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
@@ -989,7 +992,6 @@ function initFM() {
     ctx.textAlign = 'left';
     ctx.fillText('осцилляций: ' + nOscDisplay.toFixed(0) + ' · индекс β = Δf/F_м = ' + beta.toFixed(2), x0, tY + tH + 14);
 
-    // readout
     const lamC = 300 / Fc;
     const carson = 2 * (Dev + Fm);
     document.getElementById('v-fm-fm').textContent = Fm.toFixed(1) + ' кГц';
@@ -1005,8 +1007,8 @@ function initFM() {
 
     let fb;
     if (beta < 0.5) fb = 'Узкополосная ЧМ (β < 0.5): по спектру похожа на АМ, но амплитуда постоянна — это плюс по помехоустойчивости.';
-    else if (beta < 5) fb = 'Обычная ЧМ (0.5 ≤ β < 5): заметно сгущение и разрежение осцилляций, амплитуда постоянна.';
-    else fb = 'Широкополосная ЧМ (β ≥ 5): большая девиация, широкая полоса, высокая помехоустойчивость. Цена — полоса.';
+    else if (beta < 5) fb = 'Обычная ЧМ (0.5 ≤ β < 5): заметно сгущение и разрежение осцилляций.';
+    else fb = 'Широкополосная ЧМ (β ≥ 5): большая девиация, широкая полоса, высокая помехоустойчивость.';
     document.getElementById('fb-fm').textContent = fb;
   }
 
@@ -1018,13 +1020,7 @@ function initFM() {
 }
 
 /* ============================================================
-   SCENE 6 — ФМ (PM)
-   Логика:
-   - Мгновенная фаза: φ(t) = 2π·Fc·t + β·sin(2π·Fm·t)
-   - Мгновенная частота: f(t) = Fc + β·Fm·cos(2π·Fm·t)
-   - Амплитуда постоянна.
-   Нормализованно: φ(t) = 2π·nOsc·t + β·sin(2π·nMod·t)
-   Отличие от ЧМ: в ФМ фаза меняется по sin, частота — по cos (сдвиг π/2).
+   SCENE 6 — ФМ
    ============================================================ */
 function initPM() {
   const canvas = document.getElementById('c-pm');
@@ -1045,7 +1041,6 @@ function initPM() {
 
     const x0 = 55, x1 = W - 20;
 
-    // верхний: модулирующий
     const mY = 22, mH = 80;
     const mMid = mY + mH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -1076,7 +1071,6 @@ function initPM() {
     ctx.stroke();
     ctx.lineWidth = 1;
 
-    // средний: эталонная несущая
     const cY = 145, cH = 80;
     const cMid = cY + cH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -1107,7 +1101,6 @@ function initPM() {
     ctx.stroke();
     ctx.lineWidth = 1;
 
-    // нижний: ФМ-сигнал
     const tY = 265, tH = 130;
     const tMid = tY + tH / 2;
     ctx.fillStyle = '#1a2a4a';
@@ -1134,7 +1127,6 @@ function initPM() {
     for (let i = 0; i <= tSamples; i++) {
       const x = x0 + (x1 - x0) * i / tSamples;
       const tNorm = i / tSamples;
-      // φ(t) = 2π·nOsc·t + β·sin(2π·nMod·t)
       const phase = 2 * Math.PI * nOscDisplay * tNorm + beta * Math.sin(2 * Math.PI * nMod * tNorm);
       const y = tMid - ampBase * Math.sin(phase);
       if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
@@ -1147,7 +1139,6 @@ function initPM() {
     ctx.textAlign = 'left';
     ctx.fillText('осцилляций: ' + nOscDisplay.toFixed(0) + ' · индекс β = ' + beta.toFixed(2) + ' рад', x0, tY + tH + 14);
 
-    // readout
     const lamC = 300 / Fc;
     const pmBand = 2 * Fm * (beta + 1);
     document.getElementById('v-fm-pm').textContent = Fm.toFixed(1) + ' кГц';
@@ -1161,9 +1152,9 @@ function initPM() {
       'Полоса B ≈ 2F_м(β + 1) = <span class="hl">' + pmBand.toFixed(0) + ' кГц</span>';
 
     let fb;
-    if (beta < 0.5) fb = 'Малый индекс: фаза колеблется слабо, спектр узкий. Похоже на узкополосную ЧМ.';
+    if (beta < 0.5) fb = 'Малый индекс: фаза колеблется слабо, спектр узкий.';
     else if (beta < 5) fb = 'Обычная ФМ: заметны сдвиги фазы, частота меняется по cos — со сдвигом π/2 относительно модулирующего.';
-    else fb = 'Большой индекс: сильные фазовые сдвиги, широкая полоса. На практике применяется редко — при том же β помехоустойчивость как у ЧМ, но полоса та же.';
+    else fb = 'Большой индекс: сильные фазовые сдвиги, широкая полоса.';
     document.getElementById('fb-pm').textContent = fb;
   }
 
@@ -1175,18 +1166,20 @@ function initPM() {
 }
 
 /* ============================================================
-   SCENE 7 — diffraction
+   SCENE 7 — дифракция
    ============================================================ */
 function initDiff() {
   const canvas = document.getElementById('c-diff');
-  const slider = document.getElementById('s-lam');
+  const sLam = document.getElementById('s-lam');
+  const sH = document.getElementById('s-h');
 
   function draw() {
-    const { ctx, W, H } = prepCanvas(canvas, 280);
-    const logLam = parseFloat(slider.value);
+    const { ctx, W, H } = prepCanvas(canvas, 320);
+    const logLam = parseFloat(sLam.value);
     const lambda = Math.pow(10, logLam);
+    const h = parseFloat(sH.value);
 
-    const v = 4 / Math.sqrt(lambda);
+    const v = h / (50 * Math.sqrt(lambda));
     let L = 0;
     if (v > -0.7) {
       L = 6.9 + 20 * Math.log10(Math.sqrt((v - 0.1) * (v - 0.1) + 1) + v - 0.1);
@@ -1197,7 +1190,13 @@ function initDiff() {
     const losY = groundY - 25;
     const txX = 50, rxX = W - 50;
     const obsX = W / 2;
-    const obsTopY = losY - 150;
+
+    const vScale = 150 / 500;
+    const hPix = h * vScale;
+    const obsTopY = losY - hPix;
+
+    const pxPerMeter = (W - 100) / 20000;
+    const stepPix = lambda * pxPerMeter;
 
     ctx.fillStyle = '#c8b890';
     ctx.fillRect(0, groundY, W, H - groundY);
@@ -1215,46 +1214,63 @@ function initDiff() {
     ctx.stroke();
     ctx.setLineDash([]);
 
-    const shadowAlpha = Math.max(0.03, Math.min(0.6, 0.6 * (1 - L / 40)));
-
-    const maxR = Math.hypot(rxX - txX, groundY - losY) + 40;
-    const steps = 44;
-    for (let r = 40; r < maxR; r += 28) {
-      for (let s = 0; s < steps; s++) {
-        const th1 = (s / steps) * Math.PI;
-        const th2 = ((s + 1) / steps) * Math.PI;
-        const x1 = txX + r * Math.cos(th1);
-        const y1 = losY - r * Math.sin(th1);
-        const x2 = txX + r * Math.cos(th2);
-        const y2 = losY - r * Math.sin(th2);
-        if (y1 > groundY || y2 > groundY) continue;
-        if (x1 < 2 || x1 > W - 2 || x2 < 2 || x2 > W - 2) continue;
-
-        let alpha = 0.5;
-        const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
-        if (mx > obsX) {
-          const yShadow = losY - 150 * (mx - txX) / (obsX - txX);
-          if (my > yShadow) alpha = shadowAlpha;
+    const shadowAmp = Math.max(0.03, Math.min(1, Math.pow(10, -L / 20)));
+
+    if (stepPix < 3) {
+      ctx.fillStyle = 'rgba(26,42,74,0.18)';
+      ctx.fillRect(txX, 0, obsX - txX, groundY);
+      const a = 0.18 * shadowAmp;
+      ctx.fillStyle = 'rgba(26,42,74,' + a.toFixed(3) + ')';
+      ctx.fillRect(obsX, 0, rxX - obsX, groundY);
+    } else {
+      const maxR = Math.hypot(rxX - txX, groundY - losY) + stepPix;
+      for (let r = stepPix; r < maxR; r += stepPix) {
+        const segCount = Math.max(24, Math.floor(Math.PI * Math.min(r, 400) / 6));
+        for (let s = 0; s < segCount; s++) {
+          const th1 = Math.PI - (s / segCount) * Math.PI;
+          const th2 = Math.PI - ((s + 1) / segCount) * Math.PI;
+          const x1 = txX + r * Math.cos(th1);
+          const y1 = losY - r * Math.sin(th1);
+          const x2 = txX + r * Math.cos(th2);
+          const y2 = losY - r * Math.sin(th2);
+          if (y1 > groundY || y2 > groundY) continue;
+          if (x1 < 2 || x1 > W - 2 || x2 < 2 || x2 > W - 2) continue;
+
+          let alpha = 0.45;
+          const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
+          if (mx > obsX) {
+            const yShadow = (losY - hPix) + (mx - obsX) / (rxX - obsX) * hPix;
+            if (my > yShadow) {
+              alpha = 0.45 * shadowAmp;
+              if (alpha < 0.02) continue;
+            }
+          }
+          ctx.strokeStyle = 'rgba(26,42,74,' + alpha.toFixed(3) + ')';
+          ctx.lineWidth = 1.5;
+          ctx.beginPath();
+          ctx.moveTo(x1, y1);
+          ctx.lineTo(x2, y2);
+          ctx.stroke();
         }
-        ctx.strokeStyle = 'rgba(26,42,74,' + alpha + ')';
-        ctx.lineWidth = 1;
-        ctx.beginPath();
-        ctx.moveTo(x1, y1);
-        ctx.lineTo(x2, y2);
-        ctx.stroke();
       }
     }
 
-    ctx.fillStyle = '#8a7a5a';
-    ctx.beginPath();
-    ctx.moveTo(obsX - 7, groundY);
-    ctx.lineTo(obsX, obsTopY);
-    ctx.lineTo(obsX + 7, groundY);
-    ctx.closePath();
-    ctx.fill();
-    ctx.strokeStyle = '#1a2a4a';
-    ctx.lineWidth = 1;
-    ctx.stroke();
+    if (hPix > 0) {
+      ctx.fillStyle = '#8a7a5a';
+      ctx.beginPath();
+      ctx.moveTo(obsX - 8, groundY);
+      ctx.lineTo(obsX, obsTopY);
+      ctx.lineTo(obsX + 8, groundY);
+      ctx.closePath();
+      ctx.fill();
+      ctx.strokeStyle = '#1a2a4a';
+      ctx.lineWidth = 1;
+      ctx.stroke();
+      ctx.fillStyle = '#3a4a6a';
+      ctx.font = '9px "Roboto Mono", monospace';
+      ctx.textAlign = 'right';
+      ctx.fillText('h = ' + h.toFixed(0) + ' м', obsX - 12, (groundY + obsTopY) / 2);
+    }
 
     ctx.fillStyle = '#1a2a4a';
     ctx.beginPath();
@@ -1276,7 +1292,7 @@ function initDiff() {
 
     const barH = 44, barW = 12;
     const barX = rxX + 18;
-    const level = Math.max(0, Math.min(1, 1 - L / 40));
+    const level = Math.max(0, Math.min(1, shadowAmp));
     ctx.fillStyle = '#e8dcc0';
     ctx.fillRect(barX, losY - barH / 2, barW, barH);
     ctx.fillStyle = L < 10 ? '#2a6a3a' : L < 25 ? '#d4a020' : '#b22222';
@@ -1288,189 +1304,321 @@ function initDiff() {
     ctx.fillStyle = '#1a2a4a';
     ctx.textAlign = 'left';
     ctx.font = '10px "Roboto Mono", monospace';
-    ctx.fillText('λ = ' + formatLambda(lambda), 10, 18);
+    ctx.fillText('λ = ' + formatLambda(lambda), 10, 16);
+    if (stepPix < 3) {
+      ctx.fillStyle = '#3a4a6a';
+      ctx.fillText('фронты λ неразличимы — показано сплошное поле', 10, 30);
+    } else {
+      ctx.fillStyle = '#3a4a6a';
+      ctx.fillText('шаг фронтов: ' + stepPix.toFixed(0) + ' px', 10, 30);
+    }
 
     document.getElementById('v-lam').textContent = formatLambda(lambda);
+    document.getElementById('v-h').textContent = h.toFixed(0) + ' м';
     document.getElementById('r-diff').innerHTML =
-      'λ = <span class="hl">' + formatLambda(lambda) + '</span><br>' +
+      'λ = <span class="hl">' + formatLambda(lambda) + '</span> · h = <span class="hl">' + h.toFixed(0) + ' м</span><br>' +
       'Параметр Френеля v = <span class="hl">' + v.toFixed(2) + '</span><br>' +
       'Потери L = <span class="hl">' + L.toFixed(1) + ' дБ</span>';
 
     let fb;
-    if (L < 8) fb = 'λ соизмерима с препятствием. Дифракция: волна огибает, сигнал доходит.';
+    if (L < 8) fb = 'λ соизмерима с h или больше. Дифракция: волна огибает препятствие, сигнал доходит.';
     else if (L < 18) fb = 'Частичная тень: волна огибает край, но с потерями.';
-    else fb = 'λ много меньше препятствия. Тень: за препятствием сигнала нет.';
+    else fb = 'λ много меньше h. Тень: за препятствием сигнала практически нет.';
     document.getElementById('fb-diff').textContent = fb;
   }
 
   currentDraw = draw;
-  slider.oninput = draw;
+  sLam.oninput = draw;
+  sH.oninput = draw;
   draw();
 }
 
 /* ============================================================
-   SCENE 8 — ionosphere
+   SCENE 8 — ионосфера
+   Передатчик смещён влево (18% ширины) и является центром дуги Земли.
    ============================================================ */
 function initIono() {
   const canvas = document.getElementById('c-iono');
-  const slider = document.getElementById('s-iono-f');
-  const bDay = document.getElementById('b-day');
-  const bNight = document.getElementById('b-night');
-  let isDay = true;
-
-  function updateDayBtn() {
-    bDay.classList.toggle('active', isDay);
-    bNight.classList.toggle('active', !isDay);
-  }
+  const sF = document.getElementById('s-iono-f');
+  const sS = document.getElementById('s-iono-s');
+
+  const H_CANVAS = 440;
+  const theta = 50 * Math.PI / 180;
+  const tanTheta = Math.tan(theta);
 
   function draw() {
-    const { ctx, W, H } = prepCanvas(canvas, 300);
-    const logf = parseFloat(slider.value);
+    const { ctx, W, H } = prepCanvas(canvas, H_CANVAS);
+    const logf = parseFloat(sF.value);
     const f = Math.pow(10, logf);
+    const S = parseFloat(sS.value);
 
-    const groundY = H - 20;
-    const topY = 20;
-    const altMax = 400;
-    const yOf = km => groundY - (km / altMax) * (groundY - topY);
+    let mode, reflectLayer = null, hReflect = null, absorbed = false;
+    if (f < 3e6) {
+      mode = 'ДВ/СВ';
+      if (S > 30) {
+        absorbed = true;
+        hReflect = 75;
+      } else {
+        reflectLayer = 'E';
+        hReflect = 90 + 30 * S / 100;
+      }
+    } else if (f <= 3e7) {
+      mode = 'КВ';
+      reflectLayer = 'F';
+      hReflect = 250 + 150 * S / 100;
+    } else {
+      mode = 'УКВ+';
+    }
 
-    ctx.fillStyle = '#c8b890';
-    ctx.fillRect(0, groundY, W, H - groundY);
-    ctx.strokeStyle = '#1a2a4a';
-    ctx.beginPath();
-    ctx.moveTo(0, groundY);
-    ctx.lineTo(W, groundY);
-    ctx.stroke();
+    let xRefKm = 0, xRxKm = 0;
+    if (hReflect !== null) {
+      xRefKm = hReflect * tanTheta;
+      xRxKm = 2 * xRefKm;
+    }
 
-    const layers = [
-      { name: 'D', from: 60, to: 90, color: 'rgba(100,100,140,0.30)', show: isDay },
-      { name: 'E', from: 90, to: 120, color: 'rgba(100,140,100,0.30)', show: true },
-      { name: 'F', from: 150, to: 400, color: 'rgba(140,100,100,0.20)', show: true }
-    ];
-    layers.forEach(l => {
-      if (!l.show) return;
-      const y1 = yOf(l.from), y2 = yOf(l.to);
-      ctx.fillStyle = l.color;
-      ctx.fillRect(0, y2, W, y1 - y2);
-      ctx.strokeStyle = 'rgba(26,42,74,0.4)';
-      ctx.setLineDash([4, 4]);
+    // передатчик — на 18% ширины; центр дуги Земли = передатчик
+    const txX = Math.round(W * 0.18);
+    const cx = txX;
+    const R_px = 1700;
+    const cy_bottom = H + R_px - 50;
+    const hScaleX = (W - txX - 60) / 2000;   // масштаб 2000 км = оставшееся справа место
+    const vScale = 0.5;
+
+    const groundAt = x => cy_bottom - Math.sqrt(R_px * R_px - (x - cx) * (x - cx));
+    const layerY = h => H - 50 - h * vScale;
+
+    // фон (небо)
+    ctx.fillStyle = '#e8eef5';
+    ctx.fillRect(0, 0, W, H);
+
+    // слои
+    function fillLayer(h1, h2, color) {
+      const r1 = R_px + h1 * vScale;
+      const r2 = R_px + h2 * vScale;
+      ctx.fillStyle = color;
       ctx.beginPath();
-      ctx.moveTo(0, y2);
-      ctx.lineTo(W, y2);
-      ctx.stroke();
-      ctx.setLineDash([]);
-      ctx.fillStyle = '#1a2a4a';
-      ctx.font = '10px "Roboto Mono", monospace';
-      ctx.textAlign = 'left';
-      ctx.fillText(l.name + ' (' + l.from + '–' + l.to + ' км)', 6, (y1 + y2) / 2 + 3);
-    });
+      for (let x = 0; x <= W; x += 6) {
+        const dx1 = x - cx;
+        const sq1 = r1 * r1 - dx1 * dx1;
+        const y = cy_bottom - Math.sqrt(Math.max(0, sq1));
+        if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
+      }
+      for (let x = W; x >= 0; x -= 6) {
+        const dx2 = x - cx;
+        const sq2 = r2 * r2 - dx2 * dx2;
+        const y = cy_bottom - Math.sqrt(Math.max(0, sq2));
+        ctx.lineTo(x, y);
+      }
+      ctx.closePath();
+      ctx.fill();
+    }
 
-    const txX = 80, rxX = W - 80;
+    if (S > 30) fillLayer(60, 90, 'rgba(140,120,160,0.35)');
+    fillLayer(90, 120, 'rgba(100,160,100,0.30)');
+    fillLayer(150, 400, 'rgba(200,120,120,0.20)');
 
-    const gwAlpha = f > 3e7 ? 0.3 : 0.85;
-    ctx.strokeStyle = 'rgba(42,106,58,' + gwAlpha + ')';
-    ctx.lineWidth = 2;
+    // подписи слоёв слева
+    function layerLabel(h, text, color) {
+      const r = R_px + h * vScale;
+      const dx = 60 - cx;
+      const yl = cy_bottom - Math.sqrt(Math.max(0, r * r - dx * dx));
+      ctx.fillStyle = color;
+      ctx.font = 'bold 11px "Roboto Mono", monospace';
+      ctx.textAlign = 'left';
+      ctx.fillText(text, 60, yl + 4);
+    }
+    if (S > 30) layerLabel(75, 'D 60–90 км', '#6a5a8a');
+    layerLabel(105, 'E 90–120 км', '#3a7a3a');
+    layerLabel(275, 'F 150–400 км', '#a06060');
+
+    // Земля
+    ctx.fillStyle = '#c8b890';
+    ctx.beginPath();
+    for (let x = 0; x <= W; x += 6) {
+      const dx = x - cx;
+      const sq = R_px * R_px - dx * dx;
+      const y = cy_bottom - Math.sqrt(Math.max(0, sq));
+      if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
+    }
+    ctx.lineTo(W, H);
+    ctx.lineTo(0, H);
+    ctx.closePath();
+    ctx.fill();
+    ctx.strokeStyle = '#1a2a4a';
+    ctx.lineWidth = 1.5;
     ctx.beginPath();
-    for (let x = txX; x <= rxX; x += 3) {
-      const t = (x - txX) / (rxX - txX);
-      const amp = 4 * (1 - t);
-      const y = groundY - 2 - amp * Math.sin((x - txX) * 0.15);
-      if (x === txX) ctx.moveTo(x, y); else ctx.lineTo(x, y);
+    for (let x = 0; x <= W; x += 6) {
+      const dx = x - cx;
+      const sq = R_px * R_px - dx * dx;
+      const y = cy_bottom - Math.sqrt(Math.max(0, sq));
+      if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
     }
     ctx.stroke();
 
-    let reflectKm = 0, absorb = false;
-    if (f < 3e6) { reflectKm = 110; absorb = isDay; }
-    else if (f <= 3e7) { reflectKm = 300; }
-    else { reflectKm = 0; }
+    const txY = groundAt(txX);
 
-    if (reflectKm > 0) {
-      const midX = (txX + rxX) / 2;
-      const peakY = yOf(reflectKm);
-      ctx.strokeStyle = absorb ? 'rgba(178,34,34,0.55)' : '#1a2a4a';
-      ctx.lineWidth = 2;
-      ctx.setLineDash(absorb ? [6, 4] : []);
+    // земная волна
+    const gwEndKm = 400;
+    ctx.strokeStyle = 'rgba(42,106,58,0.85)';
+    ctx.lineWidth = 2.5;
+    ctx.beginPath();
+    for (let d = 0; d <= gwEndKm; d += 6) {
+      const x = txX + d * hScaleX;
+      if (x > W - 4) break;
+      const y = groundAt(x);
+      if (d === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
+    }
+    ctx.stroke();
+    ctx.fillStyle = 'rgba(42,106,58,0.95)';
+    ctx.font = '10px "Roboto Mono", monospace';
+    ctx.textAlign = 'left';
+    ctx.fillText('земная волна', txX + 12, groundAt(txX + 80 * hScaleX) + 22);
+
+    // луч
+    if (absorbed) {
+      const refX = txX + xRefKm * hScaleX;
+      const refY = layerY(hReflect);
+      ctx.strokeStyle = 'rgba(178,34,34,0.85)';
+      ctx.lineWidth = 2.5;
+      ctx.setLineDash([6, 4]);
       ctx.beginPath();
-      ctx.moveTo(txX, groundY);
-      ctx.lineTo(midX, peakY);
-      ctx.lineTo(rxX, groundY);
+      ctx.moveTo(txX, txY);
+      ctx.lineTo(refX, refY);
       ctx.stroke();
       ctx.setLineDash([]);
-      ctx.fillStyle = '#1a2a4a';
+      ctx.strokeStyle = '#b22222';
+      ctx.lineWidth = 2;
+      const sz = 8;
+      ctx.beginPath();
+      ctx.moveTo(refX - sz, refY - sz);
+      ctx.lineTo(refX + sz, refY + sz);
+      ctx.moveTo(refX - sz, refY + sz);
+      ctx.lineTo(refX + sz, refY - sz);
+      ctx.stroke();
+      ctx.fillStyle = '#b22222';
+      ctx.font = 'bold 11px "Roboto Mono", monospace';
+      ctx.textAlign = 'left';
+      ctx.fillText('поглощение в D', refX + 14, refY);
+    } else if (hReflect !== null) {
+      const refX = txX + xRefKm * hScaleX;
+      const refY = layerY(hReflect);
+      const rxXCalc = txX + xRxKm * hScaleX;
+      const rxX = Math.min(rxXCalc, W - 20);
+      const rxY = groundAt(rxX);
+
+      ctx.strokeStyle = '#1a4a7a';
+      ctx.lineWidth = 2.5;
+      ctx.beginPath();
+      ctx.moveTo(txX, txY);
+      ctx.lineTo(refX, refY);
+      ctx.lineTo(rxX, rxY);
+      ctx.stroke();
+
+      // точка отражения
+      ctx.fillStyle = '#b22222';
       ctx.beginPath();
-      ctx.arc(midX, peakY, 3, 0, Math.PI * 2);
+      ctx.arc(refX, refY, 5, 0, Math.PI * 2);
       ctx.fill();
       ctx.font = '10px "Roboto Mono", monospace';
+      ctx.textAlign = 'left';
+      ctx.fillStyle = '#b22222';
+      ctx.fillText('точка отражения', refX + 10, refY - 6);
+
+      // приёмник — подпись над точкой, чтобы не уходить за край
+      ctx.fillStyle = '#1a2a4a';
+      ctx.beginPath();
+      ctx.arc(rxX, rxY, 5, 0, Math.PI * 2);
+      ctx.fill();
       ctx.textAlign = 'center';
-      ctx.fillText('отражение от ' + (reflectKm > 200 ? 'F' : 'E'), midX, peakY - 8);
+      ctx.fillText('приём', rxX, rxY - 12);
+
+      // мёртвая зона
+      const gwEndX = txX + gwEndKm * hScaleX;
+      if (rxX > gwEndX + 15) {
+        ctx.fillStyle = 'rgba(178,34,34,0.10)';
+        ctx.beginPath();
+        for (let x = gwEndX; x <= rxX; x += 6) {
+          const dx = x - cx;
+          const sq = R_px * R_px - dx * dx;
+          const y = cy_bottom - Math.sqrt(Math.max(0, sq));
+          if (x === gwEndX) ctx.moveTo(x, y); else ctx.lineTo(x, y);
+        }
+        ctx.lineTo(rxX, H);
+        ctx.lineTo(gwEndX, H);
+        ctx.closePath();
+        ctx.fill();
+        ctx.fillStyle = '#b22222';
+        ctx.font = 'bold 10px "Roboto Mono", monospace';
+        ctx.textAlign = 'center';
+        const midX = (gwEndX + rxX) / 2;
+        const dxm = midX - cx;
+        const sqm = R_px * R_px - dxm * dxm;
+        const ym = cy_bottom - Math.sqrt(Math.max(0, sqm));
+        ctx.fillText('мёртвая зона', midX, ym + 22);
+      }
     } else {
-      ctx.strokeStyle = '#1a2a4a';
-      ctx.lineWidth = 2;
+      ctx.strokeStyle = '#1a4a7a';
+      ctx.lineWidth = 2.5;
       ctx.beginPath();
-      ctx.moveTo(txX, groundY);
-      ctx.lineTo(txX + 220, topY);
+      ctx.moveTo(txX, txY);
+      ctx.lineTo(txX + 200, 20);
       ctx.stroke();
-      ctx.fillStyle = '#1a2a4a';
+      const ax = txX + 200, ay = 20;
+      ctx.fillStyle = '#1a4a7a';
       ctx.beginPath();
-      ctx.moveTo(txX + 220, topY);
-      ctx.lineTo(txX + 210, topY + 12);
-      ctx.lineTo(txX + 230, topY + 12);
+      ctx.moveTo(ax, ay);
+      ctx.lineTo(ax - 12, ay + 14);
+      ctx.lineTo(ax + 4, ay + 14);
       ctx.closePath();
       ctx.fill();
       ctx.textAlign = 'left';
-      ctx.fillText('в космос', txX + 236, topY + 6);
-    }
-
-    if (isDay && f < 3e6) {
-      const y1 = yOf(60), y2 = yOf(90);
-      ctx.fillStyle = 'rgba(178,34,34,0.32)';
-      ctx.fillRect(0, y2, W, y1 - y2);
-      ctx.fillStyle = '#b22222';
-      ctx.font = 'bold 11px "Roboto Mono", monospace';
-      ctx.textAlign = 'center';
-      ctx.fillText('ПОГЛОЩЕНИЕ В D-СЛОЕ', W / 2, yOf(75) + 4);
+      ctx.font = '10px "Roboto Mono", monospace';
+      ctx.fillText('проходит сквозь ионосферу', txX + 210, 26);
     }
 
+    // передатчик значок
     ctx.fillStyle = '#1a2a4a';
     ctx.beginPath();
-    ctx.moveTo(txX, groundY - 20);
-    ctx.lineTo(txX - 6, groundY);
-    ctx.lineTo(txX + 6, groundY);
-    ctx.closePath();
-    ctx.fill();
-    ctx.beginPath();
-    ctx.moveTo(rxX, groundY - 20);
-    ctx.lineTo(rxX - 6, groundY);
-    ctx.lineTo(rxX + 6, groundY);
+    ctx.moveTo(txX, txY - 20);
+    ctx.lineTo(txX - 8, txY);
+    ctx.lineTo(txX + 8, txY);
     ctx.closePath();
     ctx.fill();
     ctx.font = '10px "Roboto Mono", monospace';
     ctx.textAlign = 'center';
-    ctx.fillText('ПРД', txX, groundY + 14);
-    ctx.fillText('ПРМ', rxX, groundY + 14);
+    ctx.fillText('ПРД', txX, txY + 18);
 
+    // readout
     document.getElementById('v-iono-f').textContent = formatFreq(f);
-    let band, desc;
-    if (f < 3e6) {
-      band = 'ДВ/СВ';
-      desc = isDay
-        ? 'День: D-слой поглощает, дальний приём пропадает.'
-        : 'Ночь: D-слой исчез, отражение от E. Дальняя связь.';
-    } else if (f <= 3e7) {
-      band = 'КВ';
-      desc = 'Отражение от F-слоя, многократные скачки. Мёртвая зона.';
-    } else {
-      band = 'УКВ и выше';
-      desc = 'Проходит сквозь ионосферу. Только прямая видимость.';
+    document.getElementById('v-iono-s').textContent = S.toFixed(0) + '%';
+
+    let status;
+    if (absorbed) status = 'поглощение в D-слое';
+    else if (hReflect !== null) status = 'отражение от ' + reflectLayer + '-слоя';
+    else status = 'проходит сквозь ионосферу';
+
+    let extra = '';
+    if (hReflect !== null && !absorbed) {
+      extra = 'Высота отражения: <span class="hl">' + hReflect.toFixed(0) + ' км</span><br>' +
+              'Расстояние до приёма: <span class="hl">' + xRxKm.toFixed(0) + ' км</span>';
     }
+
     document.getElementById('r-iono').innerHTML =
-      'f = <span class="hl">' + formatFreq(f) + '</span> · ' + band + '<br>' + desc;
+      'f = <span class="hl">' + formatFreq(f) + '</span> · ' + mode + '<br>' +
+      'Солнце: <span class="hl">' + S.toFixed(0) + '%</span> · ' + status +
+      (extra ? '<br>' + extra : '');
+
+    let fb;
+    if (absorbed) fb = 'D-слой днём ионизирован и поглощает ДВ/СВ. Ночью D исчезает — дальний приём возвращается.';
+    else if (hReflect !== null && f <= 3e7) fb = 'Выше солнечная активность — выше F-слой — дальше точка отражения и приёма. Угол выхода луча фиксирован, меняется высота отражения.';
+    else if (hReflect !== null) fb = 'Отражение от E-слоя. Ночью слой ниже — приём ближе.';
+    else fb = 'УКВ и выше проходит сквозь ионосферу. Только прямая видимость.';
+    document.getElementById('fb-iono').textContent = fb;
   }
 
-  bDay.onclick = () => { isDay = true; updateDayBtn(); draw(); };
-  bNight.onclick = () => { isDay = false; updateDayBtn(); draw(); };
-  slider.oninput = draw;
-  updateDayBtn();
   currentDraw = draw;
+  sF.oninput = draw;
+  sS.oninput = draw;
   draw();
 }
 
@@ -1478,7 +1626,7 @@ function initIono() {
    SCENE 9 — trainer
    ============================================================ */
 const rangeData = [
-  { id: 1,  text: 'Связь с подводной лодкой',     bin: 'dv',   expl: 'ДВ/СДВ проникают в воду на десятки метров (скин-слой). УКВ в воде затухает за сантиметры.' },
+  { id: 1,  text: 'Связь с подводной лодкой',     bin: 'dv',   expl: 'ДВ/СДВ проникают в воду на десятки метров. УКВ в воде затухает за сантиметры.' },
   { id: 2,  text: 'Международное КВ-вещание',      bin: 'kv',   expl: 'КВ отражается от ионосферы (слой F) и огибает Землю многократными скачками.' },
   { id: 3,  text: 'Местная AM-радиостанция',       bin: 'sv',   expl: 'СВ — сотни кГц. Земная волна вдоль поверхности. Днём D-слой поглощает дальний приём.' },
   { id: 4,  text: 'Wi-Fi 2.4 ГГц',                 bin: 'svch', expl: '2.4 ГГц — СВЧ. Прямая видимость в помещении, мебель огибает плохо.' },
@@ -1734,7 +1882,7 @@ const testQuestions = [
     expl: 'Ионосфера отражает, пока f ниже плазменной частоты слоя. Для УКВ f выше — проходит насквозь.'
   },
   {
-    q: 'Где полоса шире — на несущей 1 МГц или 100 МГц при той же F_м (модулирующем сигнале)?',
+    q: 'Где полоса шире — на несущей 1 МГц или 100 МГц при той же F_м?',
     opts: ['На 1 МГц', 'На 100 МГц', 'Одинакова: B = 2F_м', 'Зависит от глубины модуляции'],
     correct: 2,
     expl: 'Полоса АМ-сигнала B = 2F_м зависит от модулирующего сигнала, не от несущей.'
@@ -1761,13 +1909,13 @@ const testQuestions = [
     q: 'Где заложена информация в ЧМ-сигнале?',
     opts: ['В амплитуде несущей', 'В отклонении частоты несущей от среднего (девиации)', 'В самой несущей частоте', 'В длине волны'],
     correct: 1,
-    expl: 'В ЧМ амплитуда постоянна, а мгновенная частота меняется по закону модулирующего: f(t) = f_c + Δf·sin(2π·F_м·t). Информация — в девиации Δf, а не в амплитуде и не в самой несущей частоте.'
+    expl: 'В ЧМ амплитуда постоянна, а мгновенная частота меняется по закону модулирующего: f(t) = f_c + Δf·sin(2π·F_м·t). Информация — в девиации Δf.'
   },
   {
     q: 'Чем ЧМ отличается от ФМ при одной и той же несущей и модулирующем сигнале?',
     opts: ['В ЧМ меняется амплитуда, в ФМ — частота', 'В ЧМ мгновенная частота пропорциональна модулирующему, в ФМ — сама фаза', 'Это одно и то же, только названия разные', 'В ЧМ полоса уже, чем в АМ'],
     correct: 1,
-    expl: 'В ЧМ f(t) = f_c + Δf·sin(2π·F_м·t) — частота меняется по синусу. В ФМ φ(t) = 2π·f_c·t + β·sin(2π·F_м·t) — фаза меняется по синусу, а частота (производная фазы) — по косинусу, со сдвигом π/2. Внешне похожи, но физика разная.'
+    expl: 'В ЧМ f(t) = f_c + Δf·sin(2π·F_м·t) — частота меняется по синусу. В ФМ φ(t) = 2π·f_c·t + β·sin(2π·F_м·t) — фаза меняется по синусу, а частота (производная фазы) — по косинусу, со сдвигом π/2.'
   }
 ];
 

+ 11 - 5
ПМ.03.01/3.1.100/002/specification.md

@@ -128,13 +128,19 @@ ITU-R P.526 «Propagation by diffraction» — для модели ножево
 - Чек-лист конспекта дополнен строками про ЧМ и ФМ с формулами полос.
  </td> 
 </tr><tr valign="top" textalign="left">
-
-<td> 5 </td><td> 7. Распространение: дифракция и прямая видимость </td><td> Длина волны на рисунке не изменяется. Надо чтобы огибание стало ясно видно, если длина волны сопоставима с высотой препятствия. Надо добавить ползунок, который позволяет изменять высоту препятствия</td><td>  </td><td>Коммит </td>
+<td> 5 </td><td> 7. Распространение: дифракция и прямая видимость </td><td> Длина волны на рисунке не изменяется. Надо чтобы огибание стало ясно видно, если длина волны сопоставима с высотой препятствия. Надо добавить ползунок, который позволяет изменять высоту препятствия</td><td>  </td><td>дифракция </td>
 </tr><tr valign="top" textalign="left">
-<td colspan=5> Что выдал ИИ </td> 
-
+<td colspan=5> 
+- Шаг фронтов = λ в пикселях. При масштабе 20 км = ширина canvas: λ = 100 м → шаг 3.5 px, λ = 1 км → 35 px, λ = 10 км → 350 px. Фронты реально сгущаются и разрежаются при движении ползунка.
+- Ползунок h (0–500 м). При h = 0 препятствия нет, прямая видимость. При h = 500 м — глубокое препятствие.
+- При λ << h шаг фронтов мелкий, за препятствием тень — коэффициент ослабления shadowAmp = 10^(-L/20). При L = 30 дБ фронты под тенью почти не видны.
+- При λ >> h шаг фронтов крупный, они естественно обходят препятствие.
+- При шаге < 3 px (λ < 75 м при W = 800) — вместо дуг полупрозрачная заливка, потому что λ меньше разрешения экрана. Это честно и проговорено в подписи.
+- Тень за препятствием — отдельная область: точки ниже линии от вершины препятствия к приёмнику. Фронты в ней рисуются с alpha = 0.45 · shadowAmp.
+ </td> 
+</tr><tr valign="top" textalign="left">
+<td> 6 </td><td> 8. Ионосфера: отражение и поглощение </td><td> Самая ненаглядная сцена. Надо дать расшифровку слоям атмосферы, волну показывать не на поверхности волны, а той линией, кторая сечас распространение и показывает, и менять ее в соответствии с частотой (не в масштабе конечно), поверхность изогнуть, добавить ползунок "Солнечная активность" вместо День/Ночь (0 на ползунке соответствует ночи), показать изменение угла отражения при изменении солнечной активности и изменении высоты слоя отражения. Т.е. точка/, куда сигнал отражается изменяется.</td><td>  </td><td>Ионосфера </td>
 </tr><tr valign="top" textalign="left">
-
 
 
 </tr></table>