-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpreflight.py
226 lines (165 loc) · 7.47 KB
/
preflight.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/env python2.7
#
# Project Horus - Pre-Flight Check Web Interface
#
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
# Released under GNU GPL v3 or later
#
import json
import flask
from flask_socketio import SocketIO
import time
from datetime import datetime
from horuslib import *
from horuslib.wenet import *
from horuslib.listener import *
from horuslib.earthmaths import *
# Define Flask Application, and allow automatic reloading of templates for dev
app = flask.Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.jinja_env.auto_reload = True
# SocketIO instance
socketio = SocketIO(app)
# Global stores of data.
# Incoming OziMux data.
# One dictionary element will be produced per source name, and will be updated with data, and data age.
# Each entry will contain:
# 'source name'
# 'lat'
# 'lon'
# 'alt'
# 'age'
# 'timestamp'
current_ozimux = {}
# Incoming Payload summary data.
# One dictionary element will be produced per source name, and will be updated with data, and data age.
# Each entry will contain:
# 'callsign'
# 'lat'
# 'lon'
# 'alt'
# 'age'
# 'timestamp'
current_payload_summary = {}
# LoRa Data
current_lora = {
'frequency': 0.0,
'rssi': -300.0,
'payloads': {}
}
# Do not send packets of this type to the client's 'packet sniffer' log.
LAST_PACKETS_DISCARD = ['LOWPRIORITY', 'WENET', 'OZIMUX']
#
# Flask Routes
#
@app.route("/")
def flask_index():
""" Render main index page """
return flask.render_template('index.html')
@app.route("/server_time")
def get_server_time():
""" Return server time """
return json.dumps({'time':datetime.utcnow().isoformat()})
@app.route("/current_lora")
def get_lora_data():
return json.dumps(current_lora)
@app.route("/current_ozimux")
def get_ozimux():
return json.dumps(current_ozimux)
def flask_emit_event(event_name="none", data={}):
""" Emit a socketio event to any clients. """
socketio.emit(event_name, data, namespace='/update_status')
# Packet Handlers.
# These functions process data received via UDP and update the global stores of information.
def handle_wenet_packets(packet):
''' Handle Wenet payload specific packets '''
packet_type = decode_wenet_packet_type(packet)
if packet_type == WENET_PACKET_TYPES.TEXT_MESSAGE:
_text_data = decode_text_message(packet)
_packet_str = "Debug %d: " % _text_data['id'] + _text_data['text']
_packet_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
flask_emit_event('wenet_event', {'timestamp':_packet_time, 'msg':_packet_str})
elif packet_type == WENET_PACKET_TYPES.GPS_TELEMETRY:
_gps_telem_str = gps_telemetry_string(packet)
flask_emit_event('wenet_gps', {'data':_gps_telem_str})
else:
pass
def handle_packets(packet):
''' Handle received UDP packets '''
global current_lora
# Send a text representation of the packet to the web client.
if packet['type'] not in LAST_PACKETS_DISCARD:
_packet_str = " ".join(udp_packet_to_string(packet).split(" ")[1:])
_packet_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
flask_emit_event('log_event', {'timestamp':_packet_time, 'msg':_packet_str})
# Handle LoRa Status Messages
if packet['type'] == 'STATUS':
current_lora['frequency'] = packet['frequency']
current_lora['rssi'] = float(packet['rssi'])
# Indicate to the web client there is new data.
flask_emit_event('lora_event', current_lora)
# LoRa RX Packets
elif packet['type'] == 'RXPKT':
#{u'timestamp': u'2018-07-06T10:41:17.443986', u'snr': 9.25, u'rssi': -44, u'type': u'RXPKT', u'payload': [0, 0, 1, 15, 0, 10, 41, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 67, 0], u'freq_error': -130955}
# {'uplinkSlots': 0, 'batt_voltage': 1.6411764705882352, 'second': 34, 'current_timeslot': 0, 'speed': 0, 'payload_id': 1, 'sats': 0,
# 'altitude': 0, 'batt_voltage_raw': 194, 'payload_flags': 0, 'minute': 41, 'latitude': 0.0, 'RSSI': -97, 'rxPktCount': 0,
# 'pyro_voltage': 0.0, 'packet_type': 0, 'used_timeslots': 0, 'seconds_in_day': 38494, 'hour': 10, 'temp': 0, 'counter': 15, 'longitude': 0.0, 'time': '10:41:34', 'pyro_voltage_raw': 0}
payload_type = decode_payload_type(packet['payload'])
if payload_type == HORUS_PACKET_TYPES.PAYLOAD_TELEMETRY:
telemetry = decode_horus_payload_telemetry(packet['payload'])
_payload_id_str = str(telemetry['payload_id'])
if _payload_id_str not in current_lora['payloads']:
current_lora['payloads'][_payload_id_str] = telemetry
# Update a few fields.
current_lora['payloads'][_payload_id_str]['pkt_rssi'] = packet['rssi']
current_lora['payloads'][_payload_id_str]['pkt_snr'] = packet['snr']
current_lora['payloads'][_payload_id_str] = telemetry
# Indicate to the web client there is new data.
flask_emit_event('lora_event',current_lora)
elif packet['type'] == 'WENET':
handle_wenet_packets(packet['packet'])
elif packet['type'] == 'OZIMUX':
# Add data to our store of ozimux data
_src_name = packet['source_name']
if _src_name not in current_ozimux:
current_ozimux[_src_name] = {}
current_ozimux[_src_name]['source'] = _src_name
current_ozimux[_src_name]['latitude'] = packet['latitude']
current_ozimux[_src_name]['longitude'] = packet['longitude']
current_ozimux[_src_name]['altitude'] = packet['altitude']
current_ozimux[_src_name]['timestamp'] = datetime.now().strftime("%H:%M:%S")
# Indicate to the web client there is new data.
flask_emit_event('ozimux_event',current_ozimux)
elif packet['type'] == 'PAYLOAD_SUMMARY':
_callsign = packet['callsign']
if _callsign not in current_payload_summary:
current_payload_summary[_callsign] = {'snr': 0.0}
current_payload_summary[_callsign]['callsign'] = _callsign
current_payload_summary[_callsign]['latitude'] = packet['latitude']
current_payload_summary[_callsign]['longitude'] = packet['longitude']
current_payload_summary[_callsign]['altitude'] = packet['altitude']
current_payload_summary[_callsign]['timestamp'] = datetime.now().strftime("%H:%M:%S")
if 'snr' in packet:
if packet['snr'] > -255.0:
current_payload_summary[_callsign]['snr'] = packet['snr']
# Indicate to the web client there is new data.
flask_emit_event('payload_summary_event',current_payload_summary)
elif packet['type'] == 'MODEM_STATS':
_callsign = packet['source']
if _callsign not in current_payload_summary:
current_payload_summary[_callsign] = {'callsign': _callsign, 'latitude':-999.0, 'longitude': -999.0, 'altitude': 0.0, 'timestamp': datetime.now().strftime("%H:%M:%S")}
current_payload_summary[_callsign]['snr'] = packet['snr']
flask_emit_event('payload_summary_event',current_payload_summary)
pass
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-p","--port",default=5004,help="Port to run Web Server on.")
args = parser.parse_args()
horus_udp_rx = UDPListener(callback=handle_packets)
horus_udp_rx.start()
# Run the Flask app, which will block until CTRL-C'd.
socketio.run(app, host='0.0.0.0', port=args.port)
# Attempt to close the listener.
horus_udp_rx.close()