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 #####
import time
#import sys
import subprocess
2015-04-07 21:02:56 +02:00
import os #for absolute path: os.path.dirname(os.path.abspath(__file__))
2015-04-03 15:55:10 +02:00
import mysql
import mysql . connector
2015-04-07 21:18:37 +02:00
import httplib #for the HTTP request
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-04-03 15:55:10 +02:00
2015-04-08 21:21:50 +02:00
2015-04-05 00:15:32 +02:00
# Functions
2015-04-03 15:55:10 +02:00
def curtime ( format = " % Y- % m- %d % H: % M: % S " ) :
2015-05-15 20:54:42 +02:00
return time . strftime ( format )
2015-04-03 15:55:10 +02:00
2015-04-04 23:32:51 +02:00
#Loglevel
#[LOG] for the logfile
#[INFO] normal display
#[ERROR] errors
def log ( msg , level = " log " ) :
2015-05-15 20:54:42 +02:00
log_entry = curtime ( " % H: % M: % S " ) + " [ " + level . upper ( ) + " ] " + msg
2015-04-05 00:15:32 +02:00
2015-05-15 20:54:42 +02:00
if not level == " log " and not args . quiet or args . verbose :
print log_entry
bos_log = open ( script_path + " /log/log_bos.txt " , " a " )
bos_log . write ( log_entry + " \n " )
bos_log . close ( )
2015-04-03 15:55:10 +02:00
2015-04-19 19:21:35 +02:00
# Programm
try :
2015-04-07 21:02:56 +02:00
2015-05-15 20:54:42 +02:00
#Clear the Logfiles
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-15 20:54:42 +02:00
bos_log = open ( script_path + " /log/log_bos.txt " , " w " )
rtl_log = open ( script_path + " /log/log_rtl.txt " , " w " )
mon_log = open ( script_path + " /log/log_mon.txt " , " w " )
bos_log . write ( " ##### " + curtime ( ) + " ##### \n \n " )
rtl_log . write ( " ##### " + curtime ( ) + " ##### \n \n " )
mon_log . write ( " ##### " + curtime ( ) + " ##### \n \n " )
bos_log . close ( )
rtl_log . close ( )
mon_log . close ( )
except :
log ( " cannot clear logfiles " , " error " )
2015-04-03 15:55:10 +02:00
2015-05-15 20:54:42 +02:00
try :
#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 :
log ( " cannot parse Args " , " error " )
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
demodulation = " "
if " FMS " in args . demod :
demodulation + = " -a FMSFSK "
if " ZVEI " in args . demod :
demodulation + = " -a ZVEI2 "
if " POC512 " in args . demod :
demodulation + = " -a POCSAG512 "
if " POC1200 " in args . demod :
demodulation + = " -a POCSAG1200 "
if " POC2400 " in args . demod :
demodulation + = " -a POCSAG2400 "
squelch = args . squelch
2015-04-03 15:55:10 +02:00
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! "
print " "
#variables pre-load
log ( " pre-load variables " )
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-15 20:54:42 +02:00
#ConfigParser
log ( " reading config file " )
try :
config = ConfigParser . ConfigParser ( )
config . read ( script_path + " /config/config.ini " )
fms_double_ignore_time = int ( config . get ( " FMS " , " double_ignore_time " ) )
zvei_double_ignore_time = int ( config . get ( " ZVEI " , " double_ignore_time " ) )
poc_double_ignore_time = int ( config . get ( " POC " , " double_ignore_time " ) )
poc_filter_range_start = int ( config . get ( " POC " , " filter_range_start " ) )
poc_filter_range_end = int ( config . get ( " POC " , " filter_range_end " ) )
#MySQL config
useMySQL = int ( config . get ( " Module " , " useMySQL " ) ) #use MySQL support?
if useMySQL : #only if MySQL is active
dbserver = config . get ( " MySQL " , " dbserver " )
dbuser = config . get ( " MySQL " , " dbuser " )
dbpassword = config . get ( " MySQL " , " dbpassword " )
database = config . get ( " MySQL " , " database " )
#MySQL tables
tableFMS = config . get ( " MySQL " , " tableFMS " )
tableZVEI = config . get ( " MySQL " , " tableZVEI " )
tablePOC = config . get ( " MySQL " , " tablePOC " )
#HTTPrequest config
useHTTPrequest = int ( config . get ( " Module " , " useHTTPrequest " ) ) #use HTTPrequest support?
if useHTTPrequest : #only if HTTPrequest is active
2015-05-16 18:15:44 +02:00
url_fms = config . get ( " HTTPrequest " , " url_fms " )
url_zvei = config . get ( " HTTPrequest " , " url_zvei " )
url_poc = config . get ( " HTTPrequest " , " url_poc " )
2015-05-15 20:54:42 +02:00
except :
log ( " cannot read config file " , " error " )
#in case of reading error, set standard values
log ( " set to standard configuration " )
fms_double_ignore_time = 5
zvei_double_ignore_time = 5
poc_double_ignore_time = 10
poc_filter_range_start = 0000000
poc_filter_range_end = 9999999
#no config - no modules ;-)
useMySQL = 0
useHTTPrequest = 0
2015-04-07 21:20:48 +02:00
2015-04-27 15:11:09 +02:00
2015-05-15 20:54:42 +02:00
if useMySQL : #only if MySQL is active
log ( " testing MySQL connection " )
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
log ( " connection test successful " )
except :
log ( " connection test failed - MySQL support deactivated " , " error " )
useMySQL = 0
finally :
connection . close ( ) #Close connection in every case
log ( " 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 ,
2015-05-15 21:01:35 +02:00
stderr = open ( script_path + " /log/log_rtl.txt " , " a " ) ,
2015-05-15 20:54:42 +02:00
shell = True )
except :
log ( " cannot start rtl_fm " , " error " )
#multimon_ng = subprocess.Popen("aplay -r 22050 -f S16_LE -t raw",
log ( " 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 ,
2015-05-15 21:01:35 +02:00
stderr = open ( script_path + " /log/log_mon.txt " , " a " ) ,
2015-05-15 20:54:42 +02:00
shell = True )
except :
log ( " cannot start multimon-ng " , " error " )
log ( " start decoding " )
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'
decoded = str ( multimon_ng . stdout . readline ( ) ) #Get line data from multimon stdout
if True : #if input data avalable
timestamp = int ( time . time ( ) ) #Get Timestamp
#if args.verbose: print "RAW: "+decoded #for verbose mode, print Raw input data
#FMS Decoder Section
#check FMS: -> check CRC -> validate -> check double alarm -> log -> (MySQL)
if " FMS: " in decoded :
log ( " recived FMS " )
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
if fms_id == fms_id_old and timestamp < fms_time_old + fms_double_ignore_time : #check for double alarm
log ( " FMS double alarm: " + fms_id_old )
fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new
else :
log ( " FMS: " + fms_id [ 0 : 8 ] + " Status: " + fms_status + " Richtung: " + fms_direction + " TKI: " + fms_tsi , " info " )
fms_id_old = fms_id #save last id
fms_time_old = timestamp #save last time
if useMySQL : #only if MySQL is active
log ( " FMS to MySQL " )
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tableFMS + " (time,fms,status,direction,tsi) VALUES ( %s , %s , %s , %s , %s ) " , ( curtime ( ) , fms_id [ 0 : 8 ] , fms_status , fms_direction , fms_tsi ) )
cursor . close ( )
connection . commit ( )
except :
log ( " FMS to MySQL failed " , " error " )
finally :
connection . close ( ) #Close connection in every case
if useHTTPrequest : #only if HTTPrequest is active
log ( " FMS to HTTP " )
try :
2015-05-16 18:15:44 +02:00
httprequest = httplib . HTTPConnection ( url_fms )
2015-05-15 20:54:42 +02:00
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
if str ( httpresponse . status ) == " 200 " : #Check HTTP Response an print a Log or Error
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) )
else :
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) , " error " )
except :
log ( " FMS to HTTP failed " , " error " )
else :
log ( " No valid FMS: " + fms_id )
else :
log ( " FMS CRC incorrect " )
#ZVEI Decoder Section
#check ZVEI: -> validate -> check double alarm -> log -> (MySQL)
if " ZVEI2: " in decoded :
log ( " recived ZVEI " )
zvei_id = decoded [ 7 : 12 ] #ZVEI Code
if re . search ( " [0-9F] {5} " , zvei_id ) : #if ZVEI is valid
if zvei_id == zvei_id_old and timestamp < zvei_time_old + zvei_double_ignore_time : #check for double alarm
log ( " ZVEI double alarm: " + zvei_id_old )
zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new
else :
log ( " 5-Ton: " + zvei_id , " info " )
zvei_id_old = zvei_id #save last id
zvei_time_old = timestamp #save last time
if useMySQL : #only if MySQL is active
log ( " ZVEI to MySQL " )
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tableZVEI + " (time,zvei) VALUES ( %s , %s ) " , ( curtime ( ) , zvei_id ) )
cursor . close ( )
connection . commit ( )
except :
log ( " ZVEI to MySQL failed " , " error " )
finally :
connection . close ( ) #Close connection in every case
if useHTTPrequest : #only if HTTPrequest is active
log ( " ZVEI to HTTP " )
try :
2015-05-16 18:15:44 +02:00
httprequest = httplib . HTTPConnection ( url_zvei )
2015-05-15 20:54:42 +02:00
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
if str ( httpresponse . status ) == " 200 " : #Check HTTP Response an print a Log or Error
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) )
else :
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) , " error " )
except :
log ( " ZVEI to HTTP failed " , " error " )
else :
log ( " No valid ZVEI: " + zvei_id )
2015-05-16 18:15:44 +02:00
2015-05-15 20:54:42 +02:00
#POCSAG512 Decoder Section
#check POCSAG512: -> validate -> check double alarm -> log -> (MySQL)
#POCSAG512: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh
if " POCSAG512: " in decoded :
log ( " recived POCSAG512 " )
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
if poc_id > = poc_filter_range_start :
if poc_id > = poc_filter_range_start :
if poc_id == poc_id_old and timestamp < poc_time_old + poc_double_ignore_time : #check for double alarm
log ( " POC512 double alarm: " + poc_id_old )
poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new
else :
log ( " POCSAG512: " + poc_id + " " + poc_sub + " " + poc_text , " info " )
poc_id_old = poc_id #save last id
poc_time_old = timestamp #save last time
if useMySQL : #only if MySQL is active
log ( " POC to MySQL " )
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tablePOC + " (time,ric,funktion,text) VALUES ( %s , %s , %s , %s ) " , ( curtime ( ) , poc_id , poc_sub , poc_text , ) )
cursor . close ( )
connection . commit ( )
except :
log ( " POC512 to MySQL failed " , " error " )
finally :
connection . close ( ) #Close connection in every case
if useHTTPrequest : #only if HTTPrequest is active
log ( " POC512 to HTTP " )
try :
2015-05-16 18:15:44 +02:00
httprequest = httplib . HTTPConnection ( url_poc )
2015-05-15 20:54:42 +02:00
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
if str ( httpresponse . status ) == " 200 " : #Check HTTP Response an print a Log or Error
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) )
else :
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) , " error " )
except :
log ( " POCSAG512 to HTTP failed " , " error " )
else :
log ( " POCSAG512: " + poc_id + " out of filter range " )
else :
log ( " POCSAG512: " + poc_id + " out of filter range " )
else :
log ( " No valid POCSAG512: " + poc_id )
2015-05-16 18:15:44 +02:00
2015-05-15 20:54:42 +02:00
#POCSAG1200 Decoder Section
#check POCSAG1200: -> validate -> check double alarm -> log -> (MySQL)
#POCSAG1200: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh
if " POCSAG1200: " in decoded :
log ( " recived POCSAG1200 " )
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
if poc_id > = poc_filter_range_start :
if poc_id > = poc_filter_range_start :
if poc_id == poc_id_old and timestamp < poc_time_old + poc_double_ignore_time : #check for double alarm
log ( " POC1200 double alarm: " + poc_id_old )
poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new
else :
log ( " POCSAG1200: " + poc_id + " " + poc_sub + " " + poc_text , " info " )
poc_id_old = poc_id #save last id
poc_time_old = timestamp #save last time
if useMySQL : #only if MySQL is active
log ( " POC to MySQL " )
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tablePOC + " (time,ric,funktion,text) VALUES ( %s , %s , %s , %s ) " , ( curtime ( ) , poc_id , poc_sub , poc_text , ) )
cursor . close ( )
connection . commit ( )
except :
log ( " POC1200 to MySQL failed " , " error " )
finally :
connection . close ( ) #Close connection in every case
if useHTTPrequest : #only if HTTPrequest is active
log ( " POC1200 to HTTP " )
try :
2015-05-16 18:15:44 +02:00
httprequest = httplib . HTTPConnection ( url_poc )
2015-05-15 20:54:42 +02:00
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
if str ( httpresponse . status ) == " 200 " : #Check HTTP Response an print a Log or Error
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) )
else :
log ( " HTTP response: " + str ( httpresponse . status ) + " - " + str ( httpresponse . reason ) , " error " )
except :
log ( " POCSAG1200 to HTTP failed " , " error " )
else :
log ( " POCSAG1200: " + poc_id + " out of filter range " )
else :
log ( " POCSAG1200: " + poc_id + " out of filter range " )
else :
log ( " No valid POCSAG1200: " + poc_id )
2015-04-03 15:55:10 +02:00
except KeyboardInterrupt :
2015-05-15 20:54:42 +02:00
log ( " Keyboard Interrupt " , " error " )
2015-04-04 21:47:09 +02:00
except :
2015-05-15 20:54:42 +02:00
log ( " unknown Error " , " error " )
2015-04-04 23:32:51 +02:00
finally :
2015-05-15 20:54:42 +02:00
try :
rtl_fm . terminate ( )
log ( " rtl_fm terminated " )
multimon_ng . terminate ( )
log ( " multimon-ng terminated " )
log ( " exiting BOSWatch " )
except :
log ( " failed in clean-up routine " , " error " )
finally :
exit ( 0 )