Initial sondehub v2 integration

pull/32/head
Mark Jessop 2021-04-10 10:50:00 +09:30
rodzic 6c3765896d
commit c888eef454
12 zmienionych plików z 484 dodań i 97 usunięć

Wyświetl plik

@ -14,8 +14,9 @@ It will quite happily run alongside other Project Horus applications such as [ra
You can often find me in the #highaltitude IRC Channel on [Freenode](https://webchat.freenode.net/).
### Help Wanted!
Currently Chasemapper is a bit mandrolic to set up, and this could be improved considerably, perhaps through the use of a docker container or similar. This isn't my area of expertise, so any assistance with this would be much appreciated!
## Update Notes
* If you have previously had chasemapper or auto_rx installed, you may need to update flask-socketio to the most recent version. You can do this by running `sudo pip3 install -U flask-socketio`
* As of mid-April, there have been additions to the chasemapper configuration file format to enable interfacing with the new [v2 Sondehub Tracker](https://v2.sondehub.org/), including a selection for the online tracker in use. You will need to update your configuration files for chasemapper to continue working. Integration with Sondehub v2 is still in progress.
## Dependencies
@ -32,8 +33,6 @@ You also need flask-socketio (>=5.0.0) and pytz, which can be installed using pi
$ sudo pip3 install flask-socketio pytz
```
**Note: If you have previously had chasemapper or auto_rx installed, you may need to update flask-socketio to the most recent version. You can do this by running `sudo pip3 install -U flask-socketio`**
You can then clone this repository with:
```
$ git clone https://github.com/projecthorus/chasemapper.git

Wyświetl plik

@ -8,4 +8,4 @@
# Now using Semantic Versioning (https://semver.org/) MAJOR.MINOR.PATCH
__version__ = "1.1.0"
__version__ = "1.2.0"

Wyświetl plik

@ -199,12 +199,15 @@ def parse_config_file(filename):
_profile_section, "car_source_port"
)
_profile_online_tracker = config.get(_profile_section, "online_tracker")
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,
"online_tracker": _profile_online_tracker,
}
if _default_profile == i:
chase_config["selected_profile"] = _profile_name

Wyświetl plik

@ -205,6 +205,20 @@ class HabitatChaseUploader(object):
""" Set the callsign """
self.callsign = call
def mark_payload_recovered(self, callsign, latitude, longitude, altitude, message):
""" Upload an indication that a payload (radiosonde or otherwise) has been recovered """
try:
initListenerCallsign(callsign, radio="", antenna=message)
uploadListenerPosition(callsign, latitude, longitude, altitude, chase=False)
except Exception as e:
logging.error(
"Habitat - Unable to mark payload as recovered - %s" % (str(e))
)
return
logging.info("Habitat - Payload marked as recovered.")
def close(self):
self.uploader_thread_running = False
try:

Wyświetl plik

@ -52,7 +52,7 @@ def read_last_balloon_telemetry():
if _entry["log_type"] == "BALLOON TELEMETRY":
telemetry_found = True
_last_telemetry = _entry
if telemetry_found == True:
_last_telemetry["time_dt"] = parse(_last_telemetry.pop("time"))
return _last_telemetry

Wyświetl plik

