kopia lustrzana https://github.com/projecthorus/chasemapper
Initial sondehub v2 integration
rodzic
6c3765896d
commit
c888eef454
|
@ -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
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
|
||||
# Now using Semantic Versioning (https://semver.org/) MAJOR.MINOR.PATCH
|
||||
|
||||
__version__ = "1.1.0"
|
||||
__version__ = "1.2.0"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
|
@ -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.
|
||||
#
|
||||
|
|
116
horusmapper.py
116
horusmapper.py
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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...")
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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){
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue