Gather statistics about RF links (#684)

This commit is contained in:
Tim Wilkinson 2023-01-29 19:21:58 -08:00 committed by GitHub
parent fdb9270617
commit 33684d22d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 39 additions and 5 deletions

View File

@ -34,6 +34,7 @@
local ip = require("luci.ip") local ip = require("luci.ip")
local info = require("aredn.info") local info = require("aredn.info")
local socket = require("socket")
local refresh_timeout = 15 * 60 -- refresh high cost data every 15 minutes local refresh_timeout = 15 * 60 -- refresh high cost data every 15 minutes
local pending_timeout = 5 * 60 -- pending node wait 5 minutes before they are included local pending_timeout = 5 * 60 -- pending node wait 5 minutes before they are included
@ -43,6 +44,8 @@ local quality_min_packets = 100 -- minimum number of tx packets before we can sa
local quality_injection_max = 10 -- number of packets to inject into poor links to update quality local quality_injection_max = 10 -- number of packets to inject into poor links to update quality
local tx_quality_run_avg = 0.8 -- tx quality running average local tx_quality_run_avg = 0.8 -- tx quality running average
local ping_timeout = 1.0 -- timeout before ping gives a qualtiy penalty local ping_timeout = 1.0 -- timeout before ping gives a qualtiy penalty
local ping_time_run_avg = 0.8 -- ping time runnng average
local bitrate_run_avg = 0.8 -- rx/tx running average
local dtd_distance = 50 -- distance (meters) after which nodes connected with DtD links are considered different sites local dtd_distance = 50 -- distance (meters) after which nodes connected with DtD links are considered different sites
local connect_timeout = 5 -- timeout (seconds) when fetching information from other nodes local connect_timeout = 5 -- timeout (seconds) when fetching information from other nodes
local speed_time = 10 -- local speed_time = 10 --
@ -280,7 +283,9 @@ function lqm()
["signal avg:"] = "signal", ["signal avg:"] = "signal",
["tx packets:"] = "tx_packets", ["tx packets:"] = "tx_packets",
["tx retries:"] = "tx_retries", ["tx retries:"] = "tx_retries",
["tx failed:"] = "tx_fail" ["tx failed:"] = "tx_fail",
["tx bitrate:"] = "tx_bitrate",
["rx bitrate:"] = "rx_bitrate"
} }
local station = {} local station = {}
local cnoise = iwinfo.nl80211.noise(wlan) local cnoise = iwinfo.nl80211.noise(wlan)
@ -297,7 +302,9 @@ function lqm()
mac = mac:upper(), mac = mac:upper(),
signal = 0, signal = 0,
noise = noise, noise = noise,
ip = nil ip = nil,
tx_bitrate = 0,
rx_bitrate = 0
} }
local entry = arps[station.mac] local entry = arps[station.mac]
if entry then if entry then
@ -330,7 +337,9 @@ function lqm()
mac = nil, mac = nil,
tx_packets = 0, tx_packets = 0,
tx_fail = 0, tx_fail = 0,
tx_retries = 0 tx_retries = 0,
tx_bitrate = 0,
rx_bitrate = 0
} }
stations[#stations + 1] = tunnel stations[#stations + 1] = tunnel
else else
@ -361,7 +370,9 @@ function lqm()
mac = mac:upper(), mac = mac:upper(),
tx_packets = 0, tx_packets = 0,
tx_fail = 0, tx_fail = 0,
tx_retries = 0 tx_retries = 0,
tx_bitrate = 0,
rx_bitrate = 0
} }
end end
end end
@ -388,7 +399,9 @@ function lqm()
mac = foundmac, mac = foundmac,
tx_packets = 0, tx_packets = 0,
tx_fail = 0, tx_fail = 0,
tx_retries = 0 tx_retries = 0,
tx_bitrate = 0,
rx_bitrate = 0
} }
end end
end end
@ -429,6 +442,9 @@ function lqm()
last_tx_total = nil, last_tx_total = nil,
tx_quality = 100, tx_quality = 100,
ping_quality = 100, ping_quality = 100,
ping_success_time = 0,
tx_bitrate = nil,
rx_bitrate = nil,
quality = 100, quality = 100,
exposed = false exposed = false
} }
@ -471,6 +487,16 @@ function lqm()
track.last_quality = tx_quality track.last_quality = tx_quality
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))) 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 end
if not track.tx_bitrate then
track.tx_bitrate = station.tx_bitrate
else
track.tx_bitrate = track.tx_bitrate * bitrate_run_avg + station.tx_bitrate * (1 - bitrate_run_avg)
end
if not track.rx_bitrate then
track.rx_bitrate = station.rx_bitrate
else
track.rx_bitrate = track.rx_bitrate * bitrate_run_avg + station.rx_bitrate * (1 - bitrate_run_avg)
end
track.lastseen = now track.lastseen = now
end end
@ -609,29 +635,37 @@ function lqm()
track.ping_quality = 100 track.ping_quality = 100
elseif should_ping(track) then elseif should_ping(track) then
local success = 100 local success = 100
local ptime = ping_timeout
if track.type == "Tunnel" then if track.type == "Tunnel" then
-- Tunnels have no MAC, so we can only use IP level pings. -- Tunnels have no MAC, so we can only use IP level pings.
local sigsock = nixio.socket("inet", "dgram") local sigsock = nixio.socket("inet", "dgram")
sigsock:setopt("socket", "rcvtimeo", ping_timeout) sigsock:setopt("socket", "rcvtimeo", ping_timeout)
-- Must connect or we wont see the error -- Must connect or we wont see the error
sigsock:connect(track.ip, 8080) sigsock:connect(track.ip, 8080)
local pstart = socket.gettime(0)
sigsock:send("") 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, -- 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) -- 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 if sigsock:recv(0) == false then
success = 0 success = 0
end end
ptime = socket.gettime(0) - pstart
sigsock:close() sigsock:close()
else else
-- Make an arp request to the target ip to see if we get a timely reply. By using ARP we avoid any -- 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. -- 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 -- 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. -- packet to be acked. The reply will be unicast to us, and our ack to that is unimportant to the latency test.
local pstart = socket.gettime(0)
if os.execute(ARPING .. " -f -w " .. ping_timeout .. " -I " .. track.device .. " " .. track.ip .. " >/dev/null") ~= 0 then if os.execute(ARPING .. " -f -w " .. ping_timeout .. " -I " .. track.device .. " " .. track.ip .. " >/dev/null") ~= 0 then
success = 0 success = 0
end end
ptime = socket.gettime(0) - pstart
end end
local ping_loss_run_avg = 1 - config.ping_penalty / 100 local ping_loss_run_avg = 1 - config.ping_penalty / 100
if success > 0 then
track.ping_success_time = track.ping_success_time * ping_time_run_avg + ptime * (1 - ping_time_run_avg)
end
track.ping_quality = math.ceil(ping_loss_run_avg * track.ping_quality + (1 - ping_loss_run_avg) * success) track.ping_quality = math.ceil(ping_loss_run_avg * track.ping_quality + (1 - ping_loss_run_avg) * success)
end end