kopia lustrzana https://github.com/projecthorus/chasemapper
Add telemetry source profiles, which can be selected from within the web client.
rodzic
fdefbfedeb
commit
ccc98f6324
|
@ -48,15 +48,10 @@ def parse_config_file(filename):
|
||||||
chase_config['default_lon'] = config.get('map', 'default_lon')
|
chase_config['default_lon'] = config.get('map', 'default_lon')
|
||||||
chase_config['payload_max_age'] = config.getint('map', 'payload_max_age')
|
chase_config['payload_max_age'] = config.getint('map', 'payload_max_age')
|
||||||
|
|
||||||
# Source Selection
|
|
||||||
chase_config['data_source'] = config.get('source', 'type')
|
|
||||||
chase_config['ozimux_port'] = config.getint('source', 'ozimux_port')
|
|
||||||
chase_config['horus_udp_port'] = config.getint('source', 'horus_udp_port')
|
|
||||||
|
|
||||||
# Car GPS Data
|
# GPSD Settings
|
||||||
chase_config['car_gps_source'] = config.get('car_gps','source')
|
chase_config['car_gpsd_host'] = config.get('gpsd','gpsd_host')
|
||||||
chase_config['car_gpsd_host'] = config.get('car_gps','gpsd_host')
|
chase_config['car_gpsd_port'] = config.getint('gpsd','gpsd_port')
|
||||||
chase_config['car_gpsd_port'] = config.getint('car_gps','gpsd_port')
|
|
||||||
|
|
||||||
# Predictor
|
# Predictor
|
||||||
chase_config['pred_enabled'] = config.getboolean('predictor', 'predictor_enabled')
|
chase_config['pred_enabled'] = config.getboolean('predictor', 'predictor_enabled')
|
||||||
|
@ -66,6 +61,44 @@ def parse_config_file(filename):
|
||||||
chase_config['pred_gfs_directory'] = config.get('predictor', 'gfs_directory')
|
chase_config['pred_gfs_directory'] = config.get('predictor', 'gfs_directory')
|
||||||
chase_config['pred_model_download'] = config.get('predictor', 'model_download')
|
chase_config['pred_model_download'] = config.get('predictor', 'model_download')
|
||||||
|
|
||||||
|
# Telemetry Source Profiles
|
||||||
|
|
||||||
|
_profile_count = config.getint('profile_selection', 'profile_count')
|
||||||
|
_default_profile = config.getint('profile_selection', 'default_profile')
|
||||||
|
|
||||||
|
chase_config['selected_profile'] = ""
|
||||||
|
chase_config['profiles'] = {}
|
||||||
|
|
||||||
|
for i in range(1,_profile_count+1):
|
||||||
|
_profile_section = "profile_%d" % i
|
||||||
|
try:
|
||||||
|
_profile_name = config.get(_profile_section, 'profile_name')
|
||||||
|
_profile_telem_source_type = config.get(_profile_section, 'telemetry_source_type')
|
||||||
|
_profile_telem_source_port = config.getint(_profile_section, 'telemetry_source_port')
|
||||||
|
_profile_car_source_type = config.get(_profile_section, 'car_source_type')
|
||||||
|
_profile_car_source_port = config.getint(_profile_section, 'car_source_port')
|
||||||
|
|
||||||
|
chase_config['profiles'][_profile_name] = {
|
||||||
|
'name': _profile_name,
|
||||||
|
'telemetry_source_type': _profile_telem_source_type,
|
||||||
|
'telemetry_source_port': _profile_telem_source_port,
|
||||||
|
'car_source_type': _profile_car_source_type,
|
||||||
|
'car_source_port': _profile_car_source_port
|
||||||
|
}
|
||||||
|
if _default_profile == i:
|
||||||
|
chase_config['selected_profile'] = _profile_name
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error reading profile section %d - %s" % (i, str(e)))
|
||||||
|
|
||||||
|
if len(chase_config['profiles'].keys()) == 0:
|
||||||
|
logging.critical("Could not read any profile data!")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if chase_config['selected_profile'] not in chase_config['profiles']:
|
||||||
|
logging.critical("Default profile selection does not exist.")
|
||||||
|
return None
|
||||||
|
|
||||||
return chase_config
|
return chase_config
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,28 +4,49 @@
|
||||||
# Copy this file to horusmapper.cfg and modify as required.
|
# Copy this file to horusmapper.cfg and modify as required.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Telemetry Data Source
|
#
|
||||||
[source]
|
# Telemetry Source Profiles
|
||||||
# Data source type:
|
# Multiple Telemetry source profiles can be defined, and can be selected from
|
||||||
|
# the web GUI.
|
||||||
|
#
|
||||||
|
[profile_selection]
|
||||||
|
# How many profiles have been defined
|
||||||
|
profile_count = 1
|
||||||
|
# Index of the default profile (indexing from 1)
|
||||||
|
default_profile = 1
|
||||||
|
|
||||||
|
[profile_1]
|
||||||
|
# Profile name - will be shown in the web client.
|
||||||
|
profile_name = Default
|
||||||
|
# Telemetry source type:
|
||||||
# ozimux - Read data in OziMux format
|
# ozimux - Read data in OziMux format
|
||||||
# horus_udp - Read Horus UDP Broadcast 'Payload Summary' messages
|
# horus_udp - Read Horus UDP Broadcast 'Payload Summary' messages
|
||||||
type = ozimux
|
telemetry_source_type = ozimux
|
||||||
|
# Telemetry source port (UDP)
|
||||||
|
telemetry_source_port = 8942
|
||||||
|
|
||||||
# Ozimux Data source UDP port
|
# Car Position Source
|
||||||
ozimux_port = 8942
|
# none - No Chase-Car GPS
|
||||||
|
# horus_udp - Read Horus UDP Broadcast 'Car GPS' messages
|
||||||
|
# gpsd - Poll GPSD for positions (TO BE IMPLEMENTED)
|
||||||
|
car_source_type = horus_udp
|
||||||
|
# Car position source port (UDP) - only used if horus_udp is selected
|
||||||
|
car_source_port = 55672
|
||||||
|
|
||||||
# 'Payload Summary' (also car GPS messages) UDP listen port
|
# Other profiles can be defined in sections like the following:
|
||||||
horus_udp_port = 55672
|
[profile_2]
|
||||||
|
# Example source to take telemetry data from an instance of radiosonde_auto_rx
|
||||||
|
# emitting Horus UDP packets on port 55673, but still accept car positions via the
|
||||||
|
# 'standard' Horus UDP port 55672
|
||||||
|
profile_name = auto_rx
|
||||||
|
telemetry_source_type = horus_udp
|
||||||
|
telemetry_source_port = 55673
|
||||||
|
car_source_type = horus_udp
|
||||||
|
car_source_port = 55672
|
||||||
|
|
||||||
# Chase-Car Position Settings
|
|
||||||
[car_gps]
|
|
||||||
# Chase car Position data Source
|
|
||||||
# none - No Chase-Car GPS
|
|
||||||
# horus_udp - Read Horus UDP Broadcast 'Car GPS' messages
|
|
||||||
# gpsd - Poll GPSD for positions. (TO BE IMPLEMENTED)
|
|
||||||
source = horus_udp
|
|
||||||
|
|
||||||
# GPSD Host/Port
|
[gpsd]
|
||||||
|
# GPSD Host/Port - Only used if selected in the telemetry profile
|
||||||
# TO BE IMPLEMENTED
|
# TO BE IMPLEMENTED
|
||||||
gpsd_host = localhost
|
gpsd_host = localhost
|
||||||
gpsd_port = 2947
|
gpsd_port = 2947
|
||||||
|
@ -49,7 +70,7 @@ payload_max_age = 180
|
||||||
# https://github.com/darksidelemm/cusf_predictor_wrapper
|
# https://github.com/darksidelemm/cusf_predictor_wrapper
|
||||||
# You also need to compile the predictor binary, and copy it into this directory.
|
# You also need to compile the predictor binary, and copy it into this directory.
|
||||||
[predictor]
|
[predictor]
|
||||||
# Enable Predictor (True/False)
|
# Enable Predictor (True/False) - This can also be enabled from the web client.
|
||||||
predictor_enabled = False
|
predictor_enabled = False
|
||||||
|
|
||||||
# Predictor defaults - these can be modified at runtime in the web interface.
|
# Predictor defaults - these can be modified at runtime in the web interface.
|
||||||
|
|
125
horusmapper.py
125
horusmapper.py
|
@ -211,14 +211,15 @@ def predictorThread():
|
||||||
|
|
||||||
def run_prediction():
|
def run_prediction():
|
||||||
''' Run a Flight Path prediction '''
|
''' Run a Flight Path prediction '''
|
||||||
global chasemapper_config, current_payloads, current_payload_tracks, predictor
|
global chasemapper_config, current_payloads, current_payload_tracks, predictor, predictor_semaphore
|
||||||
|
|
||||||
if (predictor == None) or (chasemapper_config['pred_enabled'] == False):
|
if (predictor == None) or (chasemapper_config['pred_enabled'] == False):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the semaphore so we don't accidentally kill the predictor object while it's running.
|
# Set the semaphore so we don't accidentally kill the predictor object while it's running.
|
||||||
predictor_semaphore = True
|
predictor_semaphore = True
|
||||||
for _payload in current_payload_tracks:
|
_payload_list = list(current_payload_tracks.keys())
|
||||||
|
for _payload in _payload_list:
|
||||||
|
|
||||||
# Check the age of the data.
|
# Check the age of the data.
|
||||||
# No point re-running the predictor if the data is older than 30 seconds.
|
# No point re-running the predictor if the data is older than 30 seconds.
|
||||||
|
@ -322,8 +323,6 @@ def run_prediction():
|
||||||
current_payloads[_payload]['abort_path'] = []
|
current_payloads[_payload]['abort_path'] = []
|
||||||
current_payloads[_payload]['abort_landing'] = []
|
current_payloads[_payload]['abort_landing'] = []
|
||||||
|
|
||||||
predictor_semaphore = False
|
|
||||||
|
|
||||||
# Send the web client the updated prediction data.
|
# Send the web client the updated prediction data.
|
||||||
if _pred_ok or _abort_pred_ok:
|
if _pred_ok or _abort_pred_ok:
|
||||||
_client_data = {
|
_client_data = {
|
||||||
|
@ -336,6 +335,9 @@ def run_prediction():
|
||||||
}
|
}
|
||||||
flask_emit_event('predictor_update', _client_data)
|
flask_emit_event('predictor_update', _client_data)
|
||||||
|
|
||||||
|
# Clear the predictor-runnign semaphore
|
||||||
|
predictor_semaphore = False
|
||||||
|
|
||||||
|
|
||||||
def initPredictor():
|
def initPredictor():
|
||||||
global predictor, predictor_thread, chasemapper_config, pred_settings
|
global predictor, predictor_thread, chasemapper_config, pred_settings
|
||||||
|
@ -420,7 +422,7 @@ def download_new_model(data):
|
||||||
@socketio.on('payload_data_clear', namespace='/chasemapper')
|
@socketio.on('payload_data_clear', namespace='/chasemapper')
|
||||||
def clear_payload_data(data):
|
def clear_payload_data(data):
|
||||||
""" Clear the payload data store """
|
""" Clear the payload data store """
|
||||||
global predictor_semaphore, current_payloads
|
global predictor_semaphore, current_payloads, current_payload_tracks
|
||||||
logging.warning("Client requested all payload data be cleared.")
|
logging.warning("Client requested all payload data be cleared.")
|
||||||
# Wait until any current predictions have finished running.
|
# Wait until any current predictions have finished running.
|
||||||
while predictor_semaphore:
|
while predictor_semaphore:
|
||||||
|
@ -542,6 +544,88 @@ def check_data_age():
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
def start_listeners(profile):
|
||||||
|
""" Stop any currently running listeners, and startup a set of data listeners based on the supplied profile
|
||||||
|
|
||||||
|
Args:
|
||||||
|
profile (dict): A dictionary containing:
|
||||||
|
'name' (str): Profile name
|
||||||
|
'telemetry_source_type' (str): Data source type (ozimux or horus_udp)
|
||||||
|
'telemetry_source_port' (int): Data source port
|
||||||
|
'car_source_type' (str): Car Position source type (none, horus_udp or gpsd)
|
||||||
|
'car_source_port' (int): Car Position source port
|
||||||
|
"""
|
||||||
|
global data_listeners
|
||||||
|
# Stop any existing listeners.
|
||||||
|
for _thread in data_listeners:
|
||||||
|
try:
|
||||||
|
_thread.close()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error closing thread.")
|
||||||
|
|
||||||
|
# Reset the listeners array.
|
||||||
|
data_listeners = []
|
||||||
|
|
||||||
|
# Start up a OziMux listener, if we are using one.
|
||||||
|
if profile['telemetry_source_type'] == "ozimux":
|
||||||
|
logging.info("Using OziMux data source on UDP Port %d" % profile['telemetry_source_port'])
|
||||||
|
_ozi_listener = OziListener(telemetry_callback=ozi_listener_callback, port=profile['telemetry_source_port'])
|
||||||
|
data_listeners.append(_ozi_listener)
|
||||||
|
|
||||||
|
|
||||||
|
# Start up UDP Broadcast Listener (which we use for car positions even if not for the payload)
|
||||||
|
|
||||||
|
# Case 1 - Both telemetry and car position sources are set to horus_udp, and have the same port set. Only start a single UDP listener
|
||||||
|
if (profile['telemetry_source_type'] == "horus_udp") and (profile['car_source_type'] == "horus_udp") and (profile['car_source_port'] == profile['telemetry_source_port']):
|
||||||
|
# In this case, we start a single Horus UDP listener.
|
||||||
|
logging.info("Starting single Horus UDP listener on port %d" % profile['telemetry_source_port'])
|
||||||
|
_telem_horus_udp_listener = UDPListener(summary_callback=udp_listener_summary_callback,
|
||||||
|
gps_callback=udp_listener_car_callback,
|
||||||
|
port=profile['telemetry_source_port'])
|
||||||
|
_telem_horus_udp_listener.start()
|
||||||
|
data_listeners.append(_telem_horus_udp_listener)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if profile['telemetry_source_type'] == "horus_udp":
|
||||||
|
# Telemetry via Horus UDP - Start up a listener
|
||||||
|
logging.info("Starting Telemetry Horus UDP listener on port %d" % profile['telemetry_source_port'])
|
||||||
|
_telem_horus_udp_listener = UDPListener(summary_callback=udp_listener_summary_callback,
|
||||||
|
gps_callback=None,
|
||||||
|
port=profile['telemetry_source_port'])
|
||||||
|
_telem_horus_udp_listener.start()
|
||||||
|
data_listeners.append(_telem_horus_udp_listener)
|
||||||
|
|
||||||
|
if profile['car_source_type'] == "horus_udp":
|
||||||
|
# Car Position via Horus UDP - Start up a listener
|
||||||
|
logging.info("Starting Car Position Horus UDP listener on port %d" % profile['car_source_port'])
|
||||||
|
_car_horus_udp_listener = UDPListener(summary_callback=None,
|
||||||
|
gps_callback=udp_listener_car_callback,
|
||||||
|
port=profile['car_source_port'])
|
||||||
|
_car_horus_udp_listener.start()
|
||||||
|
data_listeners.append(_car_horus_udp_listener)
|
||||||
|
|
||||||
|
elif profile['car_source_type'] == "gpsd":
|
||||||
|
# GPSD Car Position Source - TODO
|
||||||
|
logging.info("Starting GPSD Car Position Listener.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# No Car position.
|
||||||
|
logging.info("No car position data source.")
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on('profile_change', namespace='/chasemapper')
|
||||||
|
def profile_change(data):
|
||||||
|
""" Client has requested a profile change """
|
||||||
|
global chasemapper_config
|
||||||
|
logging.info("Client requested change to profile: %s" % data)
|
||||||
|
|
||||||
|
# Change the profile, and restart the listeners.
|
||||||
|
chasemapper_config['selected_profile'] = data
|
||||||
|
start_listeners(chasemapper_config['profiles'][chasemapper_config['selected_profile']])
|
||||||
|
|
||||||
|
# Update all clients with the new profile selection
|
||||||
|
flask_emit_event('server_settings_update', chasemapper_config)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
|
@ -568,7 +652,7 @@ if __name__ == "__main__":
|
||||||
chasemapper_config = read_config(args.config)
|
chasemapper_config = read_config(args.config)
|
||||||
# Die if we cannot read a valid config file.
|
# Die if we cannot read a valid config file.
|
||||||
if chasemapper_config == None:
|
if chasemapper_config == None:
|
||||||
logging.critical("Could not read any configuration data. Exiting")
|
logging.critical("Could not read configuration data. Exiting")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Copy out the predictor settings to another dictionary.
|
# Copy out the predictor settings to another dictionary.
|
||||||
|
@ -578,31 +662,8 @@ if __name__ == "__main__":
|
||||||
'pred_model_download': chasemapper_config['pred_model_download']
|
'pred_model_download': chasemapper_config['pred_model_download']
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start up the primary data source
|
# Start listeners using the default profile selection.
|
||||||
if chasemapper_config['data_source'] == "ozimux":
|
start_listeners(chasemapper_config['profiles'][chasemapper_config['selected_profile']])
|
||||||
logging.info("Using OziMux data source.")
|
|
||||||
_ozi_listener = OziListener(telemetry_callback=ozi_listener_callback, port=chasemapper_config['ozimux_port'])
|
|
||||||
data_listeners.append(_ozi_listener)
|
|
||||||
|
|
||||||
# Start up UDP Broadcast Listener (which we use for car positions even if not for the payload)
|
|
||||||
if (chasemapper_config['data_source'] == "horus_udp") or (chasemapper_config['car_gps_source'] == "horus_udp"):
|
|
||||||
_horus_udp_port = chasemapper_config['horus_udp_port']
|
|
||||||
if chasemapper_config['data_source'] == "horus_udp":
|
|
||||||
_summary_callback = udp_listener_summary_callback
|
|
||||||
else:
|
|
||||||
_summary_callback = None
|
|
||||||
|
|
||||||
if chasemapper_config['car_gps_source'] == "horus_udp":
|
|
||||||
logging.info("Listening for Chase Car position via Horus UDP.")
|
|
||||||
_gps_callback = udp_listener_car_callback
|
|
||||||
else:
|
|
||||||
_gps_callback = None
|
|
||||||
|
|
||||||
logging.info("Starting Horus UDP Listener")
|
|
||||||
_horus_udp_listener = UDPListener(summary_callback=_summary_callback,
|
|
||||||
gps_callback=_gps_callback)
|
|
||||||
_horus_udp_listener.start()
|
|
||||||
data_listeners.append(_horus_udp_listener)
|
|
||||||
|
|
||||||
|
|
||||||
if chasemapper_config['pred_enabled']:
|
if chasemapper_config['pred_enabled']:
|
||||||
|
@ -624,4 +685,4 @@ if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
_thread.close()
|
_thread.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Error closing thread.")
|
logging.error("Error closing thread - %s" % str(e))
|
||||||
|
|
|
@ -106,6 +106,17 @@
|
||||||
$('#predUpdateRate').val(chase_config.pred_update_rate.toFixed(0));
|
$('#predUpdateRate').val(chase_config.pred_update_rate.toFixed(0));
|
||||||
$("#predictorEnabled").prop('checked', chase_config.pred_enabled);
|
$("#predictorEnabled").prop('checked', chase_config.pred_enabled);
|
||||||
$("#abortPredictionEnabled").prop('checked', chase_config.show_abort);
|
$("#abortPredictionEnabled").prop('checked', chase_config.show_abort);
|
||||||
|
|
||||||
|
// Clear and populate the profile selection.
|
||||||
|
$('#profileSelect').children('option:not(:first)').remove();
|
||||||
|
|
||||||
|
$.each(chase_config.profiles, function(key, value) {
|
||||||
|
$('#profileSelect')
|
||||||
|
.append($("<option></option>")
|
||||||
|
.attr("value",key)
|
||||||
|
.text(key));
|
||||||
|
});
|
||||||
|
$("#profileSelect").val(chase_config.selected_profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the System config on startup.
|
// Grab the System config on startup.
|
||||||
|
@ -143,6 +154,8 @@
|
||||||
if (isNaN(_update_rate) == false){
|
if (isNaN(_update_rate) == false){
|
||||||
chase_config.pred_update_rate = _update_rate
|
chase_config.pred_update_rate = _update_rate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
socket.emit('client_settings_update', chase_config);
|
socket.emit('client_settings_update', chase_config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,6 +172,11 @@
|
||||||
updateSettings();
|
updateSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#profileSelect").change(function(){
|
||||||
|
// On a profile selection change, emit a message to the client.
|
||||||
|
socket.emit('profile_change', this.value);
|
||||||
|
});
|
||||||
|
|
||||||
// Event handler for Log data.
|
// Event handler for Log data.
|
||||||
socket.on('log_event', function(msg) {
|
socket.on('log_event', function(msg) {
|
||||||
|
|
||||||
|
@ -596,7 +614,7 @@
|
||||||
if(data.burst.length == 3){
|
if(data.burst.length == 3){
|
||||||
// There is burst data!
|
// There is burst data!
|
||||||
if (balloon_positions[_callsign].burst_marker == null){
|
if (balloon_positions[_callsign].burst_marker == null){
|
||||||
var _burst_txt = _callsign + "Burst (" + data.burst[2].toFixed(0) + "m)";
|
var _burst_txt = _callsign + " Burst (" + data.burst[2].toFixed(0) + "m)";
|
||||||
balloon_positions[_callsign].burst_marker = L.marker(data.burst,{title:_burst_txt, icon: burstIcon})
|
balloon_positions[_callsign].burst_marker = L.marker(data.burst,{title:_burst_txt, icon: burstIcon})
|
||||||
.bindTooltip(_burst_txt,{permanent:false,direction:'right'})
|
.bindTooltip(_burst_txt,{permanent:false,direction:'right'})
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
|
@ -764,6 +782,11 @@
|
||||||
<div class="sidebar-pane" id="settings">
|
<div class="sidebar-pane" id="settings">
|
||||||
<h1 class="sidebar-header">Settings<span class="sidebar-close"><i class="fa fa-caret-left"></i></span></h1>
|
<h1 class="sidebar-header">Settings<span class="sidebar-close"><i class="fa fa-caret-left"></i></span></h1>
|
||||||
</hr>
|
</hr>
|
||||||
|
<h3>Data Source</h3>
|
||||||
|
<select id="profileSelect" name="profileSelect">
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<h3>Map</h3>
|
||||||
<h4>Auto-Follow</h4>
|
<h4>Auto-Follow</h4>
|
||||||
<form>
|
<form>
|
||||||
<input type="radio" name="autoFollow" value="payload" checked> Payload<br>
|
<input type="radio" name="autoFollow" value="payload" checked> Payload<br>
|
||||||
|
@ -771,13 +794,12 @@
|
||||||
<input type="radio" name="autoFollow" value="none"> None
|
<input type="radio" name="autoFollow" value="none"> None
|
||||||
</form>
|
</form>
|
||||||
</hr>
|
</hr>
|
||||||
<h4>Map</h4>
|
|
||||||
<div class="paramRow">
|
<div class="paramRow">
|
||||||
<b>Show Car Track</b> <input type="checkbox" class="paramSelector" id="chaseCarTrack" checked>
|
<b>Show Car Track</b> <input type="checkbox" class="paramSelector" id="chaseCarTrack" checked>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</hr>
|
</hr>
|
||||||
<h4>Predictor</h4>
|
<h3>Predictor</h3>
|
||||||
<div class="paramRow" id="predictorModel">
|
<div class="paramRow" id="predictorModel">
|
||||||
<b>Current Model: </b> Predictor Disabled
|
<b>Current Model: </b> Predictor Disabled
|
||||||
</div>
|
</div>
|
||||||
|
@ -800,7 +822,7 @@
|
||||||
<b>Update Rate</b><input type="text" class="paramEntry" id="predUpdateRate"><br/>
|
<b>Update Rate</b><input type="text" class="paramEntry" id="predUpdateRate"><br/>
|
||||||
</div>
|
</div>
|
||||||
</hr>
|
</hr>
|
||||||
<h4>Other</h4>
|
<h3>Other</h3>
|
||||||
<div class="paramRow">
|
<div class="paramRow">
|
||||||
<button type="button" class="paramSelector" id="clearPayloadData">Clear Payload Data</button></br>
|
<button type="button" class="paramSelector" id="clearPayloadData">Clear Payload Data</button></br>
|
||||||
</div>
|
</div>
|
||||||
|
|
Ładowanie…
Reference in New Issue