Files
loradar_tool/Configuration 2 (RAK831)/Config2_LoRadar_Extractor.py
2020-06-11 17:24:50 +10:00

1127 lines
40 KiB
Python

### Libraries to load
import glob, matplotlib, pprint, requests, math, time, os
import pandas as pd
import numpy as np
from collections import Counter
from itertools import repeat
import matplotlib.pyplot as plt
### Loading the data
os.chdir = ("/home/pi/lora_gateway/util_pkt_logger")
docs = glob.glob(os.chdir + "/*.csv")
if len(docs) > 1:
print("1. Reading " + str(len(docs)) + " csv files...")
all_dat = []
for doc in docs:
all_dat.append(pd.read_csv(doc, encoding = 'unicode_escape'))
every = pd.concat(all_dat)
every.reset_index(inplace=True)
every = every.dropna(subset=["UTC timestamp"])
every = every.drop("index", axis=1)
elif len(docs) == 1:
print("1. Reading one csv file...")
print(docs)
every = pd.read_csv(docs[0], encoding = 'unicode_escape')
every.reset_index(inplace=True)
every = every.dropna(subset=["UTC timestamp"])
every = every.drop("index", axis=1)
elif len(docs) == 0:
print("ERROR: No Data Files!")
exit()
### Filtering the data for ABP
print("2. Filtering the data for ABP...")
info = every.payload
info = list(map(str, info)) # Convert payload to string. This is needed as some payloads only have integers.
devs = []
fcnts = []
mhdrs = []
fctrls = []
devaddr_revl = []
fcnt_revl = []
devaddr_len = 8
for i in info:
devaddr = i[2:11].split("-")
devaddr = "".join(devaddr)
fcnt = i[13:17].split("-")
fcnt = "".join(fcnt)
fctrl = i[11:13].split("-")
fctrl = "".join(fctrl)
mhdr = i[:2].split("-")
mhdr = "".join(mhdr)
devs.append(devaddr)
fcnts.append(fcnt)
fctrls.append(fctrl)
mhdrs.append(mhdr)
# reversing the little endian form of DevAddr and Fcnt
for row in devs:
fixed = []
for j in range(int(len(row)/2)):
fixed.append(row[j*2:(j+1)*2])
fixed.reverse()
devaddr_rev = "".join(fixed)
devaddr_revl.append(devaddr_rev)
for row in fcnts:
fixed = []
for j in range(int(len(row)/2)):
fixed.append(row[j*2:(j+1)*2])
fixed.reverse()
fcnt_rev = "".join(fixed)
fcnt_revl.append(fcnt_rev)
# Remove payloads shorter than the length of DevAddr, as these will not provide sufficient information to identify ABP devices
ttn_addr = []
all_addr = []
other_addr = []
network = []
activation = []
for addr in devaddr_revl:
if len(addr) == devaddr_len:
# Network prefix assignments to the World
if addr[:2] == "26" or addr[:2] == "27":
ttn_addr.append(addr) # potential DevAddr from TTN
all_addr.append(addr)
network.append("World: The Things Network")
activation.append("ABP")
elif addr[:2] == "04" or addr[:2] == "05":
all_addr.append(addr)
network.append("World: Actility")
activation.append("ABP")
elif addr[:2] == "10" or addr[:2] == "11":
all_addr.append(addr)
network.append("World: Orbiwise")
activation.append("ABP")
elif addr[:2] == "1a" or addr[:2] == "1b":
all_addr.append(addr)
network.append("World: SK Telecom")
activation.append("ABP")
elif addr[:2] == "1c" or addr[:2] == "1d":
all_addr.append(addr)
network.append("World: SagemCom")
activation.append("ABP")
elif addr[:2] == "24" or addr[:2] == "25":
all_addr.append(addr)
network.append("World: Kerlink")
activation.append("ABP")
elif addr[:2] == "2a" or addr[:2] == "2b":
all_addr.append(addr)
network.append("World: Cisco Systems")
activation.append("ABP")
elif addr[:2] == "2e" or addr[:2] == "2f":
all_addr.append(addr)
network.append("World: MultiTech Systems")
activation.append("ABP")
elif addr[:2] == "30" or addr[:2] == "31":
all_addr.append(addr)
network.append("World: Loriot")
activation.append("ABP")
elif addr[:2] == "32" or addr[:2] == "33":
all_addr.append(addr)
network.append("World: NNNCo")
activation.append("ABP")
elif addr[:2] == "34" or addr[:2] == "35":
all_addr.append(addr)
network.append("World: Flashnet")
activation.append("ABP")
elif addr[:2] == "36" or addr[:2] == "37":
all_addr.append(addr)
network.append("World: TrackNet")
activation.append("ABP")
elif addr[:2] == "38" or addr[:2] == "39":
all_addr.append(addr)
network.append("World: Lar.Tech")
activation.append("ABP")
elif addr[:2] == "3a" or addr[:2] == "3b":
all_addr.append(addr)
network.append("World: Swiss Led")
activation.append("ABP")
elif addr[:2] == "42" or addr[:2] == "43":
all_addr.append(addr)
network.append("World: Patavina Technologies")
activation.append("ABP")
elif addr[:2] == "48" or addr[:2] == "49":
all_addr.append(addr)
network.append("World: Gimasi")
activation.append("ABP")
elif addr[:2] == "4a" or addr[:2] == "4b":
all_addr.append(addr)
network.append("World: Talkpool")
activation.append("ABP")
elif addr[:2] == "4e" or addr[:2] == "4f":
all_addr.append(addr)
network.append("World: MCF88 SRL")
activation.append("ABP")
elif addr[:2] == "52" or addr[:2] == "53":
all_addr.append(addr)
network.append("World: GIoT")
activation.append("ABP")
elif addr[:2] == "54" or addr[:2] == "55":
all_addr.append(addr)
network.append("World: M2B Communications")
activation.append("ABP")
elif addr[:2] == "5a" or addr[:2] == "5b":
all_addr.append(addr)
network.append("World: Rai Way")
activation.append("ABP")
elif addr[:2] == "5c" or addr[:2] == "5d":
all_addr.append(addr)
network.append("World: Levikom")
activation.append("ABP")
elif addr[:2] == "60" or addr[:2] == "61":
all_addr.append(addr)
network.append("World: SoftBank")
activation.append("ABP")
elif addr[:2] == "62" or addr[:2] == "63":
all_addr.append(addr)
network.append("World: Inmarsat")
activation.append("ABP")
elif addr[:4] == "e006" or addr[:4] == "e007":
all_addr.append(addr)
network.append("World: IOTCAN")
activation.append("ABP")
elif addr[:4] == "e00a" or addr[:4] == "e00b":
all_addr.append(addr)
network.append("World: IoT Network AS")
activation.append("ABP")
elif addr[:4] == "e00e" or addr[:4] == "e00f":
all_addr.append(addr)
network.append("World: EDF")
activation.append("ABP")
elif addr[:4] == "e018" or addr[:4] == "e019":
all_addr.append(addr)
network.append("World: SenSys")
activation.append("ABP")
elif addr[:4] == "e01c" or addr[:4] == "e01d":
all_addr.append(addr)
network.append("World: Spark")
activation.append("ABP")
elif addr[:4] == "e020" or addr[:4] == "e021":
all_addr.append(addr)
network.append("World: Senet")
activation.append("ABP")
elif addr[:4] == "e026" or addr[:4] == "e027":
all_addr.append(addr)
network.append("World: Actility")
activation.append("ABP")
elif addr[:4] == "e02a" or addr[:4] == "e02b":
all_addr.append(addr)
network.append("World: Kerlink")
activation.append("ABP")
elif addr[:4] == "e02c" or addr[:4] == "e02d":
all_addr.append(addr)
network.append("World: Cisco")
activation.append("ABP")
elif addr[:4] == "e02e" or addr[:4] == "e02f":
all_addr.append(addr)
network.append("World: Schneider Electric")
activation.append("ABP")
elif addr[:4] == "e030" or addr[:4] == "e031":
all_addr.append(addr)
network.append("World: ZENNER")
activation.append("ABP")
elif addr[:4] == "e038" or addr[:4] == "e039":
all_addr.append(addr)
network.append("World: MachineQ/Comcast")
activation.append("ABP")
elif addr[:6] == "fc0004" or addr[:6] == "fc0005" or addr[:6] == "fc0006" or addr[:6] == "fc0007":
all_addr.append(addr)
network.append("World: Nordic Automation Systems")
activation.append("ABP")
elif addr[:6] == "fc0008" or addr[:6] == "fc0009" or addr[:6] == "fc000a" or addr[:6] == "fc000b":
all_addr.append(addr)
network.append("World: ResIOT")
activation.append("ABP")
elif addr[:6] == "fc000c" or addr[:6] == "fc000d" or addr[:6] == "fc000e" or addr[:6] == "fc000f":
all_addr.append(addr)
network.append("World: SYSDEV")
activation.append("ABP")
elif addr[:6] == "fc0020" or addr[:6] == "fc0021" or addr[:6] == "fc0022" or addr[:6] == "fc0023":
all_addr.append(addr)
network.append("World: Definium Technologies")
activation.append("ABP")
elif addr[:6] == "fc0030" or addr[:6] == "fc0031" or addr[:6] == "fc0032" or addr[:6] == "fc0033":
all_addr.append(addr)
network.append("World: nFore Technology")
activation.append("ABP")
elif addr[:6] == "fc0044" or addr[:6] == "fc0045" or addr[:6] == "fc0046" or addr[:6] == "fc0047":
all_addr.append(addr)
network.append("World: Digital Nordix AB (DNX)")
activation.append("ABP")
elif addr[:6] == "fc0058" or addr[:6] == "fc0059" or addr[:6] == "fc005a" or addr[:6] == "fc005b":
all_addr.append(addr)
network.append("World: Schneider Electric")
activation.append("ABP")
elif addr[:6] == "fc0060" or addr[:6] == "fc0061" or addr[:6] == "fc0062" or addr[:6] == "fc0063":
all_addr.append(addr)
network.append("World: ZENNER")
activation.append("ABP")
elif addr[:6] == "fc0068" or addr[:6] == "fc0069" or addr[:6] == "fc006a" or addr[:6] == "fc006b":
all_addr.append(addr)
network.append("World: REQUEA")
activation.append("ABP")
elif addr[:6] == "fc007c" or addr[:6] == "fc007d" or addr[:6] == "fc007e" or addr[:6] == "fc007f":
all_addr.append(addr)
network.append("World: mcf88 SRL")
activation.append("ABP")
elif addr[:6] == "fc0084" or addr[:6] == "fc0085" or addr[:6] == "fc0086" or addr[:6] == "fc0087":
all_addr.append(addr)
network.append("World: Hiber")
activation.append("ABP")
elif addr[:6] == "fc0098" or addr[:6] == "fc0099" or addr[:6] == "fc009a" or addr[:6] == "fc009b":
all_addr.append(addr)
network.append("World: Mirakonta")
activation.append("ABP")
# Network prefix assignments to Australia
elif addr[:2] == "0a" or addr[:2] == "0b":
all_addr.append(addr)
network.append("Singapore Indonesia Australia Africa India: SingTel")
activation.append("ABP")
elif addr[:2] == "46" or addr[:2] == "47":
all_addr.append(addr)
network.append("Australia New Zealand: Ventia")
activation.append("ABP")
elif addr[:2] == "58" or addr[:2] == "59":
all_addr.append(addr)
network.append("Australia: Airlora")
activation.append("ABP")
# Neither
else:
all_addr.append(addr)
other_addr.append(addr) # potential DevAddr from non-TTN network
network.append("-")
activation.append("-")
else:
all_addr.append("-")
network.append("-")
activation.append("-")
### Filtering the data for OTAA
print("3. Filtering the data for OTAA...")
info = every.payload
info = list(map(str, info))
deveuis = []
appeuis = []
deveui_revl = []
appeui_revl = []
deveui_len = 16
appeui_len = 16
for i in range(len(info)):
if list(map(str, mhdrs))[i] == "00":
deveui = info[i][20:38].split("-")
deveui = "".join(deveui)
appeui = info[i][2:20].split("-")
appeui = "".join(appeui)
deveuis.append(deveui)
appeuis.append(appeui)
else:
deveuis.append("-")
appeuis.append("-")
for row in deveuis: # reversing DevEUI's little endian form
fixed = []
for j in range(int(len(row)/2)):
fixed.append(row[j*2:(j+1)*2])
fixed.reverse()
deveui_rev = "".join(fixed)
deveui_revl.append(deveui_rev)
for row in appeuis: # reversing AppEUI's little endian form
fixed = []
for j in range(int(len(row)/2)):
fixed.append(row[j*2:(j+1)*2])
fixed.reverse()
appeui_rev = "".join(fixed)
appeui_revl.append(appeui_rev)
filt_deveui = [] # Remove payloads shorter than the length of DevAddr
filt_appeui = []
all_deveui = []
all_appeui = []
for deveui_s in deveui_revl:
if len(deveui_s) == deveui_len:
filt_deveui.append(deveui_s)
all_deveui.append(deveui_s)
else:
all_deveui.append("-")
for appeui_s in appeui_revl:
if len(appeui_s) == appeui_len:
filt_appeui.append(appeui_s)
all_appeui.append(appeui_s)
else:
all_appeui.append("-")
deveui_results = Counter(filt_deveui)
appeui_results = Counter(filt_appeui)
### Using API to obtain device manufacturer
print("4. Looking up device manufacturers...")
MAC_URL = "https://api.macvendors.com/v1/lookup/%s"
deveui_knowns = []
appeui_knowns = []
deveui_total = len(list(deveui_results.keys()))
appeui_total = len(list(appeui_results.keys()))
num = 1
starttime = time.time()
for eui in list(deveui_results.keys()):
print("Looking up", num, "out of", deveui_total, "DevEUIs...")
r = requests.get(MAC_URL % eui)
if "data" in list(r.json().keys()):
deveui_knowns.append([eui, r.json()])
num += 1
time.sleep(0.5)
num = 1
starttime = time.time()
for eui in list(appeui_results.keys()):
print("Looking up", num, "out of", appeui_total, "AppEUIs...")
r = requests.get(MAC_URL % eui)
if "data" in list(r.json().keys()):
appeui_knowns.append([eui, r.json()])
num += 1
time.sleep(0.5)
### Adding new columns
# Turn DevEUI and AppEUI knowns into dictionary
print("5. Adding new columns...")
deveui_knowns_dict = {}
appeui_knowns_dict = {}
for i in range(len(deveui_knowns)):
deveui_knowns_dict[deveui_knowns[i][0]] = deveui_knowns[i][1]
for i in range(len(appeui_knowns)):
appeui_knowns_dict[appeui_knowns[i][0]] = appeui_knowns[i][1]
# Get a list of devaddress and mic
devaddress = []
mic = []
for i in range(len(activation)):
if activation[i] != "-":
devaddress.append(devaddr_revl[i])
if len("".join(info[i].split("-"))[-8:]) == 8:
mic.append("".join(info[i].split("-"))[-8:])
else:
mic.append("-")
else:
devaddress.append(devaddr_revl[i])
if len("".join(info[i].split("-"))[-8:]) == 8:
mic.append("".join(info[i].split("-"))[-8:])
else:
mic.append("-")
# Create a dataframe for OTAA
dev_matcheui = []
app_matcheui = []
dev_manufeui = []
app_manufeui = []
manumac = []
for i in deveui_knowns:
dev_matcheui.append(i[0])
dev_manufeui.append(i[1].get("data").get("organization_name"))
manumac.append(i[1].get("data").get("assignment"))
for i in appeui_knowns:
app_matcheui.append(i[0])
app_manufeui.append(i[1].get("data").get("organization_name"))
#manufdf = pd.DataFrame({"DevEUI": dev_matcheui, "DevManufacturer": dev_manufeui, "MAC_prefix": manumac,
# "AppEUI": app_matcheui, "AppManufacturer": app_manufeui})
dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])
deveui_res = dictfilt(deveui_results, dev_matcheui)
appeui_res = dictfilt(appeui_results, app_matcheui)
# Update the new columns' values
dev_manufid = []
app_manufid = []
for i in range(len(all_deveui)):
#if int(every["size"][i]) == 23 and str(mhdrs[i]) == "00":
if (all_deveui[i] in dev_matcheui or all_appeui[i] in app_matcheui) and str(mhdrs[i]) == "00":
activation[i] = "OTAA"
network[i] = "-"
devaddress[i] = all_deveui[i]
if all_deveui[i] in dev_matcheui and all_appeui[i] in app_matcheui:
dev_manufid.append(deveui_knowns_dict[all_deveui[i]].get("data").get("organization_name").replace(',', ''))
app_manufid.append(appeui_knowns_dict[all_appeui[i]].get("data").get("organization_name").replace(',', ''))
elif all_deveui[i] in dev_matcheui and all_appeui[i] not in app_matcheui:
dev_manufid.append(deveui_knowns_dict[all_deveui[i]].get("data").get("organization_name").replace(',', ''))
app_manufid.append("-")
elif all_deveui[i] not in dev_matcheui and all_appeui[i] in app_matcheui:
dev_manufid.append("-")
app_manufid.append(appeui_knowns_dict[all_appeui[i]].get("data").get("organization_name").replace(',', ''))
else:
dev_manufid.append("-")
app_manufid.append("-")
elif int(every["size"][i]) == 23 and str(mhdrs[i]) == "00":
activation[i] = "OTAA"
network[i] = "-"
devaddress[i] = all_deveui[i]
if all_deveui[i] in dev_matcheui and all_appeui[i] in app_matcheui:
dev_manufid.append(deveui_knowns_dict[all_deveui[i]].get("data").get("organization_name").replace(',', ''))
app_manufid.append(appeui_knowns_dict[all_appeui[i]].get("data").get("organization_name").replace(',', ''))
elif all_deveui[i] in dev_matcheui and all_appeui[i] not in app_matcheui:
dev_manufid.append(deveui_knowns_dict[all_deveui[i]].get("data").get("organization_name").replace(',', ''))
app_manufid.append("-")
elif all_deveui[i] not in dev_matcheui and all_appeui[i] in app_matcheui:
dev_manufid.append("-")
app_manufid.append(appeui_knowns_dict[all_appeui[i]].get("data").get("organization_name").replace(',', ''))
else:
dev_manufid.append("-")
app_manufid.append("-")
else:
dev_manufid.append("-")
app_manufid.append("-")
all_fcnt = []
all_fctrl = []
all_mhdr = []
final_appeui = []
for i in range(len(activation)):
if activation[i] == "ABP":
final_appeui.append("-")
all_mhdr.append(mhdrs[i])
all_fcnt.append(fcnt_revl[i])
all_fctrl.append(fctrls[i])
elif activation[i] == "-":
final_appeui.append("-")
all_fcnt.append(fcnt_revl[i])
all_fctrl.append(fctrls[i])
all_mhdr.append(mhdrs[i])
elif activation[i] == "OTAA":
final_appeui.append(all_appeui[i])
all_fcnt.append("-")
all_fctrl.append("-")
all_mhdr.append(mhdrs[i])
# Converting MHDR (hex) to binary and getting message type
mtype_raw = []
for hexdec in all_mhdr:
if hexdec == "-":
mtype_raw.append("-")
elif hexdec == "na":
mtype_raw.append("-")
elif hexdec == "9.":
mtype_raw.append("-")
else:
scale = 16 ## equals to hexadecimal
num_of_bits = 8
mtype_raw.append(str(bin(int(hexdec, scale))[2:].zfill(num_of_bits)[:3]))
mtype_desc = []
for mtype in mtype_raw:
if mtype == "000":
mtype_desc.append("Join-request")
elif mtype == "001":
mtype_desc.append("Join-accept")
elif mtype == "010":
mtype_desc.append("Unconfirmed Data Up")
elif mtype == "011":
mtype_desc.append("Unconfirmed Data Down")
elif mtype == "100":
mtype_desc.append("Confirmed Data Up")
elif mtype == "101":
mtype_desc.append("Confirmed Data Down")
elif mtype == "110":
mtype_desc.append("Rejoin-request")
elif mtype == "111":
mtype_desc.append("Proprietary")
else:
mtype_desc.append("-")
# Converting fcnt (hex) to decimal
int_fcnt = []
for fcnt_hex in all_fcnt:
if str(fcnt_hex) == "-":
int_fcnt.append("-")
elif str(fcnt_hex) == "":
int_fcnt.append("-")
else:
dec = int(fcnt_hex, 16)
int_fcnt.append(dec)
# Correcting the time
yr = []
month = []
day = []
hrs = []
mins = []
secs = []
for i in list(every["UTC timestamp"]):
#split for date and time
date_split = i.split(" ")
time_split = date_split[1].split(":")
if int(date_split[0].split("-")[0]) == 2013:
y = int(date_split[0].split("-")[0]) + 0
if len(str(y)) == 1:
yr.append("0"+str(y))
else:
yr.append(str(y))
m = int(date_split[0].split("-")[1]) - 0
if len(str(m)) == 1:
month.append("0"+str(m))
else:
month.append(str(m))
d = int(date_split[0].split("-")[2]) - 0
if len(str(d)) == 1:
day.append("0"+str(d))
else:
day.append(str(d))
h = int(time_split[0]) + 0
if len(str(h)) == 1:
hrs.append("0"+str(h))
else:
hrs.append(str(h))
m = int(time_split[1]) + 0
if len(str(m)) == 1:
mins.append("0"+str(m))
else:
mins.append(str(m))
secs.append(time_split[2])
elif int(date_split[0].split("-")[0]) == 2014:
y = int(date_split[0].split("-")[0]) + 0
if len(str(y)) == 1:
yr.append("0"+str(y))
else:
yr.append(str(y))
m = int(date_split[0].split("-")[1]) + 0
if len(str(m)) == 1:
month.append("0"+str(m))
else:
month.append(str(m))
d = int(date_split[0].split("-")[2]) + 0
if len(str(d)) == 1:
day.append("0"+str(d))
else:
day.append(str(d))
h = int(time_split[0]) + 0
if len(str(h)) == 1:
hrs.append("0"+str(h))
else:
hrs.append(str(h))
m = int(time_split[1]) + 0
if len(str(m)) == 1:
mins.append("0"+str(m))
else:
mins.append(str(m))
secs.append(time_split[2])
else:
yr.append(date_split[0].split("-")[0])
month.append(date_split[0].split("-")[1])
day.append(date_split[0].split("-")[2])
h = int(time_split[0]) + 0
if len(str(h)) == 1:
hrs.append("0"+str(h))
else:
hrs.append(str(h))
mins.append(time_split[1])
secs.append(time_split[2])
yr = list(map(str, yr))
month = list(map(str, month))
day = list(map(str, day))
hrs = list(map(str, hrs))
mins = list(map(str, mins))
secs = list(map(str, secs))
# correcting cases where mins >= 60 and hours >= 24
for i in range(len(mins)):
if int(mins[i]) >= 60:
h = int(hrs[i])
h += 1
if len(str(h)) == 1:
hrs[i] = "0"+str(h)
else:
hrs[i] = str(h)
m = int(mins[i])
m -= 60
if len(str(m)) == 1:
mins[i] = "0"+str(m)
else:
mins[i] = str(m)
for i in range(len(hrs)):
if int(hrs[i]) >= 24:
d = int(day[i])
d += 1
if len(str(d)) == 1:
day[i] = "0"+str(d)
else:
day[i] = str(d)
h = int(hrs[i])
h -= 24
if len(str(h)) == 1:
hrs[i] = "0"+str(h)
else:
hrs[i] = str(h)
# correcting cases where day is > possible value within a certain month
for i in range(len(day)):
if int(day[i]) > 30:
if month[i] in ["4", "6", "9", "11"]:
m = int(month[i])
m += 1
if len(str(m)) == 1:
month[i] = "0"+str(m)
else:
month[i] = str(m)
d = int(day[i])
d -= 30
if len(str(d)) == 1:
day[i] = "0"+str(d)
else:
day[i] = str(d)
elif int(day[i]) > 28:
if month[i] in ["2"]:
if int(yr[i])%4 == 0 and int(day[i]) > 29:
m = int(month[i])
m += 1
if len(str(m)) == 1:
month[i] = "0"+str(m)
else:
month[i] = str(m)
d = int(day[i])
d -= 29
if len(str(d)) == 1:
day[i] = "0"+str(d)
else:
day[i] = str(d)
elif int(yr[i])%4 != 0:
m = int(month[i])
m += 1
if len(str(m)) == 1:
month[i] = "0"+str(m)
else:
month[i] = str(m)
d = int(day[i])
d -= 28
if len(str(d)) == 1:
day[i] = "0"+str(d)
else:
day[i] = str(d)
# correcting cases where year needs to be changed
for i in range(len(month)):
if int(month[i]) == 12 and int(day[i]) > 31:
y = int(yr[i])
y += 1
yr[i] = str(y)
m = 1
if len(str(m)) == 1:
month[i] = "0"+str(m)
else:
month[i] = str(m)
d = 1
if len(str(d)) == 1:
day[i] = "0"+str(d)
else:
day[i] = str(d)
# concatenating the fixed date time
date = []
aus_time = []
for i in range(len(yr)):
date.append("-".join([yr[i], month[i], day[i]]))
aus_time.append(":".join([hrs[i], mins[i], secs[i]]))
every["date"] = date
every["time"] = aus_time
every["activation"] = activation
every["network"] = network
every["DevEUI or DevAddr"] = devaddress
every["AppEUI"] = final_appeui
every["fctrl"] = all_fctrl
every["fcnt"] = int_fcnt
every["mhdr"] = all_mhdr
every["mtype"] = mtype_raw
every["mtype_desc"] = mtype_desc
every["MIC"] = mic
every["deveui_manufacturer"] = dev_manufid
every["appeui_manufacturer"] = app_manufid
every = every[['UTC timestamp', 'date', 'time', 'us count', 'frequency', 'RF chain', 'RX chain','status',
'bandwidth', 'datarate', 'coderate', 'RSSI', 'SNR', 'size', 'DevEUI or DevAddr',
'AppEUI', 'fctrl', 'fcnt', 'mhdr', 'mtype', 'mtype_desc', 'MIC', 'activation',
'network', 'deveui_manufacturer', 'appeui_manufacturer', 'payload']]
filtered = every.copy()
no_fcnt = filtered.loc[(filtered.fcnt == "-") & (filtered.activation != "OTAA")].index
filtered = filtered.drop(no_fcnt)
### Calculating the time difference
# Function for truncating decimals without rounding to 3 dp
print("6. Calculating the time difference in seconds...")
time_sec = []
timeutc = list(pd.to_datetime(filtered["UTC timestamp"]))
for i in range(len(timeutc)):
time_sec.append(pd.Timedelta(timeutc[i]-timeutc[0]).total_seconds())
# Add a new column to the dataframe
filtered["sec_diff"] = time_sec
### Algorithm for identifying unique devices
# Split it from the original dataframe by DevAddr or DevEUI
print("7. Identifying unique devices...")
l_freq = []
l_devaddr915 = []
l_devaddr923 = []
all_freq = []
filtered = filtered.loc[filtered.status == "CRC_OK "]
# ISM band
fr915 = [916800000, 917000000, 917200000, 917400000, 917600000, 917800000, 918000000, 91820000]
fr923 = [922000000, 922100000, 922200000, 922400000, 922600000, 922800000, 923000000, 923200000, 923400000]
freq915 = filtered.loc[filtered.frequency.isin(fr915)]
if freq915.shape[0] > 0:
freq915 = filtered.loc[filtered.frequency.isin(fr915)].index
filtered.loc[freq915, "Freq_Plan"] = "915band"
all_freq.append(freq915)
freq923 = filtered.loc[filtered.frequency.isin(fr923)]
if freq923.shape[0] > 0:
freq923 = filtered.loc[filtered.frequency.isin(fr923)].index
filtered.loc[freq923, "Freq_Plan"] = "923band"
all_freq.append(freq923)
if len(all_freq) == 0:
print("ERROR: No valid LoRaWAN packets!")
exit()
if (freq915.shape[0] and freq923.shape[0]) > 0:
for a, b in filtered.groupby("Freq_Plan", sort=False):
l_freq.append(b.sort_values(by=["sec_diff"]))
#915
for a, b in l_freq[0].groupby("DevEUI or DevAddr", sort=False):
l_devaddr915.append(b.sort_values(by=["sec_diff"]))
#923
for a, b in l_freq[1].groupby("DevEUI or DevAddr", sort=False):
l_devaddr923.append(b.sort_values(by=["sec_diff"]))
elif freq915.shape[0] == 0:
#923
for a, b in filtered.groupby("DevEUI or DevAddr", sort=False):
l_devaddr923.append(b.sort_values(by=["sec_diff"]))
elif freq923.shape[0] == 0:
#915
for a, b in filtered.groupby("DevEUI or DevAddr", sort=False):
l_devaddr915.append(b.sort_values(by=["sec_diff"]))
### Counting the number of devices
print("8. Counting the number of devices...")
def truncate(f):
if f == "-":
return "-"
else:
return math.floor(f * 10 ** 3) / 10 ** 3
def truncate3(f):
if f == "-":
return "-"
else:
return math.floor(f * 10 ** 3) / 10 ** 3
def truncate0(f):
if f == "-":
return "-"
else:
return math.floor(f * 10 ** 0) / 10 ** 0
dev_num915 = []
rate915 = []
dev_num923 = []
rate923 = []
num = 1
# Identifying unique devices
l_index_915 = []
if len(l_devaddr915) > 0:
for i in l_devaddr915:
sec_df = list(map(float, i["sec_diff"]))
sec_df = [x - sec_df[0] for x in sec_df]
sec_df = list(map(truncate, sec_df))
devaddr_df = list(i["DevEUI or DevAddr"])
activation_df = list(i["activation"])
# OTAA case
if activation_df[0] == "OTAA":
l_index_915.extend(i.index)
for j in range(len(devaddr_df)):
if j >= 1:
rate915.append(truncate3(float(sec_df[j]) - float(sec_df[j-1])))
else:
rate915.append("-")
dev_num915.extend(repeat(num, len(devaddr_df)))
num += 1
# ABP case
# Single packet case
elif i.shape[0] == 1 and activation_df[0] != "OTAA":
l_index_915.extend(i.index)
dev_num915.append(num)
rate915.append("-")
num += 1
# 2 packets or more
elif i.shape[0] > 1 and activation_df[0] == "ABP":
dev_num915.extend(repeat(num, i.shape[0]))
#j = i.sort_values(by=["date", "time"]) # sort by date and time
l_index_915.extend(i.index)
fcnt_df = list(map(int, i["fcnt"]))
i_rate = []
rate915.append("-")
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
if fcnt_df[k] != fcnt_df[k-1]:
i_rate.append(truncate3(((sec_df[k] - sec_df[k-1])/(fcnt_df[k] - fcnt_df[k-1]))))
else:
i_rate.append(truncate3((sec_df[k] - sec_df[k-1])))
trunc_i_rate = []
i_rate2 = list(map(np.round, i_rate))
i_rate_set = list(dict.fromkeys(i_rate2)) # list of the set intervals to look up for assigning devnumber
# Remove rates that are within +-1 rate of each other
for m in range(len(i_rate_set)):
if m == 0:
trunc_i_rate.append(i_rate_set[m])
elif m >= 1:
if (i_rate_set[m] > all([x+1 for x in trunc_i_rate])) or (i_rate_set[m] < all([x-1 for x in trunc_i_rate])):
trunc_i_rate.append(i_rate_set[m])
for element in i_rate:
for i_devnum in range(len(trunc_i_rate)):
if (trunc_i_rate[i_devnum] - 1) <= float(element) <= (trunc_i_rate[i_devnum] + 1):
break
else:
continue
rate915.append(element)
num += 1
# For neither OTAA and ABP activation and more than 1 packet
elif i.shape[0] > 1 and activation_df[0] == "-":
dev_num915.extend(repeat(num, i.shape[0]))
#j = i.sort_values(by=["date", "time"]) # sort by date and time
l_index_915.extend(i.index)
fcnt_df = list(i["fcnt"])
if any(i["fcnt"] == "-"):
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
rate915.append(truncate3((sec_df[k] - sec_df[k-1])))
else:
fcnt_df = list(map(int, i["fcnt"]))
i_rate = []
rate915.append("-")
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
if fcnt_df[k] != fcnt_df[k-1]:
i_rate.append(truncate3(((sec_df[k] - sec_df[k-1])/(fcnt_df[k] - fcnt_df[k-1]))))
else:
i_rate.append(truncate3((sec_df[k] - sec_df[k-1])))
trunc_i_rate = []
i_rate2 = list(map(np.round, i_rate))
i_rate_set = list(dict.fromkeys(i_rate2)) # list of the set intervals to look up for assigning devnumber
# Remove rates that are within +-1 rate of each other
for m in range(len(i_rate_set)):
if m == 0:
trunc_i_rate.append(i_rate_set[m])
elif m >= 1:
if (i_rate_set[m] > all([x+1 for x in trunc_i_rate])) or (i_rate_set[m] < all([x-1 for x in trunc_i_rate])):
trunc_i_rate.append(i_rate_set[m])
for element in i_rate:
for i_devnum in range(len(trunc_i_rate)):
if (trunc_i_rate[i_devnum] - 1) <= float(element) <= (trunc_i_rate[i_devnum] + 1):
break
else:
continue
rate915.append(element)
num += 1
l_index_923 = []
if len(l_devaddr923) > 0:
for i in l_devaddr923:
sec_df = list(map(float, i["sec_diff"]))
sec_df = [x - sec_df[0] for x in sec_df]
sec_df = list(map(truncate, sec_df))
devaddr_df = list(i["DevEUI or DevAddr"])
activation_df = list(i["activation"])
# OTAA case
if activation_df[0] == "OTAA":
l_index_923.extend(i.index)
for j in range(len(devaddr_df)):
if j >= 1:
rate923.append(truncate3(float(sec_df[j]) - float(sec_df[j-1])))
else:
rate923.append("-")
dev_num923.extend(repeat(num, len(devaddr_df)))
num += 1
# ABP case
# Single packet case
elif i.shape[0] == 1 and activation_df[0] != "OTAA":
l_index_923.extend(i.index)
dev_num923.append(num)
rate923.append("-")
num += 1
# 2 packets or more
elif i.shape[0] > 1 and activation_df[0] == "ABP":
dev_num923.extend(repeat(num, i.shape[0]))
#j = i.sort_values(by=["date", "time"]) # sort by date and time
l_index_923.extend(i.index)
fcnt_df = list(map(int, i["fcnt"]))
i_rate = []
rate923.append("-")
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
if fcnt_df[k] != fcnt_df[k-1]:
i_rate.append(truncate3(((sec_df[k] - sec_df[k-1])/(fcnt_df[k] - fcnt_df[k-1]))))
else:
i_rate.append(truncate3((sec_df[k] - sec_df[k-1])))
trunc_i_rate = []
i_rate2 = list(map(np.round, i_rate))
i_rate_set = list(dict.fromkeys(i_rate2)) # list of the set intervals to look up for assigning devnumber
# Remove rates that are within +-1 rate of each other
for m in range(len(i_rate_set)):
if m == 0:
trunc_i_rate.append(i_rate_set[m])
elif m >= 1:
if (i_rate_set[m] > all([x+1 for x in trunc_i_rate])) or (i_rate_set[m] < all([x-1 for x in trunc_i_rate])):
trunc_i_rate.append(i_rate_set[m])
for element in i_rate:
for i_devnum in range(len(trunc_i_rate)):
if (trunc_i_rate[i_devnum] - 1) <= float(element) <= (trunc_i_rate[i_devnum] + 1):
break
else:
continue
rate923.append(element)
num += 1
# For neither OTAA and ABP activation and more than 1 packet
elif i.shape[0] > 1 and activation_df[0] == "-":
dev_num923.extend(repeat(num, i.shape[0]))
#j = i.sort_values(by=["date", "time"]) # sort by date and time
l_index_923.extend(i.index)
fcnt_df = list(i["fcnt"])
if any(i["fcnt"] == "-"):
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
rate923.append(truncate3((sec_df[k] - sec_df[k-1])))
else:
fcnt_df = list(map(int, i["fcnt"]))
i_rate = []
rate923.append("-")
for k in range(len(fcnt_df)): # +1 to start from 2nd row
if 1 <= k < len(fcnt_df): # make sure it doesn't exceed the row index limit
if fcnt_df[k] != fcnt_df[k-1]:
i_rate.append(truncate3(((sec_df[k] - sec_df[k-1])/(fcnt_df[k] - fcnt_df[k-1]))))
else:
i_rate.append(truncate3((sec_df[k] - sec_df[k-1])))
trunc_i_rate = []
i_rate2 = list(map(np.round, i_rate))
i_rate_set = list(dict.fromkeys(i_rate2)) # list of the set intervals to look up for assigning devnumber
# Remove rates that are within +-1 rate of each other
for m in range(len(i_rate_set)):
if m == 0:
trunc_i_rate.append(i_rate_set[m])
elif m >= 1:
if (i_rate_set[m] > all([x+1 for x in trunc_i_rate])) or (i_rate_set[m] < all([x-1 for x in trunc_i_rate])):
trunc_i_rate.append(i_rate_set[m])
for element in i_rate:
for i_devnum in range(len(trunc_i_rate)):
if (trunc_i_rate[i_devnum] - 1) <= float(element) <= (trunc_i_rate[i_devnum] + 1):
break
else:
continue
rate923.append(element)
num += 1
### Sort the derived device number, interval, rate by their corresponding index (to match it with original dataframe)
print("9. Sorting the device numbers by index...")
def sortFirst(val):
return val[0]
all_l_indx = []
rate = []
dev_num = []
if len(l_devaddr915) > 0:
all_list915 = list(zip(l_index_915, rate915, dev_num915))
all_list915.sort(key = sortFirst)
for i in all_list915:
all_l_indx.append(i[0])
rate.append(i[1])
dev_num.append(i[2])
elif len(l_devaddr) > 0:
all_list923 = list(zip(l_index_923, rate923, dev_num923))
all_list923.sort(key = sortFirst)
for i in all_list923:
all_l_indx.append(i[0])
rate.append(i[1])
dev_num.append(i[2])
all_list = list(zip(all_l_indx, rate, dev_num))
all_list.sort(key = sortFirst)
### Adding the corrected ordered tx_interval and dev_number to the dataframe
print("10. Adding the transmission interval column...")
l_rate = []
l_dev_num = []
for i in all_list:
if i[1] != "-":
l_rate.append(float(i[1]))
l_dev_num.append(int(i[2]))
else:
l_rate.append(i[1])
l_dev_num.append(int(i[2]))
filtered["tx_interval"] = l_rate
filtered["dev_number"] = l_dev_num
### Save as csv
print("11. Saving as csv...")
filtered.to_csv(pd.datetime.today().strftime('%y%m%d_%H%M%S_') + "LoRadar_data.csv", index=False)
print("Data saved!")