This is an old revision of the document!
Wi-Fi automatic channel selection with iwchan
Introduction
- This instruction provides a method to automatically select a wireless channel.
- It calculates the load to frequency distribution and selects the channel with the least load.
- The threshold frequency and load parameters help to minimize excessive channel change.
- You can use this in case the ACS algorithm does not work well with your hardware/driver.
Instructions
- iwchan.awk
#!/usr/bin/awk -f # Coded by: Vladislav Grigoryev <vg[dot]aetera[at]gmail[dot]com> # License: GNU General Public License (GPL) version 3+ # Description: Select wireless channel automatically function get_iwphy() { cmd = "iw phy" while(cmd | getline) { if($0 ~ /^\s*Wiphy\s/) phy = gensub(/^\s*\w*\s(\w+)$/, "\\1", 1, $0) else if($0 ~ /^\s*Band\s/) band = gensub(/^\s*\w*\s([0-9]+):$/, "\\1", 1, $0) else if($0 ~ /^\s*\*\s*[0-9]+\s*MHz.*dBm/) { freq = gensub(/^.*\s([0-9]+)\s*MHz.*$/, "\\1", 1, $0) chan = gensub(/^.*\[([0-9]+)\].*$/, "\\1", 1, $0) iwphy[phy, freq, "band"] = band iwphy[phy, freq, "chan"] = chan } } close(cmd) } function get_iwdev() { cmd = "iw dev" while(cmd | getline) { if($0 ~ /^\s*phy\x23/) phy = gensub(/^\s*(\w+)\x23([0-9]+)$/, "\\1\\2", 1, $0) else if($0 ~ /^\s*Interface\s/) dev = gensub(/^\s*\w*\s(\w+)$/, "\\1", 1, $0) else if($0 ~ /^\s*channel\s/) { freq = gensub(/^.*\(([0-9]+)\s*MHz\).*$/, "\\1", 1, $0) iwdev[phy, dev, "freq"] = freq } } close(cmd) } function get_iwconf() { for(iwdev_subs in iwdev) { split(iwdev_subs, iwdev_sub, SUBSEP) if(iwdev_sub[3] != "freq") continue phy = iwdev_sub[1] dev = iwdev_sub[2] if(phy_conf == "") phy_conf = phy if(phy_conf != phy) continue dev_conf = dev freq_conf = iwdev[phy_conf, dev_conf, "freq"] band_conf = iwphy[phy_conf, freq_conf, "band"] } } function get_iwscan() { cmd = "iw dev "dev_conf" scan" while(cmd | getline) { if($0 ~ /^\s*BSS\s/) bssid = gensub(/^\s*\w*\s*([:0-9a-f]+).*$/, "\\1", 1, $0) else if($0 ~ /^\s*freq:/) { freq = gensub(/^\s*\w*:\s*([0-9]+).*$/, "\\1", 1, $0) iwscan[bssid, "freq"] = freq } else if($0 ~ /^\s*signal:/) { signal = gensub(/^\s*\w*:\s*([-.0-9]+).*$/, "\\1", 1, $0) + 0 iwscan[bssid, "signal"] = signal if(signal < - 100) quality = 0 else if(signal < - 50) quality = 2 * (signal + 100) else quality = 100 iwscan[bssid, "quality"] = quality } else if($0 ~ /^\s*SSID:/) { ssid = gensub(/^\s*\w*:\s*(.*)$/, "\\1", 1, $0) iwscan[bssid, "ssid"] = ssid } } close(cmd) } function get_iwload() { for(iwphy_subs in iwphy) { split(iwphy_subs, iwphy_sub, SUBSEP) if(iwphy_sub[3] != "chan") continue phy = iwphy_sub[1] freq = iwphy_sub[2] band = iwphy[phy, freq, "band"] load = iwphy[phy, freq, "load"] if(band != band_conf) continue for(iwscan_subs in iwscan) { split(iwscan_subs, iwscan_sub, SUBSEP) if(iwscan_sub[2] != "freq") continue bssid = iwscan_sub[1] freq_bssid = iwscan[bssid, "freq"] signal = iwscan[bssid, "signal"] freq_diff = freq - freq_bssid if(freq_diff < 0) freq_diff = - freq_diff if(freq_diff < 5) signal_factor = 100 else if(freq_diff < 10) signal_factor = 95 else if(freq_diff < 15) signal_factor = 85 else if(freq_diff < 20) signal_factor = 15 else if(freq_diff < 25) signal_factor = 5 else signal_factor = 0 if(signal < - 100) load += 0 else load += (signal + 100) * signal_factor iwphy[phy, freq, "load"] = load } } } function get_iwstatus() { for(iwphy_subs in iwphy) { split(iwphy_subs, iwphy_sub, SUBSEP) if(iwphy_sub[3] != "chan") continue phy = iwphy_sub[1] freq = iwphy_sub[2] band = iwphy[phy, freq, "band"] load = iwphy[phy, freq, "load"] if(band != band_conf) continue if(load_optim != "" && load_optim < load) continue freq_optim = freq load_optim = load } status_conf = "-" iwphy[phy_conf, freq_conf, "status"] = status_conf status_optim = iwphy[phy_conf, freq_optim, "status"] "+" iwphy[phy_conf, freq_optim, "status"] = status_optim } function get_iwchan() { freq_diff = freq_conf - freq_optim load_conf = iwphy[phy_conf, freq_conf, "load"] load_optim = iwphy[phy_conf, freq_optim, "load"] load_diff = load_conf - load_optim if(freq_diff < 0) freq_diff = - freq_diff if(freq_diff < freq_thr || load_diff < load_thr) return chan_optim = iwphy[phy_conf, freq_optim, "chan"] printf "%d\n", chan_optim } function print_iwinfo() { printf "Phy:\t%s\nDev:\t%s\nBand:\t%s\nFreqTh:\t%d\nLoadTh:\t%d\n", phy_conf, dev_conf, band_conf, freq_thr, load_thr } function print_iwscan() { cmd = "sort -n" printf "\nFreq\tChannel\tSignal\tQuality\tBSSID\t\t\tSSID\n" for(iwscan_subs in iwscan) { split(iwscan_subs, iwscan_sub, SUBSEP) if(iwscan_sub[2] != "ssid") continue bssid = iwscan_sub[1] freq = iwscan[bssid, "freq"] chan = iwphy[phy_conf, freq, "chan"] signal = iwscan[bssid, "signal"] quality = iwscan[bssid, "quality"] ssid = iwscan[bssid, "ssid"] printf "%d\t%d\t%d\t%d\t%s\t%s\n", freq, chan, signal, quality, bssid, ssid | cmd } close(cmd) } function print_iwlist() { cmd = "sort -n" printf "\nFreq\tChannel\tLoad\tStatus\n" for(iwphy_subs in iwphy) { split(iwphy_subs, iwphy_sub, SUBSEP) if(iwphy_sub[3] != "chan") continue phy = iwphy_sub[1] freq = iwphy_sub[2] band = iwphy[phy, freq, "band"] chan = iwphy[phy, freq, "chan"] load = iwphy[phy, freq, "load"] status = iwphy[phy, freq, "status"] if(band != band_conf) continue printf "%d\t%d\t%d\t%s\n", freq, chan, load, status | cmd } close(cmd) } BEGIN { subcmd = ARGV[1] phy_conf = ARGV[2] freq_thr = ARGV[3] load_thr = ARGV[4] if(subcmd == "") subcmd = "help" if(freq_thr == "") freq_thr = 15 if(load_thr == "") load_thr = 1000 if(subcmd == "help") printf "awk -f iwchan.awk [show|get|help] [phy] [freq_thr] [load_thr]\n" else if(subcmd == "get" || subcmd == "show") { get_iwphy() get_iwdev() get_iwconf() get_iwscan() get_iwload() get_iwstatus() if(subcmd == "get") get_iwchan() else if(subcmd == "show") { print_iwinfo() print_iwscan() print_iwlist() } } }
- iwchan.sh
# Description: OpenWrt iwchan wrapper script IWCHAN="${0%.*}.awk" PHYID="${1:-0}" CHAN="$(awk -f "${IWCHAN}" get "phy${PHYID}" 2> /dev/null)" if [ -z "${CHAN}" ]; then exit 0; fi uci set wireless."radio${PHYID}".channel="${CHAN}" wifi reload
Examples
# awk -f iwchan.awk help awk -f iwchan.awk [show|get|help] [phy] [freq_thr] [load_thr] # awk -f iwchan.awk get 5 # awk -f iwchan.awk show Phy: phy0 Dev: wlp2s0 Band: 1 FreqTh: 15 LoadTh: 1000 Freq Channel Signal Quality BSSID SSID 2412 1 -63 74 90:94:e4:ef:0c:66 vghome 2412 1 -74 52 10:fe:ed:2a:1f:cc Dead Moroz-z-z 2437 6 -72 56 c4:6e:1f:65:65:02 TP-LINK_656502 2457 10 -65 70 d8:0d:17:21:ff:a6 TP-Link_FFA6 2457 10 -83 34 c4:6e:1f:70:ed:6e TP-LINK_ED6E 2462 11 -57 86 c0:25:e9:c4:b8:68 CityLink-12 2467 12 -58 84 54:04:a6:c7:34:ac quick3 Freq Channel Load Status 2412 1 6300 - 2417 2 6125 2422 3 5775 2427 4 3325 2432 5 2975 + 2437 6 3060 2442 7 3655 2447 8 7655 2452 9 9645 2457 10 12995 2462 11 13230 2467 12 12705 2472 13 8425
Automated
URL="https://openwrt.org/_export/code/docs/guide-user/network/wifi/iwchan" wget -U "" -O /root/iwchan.awk "${URL}?codeblock=0" wget -U "" -O /root/iwchan.sh "${URL}?codeblock=1" cat << EOF >> /etc/sysupgrade.conf /root EOF cat << "EOF" >> /etc/crontabs/root 0 4 * * * . /root/iwchan.sh EOF /etc/init.d/cron restart