Porównaj commity

...

36 Commity

Autor SHA1 Wiadomość Data
Mark Jessop 583d141f7e Add ascent rate averaging config setting, default this to 10 points 2024-05-05 17:05:02 +09:30
Mark Jessop e6e14c38de Fix stupid replay bug 2024-05-05 15:44:45 +09:30
Mark Jessop 6e3caf005a Add SV information to payload info table 2024-05-03 10:15:35 +09:30
Mark Jessop 48e8eae239 add openblas to dependencies 2023-11-02 19:53:00 +10:30
Mark Jessop c7df0982de Add libatlas to all docker builds 2023-02-11 21:24:12 +10:30
Mark Jessop 8aec30178c Handle repeated GPSD position packets 2023-01-08 10:31:20 +11:00
Mark Jessop 3b02f751f2 Merge branch 'master' of github.com:projecthorus/chasemapper 2022-10-01 21:37:51 +09:30
Mark Jessop c3d371ac39 Fix bug with importing chase cars from sondehub 2022-10-01 21:37:25 +09:30
Steven Honson d0df0e7ad8
Always login
Required for registry based cache
2022-09-23 21:05:27 +10:00
Steven Honson 90b8e180c6
Merge pull request #46 from projecthorus/libatlas3-base
Container build fixes
2022-09-23 17:02:51 +10:00
Steven Honson 8eec272d7f
Use registry cache 2022-09-23 10:05:38 +10:00
Steven Honson 8ec7127a76
Update package name 2022-09-22 21:54:25 +10:00
Steven Honson 110b297bf4
Tweak pip args 2022-09-22 21:49:13 +10:00
Steven Honson 75c75aee90
libatlas3-base-dev 2022-09-22 21:48:02 +10:00
Mark Jessop beb9db5b4e Update readme and example config 2022-09-22 16:02:22 +09:30
Mark Jessop 8f4a73245f Use websockets to access sondehub/sondehub-amateur chase car data. 2022-09-22 15:47:51 +09:30
Mark Jessop aa2176e518 Update boostrap, change checkboxes to switches 2022-09-17 17:32:27 +09:30
Mark Jessop 17ffc42749 Removed Habitat chase car position uploader 2022-09-17 11:30:55 +09:30
Mark Jessop 1d446ef41d Switch to using send_from_directory insead of send_file 2022-09-17 11:19:57 +09:30
Steven Honson 2709e7733d
Add libatlas3-base 2022-09-16 22:34:24 +10:00
Mark Jessop cee93c90bc Merge branch 'master' of github.com:projecthorus/chasemapper 2022-09-16 21:18:18 +09:30
Mark Jessop 54e54855af Update tawhiri URL 2022-09-16 21:17:48 +09:30
Steven Honson 36d9948a22
Merge pull request #43 from snh/bullseye
Bump base to bullseye
2022-08-29 21:32:47 +10:00
Steven Honson 8ddf9feab4 Bump base to bullseye 2022-08-29 20:56:50 +10:00
Mark Jessop 7f4ae413cd Try and handle flask-socketio 5.3.0 in a docker container correctly. 2022-08-29 18:22:03 +09:30
Mark Jessop 11ded492db Merge branch 'master' of github.com:projecthorus/chasemapper 2022-08-28 21:05:24 +09:30
Mark Jessop 3b6352f7d5 pin flask and flask-socketio 2022-08-28 21:05:03 +09:30
Steven Honson fc1e3c7e13
Remove armv6 support 2022-08-27 18:44:58 +10:00
Mark Jessop 8b27726a90 Tweaks 2022-07-18 16:10:16 +09:30
Mark Jessop 7356c1a0f3 Propagate flush bearings to other clients 2022-07-18 15:56:05 +09:30
Mark Jessop 622766a0c6 Fix flipped DoA plots 2022-07-16 20:45:19 +09:30
Mark Jessop f69d5b71cf Updates for KrakenSDR compatability. 2022-07-15 16:09:58 +09:30
Mark Jessop e44115a4b9 Fix bug on profile change 2022-04-24 14:11:08 +09:30
Mark Jessop baa3e01313 Add Sondehub Amateur chase car upload support 2022-04-18 14:59:19 +09:30
Mark Jessop c4b613b648 Add bearing entry on main page 2022-02-10 17:53:59 +10:30
Mark Jessop a758375e82 Tweaks to help with replaying log data 2021-12-11 17:33:54 +10:30
24 zmienionych plików z 2922 dodań i 319 usunięć

Wyświetl plik

@ -28,34 +28,20 @@ jobs:
- name: Setup Buildx
uses: docker/setup-buildx-action@v1
- name: Cache Layers
uses: actions/cache@v2
with:
path: /tmp/buildx-cache
key: buildx-cache-${{ github.sha }}
restore-keys: |
buildx-cache-
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Images
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64, linux/386, linux/arm64, linux/arm/v6, linux/arm/v7
cache-from: type=local,src=/tmp/buildx-cache
cache-to: type=local,dest=/tmp/buildx-cache-new,mode=max
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Move Cache
run: |
rm -rf /tmp/buildx-cache
mv /tmp/buildx-cache-new /tmp/buildx-cache

Wyświetl plik

