Skip to content
Browser-Based High-Altitude Balloon Chase Map
JavaScript Python CSS HTML
Branch: master
Clone or download

Project Horus - Browser-Based HAB Chase Map

Chasemapper is a mapping system designed specifically for chasing high-altitude weather balloons, be it those you might launch yourself, or those launched by your local weather bureau. It is Project Horus's cross-platform replacement for oziplotter, which was our original offline mapping system, used during many high-altitude balloon flights from 2010 through 2019.

ChaseMapper Screenshot

The primary purpose of chasemapper is to provide an easy-to-use mapping interface to help you as close as possible to the landing location of a high-altitude balloon payload, ideally before the payload gets there so you can watch it land! It does this by providing live predictions of the balloon flight path during the flight, calculated from GFS weather models which are downloaded before you head off on the chase. Maps can also be served up from a local cache, allowing use without internet connectivity (useful here in Australia!).

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.


You can often find me in the #highaltitude IRC Channel on Freenode.

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!


On a Raspbian/Ubuntu/Debian system, you can get most of the required dependencies using:

$ sudo apt-get install git python-numpy python-requests python-serial python-dateutil python-flask

On other OSes the required packages should be named something similar.

You also need flask-socketio and pytz, which can be installed using pip:

$ sudo pip install flask-socketio pytz

You can then clone this repository with:

$ git clone

Telemetry Sources

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. The standard ports used for these are 55672 (for hobbyist HAB payloads) and 55673 (radiosondes). These can be generated by:
    • radiosonde_auto_rx - See here for configuration info.
    • Various 'bridge' utilities within the horus_utils repository. For example, FldigiBridge or HabitatBridge
    • The horusbinary 4FSK telemetry decoder will emit these messages on port 55672 by default.
    • My Kerberos-SDR fork 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.
    • radiosonde_auto_rx - See here 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 - Using the OziPort=8942 configuration option.

Configuration & Startup

Many settings are defined in the horusmapper.cfg configuration file. Create a copy of the example config file using

$ cp horusmapper.cfg.example horusmapper.cfg

Edit this file with your preferred text editor. The configuration file is fairly descriptive - you will need to set:

  • At least one telemetry 'profile', which defines where payload and (optionally) car position telemetry data is sourced from.
  • A default latitude and longitude for the map to centre on.

The example configuration file includes profiles suitable for receiving data from radiosonde_auto_rx, and from OziMux messages.

Once configured, you can start-up the horusmapper server with:

$ python

The server can be stopped with CTRL+C. Sometimes the server doesn't stop cleanly and may the process may need to be killed. (Sorry!)

You should then be able to access the webpage by visiting http://your_ip_here:5001/

Live Predictions

We can also run live predictions of the flight path.

To do this you need cusf_predictor_wrapper and it's dependencies installed. Refer to the documentation on how to install this.

Once compiled and the python library installed, you will need to:

  • Copy the 'pred' binary into this directory. If using the Windows build, this will be pred.exe; under Linux/OSX, just pred.
  • Copy the '' script from cusf_predictor_wrapper/apps into this directory.

You will then need to modify the horusmapper.cfg Predictor section setting as necessary to reflect the predictory binary location, the appropriate model_download command, and set [predictor] predictor_enabled = True

You can then click 'Download Model' in the web interface's setting tab to trigger a download of the latest GFS model data. Predictions will start automatically once a valid model is available.

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 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.

Offline Mapping via FoxtrotGPS's Tile Cache

(This is a work in progress, but is functional.) 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 server! FoxtrotGPS can help us with this, as it caches map tiles to ~/Maps/, with one subdirectory per map layer (i.e. ~/Maps/OSM/, ~/Maps/opencyclemap/).

This can be enabled by setting [offline_maps] tile_server_enabled = True, and changing [offline_maps] tile_server_path to point to your tile cache directory (i.e. /home/pi/Maps/). Chasemapper will assume each subdirectory in this folder is a valid map layer and will add them to the map layer list at the top-right of the interface.

Caching Maps

To grab map tiles to use with this, we're going to use FoxtrotGPS's Cached Maps feature.

  • Install FoxtrotGPS (Linux only unfortunately, works OK on a Pi!) either from source, or via your system package manager (sudo apt-get install foxtrotgps).
  • Load up FoxtrotGPS, and pan around the area you are intersted in caching. Pick the map layer you want, right-click on the map, and choose 'Map download'. You can then select how many zoom levels you want to cache, and start it downloading (this may take a while!)
  • Once you have a set of folders within your ~/Maps cache directory, you can startup Chasemapper and start using them! Tiles will be served up as they become available.

(If anyone has managed to get ECW support working in GDAL recently, please contact me! I would like to convert some topographic maps in ECW format to tiles for use with Chasemapper.)

Running as a Systemd Service

Chasemapper can be operated in a 'continuous' mode, running as a systemd service. I use this in my chase car so that I can power up my car Raspberry Pi, and have services like auto_rx and chasemapper running immediately.

To set this up, the chasemapper.service file must be edited to include your username, and the path to this directory.

$ sudo cp chasemapper.service /etc/systemd/system/
$ sudo nano /etc/systemd/system/chasemapper.service

If you are not running chasemapper on a Raspberry Pi as the 'pi' user, you will need to edit the chasemapper.service file and modify the ExecStart, WorkingDirectory and User fields. Otherwise, leave all settings at their defaults:


ExecStart=/usr/bin/python /home/pi/chasemapper/


Once/if edited, install and start the service using:

$ sudo systemctl enable chasemapper.service
$ sudo systemctl start chasemapper.service

The debug log output can be viewed buy running:

$ sudo journalctl -u chasemapper.service -f -n

To stop the service, simply run:

$ sudo systemctl stop chasemapper.service

Radio Direction Finding Support

As of August 2019, Chasemapper can also plot bearings from radio-direction-finding devices. Bearing information is accepted in the 'horus_udp' format (essentially, JSON over UDP broadcast), and can be provided as either 'relative' (bearing relative to front-of-car, with no source position information), or 'absolute' (bearing relative to true north, with a source lat/lon). Relative bearings will be fused with the instantaneous car heading, which is currently calculated from speed-gated GPS headings.

The following formats are currently supported:

# Absolute bearings - lat/lon and true bearing provided
{'type': 'BEARING', 'bearing_type': 'absolute', 'latitude': latitude, 'longitude': longitude, 'bearing': bearing}

# Relative bearings - only relative bearing is provided.
{'type': 'BEARING', 'bearing_type': 'relative', 'bearing': bearing}

The following optional fields can be provided:
    'source': An identifier for the source of the bearings, i.e. 'kerberos-sdr', 'yagi-1'
    'timestamp': A timestamp of the bearing provided by the source.
    'confidence': A confidence value for the bearing, from 0 to [MAX VALUE ??]
    'power': A reading of signal power
    'raw_bearing_angles': A list of angles, associated with...
    'raw_doa': A list of TDOA result values, for each of the provided angles.

The above formats are accepted via a horus_udp listener, and so you must have a profile set up with a telemetry_source_type of horus_udp.

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 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).

You can’t perform that action at this time.