--[[ Part of AREDN® -- Used for creating Amateur Radio Emergency Data Networks Copyright (C) 2019 Darryl Quinn Copyright (C) 2021 Tim Wilkinson See Contributors file for additional contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional Terms: Additional use restrictions exist on the AREDN® trademark and logo. See AREDNLicense.txt for more info. Attributions to the AREDN® Project must be retained in the source code. If importing this code into a new or existing project attribution to the AREDN® project must be added to the source code. You must not misrepresent the origin of the material contained within. Modified versions must be modified to attribute to the original source and be marked in reasonable ways as differentiate it from the original version --]] function snrlog() while true do run_snrlog() wait_for_ticks(60) -- 1 minute end end local MAXLINES = 2880 -- 2 days worth local AGETIME = 43200 local INACTIVETIMEOUT = 10000 local tmpdir = "/tmp/snrlog" local lastdat = "/tmp/snr.dat" local defnoise = -95 local cursor = uci.cursor() -- create tmp dir if needed nixio.fs.mkdir(tmpdir) function run_snrlog() -- get system uptime local now = nixio.sysinfo().uptime -- get wifi interface name local wifiiface = get_ifname("wifi") -- if Mesh RF is turned off do nothing if wifiiface == string.match(wifiiface, 'eth.*') then return end -- get radio noise floor local nf = iwinfo.nl80211.noise(wifiiface) if not nf or nf < -110 or nf > -50 then nf = defnoise end -- get all stations local stations = iwinfo.nl80211.assoclist(wifiiface) -- load up arpcache local arpcache = {} arptable(function(a) arpcache[a["HW address"]:upper()] = a end) -- get the current bandwidth setting local radio = "radio0" cursor:foreach("wireless", "wifi-iface", function(i) if i.mode == "adhoc" then radio = i.device end end ) local bandwidth = cursor:get("wireless", radio, "chanbw") -- load the lasttime table local lasttime = {} local nulledout = {} if nixio.fs.stat(lastdat) then for line in io.lines(lastdat) do local mac, last, nulled = string.match(line, "(.*)|(.*)|(.*)") lasttime[mac] = last nulledout[mac] = nulled end end -- iterate over all the stations and log neighbors local trigger_auto_distance = false local snrdatcache = {} for mstation in pairs(stations) do local mac = mstation:upper() snrdatcache[mac] = now -- find current data file local efn = nil for fn in nixio.fs.glob(tmpdir.."/"..mac.."-*") do efn = fn break end -- improve existing filename if we can local datafile = tmpdir.."/"..mac.."-" local arp = arpcache[mac] if arp then local ip = arp["IP address"] local hostname = nixio.getnameinfo(ip) if hostname then datafile = datafile..hostname:lower():gsub("^dtdlink%.", ""):gsub("^mid%d+%.", ""):gsub("^xlink%d+%.", ""):gsub("%.local%.mesh$", "") elseif ip then datafile = datafile..ip end end -- rename if necessary if efn and efn ~= datafile then nixio.fs.rename(efn, datafile) end -- check if auto-distance reset is required (new node) -- note and run auto distancing right at the end if efn == nil or now - lasttime[mac] > 100 then trigger_auto_distance = true end local signal = stations[mac].signal or "" local update = true; if lasttime[mac] and stations[mac].inactive >= INACTIVETIMEOUT then -- beacons expired if nulledout[mac] == "true" then -- No need to double log inactive null's update = false end signal = "null" end if signal == 0 then if nulledout[mac] == nil then -- First time we have seen this show up -- but it is at 0 wont be logged but will -- end up in snrcache nulledout[mac] = "true" end update = false end -- log neighbor data to datafile if update then -- trim datafile file_trim(datafile, MAXLINES) local f, err = assert(io.open(datafile, "a"),"Cannot open file ("..datafile..") for appending!") if f then local noise = stations[mac].noise or "" local tx_mcs = stations[mac].tx_mcs or -1 local tx_rate = adjust_rate((stations[mac].tx_rate) / 1000, bandwidth) local rx_mcs = stations[mac].rx_mcs or -1 local rx_rate = adjust_rate((stations[mac].rx_rate) / 1000, bandwidth) f:write(string.format("%s,%s,%s,%s,%s,%s,%s\n", os.date("%m/%d/%Y %H:%M:%S",os.time()), signal, noise, tx_mcs, tx_rate, rx_mcs, rx_rate)) f:close() else print(err) end if signal == "null" then nulledout[mac] = "true" else nulledout[mac] = "false" end lasttime[mac] = now end end -- update snr.dat for mac, last in pairs(lasttime) do if now - last < AGETIME then -- If not a neighbor and wasn't previously nulled out, write a null if not snrdatcache[mac] and nulledout[mac] == "false" then -- find the log file name for logdatafile in nixio.fs.glob(tmpdir.."/"..mac.."*") do -- Write a null to the log file local f, err = assert(io.open(logdatafile, "a"),"Cannot open file ("..logdatafile..") for appending!") if f then f:write(string.format("%s,%s,%s,%s,%s,%s,%s\n", os.date("%m/%d/%Y %H:%M:%S", os.time()), 'null', nf, '0', '0', '0', '0')) f:close() nulledout[mac] = "true" else -- Don't log the null into SNRLog cause we were not successful -- Though the assert() above should cause this too. nulledout[mac] = "false" end break end end -- keep it snrdatcache[mac] = snrdatcache[mac] or last else -- find the file and purge it for maclist in nixio.fs.glob(tmpdir.."/"..mac.."*") do os.remove(maclist) break end end end -- re-write snr.dat file local f, err = assert(io.open(lastdat,"w+"),"Cannot overwrite "..lastdat) for mac, last in pairs(snrdatcache) do f:write(string.format("%s|%s|%s\n", mac, last, nulledout[mac])) end f:close() -- trigger auto distancing if necessary if trigger_auto_distance and cursor:get("aredn", "@lqm[0]", "enable") ~= "1" then reset_auto_distance() nixio.syslog("notice", "snrlog: reset_auto_distance, " .. now) end end return snrlog