@ -1,14 +1,15 @@
# -------------------
# The build container
# -------------------
FROM python:3.7-buster AS build
FROM python:3.9-bullseye AS build
# Upgrade base packages.
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y \
cmake \
libgeos-dev && \
libgeos-dev \
libatlas-base-dev && \
rm -rf /var/lib/apt/lists/*
# Copy in requirements.txt.
@ -16,7 +17,7 @@ COPY requirements.txt /root/chasemapper/requirements.txt
# Install Python packages.
RUN pip3 --no-cache-dir install --user --no-warn-script-location \
--extra-index-url https://www.piwheels.org/simple \
--ignore-installed --no-binary numpy \
-r /root/chasemapper/requirements.txt
# Copy in chasemapper.
@ -35,13 +36,13 @@ RUN unzip /root/cusf_predictor_wrapper-master.zip -d /root && \
# -------------------------
# The application container
# -------------------------
FROM python:3.7-slim-buster
FROM python:3.9-slim-bullseye
EXPOSE 5001/tcp
# Upgrade base packages and install application dependencies.
RUN case $(uname -m) in \
"armv6l") extra_packages="libatlas3-base libgfortran5" ;; \
"armv7l") extra_packages="libatlas3-base libgfortran5" ;; \
"armv6l") extra_packages="" ;; \
"armv7l") extra_packages="" ;; \
esac && \
apt-get update && \
apt-get upgrade -y && \
@ -49,7 +50,8 @@ RUN case $(uname -m) in \
libeccodes0 \
libgeos-c1v5 \
libglib2.0-0 \
${extra_packages} \
libatlas3-base \
libgfortran5 \
tini && \
rm -rf /var/lib/apt/lists/*

Wyświetl plik

@ -7,15 +7,14 @@ The primary purpose of chasemapper is to provide an easy-to-use mapping interfac
Chasemapper is intended to be run on a 'headless' machine like a Raspberry Pi and is accessed from a tablet or laptop computer via a web browser. Multiple clients can connect to the server to see what's going on, which is a nice way of keeping passengers entertained ;-)
It will quite happily run alongside other Project Horus applications such as [radiosonde_auto_rx](https://github.com/projecthorus/radiosonde_auto_rx).
It will quite happily run alongside other Project Horus applications such as [radiosonde_auto_rx](https://github.com/projecthorus/radiosonde_auto_rx/wiki), or [horusdemodlib](https://github.com/projecthorus/horusdemodlib/wiki)
### Contacts
* [Mark Jessop](https://github.com/darksidelemm) - vk5qi@rfhead.net
## 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.
* Further updates to interface with SondeHub's payload recovery reporting system have been introduced in early June (v1.3.0).
* Chasemapper has now dropped support for the HabHub tracker, which is due to be retired later this year (2022). Chasemapper supports uploading chase-car position information to both [SondeHub](https://tracker.sondehub.org) (for radiosonde chasers), and [SondeHub-Amateur]((https://amateur.sondehub.org)) (for amateur high-altitude balloon launches)
## Docker Install
The fastest (and most recommended) way to get chasemapper up and running is to use the pre-built docker container. Information on using this is available here: https://github.com/projecthorus/chasemapper/wiki/Docker
@ -28,7 +27,7 @@ If you are using Docker, you can skip this section.
On a Raspbian/Ubuntu/Debian system, you can get most of the required dependencies using:
```
$ sudo apt-get install git python3-numpy python3-requests python3-serial python3-dateutil python3-flask python3-pip
$ sudo apt-get install git python3-numpy python3-requests python3-serial python3-dateutil python3-flask python3-pip libatlas3-base libgfortran5 libopenblas-dev
```
On other OSes the required packages should be named something similar.
@ -46,9 +45,8 @@ $ git clone https://github.com/projecthorus/chasemapper.git
To use the map, you need some kind of data to plot on it! The mapping backend accepts telemetry data in a few formats:
* 'Payload Summary', 'Chase Car Position' and 'Bearing' messages, via UDP broadcast in a JSON format [described here](https://github.com/projecthorus/horus_utils/wiki/5.-UDP-Broadcast-Messages#payload-summary-payload_summary). The standard ports used for these are 55672 (for hobbyist HAB payloads) and 55673 (radiosondes). These can be generated by:
* [radiosonde_auto_rx](https://github.com/projecthorus/radiosonde_auto_rx/wiki) - See [here](https://github.com/projecthorus/radiosonde_auto_rx/wiki/Configuration-Settings#payload-summary-output) for configuration info.
* Various 'bridge' utilities within the [horus_utils](https://github.com/projecthorus/horus_utils/wiki) repository. For example, [FldigiBridge](https://github.com/projecthorus/horus_utils/wiki#fldigibridge-processing-of-data-from-dl-fldigi) or [HabitatBridge](https://github.com/projecthorus/horus_utils/wiki#habitat-bridge)
* The [Horus-GUI](https://github.com/projecthorus/horus-gui) and [Horus Binary](https://github.com/projecthorus/horusdemodlib/wiki) 4FSK telemetry decoders will emit these messages on port 55672 by default.
* My [Kerberos-SDR fork](https://github.com/darksidelemm/kerberossdr) will emit TDOA bearing information in the appropriate UDP message format on port 55672. Note that the bearing functionality is very much experimental at this time.
* My [Kraken-SDR fork](https://github.com/darksidelemm/krakensdr_doa) will emit TDOA bearing information in the appropriate UDP message format on port 55672. Note that the bearing functionality is very much experimental at this time.
* 'OziMux' messages, via UDP broadcast in a simple CSV format [described here](https://github.com/projecthorus/oziplotter/wiki/3---Data-Sources#3---oziplotter-data-inputs).
* [radiosonde_auto_rx](https://github.com/projecthorus/radiosonde_auto_rx/wiki) - See [here](https://github.com/projecthorus/radiosonde_auto_rx/wiki/Configuration-Settings#sending-payload-data-to-chasemapper--oziplotter--ozimux) for configuration info, though I suggest using the 'Payload Summary' message as described above as it provides callsign information.
* Pi-in-the-Sky's [lora_gateway](https://github.com/PiInTheSky/lora-gateway) - Using the `OziPort=8942` configuration option.
@ -76,7 +74,7 @@ The server can be stopped with CTRL+C. Sometimes the server doesn't stop cleanly
You should then be able to access the webpage by visiting http://your_ip_here:5001/
## Live Predictions
By default, chasemapper will attempt to request flight-path predictions from the [Tawhiri Predictor API](https://tawhiri.readthedocs.io/en/latest/api.html), which requires an internet connection. If you have a semi-reliable internet connection during the flight, this might be all you need to get chasing!
By default, chasemapper will attempt to request flight-path predictions from the SondeHub instance of the [Tawhiri Predictor](https://github.com/projecthorus/tawhiri), which requires an internet connection. If you have a semi-reliable internet connection during the flight, this might be all you need to get chasing!
However, if you think you might be going out of phone coverage range, you may want to set up offline predictions:
@ -93,7 +91,7 @@ You can then click 'Download Model' in the web interface's setting tab to trigge
## Chase Car Positions
At the moment Chasemapper supports receiving chase-car positions via either GPSD, a Serial-attached GPS, or Horus UDP messages. Refer to the configuration file for setup information for these options.
This application can also plot your position onto the tracker.habhub.org map, so others can see when you're out balloon chasing. You can also fetch positions of nearby chase cars from Habitat, to see if others are out chasing as well :-) These options can be enabled from the control pane on the left of the web interface, and can also be set within the configuration file.
This application can also plot your position onto the tracker.habhub.org map, so others can see when you're out balloon chasing. You can also fetch positions of nearby chase cars from SondeHub/SondeHub-Amateur, to see if others are out chasing as well :-) These options can be enabled from the control pane on the left of the web interface, and can also be set within the configuration file.
## Offline Mapping
Chasemapper can serve up map tiles from a specified directory to the web client. Of course, for this to be useful, we need map tiles to serve!
@ -192,8 +190,8 @@ The above formats are accepted via a horus_udp listener, and so you must have a
Bearings are plotted on the map as thin lines, which slowly become transparent as they get older, and then disappear. The style of the line and the maximum age bearings shown can be configured in the new bearing settings tab on the left of the screen (click the compass icon). You can also filter bearings by the optionally supplied confidence level ('Confidence Threshold'). Bearings provided while the chase-car is stationary (i.e. when the heading is essentially unknown) are filtered out of the display by default, but can be enabled if desired ('Show stationary bearings'). Most of the filter settings will only take effect by clicking the 'Redraw Bearings' button.
My [Kerberos-SDR fork](https://github.com/darksidelemm/kerberossdr) will emit relative bearings in the above format on UDP port 55672, including the raw TDOA data, which is plotted on a polar plot on the bottom-right of the display. Bearing data will be emitted as soon as TDOA processing is started. Note that I have only tested with data from a Uniform Circular Array and do not currently handle forward/reverse ambiguities from a linear array configuration. I would *not* suggest running Chasemapper on the same device as the Kerberos-SDR software, due to the high processor load of the Kerberos algorithms.
My [Kraken-SDR fork](https://github.com/darksidelemm/krakensdr_doa) will emit relative bearings in the above format on UDP port 55672, including the raw TDOA data, which is plotted on a polar plot on the bottom-right of the display. Bearing data will be emitted as soon as TDOA processing is started. Note that I have only tested with data from a Uniform Circular Array and do not currently handle forward/reverse ambiguities from a linear array configuration. I would *not* suggest running Chasemapper on the same device as the Kerberos-SDR software, due to the high processor load of the Kerberos algorithms.
Note that the bearing display (in particular the TDOA data polar plot) does put a fairly big strain on some slower devices. Currently the polar plot is generated in a fairly naive way, and definitely has room for improvement.
I make no promises as to the usefulness and/or performance of this feature in chasemapper - it's essentially a re-implementation of a radio-direction finding mapping system developed by fellow Amateur Radio Experimenters Group members a very long time ago, and has yet to be used 'in anger'. It's also important to note that attempting to direction-find radiosonde/high-altitude balloon payloads which are located at high relative elevations (>40 degrees or so) is likely to lead to very inaccurate results due to coning angle limitations (where a bearing cannot be resolved due to insufficient phase-delta between receive antennae).
I make no promises as to the usefulness and/or performance of this feature in chasemapper - it's essentially a re-implementation of a radio-direction finding mapping system developed by fellow Amateur Radio Experimenters Group members a very long time ago. I've used it in a few local amateur radio direction finding competitions have found it to be useful. It's also important to note that attempting to direction-find radiosonde/high-altitude balloon payloads which are located at high relative elevations (>40 degrees or so) is likely to lead to very inaccurate results due to coning angle limitations (where a bearing cannot be resolved due to insufficient phase-delta between receive antennae).

Wyświetl plik

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

Wyświetl plik

@ -165,10 +165,11 @@ class Bearings(object):
# Relative bearing - we need to fuse this with the current car position.
# Temporary hack for KerberosSDR bearings, which are reflected across N/S
if _source == "kerberos-sdr":
if _source == "krakensdr_doa":
bearing["bearing"] = 360.0 - bearing["bearing"]
bearing["raw_doa"] = bearing["raw_doa"][::-1]
_new_bearing = {
"timestamp": _arrival_time,
"src_timestamp": _src_timestamp,

Wyświetl plik

@ -176,6 +176,11 @@ def parse_config_file(filename):
logging.info("Missing turn rate gate setting, using default (4m/s)")
chase_config["turn_rate_threshold"] = 4.0
try:
chase_config["ascent_rate_averaging"] = config.getint("predictor", "ascent_rate_averaging")
except:
logging.info("Missing ascent_rate_averaging setting, using default (10)")
chase_config["ascent_rate_averaging"] = 10
# Telemetry Source Profiles

Wyświetl plik

@ -152,7 +152,18 @@ class GenericTrack(object):
"Zero time-step encountered in ascent rate calculation - are multiple receivers reporting telemetry simultaneously?"
)
continue
# _mean2_time_delta = (
# self.track_history[-1][0] - self.track_history[-1*_num_samples][0]
# ).total_seconds()
# _mean2_altitude_delta = (
# self.track_history[-1][3] - self.track_history[-1*_num_samples][3]
# )
# _asc_rate2 = _mean2_altitude_delta / _mean2_time_delta
#print(f"asc_rates: {_asc_rates}, Mean: {np.mean(_asc_rates)}")
return np.mean(_asc_rates)
def calculate_heading(self):

Wyświetl plik

@ -171,8 +171,11 @@ class SerialGPS(object):
# '12319.943281'
if not dm or dm == "0":
return 0.0
d, m = re.match(r"^(\d+)(\d\d\.\d+)$", dm).groups()
try:
d, m = re.match(r"^(\d+)(\d\d\.\d+)$", dm).groups()
except:
return 0.0
return float(d) + float(m) / 60
def parse_nmea(self, data):

Wyświetl plik

@ -366,6 +366,8 @@ class GPSDAdaptor(object):
_gpsd_socket.watch(gpsd_protocol="json")
logging.info("GPSD - Connected to GPSD instance at %s" % self.hostname)
_old_state = {}
while self.gpsd_thread_running:
# We should be getting GPS data every second.
# If this isn't the case, we should close the connection and re-connect.
@ -403,7 +405,10 @@ class GPSDAdaptor(object):
"valid": True,
}
self.send_to_callback(_gps_state)
if _gps_state != _old_state:
self.send_to_callback(_gps_state)
_old_state = _gps_state
# Close the GPSD connection.
try:

Wyświetl plik

@ -29,7 +29,9 @@ class SondehubChaseUploader(object):
""" Upload supplied chase car positions to Sondehub on a regular basis """
SONDEHUB_STATION_POSITION_URL = "https://api.v2.sondehub.org/listeners"
SONDEHUB_STATION_POSITION_URL_AMATEUR = "https://api.v2.sondehub.org/amateur/listeners"
SONDEHUB_SONDE_RECOVERED_URL = "https://api.v2.sondehub.org/recovered"
SONDEHUB_SONDE_RECOVERED_URL_AMATEUR = "https://api.v2.sondehub.org/amateur/recovered"
def __init__(
self,
@ -38,6 +40,7 @@ class SondehubChaseUploader(object):
upload_enabled=True,
upload_timeout=10,
upload_retries=2,
amateur=False # Upload to amateur DB instead of regular sondehub
):
""" Initialise the Sondehub Chase uploader, and start the update thread """
@ -47,6 +50,7 @@ class SondehubChaseUploader(object):
self.upload_enabled = upload_enabled
self.upload_timeout = upload_timeout
self.upload_retries = upload_retries
self.amateur = amateur
self.car_position = None
self.car_position_lock = Lock()
@ -55,7 +59,14 @@ class SondehubChaseUploader(object):
self.uploader_thread = Thread(target=self.upload_thread)
self.uploader_thread.start()
logging.info("Sondehub - Chase-Car Position Uploader Started")
if amateur:
self.position_url = self.SONDEHUB_STATION_POSITION_URL_AMATEUR
self.recovery_url = self.SONDEHUB_SONDE_RECOVERED_URL_AMATEUR
logging.info("Sondehub-Amateur - Chase-Car Position Uploader Started")
else:
self.position_url = self.SONDEHUB_STATION_POSITION_URL
self.recovery_url = self.SONDEHUB_SONDE_RECOVERED_URL
logging.info("Sondehub - Chase-Car Position Uploader Started")
def update_position(self, position):
""" Update the chase car position state
@ -137,7 +148,7 @@ class SondehubChaseUploader(object):
"Content-Type": "application/json",
}
_req = requests.put(
self.SONDEHUB_STATION_POSITION_URL,
self.position_url,
json=_position,
# TODO: Revisit this second timeout value.
timeout=(self.upload_timeout, 6.1),
@ -203,7 +214,7 @@ class SondehubChaseUploader(object):
"Content-Type": "application/json",
}
_req = requests.put(
self.SONDEHUB_SONDE_RECOVERED_URL,
self.recovery_url,
json=_doc,
# TODO: Revisit this second timeout value.
timeout=(self.upload_timeout, 6.1),

Wyświetl plik

@ -16,7 +16,7 @@ import subprocess
from dateutil.parser import parse
from threading import Thread
TAWHIRI_API_URL = "http://predict.cusf.co.uk/api/v1/"
TAWHIRI_API_URL = "http://api.v2.sondehub.org/tawhiri"
def get_tawhiri_prediction(

Wyświetl plik

@ -3,7 +3,7 @@
#
# Copy this file to horusmapper.cfg and modify as required.
#
# This is the default config file for chasemapper 1.3.0, be aware there might be changes and it might not run on older/newer versions.
# This is the default config file for chasemapper 1.5.0, be aware there might be changes and it might not run on older/newer versions.
#
#
@ -32,17 +32,23 @@ telemetry_source_port = 55673
# Car Position Source
# none - No Chase-Car GPS
# horus_udp - Read Horus UDP Broadcast 'Car GPS' messages
# serial - Read GPS positions from a serial-connected GPS receiver.
# gpsd - Poll GPSD for positions.
# serial - Read GPS positions from a serial-connected GPS receiver. RECOMMENDED
# gpsd - Poll a local GPSD instance for positions. (Known to have some reliability issues...)
# station - Stationary position (set in the [map] section below)
car_source_type = gpsd
car_source_type = serial
# Car position source port (UDP) - only used if horus_udp is selected, but still needs to be provided.
car_source_port = 12345
car_source_port = 55672
# Online Tracker System
# Where to upload chase-car positions and balloon recovery notifications to.
# Note - you can only select one of these at a time.
#
# 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)
# Use this for chasing meteorological radiosondes!
#
# sondehubamateur = SondeHub Amateur Database, for viewing on the SondeHub-Amateur Tracker (https://amateur.sondehub.org)
# Use this when chasing your own flights, and you want to show up on the sondehub-amateur tracker.
#
online_tracker = sondehub
# Other profiles can be defined in sections like the following:
@ -63,8 +69,8 @@ car_source_port = 55672
# 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)
online_tracker = habitat
# sondehubamateur = SondeHub Amateur Database, for viewing on the SondeHub-Amateur Tracker (https://amateur.sondehub.org)
online_tracker = sondehubamateur
# If you want add more profiles, you can do so here, e.g.
# [profile_3]
@ -75,6 +81,7 @@ online_tracker = habitat
[gpsd]
# GPSD Host/Port - Only used if selected in a telemetry profile above.
# Note that GPSD support is somewhat buggy.
gpsd_host = localhost
gpsd_port = 2947
@ -123,10 +130,15 @@ stadia_api_key = none
# Enable Predictor (True/False) - This can also be enabled from the web client.
predictor_enabled = True
# Predictor defaults - these can be modified at runtime in the web interface.
# Predictor defaults - these can also be modified at runtime in the web interface.
default_burst = 30000
default_descent_rate = 5.0
# How many data points to average the payload's ascent rate over.
# Note that this is data points, not *seconds*, so you may need to tune this for your payload's
# position update rate.
# Longer averaging means a smoother ascent rate. ~10 seems ok for a typical Horus Binary payload.
ascent_rate_averaging = 10
# Offline Predictions
# Use of the offline predictor requires installing the CUSF Predictor Python Wrapper from here:
@ -175,20 +187,24 @@ tile_server_path = /home/pi/Maps/
#tile_server_path = /opt/chasemapper/Maps/
#
# Habitat / SondeHub Chase-Car Position Upload
# If you want, this application can upload your chase-car position to the Habhub / Sondehub trackers,
# SondeHub Chase-Car Position Upload
# If you want, this application can upload your chase-car position to the SondeHub/SondeHub-Amateur 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.
#
# Note - Variables in this section still refer to habitat to avoid breaking existing configurations.
[habitat]
# Enable uploading of chase-car position to Habitat (True / False)
# Enable uploading of chase-car position to SondeHub / SondeHub-Amateur (True / False)
# Which tracker positions are uploaded to depends on the online_tracker setting of the selected
# profile (further up in this config file).
habitat_upload_enabled = False
# Callsign to use when uploading. Note that _chase is automatically appended to this callsign
# i.e. N0CALL will show up as N0CALL_chase on tracker.habhub.org
# when displayed on the tracker maps.
# i.e. N0CALL will show up as N0CALL_chase on sondehub.org
habitat_call = N0CALL
# Attempt to upload position to habitat every x seconds.
# Attempt to upload position to SondeHub every x seconds.
habitat_update_rate = 30

Wyświetl plik

@ -137,11 +137,7 @@ def flask_server_tiles(filename):
""" Serve up a file from the tile server location """
global map_settings
if map_settings["tile_server_enabled"]:
_filename = flask.safe_join(map_settings["tile_server_path"], filename)
if os.path.isfile(_filename):
return flask.send_file(_filename)
else:
flask.abort(404)
return flask.send_from_directory(map_settings["tile_server_path"], filename)
else:
flask.abort(404)
@ -196,15 +192,20 @@ def client_settings_update(data):
chasemapper_config["selected_profile"]
]["online_tracker"]
if _tracker == "habitat":
online_uploader = HabitatChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
logging.error(
"Habitat uploader now deprecated due to Habitat retirement, not starting uploader."
)
elif _tracker == "sondehub":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
elif _tracker == "sondehubamateur":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
amateur=True
)
else:
logging.error(
"Unknown Online Tracker %s, not starting uploader." % _tracker
@ -235,7 +236,7 @@ def handle_new_payload_position(data, log_position=True):
if _callsign not in current_payloads:
# New callsign! Create entries in data stores.
current_payload_tracks[_callsign] = GenericTrack()
current_payload_tracks[_callsign] = GenericTrack(ascent_averaging=chasemapper_config["ascent_rate_averaging"])
current_payloads[_callsign] = {
"telem": {
@ -705,6 +706,7 @@ def clear_bearing_data(data):
global bearing_store
logging.warning("Client requested bearing data be cleared.")
bearing_store.flush()
flask_emit_event("server_bearings_cleared", {"foo":"bar"})
@socketio.on("mark_recovered", namespace="/chasemapper")
@ -863,6 +865,12 @@ def udp_listener_car_callback(data):
"speed": _speed,
}
if 'replay_time' in data:
# We are getting data from a log file replay, make sure to pass this on
_replay_time = parse(data['replay_time'])
_replay_time_str = _replay_time.strftime("%Y-%m-%d %H:%M:%SZ")
_car_telem['replay_time'] = _replay_time_str
# Add in some additional status fields if we have them.
if 'numSV' in data:
_car_telem['numSV'] = data['numSV']
@ -953,7 +961,7 @@ 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')
'online_tracker' (str): Which online tracker to upload chase-car info to ('sondehub' or 'sondehubamateur')
"""
global data_listeners, current_profile, online_uploader, chasemapper_config
@ -977,15 +985,20 @@ def start_listeners(profile):
# 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"],
logging.error(
"Habitat uploader now deprecated due to Habitat retirement, not starting uploader."
)
elif profile["online_tracker"] == "sondehub":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
)
elif profile["online_tracker"] == "sondehubamateur":
online_uploader = SondehubChaseUploader(
update_rate=chasemapper_config["habitat_update_rate"],
callsign=chasemapper_config["habitat_call"],
amateur=True
)
else:
logging.error(
"Unknown Online Tracker %s, not starting uploader"
@ -1243,11 +1256,21 @@ if __name__ == "__main__":
"Starting Chasemapper Server on: http://%s:%d/"
% (chasemapper_config["flask_host"], chasemapper_config["flask_port"])
)
socketio.run(
app,
host=chasemapper_config["flask_host"],
port=chasemapper_config["flask_port"],
)
try:
socketio.run(
app,
host=chasemapper_config["flask_host"],
port=chasemapper_config["flask_port"],
allow_unsafe_werkzeug=True
)
except TypeError as e:
print(e)
logging.debug("Not using allow_unsafe_werkzeug argument.")
socketio.run(
app,
host=chasemapper_config["flask_host"],
port=chasemapper_config["flask_port"]
)
# Close the predictor and data age monitor threads.
predictor_thread_running = False

Wyświetl plik

@ -36,16 +36,18 @@ def send_bearing(json_data, udp_port=55672, hostname='<broadcast>'):
"type": "BEARING",
"log_type": "BEARING"}
"""
packet = {
'type' : 'BEARING',
'bearing' : int(json_data['bearing']),
'confidence': int(json_data['confidence']),
'power': int(json_data['power']),
'raw_bearing_angles': [int(x) for x in json_data['raw_bearing_angles']],
'raw_doa': [round(x,2) for x in json_data['raw_doa']],
'bearing_type': 'relative',
'source': 'playback'
}
# Also get bearings of form:
# {"type": "BEARING", "bearing_type": "absolute", "source": "EasyBearing", "latitude": -34.9016115,
#"longitude": 138.58986819999998, "bearing": 0, "log_type": "BEARING", "log_time": "2021-12-10T07:33:14.156227+00:00"}
packet = json_data
packet['replay_time'] = json_data['log_time']
if 'kerberos' in json_data['source']:
# Log data from the kerberos has been flipped in bearing already. Need to make sure this isn't done twice.
packet['source'] = 'replay'
# Set up our UDP socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
@ -88,7 +90,8 @@ def send_car_position(json_data, udp_port=55672):
'longitude' : json_data['lon'],
'altitude': json_data['alt'],
'speed': json_data['speed'],
'valid': True
'valid': True,
'replay_time': json_data['log_time']
}
if 'heading' in json_data:
@ -132,8 +135,9 @@ def send_balloon_telemetry(json_data, udp_port=55672):
'longitude' : json_data['lon'],
'altitude': json_data['alt'],
'callsign': json_data['callsign'],
'time': parse(json_data['time']).strftime("%H:%H:%S"),
'comment': "Log Playback"
'time': parse(json_data['time']).strftime("%H:%M:%S"),
'comment': "Log Playback",
'replay_time': json_data['log_time']
}
# Set up our UDP socket
@ -191,11 +195,11 @@ def playback_json(filename, udp_port=55672, speed=1.0, start_time = 0, hostname=
if _log_data['log_type'] == 'CAR POSITION':
send_car_position(_log_data, udp_port)
print("%02d:%.2f - Car Position" % (_time_min, _time_sec))
print("%s - %02d:%.2f - Car Position" % (_log_data['log_time'], _time_min, _time_sec))
elif _log_data['log_type'] == 'BEARING':
send_bearing(_log_data, udp_port, hostname=hostname)
print("%02d:%.2f - Bearing Data" % (_time_min, _time_sec))
print("%s - %02d:%.2f - Bearing Data" % (_log_data['log_time'], _time_min, _time_sec))
elif _log_data['log_type'] == 'BALLOON TELEMETRY':
send_balloon_telemetry(_log_data, udp_port)

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -56,6 +56,14 @@ html, body, #map {
width: 10em;
}
.paramEntryLeft {
display: inline-block;
margin-left: auto;
text-align: left;
padding-right: 0.2em;
width: 10em;
}
.predictorModelValue {
display: inline-block;
margin-left: auto;
@ -74,6 +82,12 @@ html, body, #map {
font-size:3em;
}
.logTimeData {
color:black;
font-weight: bold;
font-size:3em;
}
.bearingData {
color:red;
font-weight: bold;
@ -167,4 +181,11 @@ html, body, #map {
}
.ui-dialog { z-index: 1000 !important ;}
.ui-dialog { z-index: 1000 !important ;}
.form-switch {
display: flex !important;
margin: 5px;
flex-direction: row-reverse !important;
justify-content: space-between !important;
}

Wyświetl plik

@ -254,7 +254,7 @@ function handleTelemetry(data){
$("#numSVStatus").text(data.numSV.toFixed(0));
}
console.log(data);
//console.log(data);
// Update Chase Car Speed
if (document.getElementById("showCarSpeed").checked){
@ -269,6 +269,11 @@ function handleTelemetry(data){
$("#chase_car_speed_header").text("");
}
if(data.hasOwnProperty('replay_time')){
// Data is coming from a log file, display the time.
$("#log_time").text(data.replay_time);
}
// Update heading information
if (document.getElementById("showCarHeading").checked){
$("#chase_car_heading").text(chase_car_position.heading.toFixed(0) + "˚");

Wyświetl plik

@ -21,8 +21,8 @@ var bearings_on = true;
var bearings_only_mode = false;
var bearing_confidence_threshold = 50.0;
var bearing_max_age = 20*60.0;
var bearing_confidence_threshold = 5.0;
var bearing_max_age = 10*60.0;
var bearing_length = 10000;
var bearing_weight = 0.5;
@ -152,7 +152,7 @@ function addBearing(timestamp, bearing, live){
}else {
_valid_text = "NO";
}
$("#bearing_table").tabulator("setData", [{id:1, valid_bearing:_valid_text, bearing: bearing_store[timestamp].raw_bearing.toFixed(0), confidence: bearing_store[timestamp].confidence.toFixed(0), power: bearing_store[timestamp].power.toFixed(0)}]);
$("#bearing_table").tabulator("setData", [{id:1, valid_bearing:_valid_text, bearing: bearing_store[timestamp].raw_bearing.toFixed(0), confidence: bearing_store[timestamp].confidence.toFixed(1), power: bearing_store[timestamp].power.toFixed(0)}]);
$("#bearing_table").show();
if(document.getElementById("tdoaEnabled").checked == true){
@ -329,7 +329,6 @@ function flushBearings(){
var _confirm = confirm("Really clear all Bearing data?");
if (_confirm == true){
socket.emit('bearing_store_clear', {data: 'plzkthx'});
destroyAllBearings();
}
@ -459,3 +458,18 @@ function calculateBearingOpacity(bearing_timestamp){
}
function manualBearing(){
current_bearing = parseFloat($('#bearingManualEntry').val());
_bearing_info = {
'type': 'BEARING',
'bearing_type': 'absolute',
'source': 'EasyBearing',
'latitude': chase_car_position.latest_data[0],
'longitude': chase_car_position.latest_data[1],
'bearing': current_bearing
};
socket.emit('add_manual_bearing', _bearing_info);
}

Wyświetl plik

@ -159,22 +159,3 @@ function get_habitat_vehicles(){
}
// Show/Hide all vehicles.
function show_habitat_vehicles(){
var state = document.getElementById("showOtherCars").checked;
for (_car in chase_vehicles){
// Add to map, if its not already on there.
if(state){
if(!chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.addTo(map);
chase_vehicles[_car].onmap = true;
}
} else{
if(chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.remove();
chase_vehicles[_car].onmap = false;
}
}
}
}

Plik diff jest za duży Load Diff

Wyświetl plik

@ -79,6 +79,7 @@ function serverSettingsUpdate(data){
// Update version
$('#chasemapper_version').html(chase_config.version);
}
function clientSettingsUpdate(){

Wyświetl plik

@ -1,168 +1,233 @@
//
// Project Horus - Browser-Based Chase Mapper - SondeHub Data Scraping
// Project Horus - Browser-Based Chase Mapper - SondeHub Websockets Connection.
//
// Copyright (C) 2021 Mark Jessop <vk5qi@rfhead.net>
// Copyright (C) 2022 Mark Jessop <vk5qi@rfhead.net>
// Released under GNU GPL v3 or later
//
// URL to scrape recent vehicle position data from.
var sondehub_vehicle_url = "https://api.v2.sondehub.org/listeners/telemetry?duration=1h";
function handleSondeHubWebSocketPacket(data){
// Handle a packet of vehicle / listener telemetry from a SondeHub / SondeHub-Amateur Websockets Connection.
// Only process frames where the 'mobile' flag is present and is true.
if (data.hasOwnProperty('mobile')){
if(data['mobile'] == true){
// We have found a mobile station!
//console.log(data);
// Not really sure if this is necessary.
var sondehub_request_running = false;
// Extract position.
var v_lat = parseFloat(data.uploader_position[0]);
var v_lon = parseFloat(data.uploader_position[1]);
var v_alt = parseFloat(data.uploader_position[2]);
var vcallsign = data.uploader_callsign;
// 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)){
//console.log("Updating: " + vcallsign);
// Update the position ID.
chase_vehicles[vcallsign].position_id = data.ts;
var temp_sondehub = null;
// 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;
function process_sondehub_vehicles(data){
// Check we have a 'valid' response to process.
if (data === null ||
data === {}) {
sondehub_request_running = false;
return;
}
// Update the position data.
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
// Now iterate over all the callsigns in the response.
Object.keys(data).forEach(function(vcallsign){
// Skip over our own data.
if (vcallsign.startsWith(chase_config.habitat_call)){
return;
}
// Update the marker position.
chase_vehicles[vcallsign].marker.setLatLng(chase_vehicles[vcallsign].latest_data).update();
if( (data[vcallsign] === null) || (data[vcallsign] == {})){
// No data. This shouldn't happen, as the DB shouldn't give us empty results.
return;
// 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);
}
}else{
// Array of times for each callsign
_position_times = Object.keys(data[vcallsign]);
// Get last position, which should be the latest.
_last_position = data[vcallsign][_position_times[_position_times.length - 1]];
} else {
// Check if the vehicle is marked as a 'mobile' station
if (_last_position.hasOwnProperty('mobile')){
if(_last_position['mobile'] == true){
// We have found a mobile station!
//console.log(_last_position);
// Otherwise, we need to decide if we're going to add it or not.
// Extract position.
var v_lat = parseFloat(_last_position.uploader_position[0]);
var v_lon = parseFloat(_last_position.uploader_position[1]);
var v_alt = parseFloat(_last_position.uploader_position[2]);
// 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)){
// Is it us?
if(vcallsign.startsWith(chase_config.habitat_call)){
// Don't add!
return;
}
// Only update if the position ID of this position is newer than that last seen.
if (chase_vehicles[vcallsign].position_id !== _last_position.ts){
//console.log("Updating: " + vcallsign);
// Update the position ID.
chase_vehicles[vcallsign].position_id = _last_position.ts;
// 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;
// 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;
// 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 = data.ts;
// Update the position data.
chase_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt];
// 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;
// Update the marker position.
chase_vehicles[vcallsign].marker.setLatLng(chase_vehicles[vcallsign].latest_data).update();
// 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"});
// 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 = _last_position.ts;
// 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);
// 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();
if(document.getElementById("showOtherCars").checked){
// Add the car to the map if we have the show other cars button checked.
chase_vehicles[vcallsign].marker.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();
}
}
}
}
}
});
sondehub_request_running = false;
}
}
// Request the latest vehicle positions from Sondehub
function get_sondehub_vehicles(){
if(!sondehub_request_running){
sondehub_request_running = true;
console.log("Requesting vehicles from Sondehub...")
$.ajax({
url: sondehub_vehicle_url,
dataType: 'json',
timeout: 15000,
async: true,
success: function(data) {
console.log(data);
process_sondehub_vehicles(data);
}
});
function flush_sondehub_vehicles(){
for (_car in chase_vehicles){
// Remove from map if present.
if(chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.remove();
chase_vehicles[_car].onmap = false;
}
delete chase_vehicles[_car];
}
}
//
// SondeHub Websockets connection.
//
var livedata = "wss://ws-reader.v2.sondehub.org/";
var clientID = "ChaseMapper-" + Math.floor(Math.random() * 10000000000);
var client;
var clientConnected = false;
var clientActive = false;
var clientTopic;
function onConnect() {
if (chase_config.profiles[chase_config.selected_profile].online_tracker === "sondehub") {
var topic = "listener/#";
client.subscribe(topic);
clientTopic = topic;
} else if (chase_config.profiles[chase_config.selected_profile].online_tracker === "sondehubamateur") {
var topic = "amateur-listener/#";
client.subscribe(topic);
clientTopic = topic;
} else {
return;
}
clientConnected = true;
clientActive = true;
console.log("SondeHub Websockets Connected - Subscribed to " + clientTopic);
};
function connectionError(error) {
clientConnected = false;
clientActive = false;
console.log("SondeHub Websockets Connection Error");
};
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
clientConnected = false;
clientActive = false;
console.log("SondeHub Websockets Connection Lost");
}
};
function onMessageArrived(message) {
try {
if (clientActive) {
var frame = JSON.parse(message.payloadString.toString());
handleSondeHubWebSocketPacket(frame);
}
}
catch(err) {}
};
function startSondeHubWebsockets() {
if(document.getElementById("showOtherCars").checked){
// Clear off any vehicles on the map.
flush_sondehub_vehicles();
if(clientConnected == false){
// Not connected yet. Start a new connection.
client = new Paho.Client(livedata, clientID);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
client.connect({onSuccess:onConnect,onFailure:connectionError,reconnect:true});
} else {
// Already connected, un-sub and re-sub to the correct topic.
client.unsubscribe(clientTopic);
onConnect();
}
} else {
if(clientConnected || (client != null)){
client.disconnect();
clientConnected = false;
console.log("SondeHub Websockets Disconnected.")
}
}
}
// Show/Hide all vehicles.
function show_sondehub_vehicles(){
var state = document.getElementById("showOtherCars").checked;
for (_car in chase_vehicles){
// Add to map, if its not already on there.
if(state){
if(!chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.addTo(map);
chase_vehicles[_car].onmap = true;
}
} else{
if(chase_vehicles[_car].onmap){
chase_vehicles[_car].marker.remove();
chase_vehicles[_car].onmap = false;
}
}
}
// Re-connect to websockets if necessary.
startSondeHubWebsockets();
}
/* Habitat ChaseCar lib (copied from SondeHub Tracker)
* Uploads geolocation for chase cars to habitat

Wyświetl plik

@ -184,6 +184,7 @@ function initTables(){
{title:"Longitude", field:"lon", headerSort:false},
{title:"Alt (m)", field:"alt", headerSort:false},
{title:"V_rate (m/s)", field:"vel_v", headerSort:false},
{title:"SVs", field:'sats', headerSort:false, visible:false},
{title:"SNR", field:'snr', headerSort:false, visible:false},
{title:"Aux", field:'aux', headerSort:false, visible:false}
],
@ -243,6 +244,7 @@ function initTablesImperial(){
{title:"Longitude", field:"lon", headerSort:false},
{title:"Alt (ft)", field:"alt", headerSort:false},
{title:"V_rate (ft/min)", field:"vel_v", headerSort:false},
{title:"SVs", field:'sats', headerSort:false, visible:false},
{title:"SNR", field:'snr', headerSort:false, visible:false},
{title:"Aux", field:'aux', headerSort:false, visible:false}
],
@ -323,6 +325,11 @@ function updateTelemetryTable(){
}
}
if (balloon_call_data.hasOwnProperty('sats')){
balloon_call_data.sats = balloon_call_data.sats.toFixed(0);
$("#telem_table").tabulator("showColumn", "sats");
}
// Update table
telem_data.push(balloon_call_data);
}

Wyświetl plik

@ -44,7 +44,7 @@
<script src="{{ url_for('static', filename='js/predictions.js') }}"></script>
<script src="{{ url_for('static', filename='js/car.js') }}"></script>
<script src="{{ url_for('static', filename='js/bearings.js') }}"></script>
<script src="{{ url_for('static', filename='js/paho-mqtt.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
@ -134,6 +134,8 @@
// (i.e. when another client makes a change)
socket.on('server_settings_update', function(data){
serverSettingsUpdate(data);
// Re-Connect to websockets if necessary.
startSondeHubWebsockets();
});
// Add handlers for various text fields.
@ -441,6 +443,21 @@
})
.addTo(map);
// Used to show log replay time information, if received.
L.control.custom({
position: 'bottomcenter',
content : "<div id='log_time' class='chaseCarSpeed'></div>",
classes : 'btn-group-vertical btn-group-sm',
id: 'log_time_control',
style :
{
margin: '5px',
padding: '0px 0 0 0',
cursor: 'pointer',
}
})
.addTo(map);
// Follow buttons - these just set the radio buttons on the settings pane.
@ -540,6 +557,10 @@
bearingUpdate(data);
});
socket.on('server_bearings_cleared', function(data){
destroyAllBearings();
});
$("#downloadModel").click(function(){
socket.emit('download_model', {data: 'plzkthx'});
@ -656,22 +677,8 @@
}
}, age_update_rate);
// Habitat/Sondehub Chase Car Position Grabber
var habitat_update_rate = 20000;
window.setInterval(function(){
if(document.getElementById("showOtherCars").checked){
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);
// Start connection to Sondehub Websockets.
startSondeHubWebsockets();
// Sadly we can't request location information unless chasemapper is served up via HTTPS
// which is a bit difficult to make happen.
@ -776,23 +783,33 @@
<input type="radio" name="autoFollow" value="none"> None
</form>
</hr>
<div class="paramRow">
<b>Show Car Track</b> <input type="checkbox" class="paramSelector" id="chaseCarTrack" onclick='setChaseCarTrack();' checked>
</div>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="chaseCarTrack" onclick='setChaseCarTrack();' checked>
<label class="form-check-label" for="chaseCarTrack">
<b>Show Car Track </b>
</label>
</div>
</hr>
<h3>Predictor</h3>
<div class="paramRow" id="predictorModel">
<b>Current Model: </b> <div class="predictorModelValue" id="predictorModelValue">Disabled</div>
<b>Current Model </b> <div class="predictorModelValue" id="predictorModelValue">Disabled</div>
</div>
<div class="paramRow">
<b>Download Model</b> <button type="button" class="paramSelector" id="downloadModel">Download</button>
<b>Download Model </b> <button type="button" class="paramSelector" id="downloadModel">Download</button>
</div>
<div class="paramRow">
<b>Enable Predictions</b> <input type="checkbox" class="paramSelector" id="predictorEnabled" onclick='clientSettingsUpdate();'>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="predictorEnabled" onclick='clientSettingsUpdate();'>
<label class="form-check-label" for="predictorEnabled">
<b>Enable Predictions </b>
</label>
</div>
<div class="paramRow">
<b>Show 'Abort' Predictions</b> <input type="checkbox" class="paramSelector" id="abortPredictionEnabled" onclick='clientSettingsUpdate();'>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="abortPredictionEnabled" onclick='clientSettingsUpdate();'>
<label class="form-check-label" for="abortPredictionEnabled">
<b>Show 'Abort' Predictions </b>
</label>
</div>
<div class="paramRow">
<b>Burst Altitude</b><input type="text" class="paramEntry" id="burstAlt"><br/>
@ -828,32 +845,41 @@
</div>
-->
<h3>Habitat Chase Car</h3>
<div class="paramRow">
<b>Show Nearby Chase-Cars:</b> <input type="checkbox" class="paramSelector" id="showOtherCars" onclick="show_habitat_vehicles();">
<h3>Chase Car Upload</h3>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="showOtherCars" onclick="show_sondehub_vehicles();">
<label class="form-check-label" for="showOtherCars">
<b>Show Nearby Chase-Cars </b>
</label>
</div>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="habitatUploadEnabled" onclick='clientSettingsUpdate();'>
<label class="form-check-label" for="habitatUploadEnabled">
<b>Chase-Car Position Upload </b>
</label>
</div>
<div class="paramRow">
<b>Enable Chase-Car Position Upload</b> <input type="checkbox" class="paramSelector" id="habitatUploadEnabled" onclick='clientSettingsUpdate();'>
<b>Online Tracker Callsign </b><input type="text" class="paramEntry" id="habitatCall"><br/>
</div>
<div class="paramRow">
<b>Habitat Call:</b><input type="text" class="paramEntry" id="habitatCall"><br/>
</div>
<div class="paramRow">
<b>Update Rate (seconds):</b><input type="text" class="paramEntry" id="habitatUpdateRate"><br/>
<b>Update Rate (seconds) </b><input type="text" class="paramEntry" id="habitatUpdateRate"><br/>
</div>
<h3>Range Rings</h3>
<div class="paramRow">
<b>Enable Range Rings</b> <input type="checkbox" class="paramSelector" id="rangeRingsEnabled" onclick='updateRangeRings();'>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="rangeRingsEnabled" onclick='updateRangeRings();'>
<label class="form-check-label" for="rangeRingsEnabled">
<b>Enable Range Rings </b>
</label>
</div>
<div class="paramRow">
<b>Ring Qty</b><input type="text" class="paramEntry" id="ringQuantity" value="5"><br/>
</div>
<div class="paramRow">
<b> <p><p id="ring_spacing">Ring Spacing (m)</p></p></b><input type="text" class="paramEntry" id="ringSpacing" value="1000"><br/>
<b> <p><p id="ring_spacing">Ring Spacing (m) </p></p></b><input type="text" class="paramEntry" id="ringSpacing" value="1000"><br/>
</div>
<div class="paramRow">
<b>Ring Weight (px)</b><input type="text" class="paramEntry" id="ringWeight" value="2"><br/>
<b>Ring Weight (px) </b><input type="text" class="paramEntry" id="ringWeight" value="2"><br/>
</div>
<div class="paramRow">
<b>Ring Color</b>
@ -871,19 +897,24 @@
</div>
<h3>Speed/Heading Display</h3>
<div class="paramRow">
<b>Show Chase Car Speed:</b> <input type="checkbox" class="paramSelector" id="showCarSpeed">
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="showCarSpeed">
<label class="form-check-label" for="showCarSpeed">
<b>Show Chase-Car Speed </b>
</label>
</div>
<div class="paramRow">
<b>Show Chase Car Heading:</b> <input type="checkbox" class="paramSelector" id="showCarHeading">
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="showCarHeading">
<label class="form-check-label" for="showCarHeading">
<b>Show Chase-Car Heading </b>
</label>
</div>
<h3>GPS/Heading Status</h3>
<div class="paramRow">
<b>SVs Tracked:</b> <div class="predictorModelValue" id='numSVStatus'>---</div>
<b>SVs Tracked </b> <div class="predictorModelValue" id='numSVStatus'>---</div>
</div>
<div class="paramRow">
<b>Heading:</b> <div class="predictorModelValue" id='headingStatus'>---</div>
<b>Heading </b> <div class="predictorModelValue" id='headingStatus'>---</div>
</div>
@ -895,34 +926,47 @@
<span class="sidebar-close"><i class="fa fa-caret-left"></i></span>
</h1>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="bearingsEnabled" checked onclick='toggleBearingsEnabled();'>
<label class="form-check-label" for="bearingsEnabled">
<b>Enable Bearings </b>
</label>
</div>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="bearingsOnlyMode" onclick='toggleBearingsOnlyMode();'>
<label class="form-check-label" for="bearingsOnlyMode">
<b>Bearing-Only Mode </b>
</label>
</div>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="tdoaEnabled" checked>
<label class="form-check-label" for="tdoaEnabled">
<b>Enable TDOA Plot </b>
</label>
</div>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="bigTDOAEnabled">
<label class="form-check-label" for="bigTDOAEnabled">
<b>Big TDOA Plot </b>
</label>
</div>
<div class="paramRow">
<b>Enable Bearings</b> <input type="checkbox" class="paramSelector" id="bearingsEnabled" checked onclick='toggleBearingsEnabled();'>
<b>Confidence Threshold</b><input type="text" class="paramEntry" id="bearingConfidenceThreshold" value="5.0"><br/>
</div>
<div class="paramRow">
<b>Bearing-Only Mode</b> <input type="checkbox" class="paramSelector" id="bearingsOnlyMode" onclick='toggleBearingsOnlyMode();'>
<b>Maximum Age (min)</b><input type="text" class="paramEntry" id="bearingMaximumAge" value="10"><br/>
</div>
<div class="paramRow">
<b>Enable TDOA Plot</b> <input type="checkbox" class="paramSelector" id="tdoaEnabled" checked>
<div class="form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" role="switch" value="" id="showStationaryBearings">
<label class="form-check-label" for="showStationaryBearings">
<b>Show Stationary Bearings </b>
</label>
</div>
<div class="paramRow">
<b>Big TDOA Plot</b> <input type="checkbox" class="paramSelector" id="bigTDOAEnabled">
</div>
<div class="paramRow">
<b>Confidence Threshold</b><input type="text" class="paramEntry" id="bearingConfidenceThreshold" value="50"><br/>
</div>
<div class="paramRow">
<b>Maximum Age (min)</b><input type="text" class="paramEntry" id="bearingMaximumAge" value="20"><br/>
</div>
<div class="paramRow">
<b>Show Stationary Bearings</b> <input type="checkbox" class="paramSelector" id="showStationaryBearings">
</div>
<div class="paramRow">
<button type="button" class="paramSelector" id="clearBearingsBtn" onclick='destroyAllBearings();'>Clear Map</button></br>
</div>
@ -966,6 +1010,11 @@
<button type="button" class="paramSelector" id="redrawBearingsBtn" onclick='redrawBearings();'>Redraw Bearings</button></br>
</div>
<h3>Manual Bearing Entry</h3>
<div class="paramRow">
<input type="text" id="bearingManualEntry" value="0"><br/><button type="button" class="paramSelector" id="manualBearingBtn" onclick='manualBearing();'>Plot</button></br>
</div>
</div>
</div>
</div>