@ -0,0 +1,186 @@
#!/usr/bin/env python
#
# Project Horus - Browser-Based Chase Mapper
# Sondehub Communication (Chase car position upload)
#
# Copyright (C) 2021 Mark Jessop <vk5qi@rfhead.net>
# Released under GNU GPL v3 or later
#
import chasemapper
import datetime
import logging
import requests
import time
import traceback
import json
from base64 import b64encode
from hashlib import sha256
from threading import Thread, Lock
try:
# Python 2
from Queue import Queue
except ImportError:
# Python 3
from queue import Queue
class SondehubChaseUploader(object):
""" Upload supplied chase car positions to Sondehub on a regular basis """
SONDEHUB_STATION_POSITION_URL = "https://api.v2.sondehub.org/listeners"
def __init__(
self,
update_rate=30,
callsign="N0CALL",
upload_enabled=True,
upload_timeout=10,
upload_retries=2,
):
""" Initialise the Sondehub Chase uploader, and start the update thread """
self.update_rate = update_rate
self.callsign = callsign
self.callsign_init = False
self.upload_enabled = upload_enabled
self.upload_timeout = upload_timeout
self.upload_retries = upload_retries
self.car_position = None
self.car_position_lock = Lock()
self.uploader_thread_running = True
self.uploader_thread = Thread(target=self.upload_thread)
self.uploader_thread.start()
logging.info("Sondehub - Chase-Car Position Uploader Started")
def update_position(self, position):
""" Update the chase car position state
This function accepts and stores a copy of the same dictionary structure produced by both
Horus UDP broadcasts, and the serial GPS and GPSD modules
"""
with self.car_position_lock:
self.car_position = position.copy()
def upload_thread(self):
""" Uploader thread """
while self.uploader_thread_running:
# Grab a copy of the most recent car position.
with self.car_position_lock:
if self.car_position != None:
_position = self.car_position.copy()
else:
_position = None
if self.upload_enabled and _position != None:
try:
# Upload the listener position.
self.upload_position(
self.callsign,
_position["latitude"],
_position["longitude"],
_position["altitude"],
)
except Exception as e:
logging.error(
"Sondehub - Error uploading chase-car position - %s" % str(e)
)
# Wait for next update.
_i = 0
while (_i < self.update_rate) and self.uploader_thread_running:
time.sleep(1)
_i += 1
def set_update_rate(self, rate):
""" Set the update rate """
self.update_rate = int(rate)
def set_callsign(self, call):
""" Set the callsign """
self.callsign = call
def upload_position(
self, callsign, latitude, longitude, altitude, antenna="Chase Car", mobile=True
):
""" Upload a chase car position to Sondehub
This uses the PUT /listeners API described here:
https://github.com/projecthorus/sondehub-infra/wiki/API-(Beta)
"""
_position = {
"software_name": "ChaseMapper",
"software_version": chasemapper.__version__,
"uploader_callsign": callsign,
"uploader_position": [latitude, longitude, altitude],
"uploader_antenna": antenna,
"uploader_contact_email": "none@none.com",
"mobile": mobile,
}
_retries = 0
_upload_success = False
_start_time = time.time()
while _retries < self.upload_retries:
# Run the request.
try:
headers = {
"User-Agent": "chasemapper-" + chasemapper.__version__,
"Content-Type": "application/json",
}
_req = requests.put(
self.SONDEHUB_STATION_POSITION_URL,
json=_position,
# TODO: Revisit this second timeout value.
timeout=(self.upload_timeout, 6.1),
headers=headers,
)
except Exception as e:
logging.error("Sondehub - Upload Failed: %s" % str(e))
return
if _req.status_code == 200:
# 200 is the only status code that we accept.
_upload_time = time.time() - _start_time
logging.debug("Sondehub - Uploaded chase-car position to Sondehub.")
_upload_success = True
break
elif _req.status_code == 500:
# Server Error, Retry.
_retries += 1
continue
else:
logging.error(
"Sondehub - Error uploading chase-car position to Sondehub. Status Code: %d %s."
% (_req.status_code, _req.text)
)
break
if not _upload_success:
logging.error(
"Sondehub - Chase-car position upload failed after %d retries"
% (_retries)
)
logging.debug(f"Attempted to upload {json.dumps(_position)}")
def mark_payload_recovered(self, callsign, latitude, longitude, altitude, message):
""" Upload an indication that a payload (radiosonde or otherwise) has been recovered """
# TODO
pass
def close(self):
self.uploader_thread_running = False
try:
self.uploader_thread.join()
except:
pass
logging.info("Sondehub - Chase-Car Position Uploader Closed")

Wyświetl plik

