Wi-Fi automatic channel selection with iwchan

  • 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.
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
# 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	
URL="https://openwrt.org/_export/code/docs/guide-user/network/wifi/iwchan"
uclient-fetch -O /root/iwchan.awk "${URL}?codeblock=0"
uclient-fetch -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
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2023/01/16 23:24
  • by vgaetera