netdiag-app/tools/test-recommend.mjs
Eduard Wisch 3898f2aa27 Kanalempfehlung und DFS-Anzeige mit dem Diagramm in Einklang gebracht
Zwei Widersprüche, die beim Durchsehen der offenen Punkte auffielen. Beide
entstanden erst dadurch, dass der Kanalgraph seit dem letzten Commit die
Wahrheit zeigt — Empfehlung und Kennzeichnung waren noch auf dem alten Stand.

1. Kanalempfehlung ignorierte die Kanalbreite.
   interferenceWeight() rechnete nur mit dem Abstand der Kanalnummern. Ein
   40-MHz-Netz auf Kanal 1 belegt real 2402-2442 MHz und damit auch Kanal 6
   (2437), wurde wegen |1-6| = 5 aber als "stört nicht" gewertet. Gerechnet
   wird jetzt in MHz, mit derselben occupiedRange(), die auch das Diagramm
   zeichnet.
   Belegt mit tools/test-recommend.mjs: in zwei von drei realistischen Lagen
   nennt die alte Formel einen ANDEREN Kanal als den tatsächlich besten —
   z.B. bei einem starken 40-MHz-Netz auf Kanal 1 empfahl sie Kanal 6, obwohl
   dieses Netz genau dorthin reicht (richtig ist Kanal 11). In der Demo-Lage
   ändert sich die Spitzenempfehlung nicht, nur die Bewertung der übrigen
   Kanäle (K6 von 0,62 auf 0,80).

2. DFS-Kanäle 52-64 waren im Diagramm nicht als solche erkennbar.
   Der Block hieß "UNII-1/2A" und trug keine DFS-Kennzeichnung, während
   channelWarnings() Kanal 52-144 als DFS behandelt. Ein im Diagramm "frei"
   wirkender Kanal 56 hätte den Kunden ungewollt auf DFS gesetzt (möglicher
   Radar-Zwangswechsel), obwohl die Warnung darunter das Gegenteil sagte.
   UNII-1 (36-48) und UNII-2A (52-64) sind jetzt getrennte Blöcke mit
   korrekter Kennzeichnung.

Damit die zusätzliche Aufteilung nicht zu mehr Scrollen führt: ein Block ohne
Netze wird kompakt gezeichnet (ohne dBm-Skala, ~1/4 der Höhe) statt in voller
Größe. Er bleibt sichtbar — er ist die Antwort auf "wo ist noch Platz".

Nebenbei im Render-Werkzeug behoben: es stapelte alle Segmente mit gleicher
Höhe und erzeugte dadurch künstliche Lücken im Testbild, die es in der App
nicht gibt.
2026-08-15 17:59:41 +02:00

61 lines
2.6 KiB
JavaScript

import { transformSync } from 'esbuild';
import { readFileSync, writeFileSync } from 'fs';
import { pathToFileURL } from 'url';
function build(src, out) {
let js = transformSync(readFileSync(src, 'utf8'), { loader: 'ts', format: 'esm', target: 'node20' }).code;
js = js.replace(/from\s+['"]\.\/chart['"]/g, "from './chart.js'");
writeFileSync(out, js);
}
build('src/lib/wifi/chart.ts', '/tmp/chart.js');
build('src/lib/wifi/recommend.ts', '/tmp/recommend.js');
const { recommend24GhzChannels } = await import(pathToFileURL('/tmp/recommend.js').href);
function alt(nets) {
const w = (c, n) => {
const d = Math.abs(c - n.channel);
if (d >= 5) return 0;
return (1 - d / 5) * Math.min(1, Math.max(0.1, (n.rssi + 90) / 60));
};
return [1, 6, 11].map((channel) => ({
channel, load: Math.round(nets.reduce((s, n) => s + w(channel, n), 0) * 100) / 100,
})).sort((a, b) => a.load - b.load);
}
const FAELLE = [
{
name: 'Demo-Lage (wie im Screenshot)',
nets: [
{ channel: 6, frequency: 2437, rssi: -53, band: '2.4 GHz', widthMhz: 20 },
{ channel: 11, frequency: 2462, rssi: -61, band: '2.4 GHz', widthMhz: 20 },
{ channel: 1, frequency: 2412, rssi: -75, band: '2.4 GHz', widthMhz: 40, centerFreq0: 2422 },
{ channel: 11, frequency: 2462, rssi: -77, band: '2.4 GHz', widthMhz: 20 },
{ channel: 13, frequency: 2472, rssi: -85, band: '2.4 GHz', widthMhz: 20 },
],
},
{
name: 'STARKES 40-MHz-Netz auf Kanal 1, schwache Nachbarn auf 6 und 11',
nets: [
{ channel: 1, frequency: 2412, rssi: -45, band: '2.4 GHz', widthMhz: 40, centerFreq0: 2422 },
{ channel: 6, frequency: 2437, rssi: -85, band: '2.4 GHz', widthMhz: 20 },
{ channel: 11, frequency: 2462, rssi: -80, band: '2.4 GHz', widthMhz: 20 },
],
},
{
name: 'Starkes 40-MHz-Netz auf Kanal 9 (überdeckt 6 UND 11)',
nets: [
{ channel: 9, frequency: 2452, rssi: -50, band: '2.4 GHz', widthMhz: 40, centerFreq0: 2442 },
{ channel: 1, frequency: 2412, rssi: -70, band: '2.4 GHz', widthMhz: 20 },
],
},
];
for (const f of FAELLE) {
const neu = recommend24GhzChannels(f.nets);
const a = alt(f.nets);
const kippt = neu[0].channel !== a[0].channel;
console.log(`\n${f.name}`);
console.log(` ALT: ${a.map(r => `K${r.channel}=${r.load}`).join(' ')} -> empfiehlt Kanal ${a[0].channel}`);
console.log(` NEU: ${neu.map(r => `K${r.channel}=${r.load}`).join(' ')} -> empfiehlt Kanal ${neu[0].channel}`);
console.log(` ${kippt ? '>>> EMPFEHLUNG KIPPT — alte Formel hätte den falschen Kanal genannt' : ' (gleiche Empfehlung, nur andere Bewertung)'}`);
}