2015-04-03 15:55:10 +02:00
#!/usr/bin/python
# -*- coding: cp1252 -*-
2015-05-25 18:07:44 +02:00
#
"""
2015-05-28 13:51:20 +02:00
BOSWatch
2015-05-25 18:07:44 +02:00
Python Script to receive and decode German BOS Information with rtl_fm and multimon - NG
2015-05-28 13:51:20 +02:00
Through a simple plugin system , data can easily be transferred to other applications
2015-05-25 18:07:44 +02:00
For more Information see the README . md
2015-05-28 13:51:20 +02:00
2015-05-31 12:40:43 +02:00
@author : Bastian Schroll
@author : Jens Herrmann
2015-05-28 13:51:20 +02:00
GitHUB : https : / / github . com / Schrolli91 / BOSWatch
2015-05-25 18:07:44 +02:00
"""
2015-04-03 15:55:10 +02:00
2015-05-20 13:29:16 +02:00
import logging
2015-05-25 18:07:44 +02:00
import logging . handlers
2015-04-03 15:55:10 +02:00
import argparse #for parse the args
import ConfigParser #for parse the config file
2015-05-22 11:44:35 +02:00
import os #for log mkdir
2015-05-22 16:44:23 +02:00
import time #timestamp
2015-05-20 19:48:46 +02:00
import subprocess
2015-04-03 15:55:10 +02:00
2015-05-22 16:44:23 +02:00
from includes import globals # Global variables
2015-05-20 19:28:10 +02:00
2015-05-25 18:07:44 +02:00
##
2015-05-31 12:40:43 +02:00
#
2015-05-25 18:07:44 +02:00
# This Class extended the TimedRotatingFileHandler with the possibility to change the backupCount after initialization.
2015-05-31 12:40:43 +02:00
#
2015-05-25 18:07:44 +02:00
##
class MyTimedRotatingFileHandler ( logging . handlers . TimedRotatingFileHandler ) :
""" Extended Version of TimedRotatingFileHandler """
def setBackupCount ( self , backupCount ) :
""" Set/Change backupCount """
self . backupCount = backupCount
2015-05-29 11:25:25 +02:00
2015-05-31 13:12:37 +02:00
##
#
# convert frequency to Hz
#
def freqToHz ( freq ) :
2015-05-29 11:25:25 +02:00
"""
2015-05-31 13:12:37 +02:00
gets a frequency and resolve it in Hz
2015-05-29 11:25:25 +02:00
@type freq : string
@param freq : frequency of the SDR Stick
2015-05-31 13:12:37 +02:00
@return : frequency in Hz
2015-05-29 11:25:25 +02:00
@exception : Exception if Error by recalc
"""
try :
freq = freq . replace ( " k " , " e3 " ) . replace ( " M " , " e6 " )
2015-05-31 13:12:37 +02:00
# freq has to be interpreted as float first...
# otherwise you will get the error: an invalid literal for int() with base 10
return int ( float ( freq ) )
2015-05-29 11:25:25 +02:00
except :
logging . exception ( " Error in freqToHz() " )
2015-05-25 18:07:44 +02:00
#
2015-04-19 19:21:35 +02:00
# Programm
2015-05-25 18:07:44 +02:00
#
2015-04-19 19:21:35 +02:00
try :
2015-05-22 18:22:29 +02:00
try :
2015-05-25 18:07:44 +02:00
#
# Script-pathes
#
2015-05-20 19:28:10 +02:00
globals . script_path = os . path . dirname ( os . path . abspath ( __file__ ) )
2015-05-15 21:00:14 +02:00
2015-05-25 18:07:44 +02:00
#
# If necessary create Log-Path
#
2015-05-20 19:28:10 +02:00
if not os . path . exists ( globals . script_path + " /log/ " ) :
os . mkdir ( globals . script_path + " /log/ " )
2015-05-25 18:07:44 +02:00
#
# Create new myLogger...
#
myLogger = logging . getLogger ( )
myLogger . setLevel ( logging . DEBUG )
2015-05-20 19:28:10 +02:00
#set log string format
formatter = logging . Formatter ( ' %(asctime)s - %(module)-12s [ %(levelname)-8s ] %(message)s ' , ' %d . % m. % Y % H: % M: % S ' )
#create a file logger
2015-05-25 18:07:44 +02:00
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
fh . setLevel ( logging . DEBUG )
2015-05-20 19:28:10 +02:00
fh . setFormatter ( formatter )
2015-05-25 18:07:44 +02:00
myLogger . addHandler ( fh )
2015-05-20 19:28:10 +02:00
#create a display logger
ch = logging . StreamHandler ( )
2015-05-25 18:07:44 +02:00
#log level for display >= info
#will be changed later after parsing args
ch . setLevel ( logging . INFO )
2015-05-20 19:28:10 +02:00
ch . setFormatter ( formatter )
2015-05-25 18:07:44 +02:00
myLogger . addHandler ( ch )
2015-05-15 20:54:42 +02:00
except :
2015-05-21 11:27:57 +02:00
logging . exception ( " cannot create logger " )
else :
2015-05-20 19:48:46 +02:00
2015-05-25 18:07:44 +02:00
try :
#
# Clear the logfiles
#
fh . doRollover ( )
2015-05-21 11:27:57 +02:00
rtl_log = open ( globals . script_path + " /log/rtl_fm.log " , " w " )
mon_log = open ( globals . script_path + " /log/multimon.log " , " w " )
rtl_log . write ( " " )
mon_log . write ( " " )
rtl_log . close ( )
mon_log . close ( )
logging . debug ( " BOSWatch has started " )
2015-05-22 18:22:29 +02:00
logging . debug ( " Logfiles cleared " )
2015-05-21 11:27:57 +02:00
except :
logging . exception ( " cannot clear Logfiles " )
2015-05-22 18:22:29 +02:00
2015-05-25 18:07:44 +02:00
try :
#
# Parse args
#
2015-05-21 11:27:57 +02:00
logging . debug ( " parse args " )
#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 " )
2015-05-22 18:22:29 +02:00
args = parser . parse_args ( )
2015-05-21 11:27:57 +02:00
except :
2015-05-22 18:22:29 +02:00
logging . error ( " cannot parse args " )
else :
2015-05-21 11:27:57 +02:00
2015-05-25 18:07:44 +02:00
try :
#
# For debug display/log args
#
2015-05-29 11:25:25 +02:00
logging . debug ( " - Frequency: %s " , freqToHz ( args . freq ) )
2015-05-21 11:27:57 +02:00
logging . debug ( " - Device: %s " , args . device )
logging . debug ( " - PPM Error: %s " , args . error )
logging . debug ( " - Squelch: %s " , args . squelch )
2015-05-15 20:54:42 +02:00
2015-05-21 11:27:57 +02:00
demodulation = " "
if " FMS " in args . demod :
demodulation + = " -a FMSFSK "
logging . debug ( " - Demod: FMS " )
if " ZVEI " in args . demod :
demodulation + = " -a ZVEI2 "
logging . debug ( " - Demod: ZVEI " )
if " POC512 " in args . demod :
demodulation + = " -a POCSAG512 "
logging . debug ( " - Demod: POC512 " )
if " POC1200 " in args . demod :
demodulation + = " -a POCSAG1200 "
2015-05-28 08:43:08 +02:00
logging . debug ( " - Demod: POC1200 " )
2015-05-21 11:27:57 +02:00
if " POC2400 " in args . demod :
demodulation + = " -a POCSAG2400 "
logging . debug ( " - Demod: POC2400 " )
2015-05-16 18:15:44 +02:00
2015-05-21 11:27:57 +02:00
logging . debug ( " - Verbose Mode: %s " , args . verbose )
logging . debug ( " - Quiet Mode: %s " , args . quiet )
if args . verbose :
ch . setLevel ( logging . DEBUG )
if args . quiet :
ch . setLevel ( logging . CRITICAL )
2015-05-15 20:54:42 +02:00
2015-05-21 11:27:57 +02:00
if not args . quiet : #only if not quiet mode
2015-05-22 16:44:23 +02:00
from includes import shellHeader
2015-05-22 18:22:29 +02:00
shellHeader . printHeader ( args )
2015-05-21 11:27:57 +02:00
except :
logging . exception ( " cannot display/log args " )
2015-05-22 16:44:23 +02:00
2015-05-25 18:07:44 +02:00
try :
#
# Read config.ini
#
2015-05-21 11:27:57 +02:00
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 " ) :
2015-05-23 07:48:02 +02:00
logging . debug ( " - %s = %s " , key , val )
2015-05-21 11:27:57 +02:00
except :
2015-05-22 20:12:57 +02:00
logging . exception ( " cannot read config file " )
2015-05-23 07:48:02 +02:00
else :
2015-05-21 11:27:57 +02:00
2015-05-23 07:48:02 +02:00
try :
2015-05-25 18:07:44 +02:00
#
# Set the loglevel and backupCount of the file handler
#
logging . debug ( " set loglevel of fileHandler to: %s " , globals . config . getint ( " BOSWatch " , " loglevel " ) )
2015-05-23 07:48:02 +02:00
fh . setLevel ( globals . config . getint ( " BOSWatch " , " loglevel " ) )
2015-05-25 18:07:44 +02:00
logging . debug ( " set backupCount of fileHandler to: %s " , globals . config . getint ( " BOSWatch " , " backupCount " ) )
fh . setBackupCount ( globals . config . getint ( " BOSWatch " , " backupCount " ) )
2015-05-23 07:48:02 +02:00
except :
logging . exception ( " cannot set loglevel of fileHandler " )
2015-05-23 09:09:13 +02:00
2015-05-25 18:07:44 +02:00
#
# Load plugins
#
2015-05-22 22:40:44 +02:00
from includes import pluginLoader
pluginLoader . loadPlugins ( )
2015-05-15 20:54:42 +02:00
2015-05-25 18:07:44 +02:00
#
# Load filters
#
2015-05-24 21:24:26 +02:00
if globals . config . getint ( " BOSWatch " , " useRegExFilter " ) :
from includes import filter
2015-05-26 07:19:24 +02:00
filter . loadFilters ( )
2015-05-23 09:09:13 +02:00
2015-05-22 18:22:29 +02:00
try :
2015-05-25 18:07:44 +02:00
#
# Start rtl_fm
#
2015-05-21 11:27:57 +02:00
logging . debug ( " starting rtl_fm " )
2015-05-29 11:25:25 +02:00
rtl_fm = subprocess . Popen ( " rtl_fm -d " + str ( args . device ) + " -f " + str ( freqToHz ( args . freq ) ) + " -M fm -s 22050 -p " + str ( args . error ) + " -E DC -F 0 -l " + str ( args . squelch ) + " -g 100 " ,
2015-05-21 11:27:57 +02:00
#stdin=rtl_fm.stdout,
stdout = subprocess . PIPE ,
stderr = open ( globals . script_path + " /log/rtl_fm.log " , " a " ) ,
2015-05-22 18:22:29 +02:00
shell = True )
2015-05-21 11:27:57 +02:00
except :
logging . exception ( " cannot start rtl_fm " )
2015-05-22 18:22:29 +02:00
else :
2015-05-21 11:27:57 +02:00
try :
2015-05-25 18:07:44 +02:00
#
# Start multimon
#
2015-05-21 11:27:57 +02:00
logging . debug ( " starting multimon-ng " )
multimon_ng = subprocess . Popen ( " multimon-ng " + str ( demodulation ) + " -f alpha -t raw /dev/stdin - " ,
stdin = rtl_fm . stdout ,
stdout = subprocess . PIPE ,
stderr = open ( globals . script_path + " /log/multimon.log " , " a " ) ,
2015-05-22 18:22:29 +02:00
shell = True )
2015-05-21 11:27:57 +02:00
except :
logging . exception ( " cannot start multimon-ng " )
else :
logging . debug ( " start decoding " )
2015-05-22 16:44:23 +02:00
2015-05-21 11:27:57 +02:00
while True :
2015-05-25 18:07:44 +02:00
#
# Get decoded data from multimon-ng and call BOSWatch-decoder
#
# 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'
# POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World
2015-05-24 21:27:50 +02:00
decoded = str ( multimon_ng . stdout . readline ( ) ) #Get line data from multimon stdout
2015-05-21 11:27:57 +02:00
2015-05-25 18:07:44 +02:00
# Test-strings only for develop
2015-05-21 11:27:57 +02:00
#decoded = "ZVEI2: 25832"
2015-05-24 23:09:38 +02:00
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'"
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'"
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'"
2015-05-25 18:07:44 +02:00
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'"
2015-05-24 23:09:38 +02:00
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'"
#decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World"
2015-05-25 18:07:44 +02:00
#time.sleep(1)
2015-05-21 11:27:57 +02:00
2015-05-22 16:44:23 +02:00
from includes import decoder
2015-05-29 11:25:25 +02:00
decoder . decode ( freqToHz ( args . freq ) , decoded )
2015-05-22 08:27:50 +02:00
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-21 11:32:21 +02:00
logging . debug ( " BOSWatch shuting down " )
2015-05-20 19:48:46 +02:00
rtl_fm . terminate ( )
2015-05-20 13:29:16 +02:00
logging . debug ( " rtl_fm terminated " )
2015-05-20 19:48:46 +02:00
multimon_ng . terminate ( )
2015-05-20 13:29:16 +02:00
logging . debug ( " multimon-ng terminated " )
logging . debug ( " exiting BOSWatch " )
2015-05-15 20:54:42 +02:00
except :
2015-05-21 11:32:21 +02:00
logging . warning ( " failed in clean-up routine " )
2015-05-26 07:19:24 +02:00
finally :
2015-05-25 18:07:44 +02:00
# Close Logging
2015-05-26 07:19:24 +02:00
logging . debug ( " close Logging " )
2015-05-26 07:57:34 +02:00
logging . info ( " BOSWatch exit() " )
2015-05-25 18:07:44 +02:00
logging . shutdown ( )
fh . close ( )
ch . close ( )
2015-05-23 07:48:02 +02:00
exit ( 0 )