|
|
@@ -13,6 +13,7 @@
|
|
|
--red: #b22222;
|
|
|
--green: #2a6a3a;
|
|
|
--blue: #1a4a7a;
|
|
|
+ --purple: #6a3a8a;
|
|
|
--mono: 'Roboto Mono', 'Courier New', monospace;
|
|
|
--serif: 'PT Serif', Georgia, serif;
|
|
|
--sans: 'Oswald', 'Arial Narrow', sans-serif;
|
|
|
@@ -48,7 +49,7 @@ header .sub {
|
|
|
opacity: 0.7;
|
|
|
}
|
|
|
#progress-bar { height: 4px; background: var(--paper-dark); }
|
|
|
-#progress-fill { height: 100%; background: var(--red); width: 11.1%; transition: width 0.3s; }
|
|
|
+#progress-fill { height: 100%; background: var(--red); width: 9.1%; transition: width 0.3s; }
|
|
|
main { max-width: 900px; margin: 0 auto; padding: 20px; }
|
|
|
.scene { display: none; }
|
|
|
.scene.active { display: block; animation: fadeIn 0.25s; }
|
|
|
@@ -215,6 +216,7 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
<li>объяснить, почему ДВ огибает холм, а Wi-Fi за тем же холмом не работает;</li>
|
|
|
<li>объяснить, почему КВ слышно за тысячи километров, а УКВ — только в прямой видимости;</li>
|
|
|
<li>выбрать диапазон под задачу (подводная связь, вещание, мобильная, спутник);</li>
|
|
|
+ <li>объяснить, чем амплитудная, частотная и фазовая модуляции отличаются и где какая применяется;</li>
|
|
|
<li>объяснить, как направленная антенна, ППРЧ и экранирование снижают риск перехвата и подавления.</li>
|
|
|
</ul>
|
|
|
<div class="nav"><span></span><button class="btn" onclick="goTo(2)">Начать →</button></div>
|
|
|
@@ -250,13 +252,15 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
<dt>Длина волны</dt>
|
|
|
<dd>λ = c / f, [м]. Частота f, [Гц].</dd>
|
|
|
<dt>Модулирующий сигнал</dt>
|
|
|
- <dd>Низкочастотный сигнал, содержащий информацию (звук, данные). Обозначение F<sub>м</sub>, [Гц].</dd>
|
|
|
+ <dd>Низкочастотный сигнал, содержащий информацию. Обозначение F<sub>м</sub>, [Гц].</dd>
|
|
|
<dt>Несущая</dt>
|
|
|
<dd>Высокочастотное колебание, переносящее сигнал. Обозначение f<sub>c</sub>, [Гц].</dd>
|
|
|
- <dt>Модуляция</dt>
|
|
|
- <dd>Изменение параметров несущей по закону модулирующего сигнала.</dd>
|
|
|
- <dt>Полоса пропускания</dt>
|
|
|
- <dd>Интервал частот, занимаемый сигналом. Для АМ: B = 2F<sub>м</sub>.</dd>
|
|
|
+ <dt>Амплитудная модуляция (АМ)</dt>
|
|
|
+ <dd>Амплитуда несущей меняется по закону сигнала. Полоса B = 2F<sub>м</sub>.</dd>
|
|
|
+ <dt>Частотная модуляция (ЧМ)</dt>
|
|
|
+ <dd>Частота несущей меняется по закону сигнала, амплитуда постоянна. Девиация Δf. Индекс β = Δf/F<sub>м</sub>. Полоса по Карсону B ≈ 2(Δf + F<sub>м</sub>).</dd>
|
|
|
+ <dt>Фазовая модуляция (ФМ)</dt>
|
|
|
+ <dd>Фаза несущей меняется по закону сигнала, амплитуда постоянна. Индекс β. Полоса B ≈ 2F<sub>м</sub>(β + 1).</dd>
|
|
|
<dt>Дифракция</dt>
|
|
|
<dd>Огибание волной препятствий, соизмеримых с λ.</dd>
|
|
|
<dt>Ионосфера</dt>
|
|
|
@@ -271,7 +275,7 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
|
|
|
<!-- 4 -->
|
|
|
<section class="scene" data-scene="4">
|
|
|
- <h2>4. Принцип радиосвязи: несущая и модуляция</h2>
|
|
|
+ <h2>4. Амплитудная модуляция (АМ)</h2>
|
|
|
<p>Информацию переносит не сама несущая, а её изменение. Передатчик формирует высокочастотную несущую, модулятор накладывает на неё сигнал, антенна излучает. Приёмник отделяет сигнал обратно.</p>
|
|
|
|
|
|
<div class="feedback" style="border-left-color:#2a6a3a;background:rgba(42,106,58,0.08)">
|
|
|
@@ -299,13 +303,71 @@ 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>то же число осцилляций</b>, что на нижнем. Нижний — АМ-сигнал: <b style="color:#b22222">красная огибающая точно повторяет верхний график (F_м)</b>, <b style="color:#1a4a7a">заполнение под ней — несущая f_c, то же число осцилляций, что на среднем</b>. При движении F_м меняется только огибающая (верхний и нижний синхронно). При движении f_c — только число осцилляций несущей (средний и нижний синхронно). Масштаб осцилляций условный (реальное отношение f_c/F_м = 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 — только число осцилляций несущей. Масштаб осцилляций условный (реальное 200:1 нечитаемо), реальные λ — в readout.</p>
|
|
|
<div class="nav"><button class="btn" onclick="goTo(3)">← Назад</button><button class="btn" onclick="goTo(5)">Далее →</button></div>
|
|
|
</section>
|
|
|
|
|
|
-<!-- 5 -->
|
|
|
+<!-- 5: ЧМ -->
|
|
|
<section class="scene" data-scene="5">
|
|
|
- <h2>5. Распространение: дифракция и прямая видимость</h2>
|
|
|
+ <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>
|
|
|
+ <canvas id="c-fm"></canvas>
|
|
|
+ <div class="controls">
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Модулирующий сигнал F<sub>м</sub></label>
|
|
|
+ <input type="range" id="s-fm-fm" min="1" max="20" step="0.5" value="5">
|
|
|
+ <span class="value-box" id="v-fm-fm">5 кГц</span>
|
|
|
+ </div>
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Несущая f<sub>c</sub></label>
|
|
|
+ <input type="range" id="s-fc-fm" min="0.5" max="5" step="0.1" value="1">
|
|
|
+ <span class="value-box" id="v-fc-fm">1.0 МГц</span>
|
|
|
+ </div>
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Девиация Δf</label>
|
|
|
+ <input type="range" id="s-dev-fm" min="1" max="50" step="1" value="10">
|
|
|
+ <span class="value-box" id="v-dev-fm">10 кГц</span>
|
|
|
+ </div>
|
|
|
+ </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>
|
|
|
+ <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 ≈ 2F<sub>м</sub>(β + 1)</b>.</p>
|
|
|
+ <canvas id="c-pm"></canvas>
|
|
|
+ <div class="controls">
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Модулирующий сигнал F<sub>м</sub></label>
|
|
|
+ <input type="range" id="s-fm-pm" min="1" max="20" step="0.5" value="5">
|
|
|
+ <span class="value-box" id="v-fm-pm">5 кГц</span>
|
|
|
+ </div>
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Несущая f<sub>c</sub></label>
|
|
|
+ <input type="range" id="s-fc-pm" min="0.5" max="5" step="0.1" value="1">
|
|
|
+ <span class="value-box" id="v-fc-pm">1.0 МГц</span>
|
|
|
+ </div>
|
|
|
+ <div class="control-row">
|
|
|
+ <label>Индекс модуляции β</label>
|
|
|
+ <input type="range" id="s-beta-pm" min="0" max="10" step="0.1" value="2">
|
|
|
+ <span class="value-box" id="v-beta-pm">2.0</span>
|
|
|
+ </div>
|
|
|
+ </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>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(5)">← Назад</button><button class="btn" onclick="goTo(7)">Далее →</button></div>
|
|
|
+</section>
|
|
|
+
|
|
|
+<!-- 7 -->
|
|
|
+<section class="scene" data-scene="7">
|
|
|
+ <h2>7. Распространение: дифракция и прямая видимость</h2>
|
|
|
<p>Волна огибает препятствие, если её длина соизмерима с препятствием. Если λ много меньше — за препятствием тень.</p>
|
|
|
<p>Геометрия: передатчик и приёмник на земле, по 10 км от препятствия. Препятствие — ножевой край на 200 м выше линии прямой видимости. Меняйте λ и смотрите, что доходит до приёмника.</p>
|
|
|
<canvas id="c-diff"></canvas>
|
|
|
@@ -319,12 +381,12 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
<div class="readout" id="r-diff"></div>
|
|
|
<div class="feedback" id="fb-diff"></div>
|
|
|
<p class="legend">Вертикальный масштаб увеличен. Модель ножевого края (ITU-R P.526); реальный холм даёт меньше потерь, но качественно то же.</p>
|
|
|
- <div class="nav"><button class="btn" onclick="goTo(4)">← Назад</button><button class="btn" onclick="goTo(6)">Далее →</button></div>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(6)">← Назад</button><button class="btn" onclick="goTo(8)">Далее →</button></div>
|
|
|
</section>
|
|
|
|
|
|
-<!-- 6 -->
|
|
|
-<section class="scene" data-scene="6">
|
|
|
- <h2>6. Ионосфера: отражение и поглощение</h2>
|
|
|
+<!-- 8 -->
|
|
|
+<section class="scene" data-scene="8">
|
|
|
+ <h2>8. Ионосфера: отражение и поглощение</h2>
|
|
|
<p>Почему ДВ/СВ/КВ идут за горизонт, а УКВ — нет. Меняйте частоту и время суток.</p>
|
|
|
<canvas id="c-iono"></canvas>
|
|
|
<div class="controls">
|
|
|
@@ -342,21 +404,21 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
<div class="readout" id="r-iono"></div>
|
|
|
<div class="feedback" id="fb-iono"></div>
|
|
|
<p class="legend">Модель пороговая: 3 МГц и 30 МГц — качественные границы, не формула. Для инженерного расчёта — МПЧ, закон секанса, солнечная активность.</p>
|
|
|
- <div class="nav"><button class="btn" onclick="goTo(5)">← Назад</button><button class="btn" onclick="goTo(7)">Далее →</button></div>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(7)">← Назад</button><button class="btn" onclick="goTo(9)">Далее →</button></div>
|
|
|
</section>
|
|
|
|
|
|
-<!-- 7 -->
|
|
|
-<section class="scene" data-scene="7">
|
|
|
- <h2>7. Тренажёр диапазонов</h2>
|
|
|
+<!-- 9 -->
|
|
|
+<section class="scene" data-scene="9">
|
|
|
+ <h2>9. Тренажёр диапазонов</h2>
|
|
|
<p>Выберите карточку, затем нажмите на корзину. Верно — карточка встанет на место с пояснением. Неверно — подсказка, попробуйте снова.</p>
|
|
|
<div id="trainer"></div>
|
|
|
<div class="feedback" id="fb-trainer" style="display:none"></div>
|
|
|
- <div class="nav"><button class="btn" onclick="goTo(6)">← Назад</button><button class="btn" onclick="goTo(8)">Далее →</button></div>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(8)">← Назад</button><button class="btn" onclick="goTo(10)">Далее →</button></div>
|
|
|
</section>
|
|
|
|
|
|
-<!-- 8 -->
|
|
|
-<section class="scene" data-scene="8">
|
|
|
- <h2>8. Применение и ИБ: атака и защита</h2>
|
|
|
+<!-- 10 -->
|
|
|
+<section class="scene" data-scene="10">
|
|
|
+ <h2>10. Применение и ИБ: атака и защита</h2>
|
|
|
<p>Распространение волн — это ещё и зона перехвата. Чем шире зона, тем больше возможностей у «красных».</p>
|
|
|
<canvas id="c-sec"></canvas>
|
|
|
<div class="toggles" id="sec-toggles">
|
|
|
@@ -367,29 +429,30 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
<div class="risk-bars" id="risk-bars"></div>
|
|
|
<div class="feedback" id="fb-sec"></div>
|
|
|
<p class="legend">Связь с триадой КЦД: перехват — конфиденциальность, подавление — доступность, имитация — целостность.</p>
|
|
|
- <div class="nav"><button class="btn" onclick="goTo(7)">← Назад</button><button class="btn" onclick="goTo(9)">Далее →</button></div>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(9)">← Назад</button><button class="btn" onclick="goTo(11)">Далее →</button></div>
|
|
|
</section>
|
|
|
|
|
|
-<!-- 9 -->
|
|
|
-<section class="scene" data-scene="9">
|
|
|
- <h2>9. Проверка</h2>
|
|
|
- <p>Семь вопросов. После ответа — пояснение. Оценка — за первую попытку.</p>
|
|
|
+<!-- 11 -->
|
|
|
+<section class="scene" data-scene="11">
|
|
|
+ <h2>11. Проверка</h2>
|
|
|
+ <p>Девять вопросов. После ответа — пояснение. Оценка — за первую попытку.</p>
|
|
|
<div id="test-container"></div>
|
|
|
- <div class="score-box" id="score-box">Отвечено: 0 / 7</div>
|
|
|
+ <div class="score-box" id="score-box">Отвечено: 0 / 9</div>
|
|
|
<div style="text-align:center"><button class="btn" onclick="initTest()">Пересдать</button></div>
|
|
|
<div class="checklist" id="checklist" style="display:none">
|
|
|
<div class="nb-title">ЧЕК-ЛИСТ КОНСПЕКТА</div>
|
|
|
<ul>
|
|
|
<li>λ = c / f, c = 3·10⁸ м/с</li>
|
|
|
- <li>B = 2F<sub>м</sub> — полоса АМ-сигнала</li>
|
|
|
- <li>Огибающая АМ повторяет модулирующий сигнал F_м; заполнение = несущая f_c</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>Радиогоризонт: d ≈ 3.57·(√h₁ + √h₂) км</li>
|
|
|
<li>Направленная антенна сужает зону перехвата, ППРЧ защищает от узкополосной помехи, экранирование снижает утечку</li>
|
|
|
</ul>
|
|
|
</div>
|
|
|
- <div class="nav"><button class="btn" onclick="goTo(8)">← Назад</button><span></span></div>
|
|
|
+ <div class="nav"><button class="btn" onclick="goTo(10)">← Назад</button><span></span></div>
|
|
|
</section>
|
|
|
|
|
|
</main>
|
|
|
@@ -401,6 +464,7 @@ footer { text-align: center; padding: 20px; font-family: var(--mono); font-size:
|
|
|
|
|
|
let currentScene = 1;
|
|
|
let currentDraw = null;
|
|
|
+const TOTAL_SCENES = 11;
|
|
|
|
|
|
/* ---------- helpers ---------- */
|
|
|
function prepCanvas(canvas, cssH) {
|
|
|
@@ -453,15 +517,17 @@ function goTo(n) {
|
|
|
const scene = document.querySelector('.scene[data-scene="' + n + '"]');
|
|
|
if (scene) scene.classList.add('active');
|
|
|
currentScene = n;
|
|
|
- document.getElementById('progress-fill').style.width = (n / 9 * 100) + '%';
|
|
|
+ document.getElementById('progress-fill').style.width = (n / TOTAL_SCENES * 100) + '%';
|
|
|
window.scrollTo(0, 0);
|
|
|
if (n === 2) initSpectrum();
|
|
|
if (n === 4) initMod();
|
|
|
- if (n === 5) initDiff();
|
|
|
- if (n === 6) initIono();
|
|
|
- if (n === 7) initTrainer();
|
|
|
- if (n === 8) initSecurity();
|
|
|
- if (n === 9) initTest();
|
|
|
+ if (n === 5) initFM();
|
|
|
+ if (n === 6) initPM();
|
|
|
+ if (n === 7) initDiff();
|
|
|
+ if (n === 8) initIono();
|
|
|
+ if (n === 9) initTrainer();
|
|
|
+ if (n === 10) initSecurity();
|
|
|
+ if (n === 11) initTest();
|
|
|
}
|
|
|
|
|
|
window.addEventListener('resize', () => { if (currentDraw) currentDraw(); });
|
|
|
@@ -563,13 +629,7 @@ function initSpectrum() {
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 4 — modulation
|
|
|
-
|
|
|
- ЛОГИКА:
|
|
|
- - Модулирующий F_м (низкочастотный) → огибающая АМ.
|
|
|
- - Несущая f_c (высокочастотная) → заполнение под огибающей.
|
|
|
- - Число осцилляций несущей ОДНО И ТО ЖЕ на среднем и нижнем графиках.
|
|
|
- - Зависит ТОЛЬКО от f_c. Масштаб условный (реальное 200:1 нечитаемо).
|
|
|
+ SCENE 4 — AM
|
|
|
============================================================ */
|
|
|
function initMod() {
|
|
|
const canvas = document.getElementById('c-mod');
|
|
|
@@ -577,34 +637,27 @@ function initMod() {
|
|
|
const sFc = document.getElementById('s-fc');
|
|
|
const sM = document.getElementById('s-m');
|
|
|
|
|
|
- const T_MOD_MS = 4; // окно времени для модулирующего и АМ, мс
|
|
|
+ const T_MOD_MS = 4;
|
|
|
|
|
|
function draw() {
|
|
|
const { ctx, W, H } = prepCanvas(canvas, 540);
|
|
|
- const Fm = parseFloat(sFm.value); // кГц
|
|
|
- const Fc = parseFloat(sFc.value); // МГц
|
|
|
+ const Fm = parseFloat(sFm.value);
|
|
|
+ const Fc = parseFloat(sFc.value);
|
|
|
const m = parseFloat(sM.value);
|
|
|
|
|
|
- // Число циклов модулирующего в окне — только от Fm
|
|
|
const nMod = Fm * T_MOD_MS;
|
|
|
-
|
|
|
- // Число осцилляций несущей в окне — только от Fc, ОДНО И ТО ЖЕ на обоих графиках.
|
|
|
- // 30..200 — условный масштаб, чтобы было читаемо при любом f_c.
|
|
|
- // Реальная f_c за 4 мс дала бы 2000–20000 периодов — нечитаемо.
|
|
|
const nOscDisplay = 30 + 170 * (Fc - 0.5) / 4.5;
|
|
|
|
|
|
const x0 = 55, x1 = W - 20;
|
|
|
const midX = (x0 + x1) / 2;
|
|
|
|
|
|
- // ============================================================
|
|
|
- // ГРАФИК 1 (верхний): МОДУЛИРУЮЩИЙ СИГНАЛ F_м — низкочастотный
|
|
|
- // ============================================================
|
|
|
+ // верхний: модулирующий сигнал
|
|
|
const mY = 22, mH = 80;
|
|
|
const mMid = mY + mH / 2;
|
|
|
ctx.fillStyle = '#1a2a4a';
|
|
|
ctx.font = '10px "Roboto Mono", monospace';
|
|
|
ctx.textAlign = 'left';
|
|
|
- ctx.fillText('МОДУЛИРУЮЩИЙ СИГНАЛ F_м = ' + Fm.toFixed(1) + ' кГц — низкочастотный, содержит информацию', x0, mY - 6);
|
|
|
+ ctx.fillText('МОДУЛИРУЮЩИЙ СИГНАЛ F_м = ' + Fm.toFixed(1) + ' кГц', x0, mY - 6);
|
|
|
|
|
|
ctx.strokeStyle = '#1a2a4a';
|
|
|
ctx.beginPath();
|
|
|
@@ -634,16 +687,13 @@ function initMod() {
|
|
|
ctx.textAlign = 'right';
|
|
|
ctx.fillText('циклов: ' + nMod.toFixed(0), x1, mY - 6);
|
|
|
|
|
|
- // ============================================================
|
|
|
- // ГРАФИК 2 (средний): НЕСУЩАЯ f_c — высокочастотная
|
|
|
- // ТО ЖЕ число осцилляций nOscDisplay, что на нижнем
|
|
|
- // ============================================================
|
|
|
+ // средний: несущая
|
|
|
const cY = 145, cH = 80;
|
|
|
const cMid = cY + cH / 2;
|
|
|
ctx.fillStyle = '#1a2a4a';
|
|
|
ctx.font = '10px "Roboto Mono", monospace';
|
|
|
ctx.textAlign = 'left';
|
|
|
- ctx.fillText('НЕСУЩАЯ f_c = ' + Fc.toFixed(1) + ' МГц — высокочастотная, переносит сигнал', x0, cY - 6);
|
|
|
+ ctx.fillText('НЕСУЩАЯ f_c = ' + Fc.toFixed(1) + ' МГц', x0, cY - 6);
|
|
|
|
|
|
ctx.strokeStyle = '#1a2a4a';
|
|
|
ctx.beginPath();
|
|
|
@@ -673,11 +723,7 @@ function initMod() {
|
|
|
ctx.textAlign = 'right';
|
|
|
ctx.fillText('осцилляций: ' + nOscDisplay.toFixed(0), x1, cY - 6);
|
|
|
|
|
|
- // ============================================================
|
|
|
- // ГРАФИК 3 (нижний): АМ-СИГНАЛ
|
|
|
- // огибающая = 1 + m·sin(2π·nMod·t) → совпадает с ВЕРХНИМ (F_м)
|
|
|
- // заполнение = sin(2π·nOscDisplay·t) → совпадает со СРЕДНИМ (f_c)
|
|
|
- // ============================================================
|
|
|
+ // нижний: АМ
|
|
|
const tY = 265, tH = 140;
|
|
|
const tMid = tY + tH / 2;
|
|
|
ctx.fillStyle = '#1a2a4a';
|
|
|
@@ -697,7 +743,6 @@ function initMod() {
|
|
|
|
|
|
const ampBase = (tH / 2 - 6) / 1.6;
|
|
|
|
|
|
- // Красные огибающие ±(1 + m·sin(2π·nMod·t))
|
|
|
ctx.strokeStyle = 'rgba(178,34,34,0.9)';
|
|
|
ctx.lineWidth = 2;
|
|
|
for (let sign = -1; sign <= 1; sign += 2) {
|
|
|
@@ -712,7 +757,6 @@ function initMod() {
|
|
|
ctx.stroke();
|
|
|
}
|
|
|
|
|
|
- // Сам АМ-сигнал: то же nOscDisplay, что на среднем
|
|
|
const tSamples = Math.max(1200, Math.ceil(nOscDisplay * 16));
|
|
|
ctx.strokeStyle = '#1a4a7a';
|
|
|
ctx.lineWidth = 1.5;
|
|
|
@@ -728,24 +772,15 @@ function initMod() {
|
|
|
ctx.stroke();
|
|
|
ctx.lineWidth = 1;
|
|
|
|
|
|
- // Подпись под графиком
|
|
|
ctx.font = '9px "Roboto Mono", monospace';
|
|
|
ctx.textAlign = 'left';
|
|
|
if (m > 1) {
|
|
|
ctx.fillStyle = '#b22222';
|
|
|
ctx.font = 'bold 11px "Roboto Mono", monospace';
|
|
|
ctx.fillText('ПЕРЕМОДУЛЯЦИЯ: огибающая пересекает ноль', x0, tY + tH + 14);
|
|
|
- ctx.fillStyle = '#3a4a6a';
|
|
|
- ctx.font = '9px "Roboto Mono", monospace';
|
|
|
- ctx.fillText('огибающая = верхний (' + nMod.toFixed(0) + ' циклов); заполнение = средний (' + nOscDisplay.toFixed(0) + ' осц.) — синхронно', x0, tY + tH + 28);
|
|
|
- } else {
|
|
|
- ctx.fillStyle = '#3a4a6a';
|
|
|
- ctx.fillText('огибающая = верхний (' + nMod.toFixed(0) + ' циклов); заполнение = средний (' + nOscDisplay.toFixed(0) + ' осц.) — синхронно', x0, tY + tH + 14);
|
|
|
}
|
|
|
|
|
|
- // ============================================================
|
|
|
- // ГРАФИК 4: Спектр
|
|
|
- // ============================================================
|
|
|
+ // спектр
|
|
|
const sY = 440, sH = 80;
|
|
|
const sBase = sY + sH;
|
|
|
ctx.fillStyle = '#1a2a4a';
|
|
|
@@ -794,48 +829,353 @@ 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) + ' м';
|
|
|
- const lamModQuarter = lamMod / 4 >= 1000 ? (lamMod / 4000).toFixed(1) + ' км' : (lamMod / 4).toFixed(0) + ' м';
|
|
|
|
|
|
document.getElementById('v-fm').textContent = Fm.toFixed(1) + ' кГц';
|
|
|
document.getElementById('v-fc').textContent = Fc.toFixed(1) + ' МГц';
|
|
|
document.getElementById('v-m').textContent = m.toFixed(2);
|
|
|
|
|
|
document.getElementById('r-mod').innerHTML =
|
|
|
- 'F_м = <span class="hl">' + Fm.toFixed(1) + ' кГц</span> (модулирующий сигнал, λ = ' + lamModStr + ', λ/4 = ' + lamModQuarter + ')<br>' +
|
|
|
- 'f_c = <span class="hl">' + Fc.toFixed(1) + ' МГц</span> (несущая, λ = ' + lamC.toFixed(0) + ' м, λ/4 = ' + (lamC / 4).toFixed(0) + ' м)<br>' +
|
|
|
+ 'F_м = <span class="hl">' + Fm.toFixed(1) + ' кГц</span> (λ = ' + lamModStr + ')<br>' +
|
|
|
+ 'f_c = <span class="hl">' + Fc.toFixed(1) + ' МГц</span> (λ = ' + lamC.toFixed(0) + ' м)<br>' +
|
|
|
'm = <span class="hl">' + m.toFixed(2) + '</span> · B = 2F_м = <span class="hl">' + (2 * Fm).toFixed(1) + ' кГц</span>';
|
|
|
|
|
|
let fb;
|
|
|
- if (m === 0) fb = 'Несущая есть, информации нет. Глубина модуляции нулевая.';
|
|
|
- else if (m <= 0.5) fb = 'Малая глубина — сигнал слабый, но искажений нет.';
|
|
|
+ if (m === 0) fb = 'Несущая есть, информации нет.';
|
|
|
+ else if (m <= 0.5) fb = 'Малая глубина — сигнал слабый.';
|
|
|
else if (m <= 1) fb = 'Глубина в норме. Боковые растут, полоса B = 2F_м.';
|
|
|
- else fb = 'Перемодуляция: огибающая пересекает ноль. Приёмник искажает.';
|
|
|
+ else fb = 'Перемодуляция: огибающая пересекает ноль, приёмник искажает.';
|
|
|
+ document.getElementById('fb-mod').textContent = fb;
|
|
|
+ }
|
|
|
+
|
|
|
+ currentDraw = draw;
|
|
|
+ sFm.oninput = draw;
|
|
|
+ sFc.oninput = draw;
|
|
|
+ sM.oninput = draw;
|
|
|
+ draw();
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================================================
|
|
|
+ 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, сгущение.
|
|
|
+ ============================================================ */
|
|
|
+function initFM() {
|
|
|
+ const canvas = document.getElementById('c-fm');
|
|
|
+ const sFm = document.getElementById('s-fm-fm');
|
|
|
+ const sFc = document.getElementById('s-fc-fm');
|
|
|
+ const sDev = document.getElementById('s-dev-fm');
|
|
|
+
|
|
|
+ const T_MOD_MS = 4;
|
|
|
+
|
|
|
+ function draw() {
|
|
|
+ const { ctx, W, H } = prepCanvas(canvas, 420);
|
|
|
+ const Fm = parseFloat(sFm.value);
|
|
|
+ const Fc = parseFloat(sFc.value);
|
|
|
+ const Dev = parseFloat(sDev.value);
|
|
|
+
|
|
|
+ const nMod = Fm * T_MOD_MS;
|
|
|
+ const nOscDisplay = 30 + 170 * (Fc - 0.5) / 4.5;
|
|
|
+ const beta = Dev / Fm;
|
|
|
+
|
|
|
+ const x0 = 55, x1 = W - 20;
|
|
|
+
|
|
|
+ // верхний: модулирующий
|
|
|
+ const mY = 22, mH = 80;
|
|
|
+ const mMid = mY + mH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('МОДУЛИРУЮЩИЙ СИГНАЛ F_м = ' + Fm.toFixed(1) + ' кГц', x0, mY - 6);
|
|
|
|
|
|
- if (Fm <= 3) fb += ' Низкая F_м: огибающая редкая, полоса узкая, но информации мало.';
|
|
|
- else if (Fm >= 15) fb += ' Высокая F_м: огибающая плотная, полоса широкая. Нужна более высокая несущая, чтобы боковые не налезли на ноль.';
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, mMid);
|
|
|
+ ctx.lineTo(x1, mMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, mY);
|
|
|
+ ctx.lineTo(x0, mY + mH);
|
|
|
+ ctx.stroke();
|
|
|
|
|
|
- if (Fc * 1e6 < 10 * Fm * 1e3) {
|
|
|
- fb += ' Внимание: f_c меньше 10·F_м — боковые налезают друг на друга, огибающая на графике не читается. Это не ошибка модели, это физика.';
|
|
|
+ const mSamples = Math.max(200, Math.ceil(nMod * 20));
|
|
|
+ ctx.strokeStyle = '#2a6a3a';
|
|
|
+ ctx.lineWidth = 2;
|
|
|
+ ctx.beginPath();
|
|
|
+ for (let i = 0; i <= mSamples; i++) {
|
|
|
+ const x = x0 + (x1 - x0) * i / mSamples;
|
|
|
+ const phase = (i / mSamples) * nMod * 2 * Math.PI;
|
|
|
+ const y = mMid - (mH / 2 - 6) * Math.sin(phase);
|
|
|
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
|
|
}
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
|
|
|
- document.getElementById('fb-mod').textContent = fb;
|
|
|
+ // средний: эталонная несущая (без модуляции) для сравнения
|
|
|
+ const cY = 145, cH = 80;
|
|
|
+ const cMid = cY + cH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('НЕСУЩАЯ f_c = ' + Fc.toFixed(1) + ' МГц (без модуляции, для сравнения)', x0, cY - 6);
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, cMid);
|
|
|
+ ctx.lineTo(x1, cMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, cY);
|
|
|
+ ctx.lineTo(x0, cY + cH);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ const cSamples = Math.max(600, Math.ceil(nOscDisplay * 16));
|
|
|
+ ctx.strokeStyle = '#1a4a7a';
|
|
|
+ ctx.lineWidth = 1.5;
|
|
|
+ ctx.beginPath();
|
|
|
+ for (let i = 0; i <= cSamples; i++) {
|
|
|
+ const x = x0 + (x1 - x0) * i / cSamples;
|
|
|
+ const phase = (i / cSamples) * nOscDisplay * 2 * Math.PI;
|
|
|
+ const y = cMid - (cH / 2 - 4) * Math.sin(phase);
|
|
|
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
|
|
+ }
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ // нижний: ЧМ-сигнал
|
|
|
+ const tY = 265, tH = 130;
|
|
|
+ const tMid = tY + tH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('ЧМ-СИГНАЛ: амплитуда постоянна, частота меняется по закону F_м', x0, tY - 6);
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, tMid);
|
|
|
+ ctx.lineTo(x1, tMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, tY);
|
|
|
+ ctx.lineTo(x0, tY + tH);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ const ampBase = (tH / 2 - 4);
|
|
|
+ const tSamples = Math.max(1500, Math.ceil(nOscDisplay * 20));
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#6a3a8a';
|
|
|
+ ctx.lineWidth = 1.5;
|
|
|
+ ctx.beginPath();
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ ctx.fillStyle = '#3a4a6a';
|
|
|
+ ctx.font = '9px "Roboto Mono", monospace';
|
|
|
+ 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) + ' кГц';
|
|
|
+ document.getElementById('v-fc-fm').textContent = Fc.toFixed(1) + ' МГц';
|
|
|
+ document.getElementById('v-dev-fm').textContent = Dev.toFixed(0) + ' кГц';
|
|
|
+
|
|
|
+ document.getElementById('r-fm').innerHTML =
|
|
|
+ 'F_м = <span class="hl">' + Fm.toFixed(1) + ' кГц</span> · ' +
|
|
|
+ 'f_c = <span class="hl">' + Fc.toFixed(1) + ' МГц</span> (λ = ' + lamC.toFixed(0) + ' м)<br>' +
|
|
|
+ 'Δf = <span class="hl">' + Dev.toFixed(0) + ' кГц</span> · ' +
|
|
|
+ 'β = Δf/F_м = <span class="hl">' + beta.toFixed(2) + '</span><br>' +
|
|
|
+ 'Полоса по Карсону B ≈ 2(Δf + F_м) = <span class="hl">' + carson.toFixed(0) + ' кГц</span>';
|
|
|
+
|
|
|
+ let fb;
|
|
|
+ if (beta < 0.5) fb = 'Узкополосная ЧМ (β < 0.5): по спектру похожа на АМ, но амплитуда постоянна — это плюс по помехоустойчивости.';
|
|
|
+ else if (beta < 5) fb = 'Обычная ЧМ (0.5 ≤ β < 5): заметно сгущение и разрежение осцилляций, амплитуда постоянна.';
|
|
|
+ else fb = 'Широкополосная ЧМ (β ≥ 5): большая девиация, широкая полоса, высокая помехоустойчивость. Цена — полоса.';
|
|
|
+ document.getElementById('fb-fm').textContent = fb;
|
|
|
}
|
|
|
|
|
|
currentDraw = draw;
|
|
|
sFm.oninput = draw;
|
|
|
sFc.oninput = draw;
|
|
|
- sM.oninput = draw;
|
|
|
+ sDev.oninput = draw;
|
|
|
draw();
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 5 — diffraction
|
|
|
+ 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).
|
|
|
+ ============================================================ */
|
|
|
+function initPM() {
|
|
|
+ const canvas = document.getElementById('c-pm');
|
|
|
+ const sFm = document.getElementById('s-fm-pm');
|
|
|
+ const sFc = document.getElementById('s-fc-pm');
|
|
|
+ const sBeta = document.getElementById('s-beta-pm');
|
|
|
+
|
|
|
+ const T_MOD_MS = 4;
|
|
|
+
|
|
|
+ function draw() {
|
|
|
+ const { ctx, W, H } = prepCanvas(canvas, 420);
|
|
|
+ const Fm = parseFloat(sFm.value);
|
|
|
+ const Fc = parseFloat(sFc.value);
|
|
|
+ const beta = parseFloat(sBeta.value);
|
|
|
+
|
|
|
+ const nMod = Fm * T_MOD_MS;
|
|
|
+ const nOscDisplay = 30 + 170 * (Fc - 0.5) / 4.5;
|
|
|
+
|
|
|
+ const x0 = 55, x1 = W - 20;
|
|
|
+
|
|
|
+ // верхний: модулирующий
|
|
|
+ const mY = 22, mH = 80;
|
|
|
+ const mMid = mY + mH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('МОДУЛИРУЮЩИЙ СИГНАЛ F_м = ' + Fm.toFixed(1) + ' кГц', x0, mY - 6);
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, mMid);
|
|
|
+ ctx.lineTo(x1, mMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, mY);
|
|
|
+ ctx.lineTo(x0, mY + mH);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ const mSamples = Math.max(200, Math.ceil(nMod * 20));
|
|
|
+ ctx.strokeStyle = '#2a6a3a';
|
|
|
+ ctx.lineWidth = 2;
|
|
|
+ ctx.beginPath();
|
|
|
+ for (let i = 0; i <= mSamples; i++) {
|
|
|
+ const x = x0 + (x1 - x0) * i / mSamples;
|
|
|
+ const phase = (i / mSamples) * nMod * 2 * Math.PI;
|
|
|
+ const y = mMid - (mH / 2 - 6) * Math.sin(phase);
|
|
|
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
|
|
+ }
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ // средний: эталонная несущая
|
|
|
+ const cY = 145, cH = 80;
|
|
|
+ const cMid = cY + cH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('НЕСУЩАЯ f_c = ' + Fc.toFixed(1) + ' МГц (без модуляции, для сравнения)', x0, cY - 6);
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, cMid);
|
|
|
+ ctx.lineTo(x1, cMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, cY);
|
|
|
+ ctx.lineTo(x0, cY + cH);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ const cSamples = Math.max(600, Math.ceil(nOscDisplay * 16));
|
|
|
+ ctx.strokeStyle = '#1a4a7a';
|
|
|
+ ctx.lineWidth = 1.5;
|
|
|
+ ctx.beginPath();
|
|
|
+ for (let i = 0; i <= cSamples; i++) {
|
|
|
+ const x = x0 + (x1 - x0) * i / cSamples;
|
|
|
+ const phase = (i / cSamples) * nOscDisplay * 2 * Math.PI;
|
|
|
+ const y = cMid - (cH / 2 - 4) * Math.sin(phase);
|
|
|
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
|
|
+ }
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ // нижний: ФМ-сигнал
|
|
|
+ const tY = 265, tH = 130;
|
|
|
+ const tMid = tY + tH / 2;
|
|
|
+ ctx.fillStyle = '#1a2a4a';
|
|
|
+ ctx.font = '10px "Roboto Mono", monospace';
|
|
|
+ ctx.textAlign = 'left';
|
|
|
+ ctx.fillText('ФМ-СИГНАЛ: амплитуда постоянна, фаза сдвигается по закону F_м', x0, tY - 6);
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#1a2a4a';
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, tMid);
|
|
|
+ ctx.lineTo(x1, tMid);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x0, tY);
|
|
|
+ ctx.lineTo(x0, tY + tH);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ const ampBase = (tH / 2 - 4);
|
|
|
+ const tSamples = Math.max(1500, Math.ceil(nOscDisplay * 20));
|
|
|
+
|
|
|
+ ctx.strokeStyle = '#6a3a8a';
|
|
|
+ ctx.lineWidth = 1.5;
|
|
|
+ ctx.beginPath();
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ ctx.fillStyle = '#3a4a6a';
|
|
|
+ ctx.font = '9px "Roboto Mono", monospace';
|
|
|
+ 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) + ' кГц';
|
|
|
+ document.getElementById('v-fc-pm').textContent = Fc.toFixed(1) + ' МГц';
|
|
|
+ document.getElementById('v-beta-pm').textContent = beta.toFixed(1);
|
|
|
+
|
|
|
+ document.getElementById('r-pm').innerHTML =
|
|
|
+ 'F_м = <span class="hl">' + Fm.toFixed(1) + ' кГц</span> · ' +
|
|
|
+ 'f_c = <span class="hl">' + Fc.toFixed(1) + ' МГц</span> (λ = ' + lamC.toFixed(0) + ' м)<br>' +
|
|
|
+ 'β = <span class="hl">' + beta.toFixed(2) + ' рад</span><br>' +
|
|
|
+ 'Полоса B ≈ 2F_м(β + 1) = <span class="hl">' + pmBand.toFixed(0) + ' кГц</span>';
|
|
|
+
|
|
|
+ let fb;
|
|
|
+ if (beta < 0.5) fb = 'Малый индекс: фаза колеблется слабо, спектр узкий. Похоже на узкополосную ЧМ.';
|
|
|
+ else if (beta < 5) fb = 'Обычная ФМ: заметны сдвиги фазы, частота меняется по cos — со сдвигом π/2 относительно модулирующего.';
|
|
|
+ else fb = 'Большой индекс: сильные фазовые сдвиги, широкая полоса. На практике применяется редко — при том же β помехоустойчивость как у ЧМ, но полоса та же.';
|
|
|
+ document.getElementById('fb-pm').textContent = fb;
|
|
|
+ }
|
|
|
+
|
|
|
+ currentDraw = draw;
|
|
|
+ sFm.oninput = draw;
|
|
|
+ sFc.oninput = draw;
|
|
|
+ sBeta.oninput = draw;
|
|
|
+ draw();
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================================================
|
|
|
+ SCENE 7 — diffraction
|
|
|
============================================================ */
|
|
|
function initDiff() {
|
|
|
const canvas = document.getElementById('c-diff');
|
|
|
@@ -969,7 +1309,7 @@ function initDiff() {
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 6 — ionosphere
|
|
|
+ SCENE 8 — ionosphere
|
|
|
============================================================ */
|
|
|
function initIono() {
|
|
|
const canvas = document.getElementById('c-iono');
|
|
|
@@ -1113,7 +1453,7 @@ function initIono() {
|
|
|
if (f < 3e6) {
|
|
|
band = 'ДВ/СВ';
|
|
|
desc = isDay
|
|
|
- ? 'День: D-слой поглощает, дальний приём пропадает. Земная волна вдоль поверхности.'
|
|
|
+ ? 'День: D-слой поглощает, дальний приём пропадает.'
|
|
|
: 'Ночь: D-слой исчез, отражение от E. Дальняя связь.';
|
|
|
} else if (f <= 3e7) {
|
|
|
band = 'КВ';
|
|
|
@@ -1135,18 +1475,18 @@ function initIono() {
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 7 — range trainer
|
|
|
+ SCENE 9 — trainer
|
|
|
============================================================ */
|
|
|
const rangeData = [
|
|
|
{ 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 ГГц — СВЧ. Прямая видимость в помещении, мебель огибает плохо.' },
|
|
|
- { id: 5, text: 'Мобильный телефон 900 МГц', bin: 'ukv', expl: '900 МГц — УКВ (дециметровый). Прямая видимость до базовой станции, дифракция за зданиями слабая.' },
|
|
|
- { id: 6, text: 'Радиорелейная линия', bin: 'svch', expl: 'СВЧ, узкий луч, прямая видимость. Между вышками — потому что за горизонт не уходит.' },
|
|
|
+ { id: 5, text: 'Мобильный телефон 900 МГц', bin: 'ukv', expl: '900 МГц — УКВ (дециметровый). Прямая видимость до базовой станции.' },
|
|
|
+ { id: 6, text: 'Радиорелейная линия', bin: 'svch', expl: 'СВЧ, узкий луч, прямая видимость. Между вышками.' },
|
|
|
{ id: 7, text: 'Спутниковое ТВ', bin: 'svch', expl: 'СВЧ проходит сквозь ионосферу — спутник видит Землю, Земля видит спутник.' },
|
|
|
- { id: 8, text: 'Радиолокатор аэропорта', bin: 'svch', expl: 'СВЧ, узкий луч, малая λ — высокое разрешение по дальности и углу.' },
|
|
|
- { id: 9, text: 'Телевидение ДМВ', bin: 'ukv', expl: 'ДМВ (дециметровые) — УКВ. Прямая видимость, вышки на холмах.' },
|
|
|
+ { id: 8, text: 'Радиолокатор аэропорта', bin: 'svch', expl: 'СВЧ, узкий луч, малая λ — высокое разрешение.' },
|
|
|
+ { id: 9, text: 'Телевидение ДМВ', bin: 'ukv', expl: 'ДМВ — УКВ. Прямая видимость, вышки на холмах.' },
|
|
|
{ id: 10, text: 'Навигационный радиомаяк', bin: 'dv', expl: 'ДВ/СВ — земная волна, устойчивый приём на большие расстояния.' }
|
|
|
];
|
|
|
const binNames = { dv: 'ДВ/СДВ', sv: 'СВ', kv: 'КВ', ukv: 'УКВ', svch: 'СВЧ' };
|
|
|
@@ -1163,7 +1503,7 @@ function initTrainer() {
|
|
|
fb.style.borderColor = '#2a6a3a';
|
|
|
fb.style.background = 'rgba(42,106,58,0.1)';
|
|
|
} else {
|
|
|
- fb.textContent = 'Неверно. Вспомните: как волна распространяется в этом диапазоне? Где он на шкале?';
|
|
|
+ fb.textContent = 'Неверно. Вспомните: как волна распространяется в этом диапазоне?';
|
|
|
fb.style.borderColor = '#b22222';
|
|
|
fb.style.background = 'rgba(178,34,34,0.1)';
|
|
|
}
|
|
|
@@ -1172,7 +1512,6 @@ function initTrainer() {
|
|
|
|
|
|
function render() {
|
|
|
container.innerHTML = '';
|
|
|
-
|
|
|
const cardsDiv = document.createElement('div');
|
|
|
cardsDiv.className = 'cards-area';
|
|
|
rangeData.forEach(card => {
|
|
|
@@ -1219,7 +1558,7 @@ function initTrainer() {
|
|
|
if (Object.keys(placed).length === rangeData.length) {
|
|
|
const done = document.createElement('div');
|
|
|
done.className = 'feedback';
|
|
|
- done.innerHTML = '<b>Все карточки разложены.</b> Почему СВЧ так часто? Высокая частота даёт широкую полосу и узкий луч, малая λ — высокое разрешение. Цена — прямая видимость и затухание в дожде.';
|
|
|
+ done.innerHTML = '<b>Все карточки разложены.</b> Почему СВЧ так часто? Высокая частота даёт широкую полосу и узкий луч, малая λ — высокое разрешение. Цена — прямая видимость.';
|
|
|
container.appendChild(done);
|
|
|
}
|
|
|
}
|
|
|
@@ -1228,7 +1567,7 @@ function initTrainer() {
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 8 — security
|
|
|
+ SCENE 10 — security
|
|
|
============================================================ */
|
|
|
function initSecurity() {
|
|
|
const canvas = document.getElementById('c-sec');
|
|
|
@@ -1353,8 +1692,8 @@ function initSecurity() {
|
|
|
if (!state.antenna && !state.fhss && !state.shield) {
|
|
|
fb.push('Все меры выключены: широкая зона перехвата, помеха накрывает сигнал, утечка через корпус.');
|
|
|
}
|
|
|
- if (state.antenna) fb.push('Направленная антенна сузила зону перехвата, но требует точного наведения.');
|
|
|
- if (state.fhss) fb.push('ППРЧ: сигнал скачет по частотам, узкополосная помеха промахивается. От широкополосной не спасает.');
|
|
|
+ if (state.antenna) fb.push('Направленная антенна сузила зону перехвата, но требует наведения.');
|
|
|
+ if (state.fhss) fb.push('ППРЧ: узкополосная помеха промахивается. От широкополосной не спасает.');
|
|
|
if (state.shield) fb.push('Экранирование снизило утечку, но не дальность полезного сигнала.');
|
|
|
document.getElementById('fb-sec').textContent = fb.join(' ');
|
|
|
}
|
|
|
@@ -1373,7 +1712,7 @@ function initSecurity() {
|
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
|
- SCENE 9 — test
|
|
|
+ SCENE 11 — test
|
|
|
============================================================ */
|
|
|
const testQuestions = [
|
|
|
{
|
|
|
@@ -1398,7 +1737,7 @@ const testQuestions = [
|
|
|
q: 'Где полоса шире — на несущей 1 МГц или 100 МГц при той же F_м (модулирующем сигнале)?',
|
|
|
opts: ['На 1 МГц', 'На 100 МГц', 'Одинакова: B = 2F_м', 'Зависит от глубины модуляции'],
|
|
|
correct: 2,
|
|
|
- expl: 'Полоса АМ-сигнала B = 2F_м зависит от модулирующего сигнала, не от несущей. Но на высокой несущей можно взять больший F_м.'
|
|
|
+ expl: 'Полоса АМ-сигнала B = 2F_м зависит от модулирующего сигнала, не от несущей.'
|
|
|
},
|
|
|
{
|
|
|
q: 'Какой диапазон для связи со спутником?',
|
|
|
@@ -1417,6 +1756,18 @@ const testQuestions = [
|
|
|
opts: ['D-слой поглощает', 'Ионосфера исчезает', 'Антенна хуже работает', 'Помехи от Солнца'],
|
|
|
correct: 0,
|
|
|
expl: 'Днём D-слой ионизирован и поглощает СВ. Ночью D исчезает — дальний приём возвращается.'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ q: 'Где заложена информация в ЧМ-сигнале?',
|
|
|
+ opts: ['В амплитуде несущей', 'В отклонении частоты несущей от среднего (девиации)', 'В самой несущей частоте', 'В длине волны'],
|
|
|
+ correct: 1,
|
|
|
+ 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. Внешне похожи, но физика разная.'
|
|
|
}
|
|
|
];
|
|
|
|