feature: pm applet

This commit is contained in:
Matthias Jung
2026-03-24 22:07:58 +01:00
parent 3a2db6284f
commit 52343c2588
2 changed files with 188 additions and 1 deletions
+181
View File
@@ -0,0 +1,181 @@
<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>
+7 -1
View File
@@ -12,4 +12,10 @@ Die beiden Modulationsarten stehen in einem engen Zusammenhang: Die Phasenmodula
In technischen Systemen ist es oft möglich, FM durch PM zu realisieren, indem das Modulationssignal integriert wird. Da die Frequenzänderung die Ableitung der Phase ist, entspricht die Integration des Modulationssignals einer schrittweisen Anpassung der Phase, was die gleiche Wirkung wie Frequenzmodulation erzielt.
</indepth>
[question:AE313]
[question:AE313]
<webonly>
<margin>
[include:applet_pm]
</margin>
</webonly>