""" Python program using flask and socketio to provide real tiime asynchronous signal level and SNR data from a ATSC signal received with a connected Hauppauge WinTV dualHD tune. This program and the associated index.html and signalapp.js files are based on the files presented in Shane Lynn's tutorial at: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io and files at: https://github.com/shanealynn/async_flask with updates and modifications by Doug Lung The latest version and support files are available at: http://www.transmitter.com/bts/Raspberry%20Pi%20Remote%20ATSC-DVB%20Test%20Set.html """ from flask_socketio import SocketIO, emit from flask import Flask, render_template, url_for, copy_current_request_context from random import random import time from time import sleep from threading import Thread, Event import subprocess import shlex import eventlet eventlet.monkey_patch() async_mode = "eventlet" #create the flask web server app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' app.config['DEBUG'] = True #turn the flask app into a socketio app socketio = SocketIO(app, async_mode="eventlet", logger=True, engineio_logger=True) #SignalDataServer thread thread = Thread() thread_stop_event = Event() def SignalDataServer(): """ Use subprocess to launch a continuously running shell script sigdata2.sh. parse the output from sigdata2.sh to extract the readings for signal level and SNR. Convert the strings to a floating point number and save it as the maximum if it is greater than the previously saved maximum. """ print("Serving Signal Data") continuity_count = 0 siglevel = '' snrlevel = '' dBm = -99.9 snrdB = 0.1 maxdBm = -99 maxSNR = 5 sigcount = 0 snrcount = 0 command = 'sh ./sigdata2.sh' process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) while not thread_stop_event.isSet(): output = process.stdout.readline() if output: output = output.decode() if ") Signal=" in output : sigcount = sigcount + 1 # Throw away first three readings to give the tuner time to stabilize if sigcount > 3 : siglevel = output.split(') Signal=')[1].strip().split()[0] print('level',siglevel) dBm = float(siglevel.split('dBm')[0]) if "C/N= " in output : snrlevel = output.split('C/N= ')[1].strip().split()[0] print('snrlevel =',snrlevel) snrdB = float(snrlevel.split('dB')[0]) if dBm > maxdBm : maxdBm = dBm if snrdB > maxSNR : maxSNR = snrdB if (snrlevel != '') and (siglevel != '') : SignalData = 'Signal strength: ' + siglevel + ' SNR: ' + snrlevel +'\n'+ 'Maximum Signal: ' + str(maxdBm) + ' dBm' + ' SNR: ' + str(maxSNR) + ' dB' print(SignalData) #Send the data to the browser socketio.emit('newdata', {'SignalData': SignalData}, namespace='/test') snrlevel = '' siglevel = '' socketio.sleep(1) #Pause for a second @app.route('/') def index(): #only by sending this page first will the client be connected to the socketio instance return render_template('index.html') @socketio.on('connect', namespace='/test') def test_connect(): # need visibility of the global thread object global thread print('Client connected') #Start the SignalDataServer thread only if the thread has not been started before. if not thread.isAlive(): print("Starting Thread") thread = socketio.start_background_task(SignalDataServer) @socketio.on('disconnect', namespace='/test') def test_disconnect(): print('Client disconnected') #This IP address should serve the web page on the IP address of the device running #the program. Use the actual IP address of the device if this isn't working. #The browser will have to specify the specified port number to access the web page. if __name__ == '__main__': socketio.run(app,host='0.0.0.0',port=5000)