diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd9df5..c669cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### __[v2.5.3]__ - unreleased ##### Added +- Functionality to fill coordinate values in POC data structure (lat, lon) based on configured locations that match a regular expression in POC message [#510](https://github.com/Schrolli91/BOSWatch/pull/510) - Extending POC data-structure by Regex named groups matching. [#508](https://github.com/Schrolli91/BOSWatch/pull/508) ##### Changed ##### Deprecated diff --git a/boswatch.py b/boswatch.py index 8d316b6..0c2457f 100755 --- a/boswatch.py +++ b/boswatch.py @@ -287,6 +287,18 @@ try: logging.error("cannot load description lists") logging.debug("cannot load description lists", exc_info=True) + # + # Load location RegEx + # + try: + if globalVars.config.getboolean("LocationCoordinates", "locationCoordinates"): + from includes import locationCoordinates + locationCoordinates.loadFilters() + except: + # It's an error, but we could work without that stuff... + logging.error("cannot load location regex") + logging.debug("cannot load location regex", exc_info=True) + # # Start rtl_fm # diff --git a/config/config.template.ini b/config/config.template.ini index e49cbea..4469824 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -144,6 +144,31 @@ geo_order = LON, lon, LAT, lat #schemaRegex = ^(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$ +[LocationCoordinates] +# Regex Coordinate replacement (only for POC) +# All fields in data structure can be used, also dynamically added fields that have been evaluated in "schemaPOCMsg". +# Multiple search criteria can be given, then all of them must be hit (AND-condition). +# Coordinates must be the last field, consisting of latitude and longitude (order is important), split by comma. +# First match has priority; search will not proceed as soon as one hit is found. +# Important: Semicolon and comma must not be part of a field or regex, as they are used internally for splitting up the config value correctly. + +# Do you want to enable this feature? (0 - off | 1 - on) +locationCoordinates = 0 + +# LocationName = field1;regex1;...;lat, lon + +# Examples: + +# msg starting with "BOSWatch-Test" +#Location1 = msg;^BOSWatch-Test;49.344394413024084, 8.167496841047555 + +# Objekt containing "VS Wachtenburg" +#Location2 = Objekt;VS Wachtenburg;49.437673, 8.173793 + +# Ort starting with "B9 ", Ortsteil starting with "16 AK " +#B9_16 = Ort;^B9 .*$;Ortsteil;^16 AK .*$;49.428685, 8.408548 + + [multicastAlarm] # Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks). # In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before. diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index 7c5fec5..9166d98 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -174,6 +174,11 @@ def decode(freq, decoded): if has_geo == True: data["lon"] = lon data["lat"] = lat + else: + if globalVars.config.getboolean("LocationCoordinates", "locationCoordinates"): + from includes import locationCoordinates + locationCoordinates.findCoordinates(data) + # Add function as character a-d to dataset data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") data["ricFuncChar"] = data["ric"] + data["functionChar"] diff --git a/includes/locationCoordinates.py b/includes/locationCoordinates.py new file mode 100644 index 0000000..860aa0f --- /dev/null +++ b/includes/locationCoordinates.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +Functions for the location RegEX + +@author: Marco Schotthöfer + +@requires: Configuration has to be set in the config.ini +""" + +import logging # Global logger +import re #Regex for Filter Check + +from includes import globalVars # Global variables + +# local variables +filterList = [] + + +def loadFilters(): + try: + logging.debug("Loading location coordinates") + + for key,val in globalVars.config.items("LocationCoordinates"): + logging.debug(" - %s = %s", key, val) + filterData = val.split(";") + + # at least 3 items needed (field1;pattern1;lat,lon), and in any case an uneven count of items + if len(filterData) < 3 and len(filterData) % 2 == 0: + logging.debug("Invalid argument count; skipping") + else: + # first store all regular expressions in list + filterItem = [] + i = 0 + + while i < len(filterData) - 2: + filterItem.append({"field": filterData[i], "pattern": filterData[i+1]}) + + # step to next field + i += 2 + # then transfer to filterList; include coordinates + filterList.append({"name": key, "filterItem": filterItem, "coordinates": filterData[len(filterData) - 1]}) + except: + logging.error("cannot read config file") + logging.debug("cannot read config file", exc_info=True) + return + +def findCoordinates(data): + try: + logging.debug("Find coordinates") + for i in filterList: + logging.debug("Filter: " + str(i)) + regexMatch = True + for k in i["filterItem"]: + logging.debug("Pattern : " + str(k)) + if k["field"] not in data.keys(): + logging.debug("Field " + k["field"] + " not in data structure, hence no match") + regexMatch = False + break + else: + if not re.search(k["pattern"], data.get(k["field"])): + logging.debug("No match") + regexMatch = False + break + if regexMatch: + coordinatesString = i["coordinates"] + logging.debug("Coordinates string: " + coordinatesString) + coordinatesList = coordinatesString.replace(" ", "").split(",") + if len(coordinatesList) == 2: + data["lat"] = coordinatesList[0] + data["lon"] = coordinatesList[1] + data["has_geo"] = True + logging.debug("Coordinates found!") + break + except: + logging.error("cannot read config file") + logging.debug("cannot read config file", exc_info=True)