2015-04-03 15:55:10 +02:00
#!/usr/bin/python
# -*- coding: cp1252 -*-
##### Info #####
# BOSWatch
2015-04-04 19:21:42 +02:00
# Autor: Bastian Schroll
2015-04-08 08:24:02 +02:00
# Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG
2015-04-03 16:35:34 +02:00
# For more Information see the README.md
2015-04-03 15:55:10 +02:00
##### Info #####
2015-05-20 13:29:16 +02:00
import globals # Global variables
import pluginloader
import logging
2015-04-03 15:55:10 +02:00
import argparse #for parse the args
import ConfigParser #for parse the config file
2015-04-07 21:18:37 +02:00
import re #Regex for validation
2015-05-20 13:29:16 +02:00
import os #for script path
import time #timestamp for doublealarm
2015-04-03 15:55:10 +02:00
2015-05-20 13:29:16 +02:00
#create new logger
logger = logging . getLogger ( )
logger . setLevel ( logging . DEBUG )
#set log string format
2015-05-20 15:20:40 +02:00
formatter = logging . Formatter ( ' %(asctime)s - %(module)-12s [ %(levelname)-8s ] %(message)s ' , ' %d . % m. % Y % H: % M: % S ' )
2015-05-20 13:29:16 +02:00
#create a file logger
fh = logging . FileHandler ( ' log/boswatch.log ' , ' w ' )
fh . setLevel ( logging . DEBUG ) #log level >= Debug
fh . setFormatter ( formatter )
logger . addHandler ( fh )
#create a display logger
ch = logging . StreamHandler ( )
ch . setLevel ( logging . INFO ) #log level >= info
ch . setFormatter ( formatter )
logger . addHandler ( ch )
2015-04-08 21:21:50 +02:00
2015-04-03 15:55:10 +02:00
2015-05-20 13:29:16 +02:00
def throwAlarm ( typ , data ) :
for i in pluginloader . getPlugins ( ) :
plugin = pluginloader . loadPlugin ( i )
logging . debug ( " call Plugin: %s " , i [ " name " ] )
plugin . run ( typ , " 0 " , data )
2015-04-19 19:21:35 +02:00
# Programm
try :
2015-04-07 21:02:56 +02:00
2015-05-20 13:29:16 +02:00
#first Clear the Logfiles for logging
2015-05-15 20:54:42 +02:00
try :
script_path = os . path . dirname ( os . path . abspath ( __file__ ) )
2015-05-15 21:00:14 +02:00
if not os . path . exists ( script_path + " /log/ " ) :
os . mkdir ( script_path + " /log/ " )
2015-05-20 13:29:16 +02:00
bos_log = open ( script_path + " /log/boswatch.log " , " w " )
rtl_log = open ( script_path + " /log/rtl_fm.log " , " w " )
mon_log = open ( script_path + " /log/multimon.log " , " w " )
# bos_log.write("##### "+curtime()+" #####\n\n")
# rtl_log.write("##### "+curtime()+" #####\n\n")
# mon_log.write("##### "+curtime()+" #####\n\n")
2015-05-15 20:54:42 +02:00
bos_log . close ( )
rtl_log . close ( )
mon_log . close ( )
2015-05-20 13:29:16 +02:00
logging . debug ( " BOSWatch has started " )
logging . debug ( " Logfiles cleared " )
2015-05-15 20:54:42 +02:00
except :
2015-05-20 13:29:16 +02:00
logging . exception ( " cannot clear Logfiles " )
2015-04-03 15:55:10 +02:00
2015-05-15 20:54:42 +02:00
try :
2015-05-20 13:29:16 +02:00
logging . debug ( " parse args " )
2015-05-15 20:54:42 +02:00
#With -h or --help you get the Args help
#ArgsParser
parser = argparse . ArgumentParser ( prog = " boswatch.py " , description = " BOSWatch is a Python Script to Recive and Decode German BOS Information with rtl_fm and multimon-NG " , epilog = " More Options you can find in the extern config.ini File in this Folder " )
#parser.add_argument("-c", "--channel", help="BOS Channel you want to listen")
parser . add_argument ( " -f " , " --freq " , help = " Frequency you want to listen " , required = True )
parser . add_argument ( " -d " , " --device " , help = " Device you want to use (Check with rtl_test) " , type = int , default = 0 )
parser . add_argument ( " -e " , " --error " , help = " Frequency-Error of your Device in PPM " , type = int , default = 0 )
parser . add_argument ( " -a " , " --demod " , help = " Demodulation Functions " , choices = [ ' FMS ' , ' ZVEI ' , ' POC512 ' , ' POC1200 ' , ' POC2400 ' ] , required = True , nargs = " + " )
parser . add_argument ( " -s " , " --squelch " , help = " Level of Squelch " , type = int , default = 0 )
parser . add_argument ( " -v " , " --verbose " , help = " Shows more Information " , action = " store_true " )
parser . add_argument ( " -q " , " --quiet " , help = " Shows no Information. Only Logfiles " , action = " store_true " )
args = parser . parse_args ( )
except :
2015-05-20 13:29:16 +02:00
logging . exception ( " cannot parse args " )
2015-04-03 15:55:10 +02:00
2015-05-15 20:54:42 +02:00
#Read Data from Args, Put it into working Variables
freq = args . freq
device = args . device
error = args . error
2015-05-20 13:29:16 +02:00
squelch = args . squelch
logging . debug ( " - Frequency: %s " , freq )
logging . debug ( " - Device: %s " , device )
logging . debug ( " - PPM Error: %s " , error )
logging . debug ( " - Squelch: %s " , squelch )
2015-05-15 20:54:42 +02:00
demodulation = " "
if " FMS " in args . demod :
demodulation + = " -a FMSFSK "
2015-05-20 13:29:16 +02:00
logging . debug ( " - Demod: FMS " )
2015-05-15 20:54:42 +02:00
if " ZVEI " in args . demod :
demodulation + = " -a ZVEI2 "
2015-05-20 13:29:16 +02:00
logging . debug ( " - Demod: ZVEI " )
2015-05-15 20:54:42 +02:00
if " POC512 " in args . demod :
demodulation + = " -a POCSAG512 "
2015-05-20 13:29:16 +02:00
logging . debug ( " - Demod: POC512 " )
2015-05-15 20:54:42 +02:00
if " POC1200 " in args . demod :
2015-05-20 13:29:16 +02:00
demodulation + = " -a POCSAG1200 "
logging . debug ( " - Demod: P " )
2015-05-15 20:54:42 +02:00
if " POC2400 " in args . demod :
demodulation + = " -a POCSAG2400 "
2015-05-20 13:29:16 +02:00
logging . debug ( " - Demod: POC2400 " )
logging . debug ( " - Verbose Mode: %s " , args . verbose )
logging . debug ( " - Quiet Mode: %s " , args . quiet )
2015-04-03 15:55:10 +02:00
2015-05-20 13:29:16 +02:00
if args . verbose :
ch . setLevel ( logging . DEBUG )
if args . quiet :
ch . setLevel ( logging . CRITICAL )
2015-05-15 20:54:42 +02:00
if not args . quiet : #only if not quiet mode
print " ____ ____ ______ __ __ __ "
print " / __ )/ __ \ / ___/ | / /___ _/ /______/ /_ b "
print " / __ / / / / \ __ \ | | /| / / __ `/ __/ ___/ __ \ e "
print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t "
print " /_____/ \ ____//____/ |__/|__/ \ __,_/ \ __/ \ ___/_/ /_/ a "
print " German BOS Information Script "
print " by Bastian Schroll "
print " "
print " Frequency: " + freq
print " Device-ID: " + str ( device )
print " Error in PPM: " + str ( error )
print " Active Demods: " + str ( len ( args . demod ) )
if " FMS " in args . demod :
print " - FMS "
if " ZVEI " in args . demod :
print " - ZVEI "
if " POC512 " in args . demod :
print " - POC512 "
if " POC1200 " in args . demod :
print " - POC1200 "
if " POC2400 " in args . demod :
print " - POC2400 "
print " Squelch: " + str ( squelch )
if args . verbose :
print " Verbose Mode! "
2015-05-20 13:29:16 +02:00
print " "
2015-05-15 20:54:42 +02:00
#variables pre-load
2015-05-20 13:29:16 +02:00
logging . debug ( " pre-load variables " )
2015-05-15 20:54:42 +02:00
fms_id = 0
fms_id_old = 0
fms_time_old = 0
zvei_id = 0
zvei_id_old = 0
zvei_time_old = 0
poc_id = 0
poc_id_old = 0
poc_time_old = 0
2015-04-07 21:36:15 +02:00
2015-05-20 13:29:16 +02:00
2015-05-15 20:54:42 +02:00
#ConfigParser
2015-05-20 13:29:16 +02:00
logging . debug ( " reading config file " )
2015-05-15 20:54:42 +02:00
try :
2015-05-20 13:29:16 +02:00
globals . config = ConfigParser . ConfigParser ( )
globals . config . read ( script_path + " /config/config.ini " )
2015-05-20 14:26:17 +02:00
for key , val in globals . config . items ( " BOSWatch " ) :
2015-05-20 13:29:16 +02:00
logging . debug ( " - %s = %s " , key , val )
2015-05-15 20:54:42 +02:00
except :
2015-05-20 13:29:16 +02:00
logging . exception ( " cannot read config file " )
2015-05-15 20:54:42 +02:00
2015-05-20 13:29:16 +02:00
logging . debug ( " starting rtl_fm " )
# try:
# rtl_fm = subprocess.Popen("rtl_fm -d "+str(device)+" -f "+str(freq)+" -M fm -s 22050 -p "+str(error)+" -E DC -F 0 -l "+str(squelch)+" -g 100",
# #stdin=rtl_fm.stdout,
# stdout=subprocess.PIPE,
# stderr=open(script_path+"/log/rtl_fm.log","a"),
# shell=True)
# except:
# logging.exception("cannot start rtl_fm")
#
logging . debug ( " starting multimon-ng " )
# try:
# multimon_ng = subprocess.Popen("multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - ",
# stdin=rtl_fm.stdout,
# stdout=subprocess.PIPE,
# stderr=open(script_path+"/log/multimon.log","a"),
# shell=True)
# except:
# logging.exception("cannot start multimon-ng")
2015-05-15 20:54:42 +02:00
2015-05-20 13:29:16 +02:00
logging . debug ( " start decoding " )
2015-05-15 20:54:42 +02:00
while True :
#RAW Data from Multimon-NG
#ZVEI2: 25832
#FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n'
2015-05-20 13:29:16 +02:00
#decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout
#only for develop
decoded = " ZVEI2: 25832 "
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=III(mit NA,ohneSIGNAL)) CRC correct\n'"
time . sleep ( 1 )
2015-05-15 20:54:42 +02:00
if True : #if input data avalable
timestamp = int ( time . time ( ) ) #Get Timestamp
#FMS Decoder Section
2015-05-20 13:29:16 +02:00
#check FMS: -> check CRC -> validate -> check double alarm -> log
2015-05-15 20:54:42 +02:00
if " FMS: " in decoded :
2015-05-20 13:29:16 +02:00
logging . debug ( " recieved FMS " )
2015-05-15 20:54:42 +02:00
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_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
2015-05-20 14:26:17 +02:00
if fms_id == fms_id_old and timestamp < fms_time_old + globals . config . getint ( " BOSWatch " , " fms_double_ignore_time " ) : #check for double alarm
2015-05-20 13:29:16 +02:00
logging . warning ( " FMS double alarm: %s within %s second(s) " , fms_id_old , timestamp - fms_time_old )
2015-05-15 20:54:42 +02:00
fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new
else :
2015-05-20 13:29:16 +02:00
logging . info ( " FMS: %s Status: %s Richtung: %s TKI: %s " , fms_id [ 0 : 8 ] , fms_status , fms_direction , fms_tsi )
data = { " fms " : fms_id [ 0 : 8 ] , " status " : fms_status , " direction " : fms_direction , " tki " : fms_tsi }
throwAlarm ( " FMS " , data )
2015-05-15 20:54:42 +02:00
2015-05-20 13:29:16 +02:00
fms_id_old = fms_id #save last id
fms_time_old = timestamp #save last time
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " No valid FMS: %s " , fms_id )
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " FMS CRC incorrect " )
2015-05-15 20:54:42 +02:00
2015-05-20 13:29:16 +02:00
2015-05-15 20:54:42 +02:00
#ZVEI Decoder Section
2015-05-20 13:29:16 +02:00
#check ZVEI: -> validate -> check double alarm -> log
2015-05-15 20:54:42 +02:00
if " ZVEI2: " in decoded :
2015-05-20 13:29:16 +02:00
logging . debug ( " recieved ZVEI " )
2015-05-15 20:54:42 +02:00
zvei_id = decoded [ 7 : 12 ] #ZVEI Code
if re . search ( " [0-9F] {5} " , zvei_id ) : #if ZVEI is valid
2015-05-20 14:26:17 +02:00
if zvei_id == zvei_id_old and timestamp < zvei_time_old + globals . config . getint ( " BOSWatch " , " zvei_double_ignore_time " ) : #check for double alarm
2015-05-20 13:29:16 +02:00
logging . warning ( " ZVEI double alarm: %s within %s second(s) " , zvei_id_old , timestamp - zvei_time_old )
2015-05-15 20:54:42 +02:00
zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new
else :
2015-05-20 13:29:16 +02:00
logging . info ( " 5-Ton: %s " , zvei_id )
data = { " zvei " : zvei_id }
throwAlarm ( " ZVEI " , data )
2015-05-15 20:54:42 +02:00
zvei_id_old = zvei_id #save last id
zvei_time_old = timestamp #save last time
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " No valid ZVEI: %s " , zvei_id )
2015-05-15 20:54:42 +02:00
2015-05-16 18:15:44 +02:00
2015-05-15 20:54:42 +02:00
#POCSAG512 Decoder Section
2015-05-20 13:29:16 +02:00
#check POCSAG512: -> validate -> check double alarm -> log
2015-05-15 20:54:42 +02:00
#POCSAG512: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh
if " POCSAG512: " in decoded :
2015-05-20 13:29:16 +02:00
logging . debug ( " recieved POCSAG512 " )
2015-05-15 20:54:42 +02:00
poc_id = decoded [ 20 : 27 ] #POC Code
poc_sub = decoded [ 39 ] . replace ( " 3 " , " 4 " ) . replace ( " 2 " , " 3 " ) . replace ( " 1 " , " 2 " ) . replace ( " 0 " , " 1 " )
if " Alpha: " in decoded : #check if there is a text message
poc_text = decoded . split ( ' Alpha: ' ) [ 1 ] . strip ( ) . rstrip ( ' <EOT> ' ) . strip ( )
else :
poc_text = " "
if re . search ( " [0-9] {7} " , poc_id ) : #if POC is valid
2015-05-20 14:26:17 +02:00
if poc_id > = globals . config . getint ( " BOSWatch " , " poc_filter_range_start " ) :
if poc_id < = globals . config . getint ( " BOSWatch " , " poc_filter_range_end " ) :
if poc_id == poc_id_old and timestamp < poc_time_old + globals . config . getint ( " BOSWatch " , " poc_double_ignore_time " ) : #check for double alarm
2015-05-20 13:29:16 +02:00
logging . warning ( " POC512 double alarm: %s within %s second(s) " , poc_id_old , timestamp - poc_time_old )
2015-05-15 20:54:42 +02:00
poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new
else :
2015-05-20 13:29:16 +02:00
logging . info ( " POCSAG512: %s %s %s " , poc_id , poc_sub , poc_text )
data = { " ric " : poc_id , " function " : poc_sub , " msg " : poc_text }
throwAlarm ( " POC " , data )
2015-05-15 20:54:42 +02:00
poc_id_old = poc_id #save last id
2015-05-20 13:29:16 +02:00
poc_time_old = timestamp #save last time
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " POCSAG512: %s out of filter range " , poc_id )
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " POCSAG512: %s out of filter range " , poc_id )
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " No valid POCSAG512: %s " , poc_id )
2015-05-16 18:15:44 +02:00
2015-05-15 20:54:42 +02:00
#POCSAG1200 Decoder Section
2015-05-20 13:29:16 +02:00
#check POCSAG1200: -> validate -> check double alarm -> log
2015-05-15 20:54:42 +02:00
#POCSAG1200: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh
if " POCSAG1200: " in decoded :
2015-05-20 13:29:16 +02:00
logging . debug ( " recieved POCSAG1200 " )
2015-05-15 20:54:42 +02:00
poc_id = decoded [ 21 : 28 ] #POC Code
poc_sub = decoded [ 40 ] . replace ( " 3 " , " 4 " ) . replace ( " 2 " , " 3 " ) . replace ( " 1 " , " 2 " ) . replace ( " 0 " , " 1 " )
if " Alpha: " in decoded : #check if there is a text message
poc_text = decoded . split ( ' Alpha: ' ) [ 1 ] . strip ( ) . rstrip ( ' <EOT> ' ) . strip ( )
else :
poc_text = " "
if re . search ( " [0-9] {7} " , poc_id ) : #if POC is valid
2015-05-20 14:26:17 +02:00
if poc_id > = globals . config . getint ( " BOSWatch " , " poc_filter_range_start " ) :
if poc_id < = globals . config . getint ( " BOSWatch " , " poc_filter_range_end " ) :
if poc_id == poc_id_old and timestamp < poc_time_old + globals . config . getint ( " BOSWatch " , " poc_double_ignore_time " ) : #check for double alarm
2015-05-20 13:29:16 +02:00
logging . warning ( " POC1200 double alarm: %s within %s second(s) " , poc_id_old , timestamp - poc_time_old )
2015-05-15 20:54:42 +02:00
poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new
else :
2015-05-20 13:29:16 +02:00
logging . info ( " POCSAG1200: %s %s %s " , poc_id , poc_sub , poc_text )
data = { " ric " : poc_id , " function " : poc_sub , " msg " : poc_text }
throwAlarm ( " POC " , data )
2015-05-15 20:54:42 +02:00
poc_id_old = poc_id #save last id
2015-05-20 13:29:16 +02:00
poc_time_old = timestamp #save last time
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " POCSAG1200: %s out of filter range " , poc_id )
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " POCSAG1200: %s out of filter range " , poc_id )
2015-05-15 20:54:42 +02:00
else :
2015-05-20 13:29:16 +02:00
logging . warning ( " No valid POCSAG1200: %s " , poc_id )
2015-04-03 15:55:10 +02:00
except KeyboardInterrupt :
2015-05-20 13:29:16 +02:00
logging . warning ( " Keyboard Interrupt " )
2015-04-04 21:47:09 +02:00
except :
2015-05-20 13:29:16 +02:00
logging . exception ( " unknown error " )
2015-04-04 23:32:51 +02:00
finally :
2015-05-15 20:54:42 +02:00
try :
2015-05-20 13:29:16 +02:00
# rtl_fm.terminate()
logging . debug ( " rtl_fm terminated " )
# multimon_ng.terminate()
logging . debug ( " multimon-ng terminated " )
logging . debug ( " exiting BOSWatch " )
2015-05-15 20:54:42 +02:00
except :
2015-05-20 13:29:16 +02:00
logging . exception ( " failed in clean-up routine " )
2015-05-15 20:54:42 +02:00
finally :
exit ( 0 )