@ -37,11 +37,11 @@ car_source_type = gpsd
# Car position source port (UDP) - only used if horus_udp is selected, but still needs to be provided.
car_source_port = 12345
# Car Position Upload System
# Where to upload chase-car positions to
# Online Tracker System
# Where to upload chase-car positions and balloon recovery notifications to.
# sondehub = Sondehub v2 Database, for viewing on the SondeHub tracker (https://tracker.sondehub.org)
# habitat = Habitat Database, for viewing on the HabHub tracker (https://tracker.habhub.org)
car_upload = sondehub
online_tracker = sondehub
# Other profiles can be defined in sections like the following:
[profile_2]
@ -58,11 +58,11 @@ car_source_type = serial
# Make sure to update the gps_serial settings further down in the configuration file!
car_source_port = 55672
# Car Upload System
# Where to upload chase-car positions to
# Online Tracker System
# Where to upload chase-car positions and balloon recovery notifications to.
# sondehub = Sondehub v2 Database, for viewing on the SondeHub tracker (https://tracker.sondehub.org)
# habitat = Habitat Database, for viewing on the HabHub tracker (https://tracker.habhub.org)
car_upload = habitat
online_tracker = habitat
# If you want add more profiles, you can do so here, e.g.
# [profile_3]
@ -165,8 +165,8 @@ tile_server_path = /home/pi/Maps/
#
# Habitat Chase-Car Position Upload
# If you want, this application can upload your chase-car position to the Habhub tracker,
# Habitat / SondeHub Chase-Car Position Upload
# If you want, this application can upload your chase-car position to the Habhub / Sondehub trackers,
# for those follwing along at home.
# The settings below can be modified from the web interface, but they will default to what is set below on startup.
#

Wyświetl plik

