Bisher zeigte die App nur eine Liste sichtbarer Netze ohne Kanalgraph, ohne
Empfehlung und ohne Möglichkeit, einen Kanalwechsel vorher/nachher zu
vergleichen. Fester 800-ms-Sleep statt auf den echten Scan zu warten, keine
Reaktion auf ausgeschaltete Ortung, Standortberechtigung auch dort nötig, wo
Android 13+ das gar nicht mehr verlangt.
Nativ (NetDiagScannerPlugin.kt):
- wifiScan liefert jetzt frequency, widthMhz, centerFreq0/1, standard
(Wi-Fi 4/5/6/7), security (offen/WEP/WPA/WPA2/WPA3), ageMs, band
(inkl. 6 GHz) je Netz. freqToChannel() um das 6-GHz-Band erweitert
(inkl. Sonderfall Kanal 2/PSC).
- wifiScan({trigger:true}) löst einen frischen Scan aus und wartet auf
SCAN_RESULTS_AVAILABLE_ACTION (5s Zeitgrenze) statt blind zu schlafen.
- isLocationEnabled()-Prüfung + openLocationSettings(): ausgeschaltete
Ortung (< Android 13) ergibt jetzt einen Klartextfehler mit direktem
Weg zu den Einstellungen statt einer stillen leeren Liste.
- NEARBY_WIFI_DEVICES (API 33+, neverForLocation) statt Standortrecht —
eigenes Berechtigungs-Alias, wifiScanPermissionAlias() wählt je nach
SDK-Version; gilt für wifiScan/startWifiScan/startWifiTrack gemeinsam.
App:
- WifiChannelChart.svelte: Kanalgraph 2,4/5/6 GHz als Inline-SVG — Bögen
mit Kanalbreite als Bogenbreite, Signalstärke als Bogenhöhe, Überlapp
(Co-/Adjacent-Channel) direkt als sichtbare Farbverdichtung.
- wifi/recommend.ts: beste der drei nicht überlappenden 2,4-GHz-Kanäle
nach Störlast + zwei feste Warnregeln (40 MHz im 2,4-GHz-Band,
160 MHz/DFS im 5-GHz-Band).
- wifi/rating.ts: rssiRating() vereinheitlicht — ersetzt drei Kopien mit
unterschiedlichen Schwellen (iptest/+page.svelte, wifi/+page.svelte
zweimal).
- Neue Route /protokoll/{id}/wifikanal/ + Werkzeug-Kachel "WLAN-Kanäle":
Netzliste nach SSID gruppiert (Mesh), Momentaufnahmen speichern
(protocol.wifiSurveys, nur lokal), automatischer Kanaländerungs-Hinweis
seit der letzten Aufnahme, Zeitverlauf mehrerer Netze aus den
gespeicherten Momentaufnahmen (echte Zeitstempel, feste Y-Skala
-100..-20 dBm, Messlücken reißen die Linie statt sie zu überbrücken).
Im Emulator getestet (API 33+): NEARBY_WIFI_DEVICES-Berechtigungsdialog
erscheint und wird korrekt verarbeitet — echte Bestätigung, kein
Codereview-only. Leerer Netz-Zustand rendert sauber. Der AVD hat keine
echte WLAN-Hardware (liefert grundsätzlich 0 Scan-Ergebnisse) — Kanalgraph
mit echten Netzen und der Ortung-aus-Zweig (<Android 13, auf diesem API-33-
AVD unerreichbar) konnten deshalb nicht live geprüft werden, nur die
Chart-Mathematik gegen die Mock-Werte durchgerechnet (keine NaN/Werte
außerhalb des Zeichenbereichs). Sollte auf einem echten Handy mit echtem
WLAN-Empfang gegengeprüft werden.
Details: ROADMAP_UMSETZUNG.md Phase 4
89 lines
3.8 KiB
TypeScript
89 lines
3.8 KiB
TypeScript
/**
|
|
* Kanalempfehlung und Warnregeln fürs 2,4-/5-GHz-Band.
|
|
*
|
|
* Bewusst einfach gehalten (keine vollständige RF-Simulation): zählt für
|
|
* jeden der drei nicht überlappenden 2,4-GHz-Kanäle (1/6/11) die Störlast
|
|
* durch alle sichtbaren Netze — Co-Channel (gleicher Kanal) zählt voll,
|
|
* Adjacent-Channel (überlappender Bereich, z.B. Kanal 4 bei Kanal 6) zählt
|
|
* abgeschwächt nach Abstand. Gewichtet zusätzlich mit der Signalstärke des
|
|
* störenden Netzes (ein schwaches Fremdnetz stört weniger als ein starkes).
|
|
*/
|
|
|
|
import type { WifiNetwork } from '../scanner';
|
|
|
|
const NONOVERLAPPING_24 = [1, 6, 11];
|
|
|
|
/** Kanalabstand zweier 2,4-GHz-Kanäle in "Kanal-Einheiten" (5 MHz je Kanal) */
|
|
function channelDistance24(a: number, b: number): number {
|
|
return Math.abs(a - b);
|
|
}
|
|
|
|
/**
|
|
* Störlast eines Kandidatenkanals durch ein einzelnes Netz — 1.0 bei
|
|
* Co-Channel, linear abnehmend bis 0 bei einem Abstand von 5 Kanälen
|
|
* (= kein spürbarer Überlapp mehr bei 20-MHz-Kanälen), gewichtet mit der
|
|
* Signalstärke (stärkeres Fremdnetz stört mehr).
|
|
*/
|
|
function interferenceWeight(candidate: number, network: WifiNetwork): number {
|
|
const dist = channelDistance24(candidate, network.channel);
|
|
if (dist >= 5) return 0;
|
|
const overlapFactor = 1 - dist / 5;
|
|
// RSSI -30 (sehr stark) -> Gewicht ~1, RSSI -90 (kaum sichtbar) -> Gewicht ~0.1
|
|
const strengthFactor = Math.min(1, Math.max(0.1, (network.rssi + 90) / 60));
|
|
return overlapFactor * strengthFactor;
|
|
}
|
|
|
|
export interface ChannelScore {
|
|
channel: number;
|
|
/** niedriger = besser (weniger Störlast) */
|
|
load: number;
|
|
}
|
|
|
|
/**
|
|
* Beste der drei nicht überlappenden 2,4-GHz-Kanäle (1/6/11), aufsteigend
|
|
* nach Störlast sortiert. `networks` sollte NUR Fremdnetze enthalten (das
|
|
* eigene Netz selbst zählt nicht als Störer für sich selbst).
|
|
*/
|
|
export function recommend24GhzChannels(networks: WifiNetwork[]): ChannelScore[] {
|
|
const in24 = networks.filter((n) => n.band === '2.4 GHz');
|
|
return NONOVERLAPPING_24.map((channel) => ({
|
|
channel,
|
|
load: Math.round(in24.reduce((sum, n) => sum + interferenceWeight(channel, n), 0) * 100) / 100,
|
|
})).sort((a, b) => a.load - b.load);
|
|
}
|
|
|
|
/**
|
|
* Zwei feste Warnregeln, die praktisch immer ein Problem anzeigen:
|
|
* 1. 40 MHz Kanalbreite im 2,4-GHz-Band — bei realistischer Nachbarschafts-
|
|
* dichte (Mehrfamilienhaus, Gewerbegebiet) fast immer eine Interferenz-
|
|
* quelle, weil es 2 der 3 nicht überlappenden Kanäle gleichzeitig belegt.
|
|
* 2. 160 MHz Kanalbreite (oder DFS-Kanäle 52-144) im 5-GHz-Band — DFS kann
|
|
* durch Radar-Erkennung jederzeit einen Kanalwechsel erzwingen (kurzer
|
|
* Aussetzer), 160 MHz ist auf vielen Consumer-Geräten instabil bzw. wird
|
|
* bei Störung automatisch auf 80 MHz zurückgestuft.
|
|
*/
|
|
export function channelWarnings(networks: WifiNetwork[]): string[] {
|
|
const warnings: string[] = [];
|
|
|
|
const wide24 = networks.filter((n) => n.band === '2.4 GHz' && (n.widthMhz ?? 20) >= 40);
|
|
for (const n of wide24) {
|
|
warnings.push(
|
|
`„${n.ssid || n.bssid}" nutzt 40 MHz im 2,4-GHz-Band (Kanal ${n.channel}) — blockiert damit ` +
|
|
`praktisch 2 der 3 störungsfreien Kanäle. In dicht besiedelter Umgebung meist ein Fehler in ` +
|
|
`der Router-Konfiguration, nicht Absicht.`,
|
|
);
|
|
}
|
|
|
|
const isDfsChannel = (ch: number) => ch >= 52 && ch <= 144;
|
|
const wideOrDfs5 = networks.filter(
|
|
(n) => n.band === '5 GHz' && ((n.widthMhz ?? 20) >= 160 || isDfsChannel(n.channel)),
|
|
);
|
|
for (const n of wideOrDfs5) {
|
|
const reasons: string[] = [];
|
|
if ((n.widthMhz ?? 20) >= 160) reasons.push('160 MHz Kanalbreite');
|
|
if (isDfsChannel(n.channel)) reasons.push('DFS-Kanal (Radar-Erkennung kann Kanalwechsel erzwingen)');
|
|
warnings.push(`„${n.ssid || n.bssid}" (Kanal ${n.channel}): ${reasons.join(', ')} — kann zu kurzen, scheinbar grundlosen Aussetzern führen.`);
|
|
}
|
|
|
|
return warnings;
|
|
}
|