mirror of
https://github.com/billz/raspap-webgui.git
synced 2025-12-26 23:26:47 +01:00
Add dynamic center frequency + HT40 direction for 802.11ax/be
This commit is contained in:
@@ -191,8 +191,6 @@ class HostapdManager
|
|||||||
$config[] = 'ssid=' . $params['ssid'];
|
$config[] = 'ssid=' . $params['ssid'];
|
||||||
$config[] = 'channel=' . $params['channel'];
|
$config[] = 'channel=' . $params['channel'];
|
||||||
|
|
||||||
// choose VHT segment index (fallback only if required)
|
|
||||||
$vht_freq_idx = ($params['channel'] < RASPI_5GHZ_CHANNEL_MIN) ? 42 : 155;
|
|
||||||
$hwMode = isset($params['hw_mode']) ? $params['hw_mode'] : '';
|
$hwMode = isset($params['hw_mode']) ? $params['hw_mode'] : '';
|
||||||
|
|
||||||
// validate channel width for 802.11ax/be
|
// validate channel width for 802.11ax/be
|
||||||
@@ -211,14 +209,24 @@ class HostapdManager
|
|||||||
$modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode);
|
$modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode);
|
||||||
$settings = $modeSettings[$hwMode]['settings'] ?? [];
|
$settings = $modeSettings[$hwMode]['settings'] ?? [];
|
||||||
|
|
||||||
error_log("HostapdManager::buildConfig() -> settings\n" . var_export($settings, true));
|
// extract channel width from settings to calculate center frequency
|
||||||
|
$chwidth = $this->extractChannelWidth($settings, $hwMode);
|
||||||
|
|
||||||
|
// calculate center frequency indices based on channel + width
|
||||||
|
$vht_freq_idx = $this->calculateCenterFreqIndex((int)$params['channel'], $chwidth);
|
||||||
|
$he_freq_idx = $vht_freq_idx; // For most cases, HE and VHT use the same center frequency
|
||||||
|
|
||||||
|
// calculate HT40 direction based on channel and width
|
||||||
|
$ht40_dir = $this->calculateHT40Direction((int)$params['channel'], $chwidth);
|
||||||
|
|
||||||
if (!empty($settings)) {
|
if (!empty($settings)) {
|
||||||
foreach ($settings as $line) {
|
foreach ($settings as $line) {
|
||||||
if (!is_string($line)) {
|
if (!is_string($line)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$replaced = str_replace('{VHT_FREQ_IDX}', (string) $vht_freq_idx ?? '',$line);
|
$replaced = str_replace('{VHT_FREQ_IDX}', (string) $vht_freq_idx ?? '', $line);
|
||||||
|
$replaced = str_replace('{HE_FREQ_IDX}', (string) $he_freq_idx ?? '', $replaced);
|
||||||
|
$replaced = str_replace('{HT40_DIR}', (string) $ht40_dir ?? '', $replaced);
|
||||||
$config[] = $replaced;
|
$config[] = $replaced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,5 +506,241 @@ class HostapdManager
|
|||||||
return $capabilities[$mode] ?? $capabilities['g'];
|
return $capabilities[$mode] ?? $capabilities['g'];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Extracts channel width from mode settings
|
||||||
|
*
|
||||||
|
* @param array $settings mode settings array
|
||||||
|
* @param string $hwMode hardware mode (ac, ax, be)
|
||||||
|
* @return int channel width in MHz (20, 40, 80, 160, 320)
|
||||||
|
*/
|
||||||
|
private function extractChannelWidth(array $settings, string $hwMode): int
|
||||||
|
{
|
||||||
|
$chwidthParam = '';
|
||||||
|
|
||||||
|
// determine parameter based on mode
|
||||||
|
if ($hwMode === 'ac') {
|
||||||
|
$chwidthParam = 'vht_oper_chwidth';
|
||||||
|
} elseif ($hwMode === 'ax') {
|
||||||
|
$chwidthParam = 'he_oper_chwidth';
|
||||||
|
} elseif ($hwMode === 'be') {
|
||||||
|
$chwidthParam = 'eht_oper_chwidth';
|
||||||
|
} else {
|
||||||
|
return 20; // 20 MHz default for other modes
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse settings to find channel width
|
||||||
|
foreach ($settings as $line) {
|
||||||
|
if (!is_string($line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip comments
|
||||||
|
if (strpos(trim($line), '#') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($line, $chwidthParam . '=') !== false) {
|
||||||
|
$parts = explode('=', $line, 2);
|
||||||
|
if (count($parts) === 2) {
|
||||||
|
// extract numeric value
|
||||||
|
$value = trim($parts[1]);
|
||||||
|
// remove any inline comments
|
||||||
|
$value = preg_replace('/\s*#.*$/', '', $value);
|
||||||
|
$chwidthCode = (int) $value;
|
||||||
|
|
||||||
|
// convert hostapd encoding to MHz based on mode
|
||||||
|
if ($hwMode === 'be') {
|
||||||
|
// EHT uses: 0=20, 1=40, 2=80, 3=160, 4=320
|
||||||
|
switch ($chwidthCode) {
|
||||||
|
case 0: return 20;
|
||||||
|
case 1: return 40;
|
||||||
|
case 2: return 80;
|
||||||
|
case 3: return 160;
|
||||||
|
case 4: return 320;
|
||||||
|
default: return 20;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// VHT/HE uses: 0=20/40, 1=80, 2=160, 3=80+80
|
||||||
|
switch ($chwidthCode) {
|
||||||
|
case 0: return 40;
|
||||||
|
case 1: return 80;
|
||||||
|
case 2: return 160;
|
||||||
|
case 3: return 160; // 80+80 treated as 160
|
||||||
|
default: return 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 20; // default to 20 MHz channel if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates center frequency segment 0 index for given channel and width
|
||||||
|
*
|
||||||
|
* @param int $channel primary channel number
|
||||||
|
* @param int $chwidthMHz channel width in MHz (20, 40, 80, 160, 320)
|
||||||
|
* @return int center frequency segment 0 index
|
||||||
|
*/
|
||||||
|
private function calculateCenterFreqIndex(int $channel, int $chwidthMHz): int
|
||||||
|
{
|
||||||
|
// determine band based on channel number
|
||||||
|
$is24GHz = ($channel >= 1 && $channel <= 14);
|
||||||
|
$is5GHz = ($channel >= 36 && $channel <= 177);
|
||||||
|
$is6GHz = ($channel >= 1 && $channel <= 233 && !$is24GHz); // 6 GHz uses 1-233
|
||||||
|
|
||||||
|
// 20 MHz - center is always primary channel
|
||||||
|
if ($chwidthMHz <= 20) {
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.4 GHz band
|
||||||
|
if ($is24GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// for 2.4 GHz, typically use HT40+ (center is primary + 2)
|
||||||
|
// channels 1-7 use HT40+, channels 8-13 use HT40-
|
||||||
|
return ($channel <= 7) ? $channel + 2 : $channel - 2;
|
||||||
|
}
|
||||||
|
// wider bandwidths not supported on 2.4 GHz
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 GHz band
|
||||||
|
if ($is5GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// HT40+ configuration: center = primary + 2
|
||||||
|
// adjust for upper/lower position in 40 MHz pair
|
||||||
|
if (in_array($channel, [36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173])) {
|
||||||
|
return $channel + 2;
|
||||||
|
} else {
|
||||||
|
return $channel - 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 80) {
|
||||||
|
// map channel to 80 MHz center frequency
|
||||||
|
if ($channel >= 36 && $channel <= 48) return 42;
|
||||||
|
if ($channel >= 52 && $channel <= 64) return 58;
|
||||||
|
if ($channel >= 100 && $channel <= 112) return 106;
|
||||||
|
if ($channel >= 116 && $channel <= 128) return 122;
|
||||||
|
if ($channel >= 132 && $channel <= 144) return 138;
|
||||||
|
if ($channel >= 149 && $channel <= 161) return 155;
|
||||||
|
if ($channel >= 165 && $channel <= 177) return 171;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 160) {
|
||||||
|
// map channel to 160 MHz center frequency
|
||||||
|
if ($channel >= 36 && $channel <= 64) return 50;
|
||||||
|
if ($channel >= 100 && $channel <= 128) return 114;
|
||||||
|
// channels 149-177 don't support 160 MHz in most regions
|
||||||
|
if ($channel >= 149 && $channel <= 177) return 163;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6 GHz band (UNII-5 through UNII-8)
|
||||||
|
if ($is6GHz && !$is24GHz) {
|
||||||
|
// 6 GHz uses different channel numbering: 1, 5, 9, 13, ... (every 4)
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// center is at the midpoint between two 20 MHz channels
|
||||||
|
return $channel + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 80) {
|
||||||
|
// calculate 80 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 16) * 16 + 1;
|
||||||
|
return $blockStart + 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 160) {
|
||||||
|
// calculate 160 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 32) * 32 + 1;
|
||||||
|
return $blockStart + 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 320) {
|
||||||
|
// calculate 320 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 64) * 64 + 1;
|
||||||
|
return $blockStart + 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback: return primary channel
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates HT40 direction (+ or -) based on channel and bandwidth
|
||||||
|
*
|
||||||
|
* @param int $channel primary channel number
|
||||||
|
* @param int $chwidthMHz channel width in MHz
|
||||||
|
* @return string HT40 direction: "HT40+" or "HT40-" or "" for 20MHz
|
||||||
|
*/
|
||||||
|
private function calculateHT40Direction(int $channel, int $chwidthMHz): string
|
||||||
|
{
|
||||||
|
// only applicable for 40 MHz and wider on 5 GHz
|
||||||
|
if ($chwidthMHz < 40) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$is24GHz = ($channel >= 1 && $channel <= 14);
|
||||||
|
$is5GHz = ($channel >= 36 && $channel <= 177);
|
||||||
|
|
||||||
|
// 2.4 GHz band
|
||||||
|
if ($is24GHz) {
|
||||||
|
// channels 1-7 use HT40+, channels 8-13 use HT40-
|
||||||
|
return ($channel <= 7) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 GHz band
|
||||||
|
if ($is5GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// for pure 40 MHz mode
|
||||||
|
if (in_array($channel, [36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173])) {
|
||||||
|
return 'HT40+';
|
||||||
|
} else {
|
||||||
|
return 'HT40-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz >= 80) {
|
||||||
|
// for 80 MHz and wider, determine based on position within the 80 MHz block
|
||||||
|
// lower half of 80 MHz block uses HT40+, upper half uses HT40-
|
||||||
|
|
||||||
|
// determine which 80 MHz block this channel belongs to
|
||||||
|
if ($channel >= 36 && $channel <= 48) {
|
||||||
|
// block: 36, 40, 44, 48 (center 42)
|
||||||
|
return ($channel <= 40) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 52 && $channel <= 64) {
|
||||||
|
// block: 52, 56, 60, 64 (center 58)
|
||||||
|
return ($channel <= 56) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 100 && $channel <= 112) {
|
||||||
|
// block: 100, 104, 108, 112 (center 106)
|
||||||
|
return ($channel <= 104) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 116 && $channel <= 128) {
|
||||||
|
// block: 116, 120, 124, 128 (center 122)
|
||||||
|
return ($channel <= 120) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 132 && $channel <= 144) {
|
||||||
|
// block: 132, 136, 140, 144 (center 138)
|
||||||
|
return ($channel <= 136) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 149 && $channel <= 161) {
|
||||||
|
// block: 149, 153, 157, 161 (center 155)
|
||||||
|
return ($channel <= 153) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 165 && $channel <= 177) {
|
||||||
|
// block: 165, 169, 173, 177 (center 171)
|
||||||
|
return ($channel <= 169) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default fallback
|
||||||
|
return 'HT40+';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user