Merge pull request #24 from Schrolli91/develop

merge little changes and bugfixes into master
This commit is contained in:
Schrolli91 2015-06-15 12:14:17 +02:00
commit b52678066d
11 changed files with 175 additions and 143 deletions

View file

@ -3,9 +3,9 @@
#
"""
BOSWatch
Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG
Python script to receive and decode German BOS information with rtl_fm and multimon-NG
Through a simple plugin system, data can easily be transferred to other applications
For more Information see the README.md
For more information see the README.md
@author: Bastian Schroll
@author: Jens Herrmann
@ -16,11 +16,11 @@ GitHUB: https://github.com/Schrolli91/BOSWatch
import logging
import logging.handlers
import argparse #for parse the args
import ConfigParser #for parse the config file
import os #for log mkdir
import time #timestamp
import subprocess
import argparse # for parse the args
import ConfigParser # for parse the config file
import os # for log mkdir
import time # for timestamp
import subprocess # for starting rtl_fm and multimon-ng
from includes import globals # Global variables
@ -61,6 +61,7 @@ def freqToHz(freq):
#
# ArgParser
# Have to be before main program
#
try:
# With -h or --help you get the Args help
@ -84,7 +85,7 @@ except:
#
# Main Programm
# Main program
#
try:
# initialization
@ -95,7 +96,7 @@ try:
globals.script_path = os.path.dirname(os.path.abspath(__file__))
#
# If necessary create Log-Path
# If necessary create log-path
#
if not os.path.exists(globals.script_path+"/log/"):
os.mkdir(globals.script_path+"/log/")
@ -105,19 +106,19 @@ try:
#
myLogger = logging.getLogger()
myLogger.setLevel(logging.DEBUG)
#set log string format
# set log string format
formatter = logging.Formatter('%(asctime)s - %(module)-15s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S')
#create a file logger
# create a file logger
fh = MyTimedRotatingFileHandler(globals.script_path+"/log/boswatch.log", "midnight", interval=1, backupCount=999)
#Starts with log level >= Debug
#will be changed with config.ini-param later
# Starts with log level >= Debug
# will be changed with config.ini-param later
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
myLogger.addHandler(fh)
#create a display logger
# create a display logger
ch = logging.StreamHandler()
#log level for display >= info
#will be changed later after parsing args
# log level for display >= info
# will be changed later after parsing args
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
myLogger.addHandler(ch)
@ -189,8 +190,20 @@ try:
logging.debug("reading config file")
globals.config = ConfigParser.ConfigParser()
globals.config.read(globals.script_path+"/config/config.ini")
for key,val in globals.config.items("BOSWatch"):
logging.debug(" - %s = %s", key, val)
# if given loglevel is debug:
if globals.config.getint("BOSWatch","loglevel") == 10:
logging.debug(" - BOSWatch:")
for key,val in globals.config.items("BOSWatch"):
logging.debug(" -- %s = %s", key, val)
logging.debug(" - FMS:")
for key,val in globals.config.items("FMS"):
logging.debug(" -- %s = %s", key, val)
logging.debug(" - ZVEI:")
for key,val in globals.config.items("ZVEI"):
logging.debug(" -- %s = %s", key, val)
logging.debug(" - POC:")
for key,val in globals.config.items("POC"):
logging.debug(" -- %s = %s", key, val)
except:
logging.exception("cannot read config file")
else:

View file

@ -2,9 +2,10 @@
# -*- coding: cp1252 -*-
"""
Handler for the Filter and Plugins at an Alarm
Handler for the filter and plugins at an alarm
@author: Bastian Schroll
@author: Jens Herrmann
@requires: none
"""
@ -13,10 +14,13 @@ import logging # Global logger
from includes import globals # Global variables
##
#
# main function for central filtering and calling the plugins
#
def processAlarm(typ,freq,data):
"""
Function to process Filters and Plugins at Alarm
Function to process filters and plugins at Alarm
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset
@ -25,29 +29,26 @@ def processAlarm(typ,freq,data):
@type data: map of data (structure see interface.txt)
@param data: Contains the parameter
@requires: active Plugins in pluginList
@requires: active plugins in pluginList
@return: nothing
@exception: Exception if Alarm processing failed
"""
try:
logging.debug("[ ALARM ]")
#Go to all Plugins in pluginList
# Go to all plugins in pluginList
for pluginName, plugin in globals.pluginList.items():
#if enabled use RegEx-Filter
# if enabled use RegEx-filter
if globals.config.getint("BOSWatch","useRegExFilter"):
from includes import filter
if filter.checkFilters(typ,data,pluginName,freq):
logging.debug("call Plugin: %s", pluginName)
plugin.run(typ,freq,data)
logging.debug("return from: %s", pluginName)
else: #RegEX Filter off - Call Plugin direct
else: # RegEX filter off - call plugin directly
logging.debug("call Plugin: %s", pluginName)
plugin.run(typ,freq,data)
logging.debug("return from: %s", pluginName)
logging.debug("[END ALARM]")
except:
logging.exception("Error in Alarm processing")

View file

@ -2,18 +2,18 @@
# -*- coding: cp1252 -*-
"""
Search for decode String and call the right decoder Funtion
Search for decode string and call the right decoder function
@author: Jens Herrmann
@requires: none
"""
import logging
import logging # Global logger
def decode(freq, decoded):
"""
Search for decode String and call the right decoder Function
Search for decode string and call the right decoder function
@type freq: string
@param freq: frequency of the SDR Stick
@ -24,23 +24,27 @@ def decode(freq, decoded):
@exception: Exception if decoder File call failed
"""
try:
#FMS Decoder Section
#check FMS: -> check CRC -> validate -> check double alarm -> log
# FMS Decoder Section
# check FMS: -> check CRC -> validate -> check double alarm -> log
if "FMS:" in decoded:
logging.debug("recieved FMS")
from includes.decoders import fms
fms.decode(freq, decoded)
#ZVEI Decoder Section
#check ZVEI: -> validate -> check double alarm -> log
if "ZVEI2:" in decoded:
# ZVEI Decoder Section
# check ZVEI: -> validate -> check double alarm -> log
elif "ZVEI2:" in decoded:
logging.debug("recieved ZVEI")
from includes.decoders import zvei
zvei.decode(freq, decoded)
#POCSAG Decoder Section
#check POCSAG -> validate -> check double alarm -> log
if "POCSAG" in decoded:
# For POCSAG we have to ignore the multimon-ng line "Enabled demodulators:"
elif "Enabled demodulators:" in decoded:
pass
# POCSAG Decoder Section
# check POCSAG -> validate -> check double alarm -> log
elif "POCSAG" in decoded:
logging.debug("recieved POCSAG")
from includes.decoders import poc
poc.decode(freq, decoded)

View file

@ -9,15 +9,15 @@ FMS Decoder
@requires: Configuration has to be set in the config.ini
"""
import logging
import time #timestamp for doublealarm
import re #Regex for validation
import logging # Global logger
import time # timestamp for doublealarm
import re # Regex for validation
from includes import globals # Global variables
##
#
# FMS Decoder Function
# FMS decoder function
# validate -> check double alarm -> log
#
def decode(freq, decoded):
@ -34,23 +34,26 @@ def decode(freq, decoded):
@return: nothing
@exception: Exception if FMS decode failed
"""
timestamp = int(time.time())#Get Timestamp
timestamp = int(time.time()) # Get Timestamp
fms_service = decoded[19] #Organisation
fms_country = decoded[36] #Bundesland
fms_location = decoded[65:67] #Ort
fms_vehicle = decoded[72:76] #Fahrzeug
fms_status = decoded[84] #Status
fms_direction = decoded[101] #Richtung
fms_directionText = decoded[103:110] #Richtung (Text)
fms_tsi = decoded[114:117] #Taktische Kruzinformation
fms_service = decoded[19] # Organisation
fms_country = decoded[36] # Bundesland
fms_location = decoded[65:67] # Ort
fms_vehicle = decoded[72:76] # Fahrzeug
fms_status = decoded[84] # Status
fms_direction = decoded[101] # Richtung
fms_directionText = decoded[103:110] # Richtung (Text)
fms_tsi = decoded[114:117] # Taktische Kruzinformation
if "CRC correct" in decoded: #check CRC is correct
fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction #build FMS id
if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id): #if FMS is valid
if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("FMS", "double_ignore_time"): #check for double alarm
fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction # build FMS id
# if FMS is valid
if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id):
# check for double alarm
if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("FMS", "double_ignore_time"):
logging.info("FMS double alarm: %s within %s second(s)", globals.fms_id_old, timestamp-globals.fms_time_old)
globals.fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new
# in case of double alarm, fms_double_ignore_time set new
globals.fms_time_old = timestamp
else:
logging.info("FMS:%s Status:%s Richtung:%s TSI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi)
data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi, "description":fms_id[0:8]}

View file

@ -10,15 +10,15 @@ POCSAG Decoder
@requires: Configuration has to be set in the config.ini
"""
import logging
import time #timestamp for doublealarm
import re #Regex for validation
import logging # Global logger
import time # timestamp for doublealarm
import re # Regex for validation
from includes import globals # Global variables
##
#
# Simple Filter
# Simple local filter
#
def isAllowed(poc_id):
"""
@ -36,27 +36,27 @@ def isAllowed(poc_id):
# If RIC is the right one return True, else False
if globals.config.get("POC", "allow_ric"):
if poc_id in globals.config.get("POC", "allow_ric"):
logging.debug("RIC %s is allowed", poc_id)
logging.info("RIC %s is allowed", poc_id)
return True
else:
logging.debug("RIC %s is not in the allowed list", poc_id)
logging.info("RIC %s is not in the allowed list", poc_id)
return False
# 2.) If denied RIC, return False
elif poc_id in globals.config.get("POC", "deny_ric"):
logging.debug("RIC %s is denied by config.ini", poc_id)
logging.info("RIC %s is denied by config.ini", poc_id)
return False
# 3.) Check Range, return False if outside def. range
elif int(poc_id) < globals.config.getint("POC", "filter_range_start"):
logging.debug("RIC %s out of filter range (start)", poc_id)
logging.info("RIC %s out of filter range (start)", poc_id)
return False
elif int(poc_id) > globals.config.getint("POC", "filter_range_end"):
logging.debug("RIC %s out of filter range (end)", poc_id)
logging.info("RIC %s out of filter range (end)", poc_id)
return False
return True
##
#
# POCSAG Decoder Function
# POCSAG decoder function
# validate -> check double alarm -> log
#
def decode(freq, decoded):
@ -93,6 +93,7 @@ def decode(freq, decoded):
if bitrate is 0:
logging.warning("POCSAG Bitrate not found")
logging.debug(" - (%s)", decoded)
else:
logging.debug("POCSAG Bitrate: %s", bitrate)
@ -103,10 +104,10 @@ def decode(freq, decoded):
if re.search("[0-9]{7}", poc_id): #if POC is valid
if isAllowed(poc_id):
#check for double alarm
# check for double alarm
if poc_id == globals.poc_id_old and timestamp < globals.poc_time_old + globals.config.getint("POC", "double_ignore_time"):
logging.info("POCSAG%s double alarm: %s within %s second(s)", bitrate, globals.poc_id_old, timestamp-globals.poc_time_old)
#in case of double alarm, poc_double_ignore_time set new
# in case of double alarm, poc_double_ignore_time set new
globals.poc_time_old = timestamp
else:
logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text)
@ -124,6 +125,6 @@ def decode(freq, decoded):
globals.poc_id_old = poc_id #save last id
globals.poc_time_old = timestamp #save last time
else:
logging.info("POCSAG%s: %s is not allowed", bitrate, poc_id)
logging.debug("POCSAG%s: %s is not allowed", bitrate, poc_id)
else:
logging.warning("No valid POCSAG%s RIC: %s", bitrate, poc_id)

View file

@ -9,56 +9,16 @@ ZVEI Decoder
@requires: Configuration has to be set in the config.ini
"""
import logging
import time #timestamp for doublealarm
import re #Regex for validation
import logging # Global logger
import time # timestamp for doublealarm
import re # Regex for validation
from includes import globals # Global variables
##
#
# ZVEI Decoder Function
# validate -> check double alarm -> log
# Local function to remove the 'F'
#
def decode(freq, decoded):
"""
Export ZVEI Information from Multimon-NG RAW String and call alarmHandler.processAlarm()
@type freq: string
@param freq: frequency of the SDR Stick
@type decoded: string
@param decoded: RAW Information from Multimon-NG
@requires: Configuration has to be set in the config.ini
@return: nothing
@exception: Exception if ZVEI decode failed
"""
timestamp = int(time.time())#Get Timestamp
zvei_id = decoded[7:12] #ZVEI Code
zvei_id = removeF(zvei_id) #resolve F
if re.search("[0-9]{5}", zvei_id): #if ZVEI is valid
if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"): #check for double alarm
logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old)
globals.zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new
else:
logging.info("5-Ton: %s", zvei_id)
data = {"zvei":zvei_id, "description":zvei_id}
# If enabled, look up description
if globals.config.getint("ZVEI", "idDescribed"):
from includes import descriptionList
data["description"] = descriptionList.getDescription("ZVEI", zvei_id)
# processing the alarm
from includes import alarmHandler
alarmHandler.processAlarm("ZVEI",freq,data)
globals.zvei_id_old = zvei_id #save last id
globals.zvei_time_old = timestamp #save last time
else:
logging.warning("No valid ZVEI: %s", zvei_id)
def removeF(zvei):
"""
Resolve the F from the repeat Tone
@ -75,4 +35,49 @@ def removeF(zvei):
if zvei[i] == "F":
zvei = zvei.replace("F",zvei[i-1],1)
logging.debug("resolve F: %s -> %s", zvei_old, zvei)
return zvei
return zvei
##
#
# ZVEI decoder function
# validate -> check double alarm -> log
#
def decode(freq, decoded):
"""
Export ZVEI Information from Multimon-NG RAW String and call alarmHandler.processAlarm()
@type freq: string
@param freq: frequency of the SDR Stick
@type decoded: string
@param decoded: RAW Information from Multimon-NG
@requires: Configuration has to be set in the config.ini
@return: nothing
@exception: Exception if ZVEI decode failed
"""
timestamp = int(time.time()) # Get Timestamp
zvei_id = decoded[7:12] # ZVEI Code
zvei_id = removeF(zvei_id) # resolve F
if re.search("[0-9]{5}", zvei_id): # if ZVEI is valid
# check for double alarm
if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"):
logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old)
# in case of double alarm, zvei_double_ignore_time set new
globals.zvei_time_old = timestamp
else:
logging.info("5-Ton: %s", zvei_id)
data = {"zvei":zvei_id, "description":zvei_id}
# If enabled, look up description
if globals.config.getint("ZVEI", "idDescribed"):
from includes import descriptionList
data["description"] = descriptionList.getDescription("ZVEI", zvei_id)
# processing the alarm
from includes import alarmHandler
alarmHandler.processAlarm("ZVEI",freq,data)
globals.zvei_id_old = zvei_id # save last id
globals.zvei_time_old = timestamp # save last time
else:
logging.warning("No valid ZVEI: %s", zvei_id)

View file

@ -11,7 +11,7 @@ Function to expand the dataset with a description.
import logging # Global logger
import csv
import csv # for loading the description files
from includes import globals # Global variables
@ -99,4 +99,6 @@ def getDescription(typ, id):
# will be thrown when there is no description for the id
# -> nothing to do...
pass
except:
logging.debug("Error during look up description lists")
return resultStr

View file

@ -2,7 +2,7 @@
# -*- coding: cp1252 -*-
"""
Functions for the RegEX Filter
Functions for the RegEX filter
@author: Bastian Schroll
@ -18,20 +18,20 @@ from includes import globals # Global variables
def loadFilters():
"""
load all Filters from the config.ini into globals.filterList
load all filters from the config.ini into globals.filterList
@requires: Configuration has to be set in the config.ini
@return: nothing
@exception: Exception if Filter loading failed
@exception: Exception if filter loading failed
"""
try:
logging.debug("loading filters")
#For each entry in config.ini [Filters] Section
# For each entry in config.ini [Filters] section
for key,val in globals.config.items("Filters"):
logging.debug(" - %s = %s", key, val)
filter = val.split(";")
#insert splitet Data into globals.filterList
# insert splitet data into globals.filterList
globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": freqToHz(filter[3]), "regex": filter[4]})
except:
logging.exception("cannot read config file")
@ -39,34 +39,34 @@ def loadFilters():
def checkFilters(typ,data,plugin,freq):
"""
Check the Typ/Plugin combination with the RegEX Filter
If no Filter for the combination is found, Function returns True.
Check the Typ/Plugin combination with the RegEX filter
If no filter for the combination is found, function returns True.
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset
@type data: map of data (structure see interface.txt)
@param data: Contains the parameter
@type plugin: string
@param plugin: Name of the Plugin to checked
@param plugin: Name of the plugin to checked
@type freq: string
@param freq: frequency of the SDR Stick
@requires: all Filters in the filterList
@requires: all filters in the filterList
@return: nothing
@exception: Exception if Filter check failed
@exception: Exception if filter check failed
"""
try:
logging.debug("search Filter for %s to %s at %s Hz", typ, plugin, freq)
foundFilter = False
#go to all Filter in globals.filterList
# go to all filter in globals.filterList
for i in globals.filterList:
#if Typ/Plugin/Freq combination is found
# if typ/plugin/freq combination is found
if i["typ"] == typ and (i["plugin"] == plugin or i['plugin'] == "*") and (i["freq"] == freq or i['freq'] == "*"):
foundFilter = True
logging.debug("found Filter: %s = %s", i["name"], i["regex"])
#Check the RegEX
# Check the RegEX
if re.search(i["regex"], data[i["dataField"]]):
logging.debug("Filter passed: %s", i["name"])
return True