@ -6,6 +6,7 @@
# Released under GNU GPL v3 or later
#
import sys
# Version check.
if sys.version_info < (3, 6):
print("CRITICAL - chasemapper requires Python 3.6 or newer!")
@ -37,6 +38,7 @@ from chasemapper.habitat import (
initListenerCallsign,
uploadListenerPosition,
)
from chasemapper.sondehub import SondehubChaseUploader
from chasemapper.logger import ChaseLogger
from chasemapper.logread import read_last_balloon_telemetry
from chasemapper.bearings import Bearings
@ -84,8 +86,8 @@ car_track = GenericTrack()
# Bearing store
bearing_store = None
# Habitat Chase-Car uploader object
habitat_uploader = None
# Habitat/Sondehub Chase-Car uploader object
online_uploader = None
# Copy out any extra fields from incoming telemetry that we want to pass on to the GUI.
# At the moment we're really only using the burst timer field.
@ -146,7 +148,7 @@ def flask_emit_event(event_name="none", data={}):
@socketio.on("client_settings_update", namespace="/chasemapper")
def client_settings_update(data):
global chasemapper_config, habitat_uploader
global chasemapper_config, online_uploader
_predictor_change = "none"
if (chasemapper_config["pred_enabled"] == False) and (data["pred_enabled"] == True):
@ -184,20 +186,33 @@ def client_settings_update(data):
# Start or Stop the Habitat Chase-Car Uploader.
if _habitat_change == "start":
if habitat_uploader == None:
habitat_uploader = HabitatChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
if online_uploader == None:
_tracker = chasemapper_config["profiles"][
chasemapper_config["selected_profile"]
]["online_tracker"]
if _tracker == "habitat":
online_uploader = HabitatChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
elif _tracker == "sondehub":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
else:
logging.error(
"Unknown Online Tracker %s, not starting uploader." % _tracker
)
elif _habitat_change == "stop":
habitat_uploader.close()
habitat_uploader = None
online_uploader.close()
online_uploader = None
# Update the habitat uploader with a new update rate, if one has changed.
if habitat_uploader != None:
habitat_uploader.set_update_rate(chasemapper_config["habitat_update_rate"])
habitat_uploader.set_callsign(chasemapper_config["habitat_call"])
if online_uploader != None:
online_uploader.set_update_rate(chasemapper_config["habitat_update_rate"])
online_uploader.set_callsign(chasemapper_config["habitat_call"])
# Push settings back out to all clients.
flask_emit_event("server_settings_update", chasemapper_config)
@ -690,24 +705,22 @@ def clear_bearing_data(data):
@socketio.on("mark_recovered", namespace="/chasemapper")
def mark_payload_recovered(data):
""" Mark a payload as recovered, by uploading a station position """
global online_uploader
_callsign = data["recovery_title"]
_lat = data["last_pos"][0]
_lon = data["last_pos"][1]
_alt = data["last_pos"][2]
_msg = data["message"]
_timestamp = "Recovered at " + datetime.utcnow().strftime("%Y-%m-%d %H:%MZ")
_msg = (
data["message"]
+ " Recovered at "
+ datetime.utcnow().strftime("%Y-%m-%d %H:%MZ")
)
try:
initListenerCallsign(_callsign, radio=_msg, antenna=_timestamp)
uploadListenerPosition(_callsign, _lat, _lon, _alt, chase=False)
except Exception as e:
logging.error(
"Unable to mark %s as recovered - %s" % (data["payload_call"], str(e))
)
return
logging.info("Payload %s marked as recovered." % data["payload_call"])
if online_uploader != None:
online_uploader.mark_payload_recovered(_callsign, _lat, _lon, _alt, _msg)
else:
logging.error("No Online Tracker enabled, could not mark payload as recovered.")
# Incoming telemetry handlers
@ -788,7 +801,7 @@ def udp_listener_car_callback(data):
""" Handle car position data """
# TODO: Make a generic car position function, and have this function pass data into it
# so we can add support for other chase car position inputs.
global car_track, habitat_uploader, bearing_store
global car_track, online_uploader, bearing_store
_lat = float(data["latitude"])
_lon = float(data["longitude"])
@ -833,9 +846,9 @@ def udp_listener_car_callback(data):
},
)
# Update the Habitat Uploader, if one exists.
if habitat_uploader != None:
habitat_uploader.update_position(data)
# Update the Online Position Uploader, if one exists.
if online_uploader != None:
online_uploader.update_position(data)
# Update the bearing store with the current car state (position & bearing)
if bearing_store != None:
@ -904,8 +917,12 @@ def start_listeners(profile):
'telemetry_source_port' (int): Data source port
'car_source_type' (str): Car Position source type (none, horus_udp, gpsd, or station)
'car_source_port' (int): Car Position source port
'online_tracker' (str): Which online tracker to upload chase-car info to ('habitat' or 'sondehub')
"""
global data_listeners
global data_listeners, current_profile, online_uploader, chasemapper_config
current_profile = profile
# Stop any existing listeners.
for _thread in data_listeners:
try:
@ -913,9 +930,32 @@ def start_listeners(profile):
except Exception as e:
logging.error("Error closing thread - %s" % str(e))
# Shut-down any online uploaders
if online_uploader != None:
online_uploader.close()
online_uploader = None
# Reset the listeners array.
data_listeners = []
# Start up a new online uploader immediately if uploading is already enabled.
if chasemapper_config["habitat_upload_enabled"] == True:
if profile["online_tracker"] == "habitat":
online_uploader = HabitatChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
elif profile["online_tracker"] == "sondehub":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
else:
logging.error(
"Unknown Online Tracker %s, not starting uploader"
% (profile["online_tracker"])
)
# Start up a OziMux listener, if we are using one.
if profile["telemetry_source_type"] == "ozimux":
logging.info(
@ -1023,6 +1063,7 @@ def profile_change(data):
# Update all clients with the new profile selection
flask_emit_event("server_settings_update", chasemapper_config)
@socketio.on("device_position", namespace="/chasemapper")
def device_position_update(data):
""" Accept a device position update from a client and process it as if it was a chase car position """
@ -1030,8 +1071,6 @@ def device_position_update(data):
udp_listener_car_callback(data)
except:
pass
class WebHandler(logging.Handler):
@ -1113,7 +1152,7 @@ if __name__ == "__main__":
sys.exit(1)
# Add in Chasemapper version information.
chasemapper_config['version'] = CHASEMAPPER_VERSION
chasemapper_config["version"] = CHASEMAPPER_VERSION
# Copy out the predictor settings to another dictionary.
pred_settings = {
@ -1147,13 +1186,6 @@ if __name__ == "__main__":
if chasemapper_config["pred_enabled"]:
initPredictor()
# Start up the Habitat Chase-Car Uploader, if enabled
if chasemapper_config["habitat_upload_enabled"]:
habitat_uploader = HabitatChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
# Read in last known position, if enabled
if chasemapper_config["reload_last_position"]:
@ -1188,8 +1220,8 @@ if __name__ == "__main__":
if chase_logger:
chase_logger.close()
if habitat_uploader != None:
habitat_uploader.close()
if online_uploader != None:
online_uploader.close()
# Attempt to close the running listeners.
for _thread in data_listeners:

Wyświetl plik

@ -1,7 +1,7 @@
//
// Project Horus - Browser-Based Chase Mapper - Habitat Data Scraping
//
// Copyright (C) 2019 Mark Jessop <vk5qi@rfhead.net>
// Copyright (C) 2021 Mark Jessop <vk5qi@rfhead.net>
// Released under GNU GPL v3 or later
//
@ -15,11 +15,6 @@ var spacenearus_last_position_id = 0;
// Not really sure if this is necessary.
var snear_request_running = false;
// Store for vehicle data.
var habitat_vehicles = {};
// Only add chase cars which are (initially) within this range limit (km).
var habitat_vehicle_max_range = 200.0;
function process_habitat_vehicles(data){
// Check we have a 'valid' response to process.
@ -55,37 +50,37 @@ function process_habitat_vehicles(data){
// If the vehicle is already known to us, then update it position.
// Update any existing entries (even if the range is above the threshold)
if (habitat_vehicles.hasOwnProperty(vcallsign)){
if (chase_vehicles.hasOwnProperty(vcallsign)){
// Only update if the position ID of this position is newer than that last seen.
if (habitat_vehicles[vcallsign].position_id < position.position_id){
if (chase_vehicles[vcallsign].position_id < position.position_id){
//console.log("Updating: " + vcallsign);
// Update the position ID.
habitat_vehicles[vcallsign].position_id = position.position_id;
chase_vehicles[vcallsign].position_id = position.position_id;
// Since we don't always get a heading with the vehicle position, calculate it.
var old_v_pos = {lat:habitat_vehicles[vcallsign].latest_data[0],
lon: habitat_vehicles[vcallsign].latest_data[1],
alt:habitat_vehicles[vcallsign].latest_data[2]};
var old_v_pos = {lat:chase_vehicles[vcallsign].latest_data[0],
lon: chase_vehicles[vcallsign].latest_data[1],
alt:chase_vehicles[vcallsign].latest_data[2]};
var new_v_pos = {lat: v_lat, lon:v_lon, alt:v_alt};
habitat_vehicles[vcallsign].heading = calculate_lookangles(old_v_pos, new_v_pos).azimuth;
chase_vehicles[vcallsign].heading = calculate_lookangles(old_v_pos, new_v_pos).azimuth;
// Update the position data.
habitat_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
// Update the marker position.
habitat_vehicles[vcallsign].marker.setLatLng(habitat_vehicles[vcallsign].latest_data).update();
chase_vehicles[vcallsign].marker.setLatLng(chase_vehicles[vcallsign].latest_data).update();
// Rotate/replace the icon to match the bearing.
var _car_heading = habitat_vehicles[vcallsign].heading - 90.0;
var _car_heading = chase_vehicles[vcallsign].heading - 90.0;
if (_car_heading<=90.0){
habitat_vehicles[vcallsign].marker.setIcon(habitat_car_icons[habitat_vehicles[vcallsign].colour]);
habitat_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
chase_vehicles[vcallsign].marker.setIcon(habitat_car_icons[chase_vehicles[vcallsign].colour]);
chase_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
}else{
// We are travelling West - we need to use the flipped car icon.
_car_heading = _car_heading - 180.0;
habitat_vehicles[vcallsign].marker.setIcon(habitat_car_icons_flipped[habitat_vehicles[vcallsign].colour]);
habitat_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
chase_vehicles[vcallsign].marker.setIcon(habitat_car_icons_flipped[chase_vehicles[vcallsign].colour]);
chase_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
}
return;
}
@ -107,31 +102,31 @@ function process_habitat_vehicles(data){
var v_range = calculate_lookangles(my_pos, v_pos).range/1000.0;
// If the range is less than the threshold, add it to our list of chase vehicles.
if(v_range < habitat_vehicle_max_range){
if(v_range < vehicle_max_range){
//console.log("Adding: " + vcallsign);
habitat_vehicles[vcallsign] = {};
chase_vehicles[vcallsign] = {};
// Initialise a few default values
habitat_vehicles[vcallsign].heading = 90;
habitat_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
habitat_vehicles[vcallsign].position_id = position.position_id;
chase_vehicles[vcallsign].heading = 90;
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
chase_vehicles[vcallsign].position_id = position.position_id;
// Get an index for the car icon. This is incremented for each vehicle,
// giving each a different colour.
habitat_vehicles[vcallsign].colour = car_colour_values[car_colour_idx];
chase_vehicles[vcallsign].colour = car_colour_values[car_colour_idx];
car_colour_idx = (car_colour_idx+1)%car_colour_values.length;
// Create marker
habitat_vehicles[vcallsign].marker = L.marker(habitat_vehicles[vcallsign].latest_data,
chase_vehicles[vcallsign].marker = L.marker(chase_vehicles[vcallsign].latest_data,
{title:vcallsign,
icon: habitat_car_icons[habitat_vehicles[vcallsign].colour],
icon: habitat_car_icons[chase_vehicles[vcallsign].colour],
rotationOrigin: "center center"})
.addTo(map);
// Keep our own record of if this marker has been added to a map,
// as we shouldn't be using the private _map property of the marker object.
habitat_vehicles[vcallsign].onmap = true;
chase_vehicles[vcallsign].onmap = true;
// Add tooltip, with custom CSS which removes all tooltip borders, and adds a text shadow.
habitat_vehicles[vcallsign].marker.bindTooltip(vcallsign,
chase_vehicles[vcallsign].marker.bindTooltip(vcallsign,
{permanent: true,
direction: 'center',
offset:[0,25],
@ -150,6 +145,7 @@ function get_habitat_vehicles(){
if(!snear_request_running){
snear_request_running = true;
console.log("Requesting vehicles from Habitat...")
$.ajax({
url: snear_request_url,
dataType: 'json',
@ -166,19 +162,19 @@ function get_habitat_vehicles(){
// Show/Hide all vehicles.
function show_habitat_vehicles(){
var state = document.getElementById("showOtherCars").checked;
for (_car in habitat_vehicles){
for (_car in chase_vehicles){
// Add to map, if its not already on there.
if(state){
if(!habitat_vehicles[_car].onmap){
habitat_vehicles[_car].marker.addTo(map);
habitat_vehicles[_car].onmap = true;
if(!chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.addTo(map);
chase_vehicles[_car].onmap = true;
}
} else{
if(habitat_vehicles[_car].onmap){
habitat_vehicles[_car].marker.remove();
habitat_vehicles[_car].onmap = false;
if(chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.remove();
chase_vehicles[_car].onmap = false;
}
}
}
}
}

Wyświetl plik

@ -0,0 +1,143 @@
//
// Project Horus - Browser-Based Chase Mapper - SondeHub Data Scraping
//
// Copyright (C) 2021 Mark Jessop <vk5qi@rfhead.net>
// Released under GNU GPL v3 or later
//
// URL to scrape recent vehicle position data from.
// TODO: Allow adjustment of the number of positions to request.
var sondehub_vehicle_url = "https://v2.api";
function process_sondehub_vehicles(data){
// Check we have a 'valid' response to process.
if (data === null ||
!data.positions ||
!data.positions.position ||
!data.positions.position.length) {
snear_request_running = false;
return;
}
data.positions.position.forEach(function(position){
// Update the highest position ID, so we don't request old data.
if (position.position_id > spacenearus_last_position_id){
spacenearus_last_position_id = position.position_id;
}
var vcallsign = position.vehicle;
// Check this isn't our callsign.
// If it is, don't process it.
if (vcallsign.startsWith(chase_config.habitat_call)){
return;
}
// Determine if the vehicle is a chase car.
// This is denoted by _chase at the end of the callsign.
if(vcallsign.search(/(chase)/i) != -1) {
var v_lat = parseFloat(position.gps_lat);
var v_lon = parseFloat(position.gps_lon);
var v_alt = parseFloat(position.gps_alt);
// If the vehicle is already known to us, then update it position.
// Update any existing entries (even if the range is above the threshold)
if (chase_vehicles.hasOwnProperty(vcallsign)){
// Only update if the position ID of this position is newer than that last seen.
if (chase_vehicles[vcallsign].position_id < position.position_id){
//console.log("Updating: " + vcallsign);
// Update the position ID.
chase_vehicles[vcallsign].position_id = position.position_id;
// Since we don't always get a heading with the vehicle position, calculate it.
var old_v_pos = {lat:chase_vehicles[vcallsign].latest_data[0],
lon: chase_vehicles[vcallsign].latest_data[1],
alt:chase_vehicles[vcallsign].latest_data[2]};
var new_v_pos = {lat: v_lat, lon:v_lon, alt:v_alt};
chase_vehicles[vcallsign].heading = calculate_lookangles(old_v_pos, new_v_pos).azimuth;
// Update the position data.
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
// Update the marker position.
chase_vehicles[vcallsign].marker.setLatLng(chase_vehicles[vcallsign].latest_data).update();
// Rotate/replace the icon to match the bearing.
var _car_heading = chase_vehicles[vcallsign].heading - 90.0;
if (_car_heading<=90.0){
chase_vehicles[vcallsign].marker.setIcon(habitat_car_icons[chase_vehicles[vcallsign].colour]);
chase_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
}else{
// We are travelling West - we need to use the flipped car icon.
_car_heading = _car_heading - 180.0;
chase_vehicles[vcallsign].marker.setIcon(habitat_car_icons_flipped[chase_vehicles[vcallsign].colour]);
chase_vehicles[vcallsign].marker.setRotationAngle(_car_heading);
}
return;
}
// No need to go any further.
return;
}
// Otherwise, we need to decide if we're going to add it or not.
// Determine the vehicle distance from our current position.
var v_pos = {lat: v_lat, lon:v_lon, alt:v_alt};
if (chase_car_position.marker === "NONE"){
var my_pos = {lat:chase_config.default_lat, lon:chase_config.default_lon, alt:0};
}else{
var my_pos = {lat:chase_car_position.latest_data[0], lon:chase_car_position.latest_data[1], alt:chase_car_position.latest_data[2]};
}
var v_range = calculate_lookangles(my_pos, v_pos).range/1000.0;
// If the range is less than the threshold, add it to our list of chase vehicles.
if(v_range < vehicle_max_range){
//console.log("Adding: " + vcallsign);
chase_vehicles[vcallsign] = {};
// Initialise a few default values
chase_vehicles[vcallsign].heading = 90;
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
chase_vehicles[vcallsign].position_id = position.position_id;
// Get an index for the car icon. This is incremented for each vehicle,
// giving each a different colour.
chase_vehicles[vcallsign].colour = car_colour_values[car_colour_idx];
car_colour_idx = (car_colour_idx+1)%car_colour_values.length;
// Create marker
chase_vehicles[vcallsign].marker = L.marker(chase_vehicles[vcallsign].latest_data,
{title:vcallsign,
icon: habitat_car_icons[chase_vehicles[vcallsign].colour],
rotationOrigin: "center center"})
.addTo(map);
// Keep our own record of if this marker has been added to a map,
// as we shouldn't be using the private _map property of the marker object.
chase_vehicles[vcallsign].onmap = true;
// Add tooltip, with custom CSS which removes all tooltip borders, and adds a text shadow.
chase_vehicles[vcallsign].marker.bindTooltip(vcallsign,
{permanent: true,
direction: 'center',
offset:[0,25],
className:'custom_label'}).openTooltip();
}
}
});
snear_request_running = false;
}
function get_sondehub_vehicles(){
// nothing here yet.
console.log("Requesting vehicles from Sondehub...")
}

Wyświetl plik

@ -63,7 +63,7 @@ function markPayloadRecovered(callsign){
"Submit": function() {
$( this ).dialog( "close" );
_recovery_data.message = $('#customRecoveryMessage').val();
_recovery_data.title = $('#customRecoveryTitle').val();
_recovery_data.recovery_title = $('#customRecoveryTitle').val();
// If the user has requested to use the chase car position, override the last position with it.
if(document.getElementById("recoveryCarPosition").checked == true){

Wyświetl plik

@ -36,6 +36,7 @@
<!-- Custom scripts -->
<script src="{{ url_for('static', filename='js/habitat.js') }}"></script>
<script src="{{ url_for('static', filename='js/sondehub.js') }}"></script>
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/tables.js') }}"></script>
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
@ -65,6 +66,11 @@
// The sonde we are currently following on the map
var balloon_currently_following = "none";
// Other chase vehicles
var chase_vehicles = {};
// Only add chase cars which are (initially) within this range limit (km).
var vehicle_max_range = 200.0;
// Chase car position.
// properties will contain:
// latest_data: [lat,lon, alt] (latest car position)
@ -614,11 +620,19 @@
}, age_update_rate);
// Habitat Chase Car Position Grabber
// Habitat/Sondehub Chase Car Position Grabber
var habitat_update_rate = 20000;
window.setInterval(function(){
if(document.getElementById("showOtherCars").checked){
get_habitat_vehicles();
if (chase_config.profiles[chase_config.selected_profile].online_tracker === "habitat"){
get_habitat_vehicles();
}
else if (chase_config.profiles[chase_config.selected_profile].online_tracker === "sondehub"){
get_sondehub_vehicles();
}
else{
// Do nothing...
}
}
}, habitat_update_rate);