diff --git a/README.md b/README.md
index 1e20588..4b2760d 100644
--- a/README.md
+++ b/README.md
@@ -106,6 +106,7 @@ Another option to obtain map tiles is [FoxtrotGPS](https://www.foxtrotgps.org/).
To grab map tiles using FoxtrotGPS, we're going to use FoxtrotGPS's [Cached Maps](https://www.foxtrotgps.org/doc/foxtrotgps.html#Cached-Maps) feature.
* Install FoxtrotGPS (Linux only unfortunately, works OK on a Pi!) either [from source](https://www.foxtrotgps.org/releases/), or via your system package manager (`sudo apt-get install foxtrotgps`).
+ * Warning - Installing foxtrotgps will also install gpsd, which may 'take over' your GPS receiver! If you aren't using GPSD, I'd recommend uninstalling it with: `sudo apt-get purge gpsd`
* 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.
diff --git a/chasemapper/__init__.py b/chasemapper/__init__.py
index 7640920..41977fd 100644
--- a/chasemapper/__init__.py
+++ b/chasemapper/__init__.py
@@ -8,4 +8,4 @@
# Now using Semantic Versioning (https://semver.org/) MAJOR.MINOR.PATCH
-__version__ = "1.5.4"
+__version__ = "1.5.7"
diff --git a/chasemapper/config.py b/chasemapper/config.py
index f16843c..6a49de3 100644
--- a/chasemapper/config.py
+++ b/chasemapper/config.py
@@ -50,6 +50,13 @@ default_config = {
"bearing_weight": 1.0,
"bearing_color": "black",
"bearing_custom_color": "#FF0000",
+ "bearings_only_mode": False,
+ # TimeSync Hunting Settings (not in config file, but needs to be shared between clients)
+ "time_seq_enabled": False,
+ "time_seq_times": [0,0,0,0],
+ "time_seq_active": 25,
+ "time_seq_cycle": 120,
+
# History
"reload_last_position": False,
}
@@ -182,6 +189,18 @@ def parse_config_file(filename):
logging.info("Missing ascent_rate_averaging setting, using default (10)")
chase_config["ascent_rate_averaging"] = 10
+ try:
+ chase_config["bearings_only_mode"] = config.getboolean("bearings", "bearings_only_mode")
+ except:
+ logging.info("Missing bearing_only_mode setting, using default (False)")
+ chase_config["bearings_only_mode"] = False
+
+ try:
+ chase_config["doa_confidence_threshold"] = config.getfloat("bearings", "doa_confidence_threshold")
+ except:
+ logging.info("Missing DoA Confidence Threshold Setting, using default (4.0)")
+ chase_config["doa_confidence_threshold"] = 4.0
+
# Telemetry Source Profiles
_profile_count = config.getint("profile_selection", "profile_count")
diff --git a/chasemapper/gpsd.py b/chasemapper/gpsd.py
index 2ea4a2b..c69194d 100644
--- a/chasemapper/gpsd.py
+++ b/chasemapper/gpsd.py
@@ -288,6 +288,7 @@ class DataStream(object):
except (ValueError, KeyError) as error:
logging.error("GPSD Parser - Other Error - %s" % str(error))
+ print(gpsd_socket_response)
return
@@ -418,6 +419,11 @@ class GPSDAdaptor(object):
if __name__ == "__main__":
+ # Little test script to print out received data from GPSD for debugging.
+ # Run with: python3 -m chasemapper.gpsd 127.0.0.1
+ # or whatever IP your gpsd server is running on.
+
+ import sys
def print_dict(data):
print(data)
@@ -426,16 +432,7 @@ if __name__ == "__main__":
format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG
)
- _gpsd = GPSDAdaptor(callback=print_dict)
- time.sleep(30)
+ _gpsd = GPSDAdaptor(callback=print_dict, hostname=sys.argv[1])
+ time.sleep(3000)
_gpsd.close()
- # gpsd_socket = GPSDSocket()
- # data_stream = DataStream()
- # gpsd_socket.connect()
- # gpsd_socket.watch()
- # for new_data in gpsd_socket:
- # if new_data:
- # data_stream.unpack(new_data)
- # print(data_stream.TPV)
- # #print(data_stream.SKY)
diff --git a/horusmapper.cfg.example b/horusmapper.cfg.example
index 045387e..7134596 100644
--- a/horusmapper.cfg.example
+++ b/horusmapper.cfg.example
@@ -243,6 +243,9 @@ chase_car_speed = True
#
[bearings]
+# If true, will hide balloon-related display elements from the map
+bearings_only_mode = False
+
# Number of bearings to store
max_bearings = 300
@@ -260,6 +263,10 @@ car_speed_gate = 10
# 4 degrees/second seems to work fairly well.
turn_rate_threshold = 4.0
+# DoA Confidence Threshold
+# Used to gate bearings provided by a KrakenSDR system
+doa_confidence_threshold = 4.0
+
# Visual Settings - these can be adjust in the Web GUI during runtime
# Bearing length in km
diff --git a/horusmapper.py b/horusmapper.py
index 4a9b35a..025b939 100644
--- a/horusmapper.py
+++ b/horusmapper.py
@@ -110,6 +110,11 @@ def flask_bearing_entry():
""" Render bearing entry page """
return flask.render_template("bearing_entry.html")
+@app.route("/oclock")
+def flask_oclock():
+ """ Render bearing o'clock page """
+ return flask.render_template("oclock.html")
+
@app.route("/get_telemetry_archive")
def flask_get_telemetry_archive():
return json.dumps(current_payloads)
diff --git a/static/css/oclock.css b/static/css/oclock.css
new file mode 100644
index 0000000..c53798f
--- /dev/null
+++ b/static/css/oclock.css
@@ -0,0 +1,125 @@
+.circle {
+ position: relative;
+ left: 0;
+ border: 1px solid black;
+ padding: 0;
+ width: 20em;
+ height: 20em;
+ border-radius: 50%;
+ list-style: none;
+ overflow: hidden;
+}
+li {
+ overflow: hidden;
+ position: absolute;
+ top: 0; right: 0;
+ width: 50%; height: 50%;
+ transform-origin: 0% 100%;
+}
+.text {
+ position: absolute;
+ left: -100%;
+ width: 200%; height: 200%;
+ text-align: center;
+ -webkit-transform: skewY(60deg) rotate(15deg);
+ -ms-transform: skewY(60deg) rotate(15deg);
+ transform: skewY(60deg) rotate(15deg);
+ padding-top: 20px;
+}
+
+li:first-child {
+ -webkit-transform: rotate(15deg) skewY(-60deg);
+ -ms-transform: rotate(15deg) skewY(-60deg);
+ transform: rotate(15deg) skewY(-60deg);
+}
+li:nth-child(2) {
+ -webkit-transform: rotate(45deg) skewY(-60deg);
+ -ms-transform: rotate(45deg) skewY(-60deg);
+ transform: rotate(45deg) skewY(-60deg);
+}
+li:nth-child(3) {
+ -webkit-transform: rotate(75deg) skewY(-60deg);
+ -ms-transform: rotate(75deg) skewY(-60deg);
+ transform: rotate(75deg) skewY(-60deg);
+}
+li:nth-child(4) {
+ -webkit-transform: rotate(105deg) skewY(-60deg);
+ -ms-transform: rotate(105deg) skewY(-60deg);
+ transform: rotate(105deg) skewY(-60deg);
+}
+li:nth-child(5) {
+ -webkit-transform: rotate(135deg) skewY(-60deg);
+ -ms-transform: rotate(135deg) skewY(-60deg);
+ transform: rotate(135deg) skewY(-60deg);
+}
+li:nth-child(6) {
+ -webkit-transform: rotate(165deg) skewY(-60deg);
+ -ms-transform: rotate(165deg) skewY(-60deg);
+ transform: rotate(165deg) skewY(-60deg);
+}
+li:nth-child(7) {
+ -webkit-transform: rotate(195deg) skewY(-60deg);
+ -ms-transform: rotate(195deg) skewY(-60deg);
+ transform: rotate(195deg) skewY(-60deg);
+}
+li:nth-child(8) {
+ -webkit-transform: rotate(225deg) skewY(-60deg);
+ -ms-transform: rotate(225deg) skewY(-60deg);
+ transform: rotate(225deg) skewY(-60deg);
+}
+li:nth-child(9) {
+ -webkit-transform: rotate(255deg) skewY(-60deg);
+ -ms-transform: rotate(255deg) skewY(-60deg);
+ transform: rotate(255deg) skewY(-60deg);
+}
+li:nth-child(10) {
+ -webkit-transform: rotate(285deg) skewY(-60deg);
+ -ms-transform: rotate(285deg) skewY(-60deg);
+ transform: rotate(285deg) skewY(-60deg);
+}
+li:nth-child(11) {
+ -webkit-transform: rotate(315deg) skewY(-60deg);
+ -ms-transform: rotate(315deg) skewY(-60deg);
+ transform: rotate(315deg) skewY(-60deg);
+}
+li:nth-child(12) {
+ -webkit-transform: rotate(345deg) skewY(-60deg);
+ -ms-transform: rotate(345deg) skewY(-60deg);
+ transform: rotate(345deg) skewY(-60deg);
+}
+li:first-child .text {
+ background: green;
+}
+li:nth-child(2) .text {
+ background: tomato;
+}
+li:nth-child(3) .text {
+ background: aqua;
+}
+li:nth-child(4) .text {
+ background: yellow;
+}
+li:nth-child(5) .text {
+ background: orange;
+}
+li:nth-child(6) .text {
+ background: purple;
+}
+li:nth-child(7) .text {
+ background: cyan;
+}
+li:nth-child(8) .text {
+ background: brown;
+}
+li:nth-child(9) .text {
+ background: gray;
+}
+li:nth-child(10) .text {
+ background: pink;
+}
+li:nth-child(11) .text {
+ background: maroon;
+}
+li:nth-child(12) .text {
+ background: gold;
+}
diff --git a/static/js/balloon.js b/static/js/balloon.js
index 882f994..04ede73 100644
--- a/static/js/balloon.js
+++ b/static/js/balloon.js
@@ -247,7 +247,17 @@ function handleTelemetry(data){
// Update Detailed GPS / Heading Info
if(data.hasOwnProperty('heading_status')){
- $("#headingStatus").text(data.heading_status);
+ if(data.heading_status != null){
+ $("#headingStatus").text(data.heading_status);
+
+ if(data.heading_status.includes("Ongoing")){
+ $('#car_warning').text("IMU Not Aligned")
+ $('#car_warning').removeClass();
+ $('#car_warning').addClass('dataAgeBad');
+ } else {
+ $('#car_warning').text("")
+ }
+ }
}
if(data.hasOwnProperty('numSV')){
diff --git a/static/js/bearings.js b/static/js/bearings.js
index f230aeb..2b92214 100644
--- a/static/js/bearings.js
+++ b/static/js/bearings.js
@@ -36,6 +36,16 @@ var bearing_large_plot = false;
// Start out with just our own local timestamp.
var latest_server_timestamp = Date.now()/1000.0;
+// Time-Sequenced Transmitter Code
+// ... which is entirely specific to one event at the Mt Gambier Convention,
+// yet took me ages to write.
+
+// These values are set to a instantaneous time when a button is clicked.
+var timeSeqEnabled = false;
+var timeSeqActive = 25;
+var timeSeqCycle = 120;
+var timeSeqTimes = [0,0,0,0];
+
function updateBearingSettings(){
// Update bearing settings, but do *not* redraw.
@@ -69,7 +79,7 @@ function destroyAllBearings(){
});
bearing_store = {};
- bearing_sources = [];
+ //bearing_sources = [];
}
@@ -114,6 +124,15 @@ function addBearing(timestamp, bearing, live){
bearing_store[timestamp] = bearing;
+ if (timeSeqEnabled){
+ // Check if this bearing is from the current time-sequenced transmitter.
+ var _current_seq = getCurrentSeqNumber();
+ if (_current_seq >= 0){
+ bearing.source = bearing.source + "_Fox" + _current_seq;
+ }
+ updateTimeSeqStatus();
+ }
+
if ( !bearing_sources.includes(bearing.source)){
bearing_sources.push(bearing.source);
_new_bearing_div_name = "bearing_source_" + bearing.source;
@@ -297,7 +316,7 @@ function toggleBearingsOnlyMode(){
var _bearings_only_enabled = document.getElementById("bearingsOnlyMode").checked;
- if ((_bearings_only_enabled == true) && (bearings_only_mode == false)){
+ if ((_bearings_only_enabled == true) ){//} && (bearings_only_mode == false)){
// The user had just enabled the bearings_only_mode, so hide things that are not relevant.
$("#summary_table").hide();
@@ -309,7 +328,7 @@ function toggleBearingsOnlyMode(){
bearings_only_mode = true;
- } else if ((_bearings_only_enabled == false) && (bearings_only_mode == true)){
+ } else if ((_bearings_only_enabled == false)){//} && (bearings_only_mode == true)){
// Un-hide balloon stuff
$("#summary_table").show();
@@ -473,3 +492,125 @@ function manualBearing(){
socket.emit('add_manual_bearing', _bearing_info);
}
+
+
+
+function updateTimeSeqStatus(){
+ // Update text indicating which sequence number is active.
+ var _current_seq = getCurrentSeqNumber();
+ if(_current_seq >= 0 ){
+ var _timeseqtext = "Current Active: " + _current_seq + "
";
+ } else {
+ var _timeseqtext = "Current Active: None
";
+ }
+ for (var n=0; n<4; n++){
+ if(timeSeqTimes[n] > 0){
+ timeseq_hms = new Date(timeSeqTimes[n]);
+ _timeseqtext += "Fox "+n+": " + timeseq_hms.toLocaleTimeString() + "
";
+ $("#timeSeqSet" + n).css("background-color", "#00FF00");
+ }else if (timeSeqTimes[n] < 0){
+ _timeseqtext += "Fox "+n+": Not Set
";
+ $("#timeSeqSet" + num).css("background-color", "#FF0000");
+ } else {
+ _timeseqtext += "Fox "+n+": Not Set
";
+ $("#timeSeqSet" + n).css("background-color", "buttonface");
+ }
+ }
+
+ $("#timeSeqStatus").html(_timeseqtext);
+}
+
+function updateTimeSeqClock(){
+ if(timeSeqEnabled == true){
+ var _current_seq = getCurrentSeqNumber();
+ if( _current_seq >= 0 ){
+
+ var _current_time = Date.now();
+ var _seqtime = timeSeqActive - ((_current_time - timeSeqTimes[_current_seq]) % (timeSeqCycle*1000))/1000.0;
+
+ $('#timeseq_notice').text("Fox " + _current_seq + ": " + _seqtime.toFixed(1));
+
+ } else {
+ $('#timeseq_notice').text("");
+ }
+
+ } else {
+ $('#timeseq_notice').text("");
+ }
+}
+
+function getCurrentSeqNumber(offset_seconds){
+ // Determine the current transmitter number, based on current time and the timeSeqTimes.
+ // Optional offset_seconds argument, to enable testing times slightly into the future.
+
+ if (typeof offset_seconds === 'undefined') {
+ offset_seconds = 0;
+ }
+
+ var _current_time = Date.now() + offset_seconds*1000;
+
+ if(timeSeqTimes[0] > 0){
+ if ((_current_time - timeSeqTimes[0]) % (timeSeqCycle*1000) < timeSeqActive*1000){
+ return 0
+ }
+ }
+ if(timeSeqTimes[1] > 0){
+ if ((_current_time - timeSeqTimes[1]) % (timeSeqCycle*1000) < timeSeqActive*1000){
+ return 1
+ }
+ }
+ if(timeSeqTimes[2] > 0){
+ if ((_current_time - timeSeqTimes[2]) % (timeSeqCycle*1000) < timeSeqActive*1000){
+ return 2
+ }
+ }
+ if(timeSeqTimes[3] > 0){
+ if ((_current_time - timeSeqTimes[3]) % (timeSeqCycle*1000) < timeSeqActive*1000){
+ return 3
+ }
+ }
+ return -1;
+}
+
+function setTimeSeq(num){
+
+ if (num>= 0){
+ timeSeqEnabled = true;
+ $("#timeSeqEnabled").prop('checked', true);
+ // Check we arent currently in the middle of a transmit period
+ if (getCurrentSeqNumber() < 0 && getCurrentSeqNumber(timeSeqActive)){
+ // Update
+ timeSeqTimes[num] = Date.now();
+ // Set button color to green.
+ $("#timeSeqSet" + num).css("background-color", "#00FF00");
+ } else {
+ timeSeqTimes[num] = -1;
+ // Set button color to red.
+ $("#timeSeqSet" + num).css("background-color", "#FF0000");
+ }
+ } else {
+ timeSeqEnabled = false;
+ $("#timeSeqEnabled").prop('checked', false);
+ timeSeqTimes = [0,0,0,0];
+ $("#timeSeqSet0").css("background-color", "buttonface");
+ $("#timeSeqSet1").css("background-color", "buttonface");
+ $("#timeSeqSet2").css("background-color", "buttonface");
+ $("#timeSeqSet3").css("background-color", "buttonface");
+ }
+ updateTimeSeqStatus();
+ clientSettingsUpdate();
+}
+
+function toggleTimeSeqEnabled(){
+ // Enable-disable time sequenced transmitters.
+ var _time_seq_enabled = document.getElementById("timeSeqEnabled").checked;
+
+ if (_time_seq_enabled == true){
+ // Enable time-sequenced transmitters.
+ timeSeqEnabled = true;
+ } else {
+ // Disable time-sequenced transmitters.
+ timeSeqEnabled = false;
+ }
+ clientSettingsUpdate();
+}
\ No newline at end of file
diff --git a/static/js/settings.js b/static/js/settings.js
index e3f5e3a..d09c699 100644
--- a/static/js/settings.js
+++ b/static/js/settings.js
@@ -65,6 +65,19 @@ function serverSettingsUpdate(data){
$('#bearingColorSelect').val(chase_config.bearing_color);
$('#bearingCustomColor').val(chase_config.bearing_custom_color);
$('#bearingMaximumAge').val((chase_config.max_bearing_age/60.0).toFixed(0));
+ $('#bearingConfidenceThreshold').val(chase_config.doa_confidence_threshold.toFixed(1));
+
+ $('#bearingsOnlyMode').prop('checked', chase_config.bearings_only_mode);
+ toggleBearingsOnlyMode()
+ // Add new time sync bearing settings here
+
+ timeSeqEnabled = chase_config.time_seq_enabled;
+ $("#timeSeqEnabled").prop('checked', timeSeqEnabled);
+ timeSeqActive = chase_config.time_seq_active;
+ timeSeqCycle = chase_config.time_seq_cycle;
+ timeSeqTimes = chase_config.time_seq_times;
+ updateTimeSeqStatus();
+
// Clear and populate the profile selection.
$('#profileSelect').children('option:not(:first)').remove();
@@ -108,6 +121,12 @@ function clientSettingsUpdate(){
chase_config.habitat_update_rate = _habitat_update_rate
}
+ // Add in a selection of the bearing settings here.
+ // These don't change anything on the backend, but need to be propagated to other clients.
+ chase_config.time_seq_times = timeSeqTimes;
+ chase_config.time_seq_enabled = timeSeqEnabled;
+ chase_config.time_seq_active = timeSeqActive;
+ chase_config.time_seq_cycle = timeSeqCycle;
socket.emit('client_settings_update', chase_config);
};
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 80ee9dc..e4a5845 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -358,7 +358,7 @@
// Data age display - shows how old the various datasets are.
L.control.custom({
position: 'topleft',
- content : "