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-03 23:11:54 +02:00
# Python Script to Recive 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-05 00:15:32 +02:00
# Functions
2015-04-03 15:55:10 +02:00
def curtime ( format = " % Y- % m- %d % H: % M: % S " ) :
return time . strftime ( format )
2015-04-04 23:32:51 +02:00
#Loglevel
#[LOG] for the logfile
#[INFO] normal display
#[ERROR] errors
def log ( msg , level = " log " ) :
2015-04-05 00:15:32 +02:00
log_entry = curtime ( " % H: % M: % S " ) + " [ " + level . upper ( ) + " ] " + msg
2015-04-05 08:11:06 +02:00
if not level == " log " and not args . quiet or args . verbose :
2015-04-05 00:15:32 +02:00
print log_entry
2015-04-07 21:02:56 +02:00
bos_log = open ( script_path + " /log_bos.txt " , " a " )
2015-04-05 00:15:32 +02:00
bos_log . write ( log_entry + " \n " )
bos_log . close ( )
2015-04-03 15:55:10 +02:00
2015-04-05 00:15:32 +02:00
try :
2015-04-03 15:55:10 +02:00
2015-04-07 21:02:56 +02:00
script_path = os . path . dirname ( os . path . abspath ( __file__ ) )
2015-04-05 00:15:32 +02:00
try :
2015-04-07 21:02:56 +02:00
bos_log = open ( script_path + " /log_bos.txt " , " w " )
rtl_log = open ( script_path + " /log_rtl.txt " , " w " )
mon_log = open ( script_path + " /log_mon.txt " , " w " )
2015-04-05 00:15:32 +02:00
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-04-05 00:15:32 +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 " )
2015-04-05 08:11:06 +02:00
parser . add_argument ( " -q " , " --quiet " , help = " Shows no Information. Only Logfiles " , action = " store_true " )
2015-04-05 00:15:32 +02:00
args = parser . parse_args ( )
except :
log ( " cannot parse Args " , " error " )
2015-04-03 15:55:10 +02:00
2015-04-05 19:20:42 +02:00
#Read Data from Args, Put it into working Variables
2015-04-05 00:15:32 +02:00
freq = args . freq
device = args . device
error = args . error
2015-04-05 08:11:06 +02:00
2015-04-05 00:15:32 +02:00
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 :
2015-04-05 08:11:06 +02:00
demodulation + = " -a POCSAG1200 "
2015-04-05 00:15:32 +02:00
if " POC2400 " in args . demod :
demodulation + = " -a POCSAG2400 "
2015-04-05 08:11:06 +02:00
2015-04-05 00:15:32 +02:00
squelch = args . squelch
2015-04-03 15:55:10 +02:00
2015-04-05 08:11:06 +02:00
if not args . quiet : #only if not quiet mode
2015-04-05 19:20:42 +02:00
print " ____ ____ ______ __ __ __ "
print " / __ )/ __ \ / ___/ | / /___ _/ /______/ /_ b "
print " / __ / / / / \ __ \ | | /| / / __ `/ __/ ___/ __ \ e "
print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t "
print " /_____/ \ ____//____/ |__/|__/ \ __,_/ \ __/ \ ___/_/ /_/ a "
print " German BOS Information Script "
print " by Bastian Schroll "
print " "
2015-04-05 08:11:06 +02:00
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 " "
2015-04-05 00:15:32 +02:00
2015-04-07 21:36:15 +02:00
#variables pre-load
log ( " pre-load variables " )
fms_id = 0
fms_id_old = 0
fms_time_old = 0
2015-04-07 21:20:48 +02:00
2015-04-07 21:36:15 +02:00
zvei_id = 0
zvei_id_old = 0
zvei_time_old = 0
2015-04-07 21:20:48 +02:00
2015-04-03 15:55:10 +02:00
#ConfigParser
2015-04-04 22:08:51 +02:00
log ( " reading config file " )
2015-04-03 15:55:10 +02:00
try :
config = ConfigParser . ConfigParser ( )
2015-04-07 21:02:56 +02:00
config . read ( script_path + " /config.ini " )
2015-04-03 15:55:10 +02:00
fms_double_ignore_time = int ( config . get ( " FMS " , " double_ignore_time " ) )
zvei_double_ignore_time = int ( config . get ( " ZVEI " , " double_ignore_time " ) )
2015-04-04 23:32:51 +02:00
2015-04-03 21:43:00 +02:00
#MySQL config
2015-04-04 19:21:42 +02:00
useMySQL = int ( config . get ( " Module " , " useMySQL " ) ) #use MySQL support?
2015-04-03 21:43:00 +02:00
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 " )
2015-04-04 23:32:51 +02:00
2015-04-03 21:43:00 +02:00
#MySQL tables
tableFMS = config . get ( " MySQL " , " tableFMS " )
tableZVEI = config . get ( " MySQL " , " tableZVEI " )
tablePOC = config . get ( " MySQL " , " tablePOC " )
2015-04-04 23:32:51 +02:00
2015-04-04 21:47:09 +02:00
#HTTPrequest config
useHTTPrequest = int ( config . get ( " Module " , " useHTTPrequest " ) ) #use HTTPrequest support?
if useHTTPrequest : #only if HTTPrequest is active
url = config . get ( " HTTPrequest " , " url " )
2015-04-03 15:55:10 +02:00
except :
2015-04-07 20:40:02 +02:00
log ( " cannot read config file " , " error " )
2015-04-07 21:36:15 +02:00
log ( " set to standard configuration " )
fms_double_ignore_time = 5
zvei_double_ignore_time = 5
useMySQL = 0
useHTTPrequest = 0
2015-04-07 21:20:48 +02:00
2015-04-05 00:15:32 +02:00
if useMySQL : #only if MySQL is active
2015-04-07 21:18:37 +02:00
log ( " testing MySQL connection " )
2015-04-05 00:15:32 +02:00
try :
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
2015-04-07 21:18:37 +02:00
log ( " connection test successful " )
2015-04-05 00:15:32 +02:00
except :
2015-04-07 21:18:37 +02:00
log ( " connection test failed - MySQL support deactivated " , " error " )
useMySQL = 0
2015-04-07 21:38:17 +02:00
finally :
connection . close ( ) #Close connection in every case
2015-04-05 00:15:32 +02:00
2015-04-04 22:08:51 +02:00
log ( " starting rtl_fm " )
2015-04-03 15:55:10 +02:00
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-04-07 21:02:56 +02:00
stderr = open ( script_path + " /log_rtl.txt " , " a " ) ,
2015-04-03 15:55:10 +02:00
shell = True )
except :
2015-04-04 23:32:51 +02:00
log ( " cannot start rtl_fm " , " error " )
2015-04-03 15:55:10 +02:00
#multimon_ng = subprocess.Popen("aplay -r 22050 -f S16_LE -t raw",
2015-04-04 22:08:51 +02:00
log ( " starting multimon-ng " )
2015-04-03 15:55:10 +02:00
try :
multimon_ng = subprocess . Popen ( " multimon-ng " + str ( demodulation ) + " -f alpha -t raw /dev/stdin - " ,
stdin = rtl_fm . stdout ,
stdout = subprocess . PIPE ,
2015-04-07 21:02:56 +02:00
stderr = open ( script_path + " /log_mon.txt " , " a " ) ,
2015-04-03 15:55:10 +02:00
shell = True )
except :
2015-04-04 23:32:51 +02:00
log ( " cannot start multimon-ng " , " error " )
2015-04-04 22:08:51 +02:00
log ( " start decoding " )
2015-04-03 15:55:10 +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'
decoded = str ( multimon_ng . stdout . readline ( ) ) #Get line data from multimon stdout
2015-04-04 23:32:51 +02:00
2015-04-03 15:55:10 +02:00
if True : #if input data avalable
2015-04-04 23:32:51 +02:00
2015-04-03 15:55:10 +02:00
timestamp = int ( time . time ( ) ) #Get Timestamp
#if args.verbose: print "RAW: "+decoded #for verbose mode, print Raw input data
#FMS Decoder Section
2015-04-05 00:54:41 +02:00
#check FMS: -> check CRC -> validate -> check double alarm -> log -> (MySQL)
2015-04-03 15:55:10 +02:00
if " FMS: " in decoded :
2015-04-04 22:08:51 +02:00
log ( " recived FMS " )
2015-04-03 15:55:10 +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
2015-04-04 23:32:51 +02:00
2015-04-03 22:38:53 +02:00
if " CRC correct " in decoded : #check CRC is correct
2015-04-03 15:55:10 +02:00
fms_id = fms_service + fms_country + fms_location + fms_vehicle + fms_status + fms_direction #build FMS id
2015-04-04 14:03:46 +02:00
if re . search ( " [0-9a-f] {2} [0-9] {6} [0-9a-f] {1} [01] {1} " , fms_id ) : #if FMS is valid
2015-04-03 22:38:53 +02:00
if fms_id == fms_id_old and timestamp < fms_time_old + fms_double_ignore_time : #check for double alarm
2015-04-04 22:08:51 +02:00
log ( " FMS double alarm: " + fms_id_old )
2015-04-03 22:38:53 +02:00
fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new
else :
2015-04-05 00:15:32 +02:00
log ( " BOS: " + fms_service + " Bundesland: " + fms_country + " Ort: " + fms_location + " Fahrzeug: " + fms_vehicle + " Status: " + fms_status + " Richtung: " + fms_direction + " TKI: " + fms_tsi , " info " )
2015-04-03 22:38:53 +02:00
fms_id_old = fms_id #save last id
fms_time_old = timestamp #save last time
2015-04-05 00:15:32 +02:00
if useMySQL : #only if MySQL is active
2015-04-07 20:40:02 +02:00
log ( " insert FMS into MySQL " )
2015-04-05 00:15:32 +02:00
try :
2015-04-07 21:18:37 +02:00
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
2015-04-04 23:32:51 +02:00
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tableFMS + " (time,service,country,location,vehicle,status,direction,tsi) VALUES ( %s , %s , %s , %s , %s , %s , %s , %s ) " , ( curtime ( ) , fms_service , fms_country , fms_location , fms_vehicle , fms_status , fms_direction , fms_tsi ) )
cursor . close ( )
connection . commit ( )
2015-04-05 00:15:32 +02:00
except :
2015-04-07 21:38:17 +02:00
log ( " FMS cannot insert into MySQL " , " error " )
finally :
connection . close ( ) #Close connection in every case
2015-04-05 00:15:32 +02:00
if useHTTPrequest : #only if HTTPrequest is active
2015-04-07 20:40:02 +02:00
log ( " FMS to HTTP request " )
2015-04-05 00:15:32 +02:00
try :
2015-04-04 23:32:51 +02:00
httprequest = httplib . HTTPConnection ( url )
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
#if args.verbose: print httpresponse.status, httpresponse.reason
2015-04-05 00:15:32 +02:00
except :
2015-04-07 21:02:56 +02:00
log ( " FMS HTTP request failed " , " error " )
2015-04-04 22:08:51 +02:00
else :
log ( " No valid FMS: " + fms_id )
else :
2015-04-07 20:40:02 +02:00
log ( " FMS CRC incorrect " )
2015-04-04 23:32:51 +02:00
2015-04-03 22:38:53 +02:00
#ZVEI Decoder Section
2015-04-05 00:54:41 +02:00
#check ZVEI: -> validate -> check double alarm -> log -> (MySQL)
2015-04-03 15:55:10 +02:00
if " ZVEI2: " in decoded :
2015-04-04 22:08:51 +02:00
log ( " recived ZVEI " )
2015-04-03 15:55:10 +02:00
2015-04-03 22:38:53 +02:00
zvei_id = decoded [ 7 : 12 ] #ZVEI Code
if re . search ( " [0-9F] {5} " , zvei_id ) : #if ZVEI is valid
2015-04-03 15:55:10 +02:00
if zvei_id == zvei_id_old and timestamp < zvei_time_old + zvei_double_ignore_time : #check for double alarm
2015-04-04 22:08:51 +02:00
log ( " ZVEI double alarm: " + zvei_id_old )
2015-04-03 15:55:10 +02:00
zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new
else :
2015-04-05 00:15:32 +02:00
log ( " 5-Ton: " + zvei_id , " info " )
2015-04-03 15:55:10 +02:00
zvei_id_old = zvei_id #save last id
zvei_time_old = timestamp #save last time
2015-04-07 21:02:56 +02:00
2015-04-05 00:15:32 +02:00
if useMySQL : #only if MySQL is active
2015-04-07 20:40:02 +02:00
log ( " insert ZVEI into MySQL " )
2015-04-05 00:15:32 +02:00
try :
2015-04-07 21:18:37 +02:00
connection = mysql . connector . connect ( host = str ( dbserver ) , user = str ( dbuser ) , passwd = str ( dbpassword ) , db = str ( database ) )
2015-04-04 23:32:51 +02:00
cursor = connection . cursor ( )
cursor . execute ( " INSERT INTO " + tableZVEI + " (time,zvei) VALUES ( %s , %s ) " , ( curtime ( ) , zvei_id ) )
cursor . close ( )
connection . commit ( )
2015-04-05 00:15:32 +02:00
except :
2015-04-07 21:38:17 +02:00
log ( " ZVEI cannot insert into MySQL " , " error " )
finally :
connection . close ( ) #Close connection in every case
2015-04-04 21:47:09 +02:00
2015-04-05 00:15:32 +02:00
if useHTTPrequest : #only if HTTPrequest is active
2015-04-07 20:40:02 +02:00
log ( " ZVEI to HTTP request " )
2015-04-05 00:15:32 +02:00
try :
2015-04-04 23:32:51 +02:00
httprequest = httplib . HTTPConnection ( url )
httprequest . request ( " HEAD " , " / " )
httpresponse = httprequest . getresponse ( )
#if args.verbose: print httpresponse.status, httpresponse.reason
2015-04-05 00:15:32 +02:00
except :
2015-04-07 21:02:56 +02:00
log ( " ZVEI HTTP request failed " , " error " )
2015-04-04 22:08:51 +02:00
else :
log ( " No valid ZVEI: " + zvei_id )
2015-04-04 23:32:51 +02:00
2015-04-03 15:55:10 +02:00
except KeyboardInterrupt :
2015-04-04 23:32:51 +02:00
log ( " Keyboard Interrupt " , " error " )
2015-04-04 21:47:09 +02:00
except :
2015-04-05 00:15:32 +02:00
log ( " unknown Error " , " error " )
2015-04-04 23:32:51 +02:00
finally :
rtl_fm . terminate ( )
log ( " rtl_fm terminated " )
multimon_ng . terminate ( )
log ( " multimon-ng terminated " )
log ( " exiting BOSWatch " )
exit ( 0 )