View file

@ -8,11 +8,11 @@ Global variables
@author: Bastian Schroll
"""
#Global variables
# Global variables
config = 0
script_path = ""
#double alarm
# double alarm
fms_id_old = 0
fms_time_old = 0
@ -22,13 +22,13 @@ zvei_time_old = 0
poc_id_old = 0
poc_time_old = 0
#pluginLoader
# pluginLoader
pluginList = {}
#filter
# filter
filterList = []
#idDescribing
# idDescribing
fmsDescribtionList = {}
zveiDescribtionList = {}
ricDescribtionList = {}

View file

@ -13,6 +13,7 @@ import logging # Global logger
import imp
import os
from ConfigParser import NoOptionError # we need this exception
from includes import globals # Global variables
def loadPlugins():
@ -24,11 +25,11 @@ def loadPlugins():
"""
try:
logging.debug("loading plugins")
#go to all Plugins from getPlugins()
# go to all Plugins from getPlugins()
for i in getPlugins():
#call for each Plugin the loadPlugin() Methode
# call for each Plugin the loadPlugin() Methode
plugin = loadPlugin(i)
#Add it to globals.pluginList
# Add it to globals.pluginList
globals.pluginList[i["name"]] = plugin
except:
logging.exception("cannot load Plugins")
@ -45,11 +46,11 @@ def getPlugins():
logging.debug("Search in Plugin Folder")
PluginFolder = globals.script_path+"/plugins"
plugins = []
#Go to all Folders in the Plugin-Dir
# Go to all Folders in the Plugin-Dir
for i in os.listdir(PluginFolder):
location = os.path.join(PluginFolder, i)
#Skip if Path.isdir() or no File DIR_NAME.py is found
# Skip if Path.isdir() or no File DIR_NAME.py is found
if not os.path.isdir(location) or not i + ".py" in os.listdir(location):
continue
@ -61,8 +62,10 @@ def getPlugins():
logging.debug("Plugin [ENABLED ] %s", i)
else:
logging.debug("Plugin [DISABLED] %s ", i)
except: #no entry for plugin found in config-file
# no entry for plugin found in config-file
except NoOptionError:
logging.warning("Plugin [NO CONF ] %s", i)
pass
except:
logging.exception("Error during Plugin search")

View file

@ -2,7 +2,7 @@
# -*- coding: cp1252 -*-
"""
Shows the Header in Shell if quiet Mode is not active
Shows the header in shell if quiet mode is not active
@author: Bastian Schroll
@author: Jens Herrmann
@ -12,13 +12,13 @@ Shows the Header in Shell if quiet Mode is not active
def printHeader(args):
"""
Prints the Header to the Shell
Prints the header to the shell
@type args: Array
@param args: All given Arguments from argsparser
@param args: All given arguments from argsparser
@return: nothing
@exception: Exception if display of the Shell Header failed
@exception: Exception if display of the shell header failed
"""
try:
print " ____ ____ ______ __ __ __ "