mirror of https://github.com/aredn/aredn.git
LQM2
This commit is contained in:
parent
79318f0b40
commit
fb2ec36bb6
|
@ -47,6 +47,10 @@ local tx_quality_run_avg = 0.8 -- tx quality running average
|
|||
local ping_timeout = 1.0 -- timeout before ping gives a qualtiy penalty
|
||||
local dtd_distance = 50 -- distance (meters) after which nodes connected with DtD links are considered different sites
|
||||
|
||||
local IPTABLES = "/usr/sbin/iptables"
|
||||
local IW = "/usr/sbin/iw"
|
||||
local ARPING = "/usr/sbin/arping"
|
||||
|
||||
local myhostname = (info.get_nvram("node") or "localnode"):lower()
|
||||
local now = 0
|
||||
|
||||
|
@ -91,11 +95,11 @@ function should_block(track)
|
|||
end
|
||||
|
||||
function should_nonpair_block(track)
|
||||
return track.blocks.dtd or track.blocks.signal or track.blocks.distance or track.blocks.user or track.blocks.quality
|
||||
return track.blocks.dtd or track.blocks.signal or track.blocks.distance or track.blocks.user or track.blocks.quality or track.type ~= "RF"
|
||||
end
|
||||
|
||||
function only_quality_block(track)
|
||||
return track.blocked and track.blocks.quality and not (
|
||||
function inject_quality_traffic(track)
|
||||
return track.ip and track.type ~= "DtD" and track.blocked and track.blocks.quality and not (
|
||||
track.blocks.dtd or track.blocks.signal or track.blocks.distance or track.blocks.user or track.blocks.dup
|
||||
)
|
||||
end
|
||||
|
@ -111,15 +115,29 @@ end
|
|||
function update_block(track)
|
||||
if should_block(track) then
|
||||
track.blocked = true
|
||||
if os.execute("/usr/sbin/iptables -C input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null") ~= 0 then
|
||||
os.execute("/usr/sbin/iptables -I input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
return "blocked"
|
||||
if track.type == "Tunnel" then
|
||||
if os.execute(IPTABLES .. " -C input_lqm -p udp --destination-port 698 --in-interface " .. track.device .. " -j DROP 2> /dev/null") ~= 0 then
|
||||
os.execute(IPTABLES .. " -I input_lqm -p udp --destination-port 698 --in-interface " .. track.device .. " -j DROP 2> /dev/null")
|
||||
return "blocked"
|
||||
end
|
||||
else
|
||||
if os.execute(IPTABLES .. " -C input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null") ~= 0 then
|
||||
os.execute(IPTABLES .. " -I input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
return "blocked"
|
||||
end
|
||||
end
|
||||
else
|
||||
track.blocked = false
|
||||
if os.execute("/usr/sbin/iptables -C input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null") == 0 then
|
||||
os.execute("/usr/sbin/iptables -D input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
return "unblocked"
|
||||
if track.type == "Tunnel" then
|
||||
if os.execute(IPTABLES .. " -C input_lqm -p udp --destination-port 698 --in-interface " .. track.device .. " -j DROP 2> /dev/null") == 0 then
|
||||
os.execute(IPTABLES .. " -D input_lqm -p udp --destination-port 698 --in-interface " .. track.device .. " -j DROP 2> /dev/null")
|
||||
return "blocked"
|
||||
end
|
||||
else
|
||||
if os.execute(IPTABLES .. " -C input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null") == 0 then
|
||||
os.execute(IPTABLES .. " -D input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
return "unblocked"
|
||||
end
|
||||
end
|
||||
end
|
||||
return "unchanged"
|
||||
|
@ -127,7 +145,8 @@ end
|
|||
|
||||
function force_remove_block(track)
|
||||
track.blocked = false
|
||||
os.execute("/usr/sbin/iptables -D input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -D input_lqm -p udp --destination-port 698 -m mac --mac-source " .. track.mac .. " -j DROP 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -D input_lqm -p udp --destination-port 698 --in-interface " .. track.device .. " -j DROP 2> /dev/null")
|
||||
end
|
||||
|
||||
-- Distance in meters between two points
|
||||
|
@ -147,15 +166,17 @@ local cursor = uci.cursor()
|
|||
|
||||
-- Get radio
|
||||
local radioname = "radio0"
|
||||
local radiomode = "none"
|
||||
for i = 0,2
|
||||
do
|
||||
if cursor:get("wireless","@wifi-iface[" .. i .. "]", "network") == "wifi" then
|
||||
radioname = cursor:get("wireless","@wifi-iface[" .. i .. "]", "device")
|
||||
if cursor:get("wireless", "@wifi-iface[" .. i .. "]", "network") == "wifi" then
|
||||
radioname = cursor:get("wireless", "@wifi-iface[" .. i .. "]", "device")
|
||||
radiomode = cursor:get("wireless", "@wifi-iface[" .. i .. "]", "mode")
|
||||
break
|
||||
end
|
||||
end
|
||||
local phy = "phy" .. radioname:match("radio(%d+)")
|
||||
local wlan = get_ifname("wifi")
|
||||
local wlan = cursor:get("network", "wifi", "ifname")
|
||||
|
||||
function lqm()
|
||||
|
||||
|
@ -168,14 +189,14 @@ function lqm()
|
|||
wait_for_ticks(math.max(1, 30 - nixio.sysinfo().uptime))
|
||||
|
||||
-- Create filters (cannot create during install as they disappear on reboot)
|
||||
os.execute("/usr/sbin/iptables -F input_lqm 2> /dev/null")
|
||||
os.execute("/usr/sbin/iptables -X input_lqm 2> /dev/null")
|
||||
os.execute("/usr/sbin/iptables -N input_lqm 2> /dev/null")
|
||||
os.execute("/usr/sbin/iptables -D INPUT -j input_lqm -m comment --comment 'block low quality links' 2> /dev/null")
|
||||
os.execute("/usr/sbin/iptables -I INPUT -j input_lqm -m comment --comment 'block low quality links' 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -F input_lqm 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -X input_lqm 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -N input_lqm 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -D INPUT -j input_lqm -m comment --comment 'block low quality links' 2> /dev/null")
|
||||
os.execute(IPTABLES .. " -I INPUT -j input_lqm -m comment --comment 'block low quality links' 2> /dev/null")
|
||||
|
||||
-- We dont know any distances yet
|
||||
os.execute("iw " .. phy .. " set distance auto")
|
||||
os.execute(IW .. " " .. phy .. " set distance auto")
|
||||
|
||||
-- Setup a first_run timeout if this is our first every run
|
||||
if cursor:get("aredn", "@lqm[0]", "first_run") == "0" then
|
||||
|
@ -185,6 +206,7 @@ function lqm()
|
|||
end
|
||||
|
||||
local tracker = {}
|
||||
local dtdlinks = {}
|
||||
while true
|
||||
do
|
||||
now = nixio.sysinfo().uptime
|
||||
|
@ -201,47 +223,109 @@ function lqm()
|
|||
end
|
||||
)
|
||||
|
||||
local kv = {
|
||||
["signal avg:"] = "signal",
|
||||
["tx packets:"] = "tx_packets",
|
||||
["tx retries:"] = "tx_retries",
|
||||
["tx failed:"] = "tx_fail",
|
||||
["tx bitrate:"] = "tx_rate"
|
||||
}
|
||||
local stations = {}
|
||||
local station = {}
|
||||
local noise = iwinfo.nl80211.noise(wlan) or -95
|
||||
for line in io.popen("iw " .. wlan .. " station dump"):lines()
|
||||
do
|
||||
local mac = line:match("^Station ([0-9a-f:]+) ")
|
||||
if mac then
|
||||
station = {
|
||||
signal = 0,
|
||||
noise = noise,
|
||||
}
|
||||
stations[mac:upper()] = station
|
||||
else
|
||||
for k, v in pairs(kv)
|
||||
do
|
||||
local val = line:match(k .. "%s*([%d%-]+)")
|
||||
if val then
|
||||
station[v] = tonumber(val)
|
||||
|
||||
-- RF
|
||||
if radiomode == "adhoc" then
|
||||
local kv = {
|
||||
["signal avg:"] = "signal",
|
||||
["tx packets:"] = "tx_packets",
|
||||
["tx retries:"] = "tx_retries",
|
||||
["tx failed:"] = "tx_fail"
|
||||
}
|
||||
local station = {}
|
||||
local noise = iwinfo.nl80211.noise(wlan) or -95
|
||||
for line in io.popen(IW .. " " .. wlan .. " station dump"):lines()
|
||||
do
|
||||
local mac = line:match("^Station ([0-9a-f:]+) ")
|
||||
if mac then
|
||||
station = {
|
||||
type = "RF",
|
||||
device = wlan,
|
||||
mac = mac:upper(),
|
||||
signal = 0,
|
||||
noise = noise,
|
||||
ip = nil
|
||||
}
|
||||
local entry = arps[station.mac]
|
||||
if entry then
|
||||
station.ip = entry["IP address"]
|
||||
end
|
||||
stations[#stations + 1] = station
|
||||
else
|
||||
for k, v in pairs(kv)
|
||||
do
|
||||
local val = line:match(k .. "%s*([%d%-]+)")
|
||||
if val then
|
||||
station[v] = tonumber(val)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for mac, station in pairs(stations)
|
||||
-- Tunnels
|
||||
local tunnel = {}
|
||||
for line in io.popen("ifconfig"):lines()
|
||||
do
|
||||
local tun = line:match("^(tun%d+)%s")
|
||||
if tun then
|
||||
tunnel = {
|
||||
type = "Tunnel",
|
||||
device = tun,
|
||||
signal = nil,
|
||||
ip = nil,
|
||||
mac = nil,
|
||||
tx_packets = 0,
|
||||
tx_fail = 0,
|
||||
tx_retries = 0
|
||||
}
|
||||
stations[#stations + 1] = tunnel
|
||||
else
|
||||
local ip = line:match("P-t-P:(%d+%.%d+%.%d+%.%d+)")
|
||||
if ip then
|
||||
tunnel.ip = ip
|
||||
-- Fake a mac from the ip
|
||||
local a, b, c, d = ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
tunnel.mac = string.format("00:00:%02X:%02X:%02X:%02X", a, b, c, d)
|
||||
end
|
||||
local txp, txf = line:match("TX packets:(%d+)%s+errors:(%d+)")
|
||||
if txp and txf then
|
||||
tunnel.tx_packets = txp
|
||||
tunnel.tx_fail = txf
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- DtD
|
||||
for mac, entry in pairs(arps)
|
||||
do
|
||||
if entry.Device:match("%.2$") and entry["Flags"] ~= "0x0" then
|
||||
stations[#stations + 1] = {
|
||||
type = "DtD",
|
||||
device = entry.Device,
|
||||
signal = nil,
|
||||
ip = entry["IP address"],
|
||||
mac = mac:upper(),
|
||||
tx_packets = 0,
|
||||
tx_fail = 0,
|
||||
tx_retries = 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
for _, station in ipairs(stations)
|
||||
do
|
||||
if station.signal ~= 0 then
|
||||
local snr = station.signal - station.noise
|
||||
if not tracker[mac] then
|
||||
tracker[mac] = {
|
||||
if not tracker[station.mac] then
|
||||
tracker[station.mac] = {
|
||||
type = station.type,
|
||||
device = station.device,
|
||||
firstseen = now,
|
||||
lastseen = now,
|
||||
pending = now + pending_timeout,
|
||||
refresh = 0,
|
||||
mac = mac,
|
||||
mac = station.mac,
|
||||
station = nil,
|
||||
ip = nil,
|
||||
hostname = nil,
|
||||
|
@ -259,8 +343,6 @@ function lqm()
|
|||
snr = 0,
|
||||
rev_snr = nil,
|
||||
avg_snr = 0,
|
||||
links = {},
|
||||
tx_rate = 0,
|
||||
last_tx = nil,
|
||||
last_tx_total = nil,
|
||||
tx_quality = 100,
|
||||
|
@ -268,28 +350,28 @@ function lqm()
|
|||
quality = 100
|
||||
}
|
||||
end
|
||||
local track = tracker[mac]
|
||||
local track = tracker[station.mac]
|
||||
|
||||
-- If we have a direct dtd connection to this device, make sure we use that
|
||||
local entry = arps[mac]
|
||||
if entry then
|
||||
track.ip = entry["IP address"]
|
||||
local a, b, c = mac:match("^(..:..:..:)(..)(:..:..)$")
|
||||
local dtd = arps[string.format("%s%02x%s", a, tonumber(b, 16) + 1, c):upper()]
|
||||
if dtd and dtd.Device:match("%.2$") then
|
||||
track.blocks.dtd = true
|
||||
end
|
||||
-- IP and Hostname
|
||||
if station.ip and station.ip ~= track.ip then
|
||||
track.ip = station.ip
|
||||
track.hostname = nil
|
||||
end
|
||||
if not track.hostname and track.ip then
|
||||
local hostname = nixio.getnameinfo(track.ip)
|
||||
if hostname then
|
||||
track.hostname = hostname:lower():match("^(.*)%.local%.mesh$")
|
||||
track.hostname = hostname:lower():gsub("^dtdlink%.",""):gsub("^mid%d+%.",""):gsub("%.local%.mesh$", "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Running average SNR
|
||||
if track.snr == 0 then
|
||||
track.snr = snr
|
||||
else
|
||||
track.snr = math.ceil(snr_run_avg * track.snr + (1 - snr_run_avg) * snr)
|
||||
if station.type == "RF" then
|
||||
local snr = station.signal - station.noise
|
||||
if track.snr == 0 then
|
||||
track.snr = snr
|
||||
else
|
||||
track.snr = math.ceil(snr_run_avg * track.snr + (1 - snr_run_avg) * snr)
|
||||
end
|
||||
end
|
||||
|
||||
-- Running average estimate of link quality
|
||||
|
@ -307,8 +389,6 @@ function lqm()
|
|||
track.tx_quality = math.min(100, math.max(0, math.ceil(tx_quality_run_avg * track.tx_quality + (1 - tx_quality_run_avg) * tx_quality)))
|
||||
end
|
||||
|
||||
track.tx_rate = station.tx_rate
|
||||
|
||||
track.lastseen = now
|
||||
end
|
||||
end
|
||||
|
@ -323,6 +403,11 @@ function lqm()
|
|||
-- Only refresh remote attributes periodically
|
||||
if track.ip and (now > track.refresh or is_pending(track)) then
|
||||
track.refresh = now + refresh_timeout
|
||||
|
||||
local old_rev_snr = track.rev_snr
|
||||
track.rev_snr = null
|
||||
dtdlinks[track.mac] = {}
|
||||
|
||||
local info = json.parse(luci.sys.httpget("http://" .. track.ip .. ":8080/cgi-bin/sysinfo.json?link_info=1&lqm=1"))
|
||||
if info then
|
||||
if tonumber(info.lat) and tonumber(info.lon) then
|
||||
|
@ -332,20 +417,11 @@ function lqm()
|
|||
track.distance = calc_distance(lat, lon, track.lat, track.lon)
|
||||
end
|
||||
end
|
||||
local old_rev_snr = track.rev_snr
|
||||
track.links = {}
|
||||
-- Note: We cannot assume a missing link means no wifi connection
|
||||
track.rev_snr = null
|
||||
if info.lqm and info.lqm.enabled then
|
||||
for _, rtrack in pairs(info.lqm.info.trackers)
|
||||
do
|
||||
if rtrack.hostname then
|
||||
local hostname = rtrack.hostname:lower():gsub("^dtdlink%.",""):gsub("%.local%.mesh$", "")
|
||||
track.links[hostname] = {
|
||||
type = "RF",
|
||||
snr = rtrack.snr
|
||||
}
|
||||
if myhostname == hostname then
|
||||
if track.type == "RF" then
|
||||
if info.lqm and info.lqm.enabled then
|
||||
for _, rtrack in pairs(info.lqm.info.trackers)
|
||||
do
|
||||
if myhostname == rtrack.hostname and (not rtrack.type or rtrack.type == "RF") then
|
||||
if not old_rev_snr or not rtrack.snr then
|
||||
track.rev_snr = rtrack.snr
|
||||
else
|
||||
|
@ -353,30 +429,22 @@ function lqm()
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for ip, link in pairs(info.link_info)
|
||||
do
|
||||
if link.hostname and link.linkType == "DTD" then
|
||||
track.links[link.hostname:lower()] = { type = "DTD" }
|
||||
for ip, link in pairs(info.link_info)
|
||||
do
|
||||
if link.hostname and link.linkType == "DTD" then
|
||||
dtdlinks[track.mac][link.hostname:lower():gsub("^dtdlink%.",""):gsub("%.local%.mesh$", "")] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif info.link_info then
|
||||
-- If there's no LQM information we fallback on using link information.
|
||||
for ip, link in pairs(info.link_info)
|
||||
do
|
||||
if link.hostname then
|
||||
local hostname = link.hostname:lower():gsub("^dtdlink%.",""):gsub("%.local%.mesh$", "")
|
||||
if link.linkType == "DTD" then
|
||||
track.links[hostname] = { type = link.linkType }
|
||||
elseif link.linkType == "RF" and link.signal and link.noise then
|
||||
local snr = link.signal - link.noise
|
||||
if not track.links[hostname] then
|
||||
track.links[hostname] = {
|
||||
type = link.linkType,
|
||||
snr = snr
|
||||
}
|
||||
end
|
||||
if myhostname == hostname then
|
||||
elseif info.link_info then
|
||||
-- If there's no LQM information we fallback on using link information.
|
||||
for ip, link in pairs(info.link_info)
|
||||
do
|
||||
if link.hostname then
|
||||
local hostname = link.hostname:lower():gsub("^dtdlink%.",""):gsub("%.local%.mesh$", "")
|
||||
if link.linkType == "DTD" then
|
||||
dtdlinks[track.mac][hostname] = true
|
||||
elseif link.linkType == "RF" and link.signal and link.noise and myhostname == hostname then
|
||||
local snr = link.signal - link.noise
|
||||
if not old_rev_snr then
|
||||
track.rev_snr = snr
|
||||
else
|
||||
|
@ -387,10 +455,6 @@ function lqm()
|
|||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Clear these if we cannot talk to the other end, so we dont use stale values
|
||||
track.links = {}
|
||||
track.rev_snr = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -415,13 +479,30 @@ function lqm()
|
|||
if config.ping_penalty <= 0 then
|
||||
track.ping_quality = 100
|
||||
elseif should_ping(track) then
|
||||
-- Make an arp request to the target ip to see if we get a timely reply. By using ARP we avoid any
|
||||
-- potential routing issues and avoid any firewall blocks on the other end.
|
||||
-- As the request is broadcast, we avoid any potential distance/scope timing issues as we dont wait for the
|
||||
-- packet to be acked. The reply will be unicast to us, and our ack to that is unimportant to the latency test.
|
||||
local success = 100
|
||||
if os.execute("/usr/sbin/arping -f -w " .. ping_timeout .. " -I " .. wlan .. " " .. track.ip .. " >/dev/null") ~= 0 then
|
||||
success = 0
|
||||
if track.type == "Tunnel" then
|
||||
-- Tunnels have no MAC, so we can only use IP level pings.
|
||||
local sigsock = nixio.socket("inet", "dgram")
|
||||
sigsock:setopt("socket", "bindtodevice", track.device)
|
||||
sigsock:setopt("socket", "dontroute", 1)
|
||||
sigsock:setopt("socket", "rcvtimeo", ping_timeout)
|
||||
-- Must connect or we wont see the error
|
||||
sigsock:connect(track.ip, 8080)
|
||||
sigsock:send("")
|
||||
-- There's no actual UDP server at the other end so recv will either timeout and return 'false' if the link is slow,
|
||||
-- or will error and return 'nil' if there is a node and it send back an ICMP error quickly (which for our purposes is a positive)
|
||||
if sigsock:recv(0) == false then
|
||||
success = 0
|
||||
end
|
||||
sigsock:close()
|
||||
else
|
||||
-- Make an arp request to the target ip to see if we get a timely reply. By using ARP we avoid any
|
||||
-- potential routing issues and avoid any firewall blocks on the other end.
|
||||
-- As the request is broadcast, we avoid any potential distance/scope timing issues as we dont wait for the
|
||||
-- packet to be acked. The reply will be unicast to us, and our ack to that is unimportant to the latency test.
|
||||
if os.execute(ARPING .. " -f -w " .. ping_timeout .. " -I " .. track.device .. " " .. track.ip .. " >/dev/null") ~= 0 then
|
||||
success = 0
|
||||
end
|
||||
end
|
||||
local ping_loss_run_avg = 1 - config.ping_penalty / 100
|
||||
track.ping_quality = math.ceil(ping_loss_run_avg * track.ping_quality + (1 - ping_loss_run_avg) * success)
|
||||
|
@ -435,11 +516,11 @@ function lqm()
|
|||
-- bad, it wont be used and we can never tell if it becomes good again. Beware injecting too
|
||||
-- much traffic because, on very poor links, this can generate multiple retries per packet, flooding
|
||||
-- the wifi channel
|
||||
if track.ip and only_quality_block(track) then
|
||||
if inject_quality_traffic(track) then
|
||||
-- Create socket we use to inject traffic into degraded links
|
||||
-- This is setup so it ignores routing and will always send to the correct wifi station
|
||||
local sigsock = nixio.socket("inet", "dgram")
|
||||
sigsock:setopt("socket", "bindtodevice", wlan)
|
||||
sigsock:setopt("socket", "bindtodevice", track.device)
|
||||
sigsock:setopt("socket", "dontroute", 1)
|
||||
for _ = 1,quality_injection_max
|
||||
do
|
||||
|
@ -498,28 +579,42 @@ function lqm()
|
|||
-- Work out what to block, unblock and limit
|
||||
for _, track in pairs(tracker)
|
||||
do
|
||||
-- When unblocked link signal becomes too low, block
|
||||
if not track.blocks.signal then
|
||||
if track.snr < config.low or (track.rev_snr and track.rev_snr < config.low) then
|
||||
track.blocks.signal = true
|
||||
end
|
||||
-- when blocked link becomes (low+margin) again, unblock
|
||||
else
|
||||
if track.snr >= config.low + config.margin and (not track.rev_snr or track.rev_snr >= config.low + config.margin) then
|
||||
track.blocks.signal = false
|
||||
-- When signal is good enough to unblock a link but the quality is low, artificially bump
|
||||
-- it up to give the link chance to recover
|
||||
if track.blocks.quality then
|
||||
track.quality = config.min_quality + config.margin_quality
|
||||
end
|
||||
end
|
||||
end
|
||||
-- SNR and distance blocks only related to RF links
|
||||
if track.type == "RF" then
|
||||
|
||||
-- If we have a direct dtd connection to this device, make sure we use that
|
||||
local a, b, c = track.mac:match("^(..:..:..:)(..)(:..:..)$")
|
||||
local dtd = tracker[string.format("%s%02X%s", a, tonumber(b, 16) + 1, c)]
|
||||
if dtd and dtd.type == "DtD" then
|
||||
track.blocks.dtd = true
|
||||
else
|
||||
track.blocks.dtd = false
|
||||
end
|
||||
|
||||
-- When unblocked link signal becomes too low, block
|
||||
if not track.blocks.signal then
|
||||
if track.snr < config.low or (track.rev_snr and track.rev_snr < config.low) then
|
||||
track.blocks.signal = true
|
||||
end
|
||||
-- when blocked link becomes (low+margin) again, unblock
|
||||
else
|
||||
if track.snr >= config.low + config.margin and (not track.rev_snr or track.rev_snr >= config.low + config.margin) then
|
||||
track.blocks.signal = false
|
||||
-- When signal is good enough to unblock a link but the quality is low, artificially bump
|
||||
-- it up to give the link chance to recover
|
||||
if track.blocks.quality then
|
||||
track.quality = config.min_quality + config.margin_quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Block any nodes which are too distant
|
||||
if not track.distance or (track.distance >= config.min_distance and track.distance <= config.max_distance) then
|
||||
track.blocks.distance = false
|
||||
else
|
||||
track.blocks.distance = true
|
||||
end
|
||||
|
||||
-- Block any nodes which are too distant
|
||||
if not track.distance or (track.distance >= config.min_distance and track.distance <= config.max_distance) then
|
||||
track.blocks.distance = false
|
||||
else
|
||||
track.blocks.distance = true
|
||||
end
|
||||
|
||||
-- Block if user requested it
|
||||
|
@ -552,8 +647,7 @@ function lqm()
|
|||
for _, track2 in pairs(tracker)
|
||||
do
|
||||
if track ~= track2 and track2.hostname and not should_nonpair_block(track2) then
|
||||
local connection = track.links[track2.hostname]
|
||||
if connection and connection.type == "DTD" then
|
||||
if dtdlinks[track.mac][track2.hostname] then
|
||||
if not (track.lat and track.lon and track2.lat and track2.lon) or calc_distance(track.lat, track.lon, track2.lat, track2.lon) < dtd_distance then
|
||||
tracklist[#tracklist + 1] = track2
|
||||
end
|
||||
|
@ -600,8 +694,8 @@ function lqm()
|
|||
track.pending = now + pending_timeout
|
||||
end
|
||||
|
||||
-- Find the most distant, unblocked, routable, node
|
||||
if not track.blocked and track.distance then
|
||||
-- Find the most distant, unblocked, routable, RF node
|
||||
if track.type == "RF" and not track.blocked and track.distance then
|
||||
if not is_pending(track) and track.routable then
|
||||
if track.distance > distance then
|
||||
distance = track.distance
|
||||
|
@ -626,13 +720,13 @@ function lqm()
|
|||
|
||||
-- Update the wifi distance
|
||||
if distance > 0 then
|
||||
coverage = math.floor((distance * 2 * 0.0033) / 3) -- airtime
|
||||
os.execute("iw " .. phy .. " set coverage " .. coverage)
|
||||
coverage = math.min(255, math.floor((distance * 2 * 0.0033) / 3)) -- airtime
|
||||
os.execute(IW .. " " .. phy .. " set coverage " .. coverage)
|
||||
elseif alt_distance > 1 then
|
||||
coverage = math.floor((alt_distance * 2 * 0.0033) / 3)
|
||||
os.execute("iw " .. phy .. " set coverage " .. coverage)
|
||||
coverage = math.min(255, math.floor((alt_distance * 2 * 0.0033) / 3))
|
||||
os.execute(IW .. " " .. phy .. " set coverage " .. coverage)
|
||||
else
|
||||
os.execute("iw " .. phy .. " set distance auto")
|
||||
os.execute(IW .. " " .. phy .. " set distance auto")
|
||||
end
|
||||
|
||||
-- Save this for the UI
|
||||
|
|
|
@ -76,16 +76,16 @@ html.print([[
|
|||
#links > div {
|
||||
padding: 2px 0;
|
||||
}
|
||||
.m {
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
.m {
|
||||
width: 220px;
|
||||
}
|
||||
.s {
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
}
|
||||
.p {
|
||||
display: inline-block;
|
||||
width: 110px;
|
||||
}
|
||||
</style>
|
||||
|
@ -105,7 +105,7 @@ html.print([[<hr>
|
|||
</td></tr>
|
||||
<tr><td>
|
||||
<div class="lt">
|
||||
<span class="m">RF Neighbor</span><span class="s">SNR</span><span class="p">Distance</span><span class="s">Quality</span><span class="p">Status <a href='/help.html#lqmstatus' target='_blank'><img src='/qmark.png'></a></span>
|
||||
<span class="m">Neighbor</span><span class="s">Link</span><span class="s">SNR</span><span class="p">Distance</span><span class="s">Quality</span><span class="p">Status <a href='/help.html#lqmstatus' target='_blank'><img src='/qmark.png'></a></span>
|
||||
</div>
|
||||
<div id="links"></div>
|
||||
</td></tr>
|
||||
|
@ -154,6 +154,9 @@ html.print([[<hr>
|
|||
}
|
||||
return "idle";
|
||||
}
|
||||
const get_snr = track => {
|
||||
return track.snr <= 0 ? "-" : track.snr + ("rev_snr" in track ? "/" + track.rev_snr : "");
|
||||
}
|
||||
const name = track => {
|
||||
if (track.hostname) {
|
||||
return `<a href="http://${track.hostname}.local.mesh:8080">${track.hostname}</a>`;
|
||||
|
@ -198,13 +201,13 @@ html.print([[<hr>
|
|||
}
|
||||
break;
|
||||
}
|
||||
links += `<div><span class="m">${name(track)}</span><span class="s">${track.snr}${"rev_snr" in track ? "/" + track.rev_snr : ""}</span><span class="p">${distance}</span><span class="s">${quality}</span><span class="p">${status}</span></div>`;
|
||||
links += `<div><span class="m">${name(track)}</span><span class="s">${track.type}</span><span class="s">${get_snr(track)}</span><span class="p">${distance}</span><span class="s">${quality}</span><span class="p">${status}</span></div>`;
|
||||
});
|
||||
if (links.length) {
|
||||
document.getElementById("links").innerHTML = links;
|
||||
}
|
||||
else {
|
||||
document.getElementById("links").innerHTML = `<div><span>Currently no RF neighbor data available</span></div>`
|
||||
document.getElementById("links").innerHTML = `<div><span>Currently no neighbor data available</span></div>`
|
||||
}
|
||||
}
|
||||
const fetchAndUpdate = () => {
|
||||
|
|
Loading…
Reference in New Issue