Files
50ohm-contents-dl/contents/html/applet_pm.html
T
2026-03-24 22:07:58 +01:00

182 lines
6.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="container" style="max-width:860px;">
<div class="mb-2">
<div class="small text-secondary fw-semibold mb-1">Modulierendes Signal (Nachricht)</div>
<canvas id="fmpm-c0" height="140" class="w-100 d-block rounded"></canvas>
</div>
<div class="mb-2">
<div class="small text-secondary fw-semibold mb-1">Trägersignal (unmoduliert)</div>
<canvas id="fmpm-c1" height="70" class="w-100 d-block rounded"></canvas>
</div>
<div class="mb-2">
<div class="small text-secondary fw-semibold mb-1">FM – Frequenz des Trägers folgt dem Signal-Wert</div>
<canvas id="fmpm-c2" height="70" class="w-100 d-block rounded"></canvas>
</div>
<div class="mb-3">
<div class="small text-secondary fw-semibold mb-1">PM – Phase des Trägers folgt dem Signal-Wert</div>
<canvas id="fmpm-c3" height="70" class="w-100 d-block rounded"></canvas>
</div>
<hr>
<div class="d-flex flex-column gap-3">
<div class="row align-items-center g-2">
<div class="col-auto" style="min-width:170px;">
<label class="col-form-label col-form-label-sm">Signalform</label>
</div>
<div class="col">
<select class="form-select form-select-sm" id="fmpm-shape">
<option value="sine">Sinus</option>
<option value="square">Rechteck</option>
<option value="saw">Sägezahn</option>
<option value="ramp">Linearer Anstieg</option>
</select>
</div>
</div>
<div class="row align-items-center g-2">
<div class="col-auto" style="min-width:170px;">
<label class="col-form-label col-form-label-sm">Anzahl Gitterlinien</label>
</div>
<div class="col">
<input type="range" class="form-range" id="fmpm-gridcount" min="4" max="24" step="1" value="12">
</div>
<div class="col-auto text-end fw-semibold small" id="fmpm-gridcount-v">12</div>
</div>
</div>
<hr>
<div id="fmpm-insight"></div>
</div>
<script>
(function () {
const IDS = ['fmpm-c0','fmpm-c1','fmpm-c2','fmpm-c3'];
const COLORS = ['#1D9E75','#888780','#378ADD','#D85A30'];
const N = 1200;
const FC = 8;
const DEPTH = 1.5;
function signal(shape, n) {
const t = n / N, fs = 1.2;
switch (shape) {
case 'sine': return Math.sin(2 * Math.PI * fs * t);
case 'square': return Math.sign(Math.sin(2 * Math.PI * fs * t));
case 'saw': return 2 * (fs * t % 1) - 1;
case 'ramp': return Math.max(-1, Math.min(1, 2 * t - 1));
default: return 0;
}
}
function draw() {
const depth = DEPTH;
const shape = document.getElementById('fmpm-shape').value;
const gridCount = parseInt(document.getElementById('fmpm-gridcount').value);
const cvs = IDS.map(id => document.getElementById(id));
const dpr = window.devicePixelRatio || 1;
const w = cvs[0].offsetWidth || 800;
cvs.forEach(c => {
c.width = w * dpr;
c.height = c.offsetHeight * dpr;
});
let fmPhase = 0;
const pts = new Array(N);
for (let n = 0; n < N; n++) {
const t = n / N;
const sig = signal(shape, n);
const car = Math.sin(2 * Math.PI * FC * t);
fmPhase += 2 * Math.PI * (FC + depth * sig) / N;
const fm = Math.sin(fmPhase);
const pm = Math.sin(2 * Math.PI * FC * t + depth * sig);
pts[n] = { sig, car, fm, pm };
}
const zeros = [];
for (let n = 1; n < N; n++) {
if (pts[n-1].car <= 0 && pts[n].car > 0) zeros.push(n / N);
}
const series = [
pts.map(p => p.sig),
pts.map(p => p.car),
pts.map(p => p.fm),
pts.map(p => p.pm),
];
const gH = 'rgba(0,0,0,1)';
const gV = 'rgba(70,70,70,.5)';
const zV = 'rgba(120,120,120,0)';
series.forEach((data, i) => {
const c = cvs[i];
const ctx = c.getContext('2d');
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.scale(dpr, dpr);
const h = c.offsetHeight;
const mid = h / 2;
const amp = mid * 0.78;
ctx.strokeStyle = gH;
ctx.lineWidth = 0.9;
ctx.setLineDash([]);
ctx.beginPath(); ctx.moveTo(0, mid); ctx.lineTo(w, mid); ctx.stroke();
ctx.strokeStyle = gV;
ctx.lineWidth = 0.9;
ctx.setLineDash([4, 4]);
for (let g = 1; g < gridCount; g++) {
const x = (g / gridCount) * w;
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke();
}
if (i === 2 || i === 3) {
ctx.strokeStyle = zV;
ctx.lineWidth = 0.5;
ctx.setLineDash([2, 5]);
zeros.forEach(t => {
const x = t * w;
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke();
});
}
if (i === 1) {
ctx.setLineDash([]);
ctx.fillStyle = 'rgba(95,95,95,.60)';
zeros.forEach(t => {
ctx.beginPath();
ctx.arc(t * w, mid, 2.5, 0, 2 * Math.PI);
ctx.fill();
});
}
ctx.setLineDash([]);
ctx.strokeStyle = COLORS[i];
ctx.lineWidth = i === 0 ? 2 : 1.5;
ctx.lineJoin = 'round';
ctx.beginPath();
data.forEach((v, n) => {
const x = n / N * w;
const y = mid - v * amp;
n === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
});
ctx.stroke();
});
const texts = {
sine: '<strong>Sinus:</strong> FM und PM sehen sehr ähnlich aus. Die gestrichelten Gitterlinien machen den leichten Versatz der Nulldurchgänge sichtbar – bei FM ist er gleichmäßig, bei PM phasenverschoben.',
square: '<strong>Rechteck:</strong> Hier wird der Unterschied deutlich! FM springt abrupt zwischen zwei Frequenzen (die Abstände der Nulldurchgänge ändern sich). PM hingegen reagiert nur an den steilen Flanken – dazwischen liegen die Nulldurchgänge fast exakt auf dem Trägerraster.',
saw: '<strong>Sägezahn:</strong> FM zeigt einen kontinuierlichen Frequenzanstieg mit hartem Reset. PM zeigt ebenfalls anwachsenden Versatz – beim Reset entsteht eine kurze Phasenkorrektur, gut an den Nulldurchgängen ablesbar.',
ramp: '<strong>Linearer Anstieg:</strong> FM erhöht die Frequenz stetig (enger werdende Nulldurchgänge). PM zeigt gleichmäßigen Versatz, da die konstante Ableitung des Anstiegs eine feste Frequenzverschiebung ergibt.',
};
document.getElementById('fmpm-insight').innerHTML = texts[shape] || '';
}
document.getElementById('fmpm-shape').addEventListener('change', draw);
document.getElementById('fmpm-gridcount').addEventListener('input', function () {
document.getElementById('fmpm-gridcount-v').textContent = this.value;
draw();
});
new ResizeObserver(draw).observe(document.getElementById('fmpm-c0'));
draw();
})();
</script>