diff --git a/README.md b/README.md index 813b208..b8fb0a0 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,22 @@ Python Script to Recive and Decode German BOS Information with rtl_fm and multimon-NG -**Please** only use Code from **master-Branch** - thats the only stable! +**Please** only use Code from **master**-Branch - thats **the only stable!** + +unless you are developer you can use the develop-Branch - may be unstable! ### Features -#####Implemented Features: +##### Implemented Features: - FMS and ZVEI decoding and Displaying - Filtering double alarms with adjustable time - FMS and ZVEI validation (plausibility test) - MySQL Database Support for FMS and ZVEI +- simple HTTP request at alarm to URL you want - All configurations in seperate File "config.ini" - simple Web Frontend with Data Parsing +- Logfiles for better Troubleshooting -#####Features for the Future: +##### Features for the Future: - extensive filtering options - POCSAG 512,1200,2400 support (need RAW data from multimon-ng) - automatic Audio recording at alarm @@ -43,9 +47,6 @@ usage: boswatch.py [-h] -f FREQ [-d DEVICE] [-e ERROR] -a {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...] [-s SQUELCH] [-v] -BOSWatch is a Python Script to Recive and Decode BOS Information with rtl_fm -and multimon-NG - optional arguments: -h, --help show this help message and exit -f FREQ, --freq FREQ Frequency you want to listen @@ -59,8 +60,6 @@ optional arguments: -s SQUELCH, --squelch SQUELCH Level of Squelch -v, --verbose Shows more Information - -More Options you can find in the extern config.ini File in this Folder ``` ### Installation @@ -72,6 +71,9 @@ You can easy install BOSWatch with the install.sh Script. Now the script downloads and compile all needed data. At the end you can find the Programm in `/home/pi/bos/BOSWatch` +Caution, script don't install a Webserver with PHP and MySQL. +So you have to make up manually if you want to use MySQL support. + ### Requirements - RTL_SDR (rtl_fm) - Multimon-NG @@ -83,4 +85,4 @@ At the end you can find the Programm in `/home/pi/bos/BOSWatch` Thanks to smith_fms and McBo from [Funkmeldesystem.de - Forum](http://www.funkmeldesystem.de/) for Inspiration and Groundwork! -######Greetz Schrolli +###### Greetz Schrolli diff --git a/boswatch.py b/boswatch.py index 824d95a..f10dec6 100644 --- a/boswatch.py +++ b/boswatch.py @@ -14,106 +14,118 @@ import subprocess #import os import mysql import mysql.connector +import httplib import argparse #for parse the args import ConfigParser #for parse the config file import re #Regex +# Functions def curtime(format="%Y-%m-%d %H:%M:%S"): return time.strftime(format) -def stop_script(err): - print "" - print "ERR: "+err +#Loglevel +#[LOG] for the logfile +#[INFO] normal display +#[ERROR] errors +def log(msg, level="log"): + log_entry = curtime("%H:%M:%S")+" ["+level.upper()+"] "+msg + + if not level == "log" or args.verbose: + print log_entry + + bos_log = open("log_bos.txt", "a") + bos_log.write(log_entry+"\n") + bos_log.close() + + +try: + try: - if useMySQL: #only if MySQL is active - if args.verbose: print "disconnect MySQL" - connection.close() - rtl_fm.terminate() - if args.verbose: print "rtl_fm terminated" - multimon_ng.terminate() - if args.verbose: print "multimon-ng terminated" - if args.verbose: print "exiting BOSWatch" + bos_log = open("log_bos.txt", "w") + rtl_log = open("log_rtl.txt", "w") + mon_log = open("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: - pass + log("cannot clear logfiles","error") + 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") + args = parser.parse_args() + except: + log("cannot parse Args","error") -#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") -args = parser.parse_args() + #Read Data from Args, Put it into working Variables and Display them + print " ____ ____ ______ __ __ __ " + print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b" + print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e" + print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t" + print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a" + print " German BOS Information Script " + print " by Bastian Schroll " + print "" - - + freq = args.freq + print "Frequency: "+freq + + #channel = args.channel + #print "Frequency: ",channel + device = args.device + print "Device-ID: "+str(device) - + error = args.error + print "Error in PPM: "+str(error) -#Read Data from Args, Put it into working Variables and Display them -print(" ____ ____ ______ __ __ __ ") -print(" / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b") -print(" / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e") -print(" / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t") -print(" /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a") -print(" German BOS Information Script ") -print(" by Bastian Schroll ") -print("") + demodulation = "" + print "Active Demods: "+str(len(args.demod)) + if "FMS" in args.demod: + demodulation += "-a FMSFSK " + print "- FMS" + if "ZVEI" in args.demod: + demodulation += "-a ZVEI2 " + print "- ZVEI" + if "POC512" in args.demod: + demodulation += "-a POCSAG512 " + print "- POC512" + if "POC1200" in args.demod: + demodulation += "-a POCSAG1200 " + print "- POC1200" + if "POC2400" in args.demod: + demodulation += "-a POCSAG2400 " + print "- POC2400" -freq = args.freq -print "Frequency: "+freq - -#channel = args.channel -#print("Frequency: ",channel) + squelch = args.squelch + print "Squelch: "+str(squelch) -device = args.device -print "Device-ID: "+str(device) + if args.verbose: + print "Verbose Mode!" -error = args.error -print "Error in PPM: "+str(error) - -demodulation = "" -print "Active Demods: "+str(len(args.demod)) -if "FMS" in args.demod: - demodulation += "-a FMSFSK " - print "- FMS" -if "ZVEI" in args.demod: - demodulation += "-a ZVEI2 " - print "- ZVEI" -if "POC512" in args.demod: - demodulation += "-a POCSAG512 " - print "- POC512" -if "POC1200" in args.demod: - demodulation += "-a POCSAG1200 " - print "- POC1200" -if "POC2400" in args.demod: - demodulation += "-a POCSAG2400 " - print "- POC2400" - -squelch = args.squelch -print "Squelch: "+str(squelch) - -if args.verbose: - print("Verbose Mode!") - -print "" - -try: + print "" + #ConfigParser - if args.verbose: print "reading config file" + log("reading config file") try: config = ConfigParser.ConfigParser() config.read("./config.ini") fms_double_ignore_time = int(config.get("FMS", "double_ignore_time")) zvei_double_ignore_time = int(config.get("ZVEI", "double_ignore_time")) - + #MySQL config useMySQL = int(config.get("Module", "useMySQL")) #use MySQL support? if useMySQL: #only if MySQL is active @@ -121,76 +133,76 @@ try: 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 + url = config.get("HTTPrequest", "url") + except: - stop_script("config reading error") - exit(0) - - - if useMySQL: #only if MySQL is active - if args.verbose: print "connect to MySQL database" - try: - connection = mysql.connector.connect(host = str(dbserver), user = str(dbuser), passwd = str(dbpassword), db = str(database)) - except: - print "MySQL connect error" - exit(0) - + log("config reading error","error") + #variables pre-load - if args.verbose: print "pre-load variables" + 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 - - if args.verbose: print "starting rtl_fm" + if useMySQL: #only if MySQL is active + log("connect to MySQL database") + try: + connection = mysql.connector.connect(host = str(dbserver), user = str(dbuser), passwd = str(dbpassword), db = str(database)) + except: + log("MySQL connect error","error") + + + 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, - stderr=open('log.txt','a'), + stderr=open('log_rtl.txt','a'), shell=True) except: - stop_script("cannot start rtl_fm") - exit(0) + log("cannot start rtl_fm","error") #multimon_ng = subprocess.Popen("aplay -r 22050 -f S16_LE -t raw", - if args.verbose: print "starting multimon-ng" + 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, - stderr=open('log.txt','a'), + stderr=open('log_mon.txt','a'), shell=True) except: - stop_script("cannot start multimon-ng") - exit(0) - - - if args.verbose: print "start decoding" - print "" + 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 -> print -> (MySQL) + #check FMS: -> check CRC -> validate -> check double alarm -> log -> (MySQL) if "FMS:" in decoded: - if args.verbose: print "recived FMS" + log("recived FMS") fms_service = decoded[19] #Organisation fms_country = decoded[36] #Bundesland @@ -199,53 +211,96 @@ try: 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]{2}[0-9]{6}[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 - if args.verbose: print "FMS double alarm: "+fms_id_old + log("FMS double alarm: "+fms_id_old) fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new else: - print curtime("%H:%M:%S")+" BOS:"+fms_service+" Bundesland:"+fms_country+" Ort:"+fms_location+" Fahrzeug:"+fms_vehicle+" Status:"+fms_status+" Richtung:"+fms_direction+" TKI:"+fms_tsi + log("BOS:"+fms_service+" Bundesland:"+fms_country+" Ort:"+fms_location+" Fahrzeug:"+fms_vehicle+" 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 - 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() - elif args.verbose: #Invalid error only in verbose mode - print "No valid FMS: "+fms_id - elif args.verbose: #crc error only in verbose mode - print "CRC incorrect" - - + log("FMS to MySQL") + try: + 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() + except: + log("FMS cannot insert","error") + + + if useHTTPrequest: #only if HTTPrequest is active + log("FMS to HTTP") + try: + httprequest = httplib.HTTPConnection(url) + httprequest.request("HEAD", "/") + httpresponse = httprequest.getresponse() + #if args.verbose: print httpresponse.status, httpresponse.reason + except: + log("FMS cannot request","error") + + else: + log("No valid FMS: "+fms_id) + else: + log("CRC incorrect") + + #ZVEI Decoder Section - #check ZVEI: -> validate -> check double alarm -> print -> (MySQL) + #check ZVEI: -> validate -> check double alarm -> log -> (MySQL) if "ZVEI2:" in decoded: - if args.verbose: print "recived ZVEI" + 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 - if args.verbose: print "ZVEI double alarm: "+zvei_id_old + log("ZVEI double alarm: "+zvei_id_old) zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new else: - print curtime("%H:%M:%S")+" 5-Ton: "+zvei_id + 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 - cursor = connection.cursor() - cursor.execute("INSERT INTO "+tableZVEI+" (time,zvei) VALUES (%s,%s)",(curtime(),zvei_id)) - cursor.close() - connection.commit() + log("ZVEI to MySQL") + try: + cursor = connection.cursor() + cursor.execute("INSERT INTO "+tableZVEI+" (time,zvei) VALUES (%s,%s)",(curtime(),zvei_id)) + cursor.close() + connection.commit() + except: + log("ZVEI cannot insert","error") + + + if useHTTPrequest: #only if HTTPrequest is active + log("ZVEI to HTTP") + try: + httprequest = httplib.HTTPConnection(url) + httprequest.request("HEAD", "/") + httpresponse = httprequest.getresponse() + #if args.verbose: print httpresponse.status, httpresponse.reason + except: + log("ZVEI cannot request","error") + + else: + log("No valid ZVEI: "+zvei_id) - elif args.verbose: #Invalid error only in verbose mode - print "No valid ZVEI: "+zvei_id - - except KeyboardInterrupt: - stop_script("Keyboard Interrupt") \ No newline at end of file + log("Keyboard Interrupt","error") +except: + log("unknown Error","error") +finally: + if useMySQL: #only if MySQL is active + log("disconnect MySQL") + connection.close() + rtl_fm.terminate() + log("rtl_fm terminated") + multimon_ng.terminate() + log("multimon-ng terminated") + log("exiting BOSWatch") + exit(0) \ No newline at end of file diff --git a/config.ini b/config.ini index 561ddf8..98969de 100644 --- a/config.ini +++ b/config.ini @@ -3,26 +3,35 @@ ######################## [FMS] -#time to ignore same alarm in a row in seconds +#time to ignore same alarm in a row (sek) double_ignore_time = 10 [ZVEI] -#time to ignore same alarm in a row in seconds +#time to ignore same alarm in a row (sek) double_ignore_time = 5 #can take on or off the modules (0|1) [Module] useMySQL = 0 #useAudiorecord = 0 -#useHTTPrequest = 0 +useHTTPrequest = 0 -#Data for MySQL connection [MySQL] +#Data for MySQL connection dbserver = localhost dbuser = root dbpassword = root database = boswatch +#tables in the database tableFMS = bos_fms tableZVEI = bos_zvei -tablePOC = bos_pocsag \ No newline at end of file +tablePOC = bos_pocsag + +#[Audiorecord] +#recording time (sek) +#record_time = 30 + +[HTTPrequest] +#url without http:// ! +url = www.google.de \ No newline at end of file diff --git a/install.sh b/install.sh index ee392be..23eac5a 100644 --- a/install.sh +++ b/install.sh @@ -1,69 +1,100 @@ #!/bin/sh -clear -echo "" -echo " ##########################" -echo " # #" -echo " # BOSWatch Installer #" -echo " # #" -echo " ##########################" +tput clear +tput civis +echo " ____ ____ ______ __ __ __ " +echo " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ " +echo " / __ / / / /\__ \| | /| / / __ / __/ ___/ __ \ " +echo " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / " +echo " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ " +echo " German BOS Information Script " +echo " by Bastian Schroll " echo "" echo "This may take a several minutes... Don't panic!" echo "" -echo "Caution, script don't installed a Webserver with PHP and MySQL" +echo "Caution, script don't install a Webserver with PHP and MySQL" echo "So you have to make up manually if you want to use MySQL support" -echo "" mkdir -p /home/pi/bos/install -echo "[ 1/10] [#---------] make a apt-get update..." +tput cup 13 15 +echo "[ 1/10] [#---------]" +tput cup 15 5 +echo "-> make a apt-get update................" apt-get update > /home/pi/bos/install/setup_log.txt 2>&1 -echo "[ 2/10] [##--------] download GIT an other stuff..." +tput cup 13 15 +echo "[ 2/10] [##--------]" +tput cup 15 5 +echo "-> download GIT an other stuff.........." apt-get -y install git cmake build-essential libusb-1.0 qt4-qmake libpulse-dev libx11-dev sox >> /home/pi/bos/install/setup_log.txt 2>&1 -echo "[ 3/10] [###-------] download rtl_fm..." +tput cup 13 15 +echo "[ 3/10] [###-------]" +tput cup 15 5 +echo "-> download rtl_fm......................" cd /home/pi/bos/install git clone git://git.osmocom.org/rtl-sdr.git >> /home/pi/bos/install/setup_log.txt 2>&1 cd rtl-sdr/ -echo "[ 4/10] [####------] compile rtl_fm..." +tput cup 13 15 +echo "[ 4/10] [####------]" +tput cup 15 5 +echo "-> compile rtl_fm......................" mkdir -p build && cd build cmake ../ -DINSTALL_UDEV_RULES=ON >> /home/pi/bos/install/setup_log.txt 2>&1 make >> /home/pi/bos/install/setup_log.txt 2>&1 make install >> /home/pi/bos/install/setup_log.txt 2>&1 ldconfig >> /home/pi/bos/install/setup_log.txt 2>&1 -echo "[ 5/10] [#####-----] download multimon-ng..." +tput cup 13 15 +echo "[ 5/10] [#####-----]" +tput cup 15 5 +echo "-> download multimon-ng................" cd /home/pi/bos/install git clone https://github.com/EliasOenal/multimonNG.git >> /home/pi/bos/install/setup_log.txt 2>&1 cd multimonNG/ -echo "[ 6/10] [######----] compile multimon-ng..." +tput cup 13 15 +echo "[ 6/10] [######----]" +tput cup 15 5 +echo "-> compile multimon-ng................." mkdir -p build cd build qmake ../multimon-ng.pro >> /home/pi/bos/install/setup_log.txt 2>&1 make >> /home/pi/bos/install/setup_log.txt 2>&1 make install >> /home/pi/bos/install/setup_log.txt 2>&1 -echo "[ 7/10] [#######---] download MySQL Connector for Python..." +tput cup 13 15 +echo "[ 7/10] [#######---]" +tput cup 15 5 +echo "-> download MySQL Connector for Python." cd /home/pi/bos/install wget "http://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python-1.0.9.tar.gz/from/http://cdn.mysql.com/" -O mysql-connector.tar >> /home/pi/bos/install/setup_log.txt 2>&1 tar xfv mysql-connector.tar >> /home/pi/bos/install/setup_log.txt 2>&1 cd mysql-connector-python* -echo "[ 8/10] [########--] install MySQL Connector for Python..." +tput cup 13 15 +echo "[ 8/10] [########--]" +tput cup 15 5 +echo "-> install MySQL Connector for Python.." chmod +x ./setup.py ./setup.py install >> /home/pi/bos/install/setup_log.txt 2>&1 -echo "[ 9/10] [#########-] download BOSWatch..." +tput cup 13 15 +echo "[ 9/10] [#########-]" +tput cup 15 5 +echo "-> download BOSWatch..................." cd /home/pi/bos git clone https://github.com/Schrolli91/BOSWatch >> /home/pi/bos/install/setup_log.txt 2>&1 -echo "[10/10] [##########] configure..." +tput cup 13 15 +echo "[10/10] [##########]" +tput cup 15 5 +echo "-> configure..........................." cd BOSWatch chmod +x * echo "# blacklist the DVB drivers to avoid conflict with the SDR driver\n blacklist dvb_usb_rtl28xxu \n blacklist rtl2830\n blacklist dvb_usb_v2\n blacklist dvb_core" >> /etc/modprobe.d/boswatch_blacklist_sdr.conf -echo "" +tput cup 17 1 echo "BOSWatch are now in /home/pi/bos/BOSWatch/" -echo "Install ready!" +echo "Install ready!" \ No newline at end of file