diff --git a/README.md b/README.md index 29aa5a4..ae2fdb5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Under active development, everything is subject to change without notice. |----------|-------------|---------| | `JSON_IN` | Semi-colon separated list of `host:port` entries to connect to for JSON ingest. | acars_router:15550 | | `SBS_OUT` | Semi-colon separated list of `host:port` entries to connect to for SBS/Basestation output. | ultrafeeder:12000 | -| `LOG_FILE` | Set to any value to message text, type, SBS output, and adsbexchange link to a file in `/log`. | Unset | +| `LOG_FILE` | Set to any value to message text, type, SBS output, and adsbexchange link to files in `/log`. | Unset | ## Docker Compose diff --git a/rootfs/scripts/acars2pos.py b/rootfs/scripts/acars2pos.py index eb7399d..b242783 100755 --- a/rootfs/scripts/acars2pos.py +++ b/rootfs/scripts/acars2pos.py @@ -82,10 +82,8 @@ def thread_wrapper(func, *args): txqs.append(SimpleQueue()) Thread(name=f"tx {s[0]}:{s[1]}", target=thread_wrapper, args=(tx_thread, s, txqs[-1])).start() -if not os.path.exists("/log"): +if getenv("LOG_FILE") and not os.path.exists("/log"): os.makedirs("/log") -logfile = open(f"/log/{datetime.now(timezone.utc):%Y_%m_%d}.log", "a", 1) -logfileh1 = open(f"/log/{datetime.now(timezone.utc):%Y_%m_%d}.h1.log", "a", 1) while True: try: @@ -99,6 +97,31 @@ def thread_wrapper(func, *args): if not sbs.get("txt") and not sbs.get("lat"): continue + if not sbs.get("reg"): + sbs["reg"] = icao2reg(sbs.get("icao", "")) + + sbs["reg"] = sub(r'[^a-zA-Z0-9-]', '', sbs["reg"]).upper() + + if not sbs.get("icao"): + sbs["icao"] = reg2icao(sbs.get("reg", "")) + if not sbs.get("icao"): + print(f'{Fore.GREEN}xxxxxxx {sbs["reg"]}{Fore.RESET}', file=stderr) + continue + + if sbs["type"] == "acars": + squawk = "1111" + elif sbs["type"] == "vdlm2": + squawk = "2222" + elif sbs["type"] == "hfdl": + squawk = "3333" + else: + squawk = "0000" + + if getenv("LOG_FILE") and sbs.get("msgtype") and sbs.get("type") != "hfdl": + with open(f"/log/{sbs.get('msgtype')}.log", "a", 1) as logfile: + logfile.write(f'{sbs["type"]}\t{sbs.get("msgtype")}\thttps://globe.adsbexchange.com/?icao={sbs["icao"]}&showTrace={datetime.fromtimestamp(sbs["time"], tz=timezone.utc):%Y-%m-%d}×tamp={sbs["time"]}\n') + logfile.write(f'{sbs["txt"]}\n\n') + if sbs.get("lat"): lat = sbs["lat"] lon = sbs["lon"] @@ -169,8 +192,8 @@ def thread_wrapper(func, *args): else: continue elif len(pos2a) and len(pos2b): - txt = sub(r'(LAT)', Fore.MAGENTA + r'\1' + Fore.RESET, sbs["txt"]) - txt = sub(r'(LON)', Fore.MAGENTA + r'\1' + Fore.RESET, txt) + txt = sub(r'(LAT)', Fore.RED + r'\1' + Fore.RESET, sbs["txt"]) + txt = sub(r'(LON)', Fore.RED + r'\1' + Fore.RESET, txt) print(f"old regex 3 matched message type {sbs['msgtype']}") print(txt) continue @@ -180,43 +203,11 @@ def thread_wrapper(func, *args): print(f'{sbs["type"]} {sbs.get("msgtype")}', file=stderr) # print(pos, file=stderr) - if not sbs.get("reg"): - sbs["reg"] = icao2reg(sbs.get("icao", "")) - - sbs["reg"] = sub(r'[^a-zA-Z0-9-]', '', sbs["reg"]).upper() - - if not sbs.get("icao"): - sbs["icao"] = reg2icao(sbs.get("reg", "")) - if not sbs.get("icao"): - print(f'{Fore.GREEN}xxxxxxx {sbs["reg"]}{Fore.RESET}', file=stderr) - continue - - if sbs["type"] == "acars": - squawk = "1111" - elif sbs["type"] == "vdlm2": - squawk = "2222" - elif sbs["type"] == "hfdl": - squawk = "3333" - else: - squawk = "0000" - out = f'MSG,3,1,1,{sbs["icao"].upper()},1,{datetime.fromtimestamp(sbs["time"], tz=timezone.utc):%Y/%m/%d,%T},{datetime.now(timezone.utc):%Y/%m/%d,%T},{sbs.get("flight", "")},,,,{lat:.3f},{lon:.3f},,{squawk},,,,' print(f'https://globe.adsbexchange.com/?icao={sbs["icao"]}&showTrace={datetime.fromtimestamp(sbs["time"], tz=timezone.utc):%Y-%m-%d}×tamp={sbs["time"]}') print(f'{Fore.BLUE}{out}{Fore.RESET}\n', file=stderr) - if getenv("LOG_FILE") and sbs.get("msgtype") and sbs.get("type") != "hfdl": - if sbs["msgtype"] == "H1": - logfileh1.write(f'{sbs["txt"]}\n') - logfileh1.write(f'{sbs["type"]} {sbs.get("msgtype")}\n') - logfileh1.write(out+"\n") - logfileh1.write(f'https://globe.adsbexchange.com/?icao={sbs["icao"]}&showTrace={datetime.fromtimestamp(sbs["time"], tz=timezone.utc):%Y-%m-%d}×tamp={sbs["time"]}\n\n') - else: - logfile.write(f'{sbs["txt"]}\n') - logfile.write(f'{sbs["type"]} {sbs.get("msgtype")}\n') - logfile.write(out+"\n") - logfile.write(f'https://globe.adsbexchange.com/?icao={sbs["icao"]}&showTrace={datetime.fromtimestamp(sbs["time"], tz=timezone.utc):%Y-%m-%d}×tamp={sbs["time"]}\n\n') - for q in txqs: q.put(out+"\r\n") except BaseException: diff --git a/rootfs/scripts/acars_decode/Decoder.py b/rootfs/scripts/acars_decode/Decoder.py index 35cbe1d..fd929b6 100644 --- a/rootfs/scripts/acars_decode/Decoder.py +++ b/rootfs/scripts/acars_decode/Decoder.py @@ -1,14 +1,55 @@ from re import compile, sub from colorama import Fore -rgxs = [compile(r"([NS])[ 0]{0,2}(\d{1,2}\.\d{3}).?([WE])[0 ]{0,2}(\d{1,3}\.\d{3})"), - compile(r"([NS])[ 0]{0,1}(\d{1,2})[ 0]{0,1}(\d{1,2}\.\d{1}).?([WE])[ 0]{0,2}(\d{1,3}?)[ 0]{0,1}(\d{1,2}\.\d{1})"), - compile(r"([NS])[ 0]{0,1}(\d{1,2})[ 0]{0,3}(\d{0,2}\.\d{2}).?([WE])[ 0]{0,2}(\d{1,3})[ 0]{0,1}(\d{1,2}\.\d{2})"), - compile(r"([NS])\s?(\d{2})(\d{2})(\d{2}).?([WE])\s?(\d{2,3})(\d{2})(\d{2})"), - compile(r"([NS])[ 0]?([ 0\d]\d)(\d{3}).?([WE])([ 0\d][ 0\d]\d)(\d{3})"), - compile(r"[0 ]{0,1}(\d{1,2})[0 ]{0,1}(\d{1,2}\.\d{1})([NS])[ 0]{0,2}(\d{1,3})[0 ]{0,1}(\d{1,2}\.\d{1})([WE])"), - compile(r"[ 0]{0,1}(\d{1,2})[ 0]{0,1}(\d{1,2})([NS])[ 0]{0,2}(\d{1,3})[ 0]{0,1}(\d{1,2})([WE])"), - compile(r"LAT ([NS]) [0 ]{0,1}(\d{1,2}):(\d{2}\.\d{1}) LONG ([WE]) [0 ]{0,1}(\d{1,2}):(\d{2}\.\d{1})")] + +dlat = r"(?P[NS])" +dlon = r"(?P[WE])" + +spdig2 = r"(?:\s|\d)\d" +spdig3 = r"(?:\s\s|\s\d|\d\d)\d" + +dig1 = r"\d" +dig2 = r"\d\d" +dig3 = r"\d\d\d" + + +def name(n, rgx): + return f"(?P<{n}>{rgx})" + +def sd2(n): + return name(n, spdig2) + +def sd3(n): + return name(n, spdig3) + +def d1(n): + return name(n, dig1) + +def d2(n): + return name(n, dig2) + +def d3(n): + return name(n, dig3) + + +rgxs = [ + compile(dlat + r"[ 0]{0,2}(\d{1,2}\.\d{3}).?" + dlon + r"[ 0]{0,2}(\d{1,3}\.\d{3})"), # thousandths of degrees + compile(dlat + r"[ 0]{0,1}(\d{1,2})[ 0]{0,1}(\d{1,2}\.\d{1}).?" + dlon + r"[ 0]{0,2}(\d{1,3}?)[ 0]{0,1}(\d{1,2}\.\d{1})"), # tenths of minutes + compile(dlat + r"[ 0]{0,1}(\d{1,2})[ 0]{0,3}(\d{0,2}\.\d{2}).?" + dlon +r"[ 0]{0,2}(\d{1,3})[ 0]{0,1}(\d{1,2}\.\d{2})"), # hundredths of minutes + compile(dlat + r"\s?(\d{2})(\d{2})(\d{2}).?" + dlon + r"\s?(\d{2,3})(\d{2})(\d{2})"), # seconds + compile(dlat + r"[ 0]?([ 0\d]\d)(\d{3}).?" + dlon + r"([ 0\d][ 0\d]\d)(\d{3})"), # tenths of minutes, no decimal + compile(r"[ 0]{0,1}(\d{1,2})[ 0]{0,1}(\d{1,2}\.\d{1})" + dlat + r"[ 0]{0,2}(\d{1,3})[ 0]{0,1}(\d{1,2}\.\d{1})" + dlon), # tenths of minutes, direction after + compile(r"[ 0]{0,1}(\d{1,2})[ 0]{0,1}(\d{1,2})" + dlat + r"[ 0]{0,2}(\d{1,3})[ 0]{0,1}(\d{1,2})" + dlon), # minutes, direction after + compile(r"LAT " + dlat + r" [ 0]{0,1}(\d{1,2}):(\d{2}\.\d{1}) LONG " + dlon + r" [ 0]{0,1}(\d{1,2}):(\d{2}\.\d{1})"), # tenths of minuts, colon separator + ] + +msgrgx = { + "10": [compile(dlat + " " + sd2("latdeg") + "\." + d3("latdeg1000") + "\/" + dlon + sd3("londeg") + "\." + d3("londeg1000"))], + "14": [compile(dlat + sd2("latdeg") + d2("latmin") + d1("latmin10") + dlon + sd3("londeg") + d2("lonmin") + d1("lonmin10"))], + "15": [compile(dlat + sd2("latdeg") + d2("latmin") + d1("latmin10") + dlon + sd3("londeg") + d2("lonmin") + d1("lonmin10")), + compile(dlat + sd2("latdeg") + d2("latmin") + d2("latsec") + dlon + sd3("londeg") + d2("lonmin") + d2("lonsec"))], + "4T": [compile(d2("latdeg") + d2("latmin") + r"\." + d1("latmin10") + dlat + d3("londeg") + d2("lonmin") + r"\." + d1("lonmin10") + dlon)], + } def decode(msg): if msg.get("vdl2"): @@ -21,39 +62,42 @@ def decode(msg): if not dat: return None - if dat.get("msgtype") == "4N": - rgx = compile(r"([NS])(\d{3})(\d{3}) ([WE])(\d{3})(\d{3})") - raw = rgx.findall(dat["txt"]) - if len(raw) == 1: - pos = dat["txt"] - for pat in raw[0]: - pos = sub(f"({pat})", Fore.GREEN + r"\1" + Fore.RESET, pos) - print(f"matched message type {dat['msgtype']}") - print(pos) - raw = rgx.search(dat["txt"]) - dat["lat"] = (int(raw.group(2)) + int(raw.group(3))/600) * (-1 if raw.group(1) == "S" else 1) - dat["lon"] = (int(raw.group(5)) + int(raw.group(6))/600) * (-1 if raw.group(4) == "W" else 1) - elif dat.get("msgtype") == "4T": - rgx = compile(r"(\d{2})(\d{2}\.\d{1})([NS])[ 0](\d{2})(\d{2}\.\d{1})([WE])") - raw = rgx.findall(dat["txt"]) - if len(raw) == 1: - pos = dat["txt"] - for pat in raw[0]: - pos = sub(f"({pat})", Fore.GREEN + r"\1" + Fore.RESET, pos) - print(f"matched message type {dat['msgtype']}") - print(pos) - raw = rgx.search(dat["txt"]) - dat["lat"] = (int(raw.group(1)) + float(raw.group(2))/60) * (-1 if raw.group(3) == "S" else 1) - dat["lon"] = (int(raw.group(4)) + float(raw.group(5))/60) * (-1 if raw.group(6) == "W" else 1) + dat["txt"] = dat.get("txt", "").upper().replace("\r", "").replace("\n", "") + + rgxl = msgrgx.get(dat.get("msgtype")) + if rgxl and dat.get("msgtype"): + for rgx in rgxl: + raw = rgx.findall(dat["txt"]) + if len(raw) == 1: + pos = rgx.sub(Fore.GREEN + r"\g<0>" + Fore.RESET, dat["txt"]) + print(f"matched message type {dat['msgtype']}") + print(pos) + raw = rgx.search(dat["txt"]).groupdict() + print(raw) + dat["lat"] = float(raw.get("latdeg", 0)) + dat["lat"] += float(raw.get("lat1000", 0))/1000 + dat["lat"] += float(raw.get("latmin", 0))/60 + dat["lat"] += float(raw.get("latmin10", 0))/600 + dat["lat"] += float(raw.get("latmin100", 0))/6000 + dat["lat"] += float(raw.get("latsec", 0))/3600 + dat["lat"] *= -1 if raw.get("dlat") == "S" else 1 + + dat["lon"] = float(raw.get("londeg", 0)) + dat["lon"] += float(raw.get("lon1000", 0))/1000 + dat["lon"] += float(raw.get("lonmin", 0))/60 + dat["lon"] += float(raw.get("lonmin10", 0))/600 + dat["lon"] += float(raw.get("lonmin100", 0))/6000 + dat["lon"] += float(raw.get("lonsec", 0))/3600 + dat["lon"] *= -1 if raw.get("dlon") == "W" else 1 + + if dat.get("lat") and abs(dat["lat"]) <= 90 and abs(dat["lon"]) <= 180 : + break if not dat.get("lat"): - dat["txt"] = dat.get("txt", "").upper().replace("\r", "").replace("\n", "") for i,rgx in enumerate(rgxs): raw = rgx.findall(dat["txt"]) if len(raw) == 1: - pos = dat["txt"] - for pat in raw[0]: - pos = sub(f"({pat})", Fore.RED + r"\1" + Fore.RESET, pos) + pos = rgx.sub(Fore.RED + r"\g<0>" + Fore.RESET, dat["txt"]) print(f"regex {i} matched message type {dat['msgtype']}") print(pos)