Add dynamic center frequency + HT40 direction for 802.11ax/be

This commit is contained in:
billz
2025-12-07 15:17:07 +01:00
parent de9f7548d5
commit c29f45867c

View File

@@ -191,8 +191,6 @@ class HostapdManager
$config[] = 'ssid=' . $params['ssid'];
$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'] : '';
// validate channel width for 802.11ax/be
@@ -211,14 +209,24 @@ class HostapdManager
$modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode);
$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)) {
foreach ($settings as $line) {
if (!is_string($line)) {
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;
}
}
@@ -498,5 +506,241 @@ class HostapdManager
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+';
}
}