From b06c18a1b8c209e855c4a9846ae7f95e1ed4d993 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 6 Apr 2025 11:05:12 +0930 Subject: [PATCH 1/7] time sequenced fox additions, oclock input --- chasemapper/__init__.py | 2 +- chasemapper/config.py | 13 +++ horusmapper.cfg.example | 3 + horusmapper.py | 5 ++ static/css/oclock.css | 126 +++++++++++++++++++++++++++ static/js/bearings.js | 128 ++++++++++++++++++++++++++- static/js/settings.js | 19 +++++ templates/index.html | 26 +++++- templates/oclock.html | 185 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 502 insertions(+), 5 deletions(-) create mode 100644 static/css/oclock.css create mode 100644 templates/oclock.html 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..32da3b4 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": 20, + "time_seq_cycle": 120, + # History "reload_last_position": False, } @@ -182,6 +189,12 @@ 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 + # Telemetry Source Profiles _profile_count = config.getint("profile_selection", "profile_count") diff --git a/horusmapper.cfg.example b/horusmapper.cfg.example index 045387e..53037b9 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 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..d35492b --- /dev/null +++ b/static/css/oclock.css @@ -0,0 +1,126 @@ +.circle { + position: relative; + left: 0; + border: 1px solid black; + padding: 0; + margin: 1em auto; + 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/bearings.js b/static/js/bearings.js index f230aeb..5c5397d 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 = 20; +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,106 @@ 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 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..d3c5a06 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -66,6 +66,19 @@ function serverSettingsUpdate(data){ $('#bearingCustomColor').val(chase_config.bearing_custom_color); $('#bearingMaximumAge').val((chase_config.max_bearing_age/60.0).toFixed(0)); + + $('#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..6e4c82f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -705,6 +705,9 @@ }); } },10000); + + // Enable bearings only mode if it's set. + toggleBearingsOnlyMode(); }); @@ -1014,7 +1017,28 @@


- +
+ EasyBearing
O'Clock Entry +
+

Mt Gambier

+
+ + +
+
+ Active Time (s)
+
+
+ Cycle Time (s)
+
+
+
+
+
+
+
diff --git a/templates/oclock.html b/templates/oclock.html new file mode 100644 index 0000000..def110a --- /dev/null +++ b/templates/oclock.html @@ -0,0 +1,185 @@ + + + + O'Clock Bearing Entry + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

O'Clock Entry

+
+
+
+
+
    +
  • 1
  • +
  • 2
  • +
  • 3
  • +
  • 4
  • +
  • 5
  • +
  • 6
  • +
  • 7
  • +
  • 8
  • +
  • 9
  • +
  • 10
  • +
  • 11
  • +
  • 12
  • +
      +
+
+
+
+
Last Bearing:
Last Time:
+
+
+
+ + + From 88b691381e82f67f9a0eb35fbc5d06291a7cf67c Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 6 Apr 2025 11:36:56 +0930 Subject: [PATCH 2/7] doa confidence from config file, add warning when imu not aligned --- chasemapper/config.py | 6 ++++++ horusmapper.cfg.example | 4 ++++ static/css/oclock.css | 1 - static/js/balloon.js | 8 ++++++++ static/js/settings.js | 2 +- templates/index.html | 2 +- 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/chasemapper/config.py b/chasemapper/config.py index 32da3b4..d806fa2 100644 --- a/chasemapper/config.py +++ b/chasemapper/config.py @@ -195,6 +195,12 @@ def parse_config_file(filename): 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/horusmapper.cfg.example b/horusmapper.cfg.example index 53037b9..7134596 100644 --- a/horusmapper.cfg.example +++ b/horusmapper.cfg.example @@ -263,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/static/css/oclock.css b/static/css/oclock.css index d35492b..c53798f 100644 --- a/static/css/oclock.css +++ b/static/css/oclock.css @@ -3,7 +3,6 @@ left: 0; border: 1px solid black; padding: 0; - margin: 1em auto; width: 20em; height: 20em; border-radius: 50%; diff --git a/static/js/balloon.js b/static/js/balloon.js index 882f994..9dfcbb7 100644 --- a/static/js/balloon.js +++ b/static/js/balloon.js @@ -248,6 +248,14 @@ function handleTelemetry(data){ // Update Detailed GPS / Heading Info if(data.hasOwnProperty('heading_status')){ $("#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/settings.js b/static/js/settings.js index d3c5a06..d09c699 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -65,7 +65,7 @@ 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() diff --git a/templates/index.html b/templates/index.html index 6e4c82f..f97912e 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 : "
Data Age
", + content : "
Data Age
", classes : 'btn-group-vertical btn-group-sm', id: 'age_display', style : From f2e6fe63b0467d6112148c19b264010195bad7be Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 6 Apr 2025 12:25:37 +0930 Subject: [PATCH 3/7] Show current fox number on map --- static/js/bearings.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/js/bearings.js b/static/js/bearings.js index 5c5397d..9507e93 100644 --- a/static/js/bearings.js +++ b/static/js/bearings.js @@ -500,8 +500,10 @@ function updateTimeSeqStatus(){ var _current_seq = getCurrentSeqNumber(); if(_current_seq >= 0 ){ var _timeseqtext = "Current Active: " + _current_seq + "
"; + $('#time_to_landing').text("Fox Active: " + _current_seq); } else { var _timeseqtext = "Current Active: None
"; + $('#time_to_landing').text(""); } for (var n=0; n<4; n++){ if(timeSeqTimes[n] > 0){ From 02cd8cd6ac3a27864ec491b723f6969b37e4c703 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 6 Apr 2025 14:14:35 +0930 Subject: [PATCH 4/7] Change timeseq fox display --- chasemapper/config.py | 2 +- chasemapper/gpsd.py | 5 +++-- static/js/balloon.js | 16 +++++++++------- static/js/bearings.js | 23 ++++++++++++++++++++--- templates/index.html | 18 +++++++++++++++++- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/chasemapper/config.py b/chasemapper/config.py index d806fa2..6a49de3 100644 --- a/chasemapper/config.py +++ b/chasemapper/config.py @@ -54,7 +54,7 @@ default_config = { # 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": 20, + "time_seq_active": 25, "time_seq_cycle": 120, # History diff --git a/chasemapper/gpsd.py b/chasemapper/gpsd.py index 2ea4a2b..07493a9 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 @@ -426,8 +427,8 @@ 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="172.17.0.3") + time.sleep(3000) _gpsd.close() # gpsd_socket = GPSDSocket() diff --git a/static/js/balloon.js b/static/js/balloon.js index 9dfcbb7..04ede73 100644 --- a/static/js/balloon.js +++ b/static/js/balloon.js @@ -247,14 +247,16 @@ 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.heading_status.includes("Ongoing")){ + $('#car_warning').text("IMU Not Aligned") + $('#car_warning').removeClass(); + $('#car_warning').addClass('dataAgeBad'); + } else { + $('#car_warning').text("") + } } } diff --git a/static/js/bearings.js b/static/js/bearings.js index 9507e93..2b92214 100644 --- a/static/js/bearings.js +++ b/static/js/bearings.js @@ -42,7 +42,7 @@ var latest_server_timestamp = Date.now()/1000.0; // These values are set to a instantaneous time when a button is clicked. var timeSeqEnabled = false; -var timeSeqActive = 20; +var timeSeqActive = 25; var timeSeqCycle = 120; var timeSeqTimes = [0,0,0,0]; @@ -500,10 +500,8 @@ function updateTimeSeqStatus(){ var _current_seq = getCurrentSeqNumber(); if(_current_seq >= 0 ){ var _timeseqtext = "Current Active: " + _current_seq + "
"; - $('#time_to_landing').text("Fox Active: " + _current_seq); } else { var _timeseqtext = "Current Active: None
"; - $('#time_to_landing').text(""); } for (var n=0; n<4; n++){ if(timeSeqTimes[n] > 0){ @@ -522,6 +520,25 @@ function updateTimeSeqStatus(){ $("#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. diff --git a/templates/index.html b/templates/index.html index f97912e..e4a5845 100644 --- a/templates/index.html +++ b/templates/index.html @@ -414,6 +414,20 @@ }) .addTo(map); + // Time-to-landing display - shows the time until landing for the currently tracked payload. + L.control.custom({ + position: 'bottomcenter', + content : "
", + classes : 'btn-group-vertical btn-group-sm', + id: 'ttl_display', + style : + { + margin: '5px', + padding: '0px 0 0 0', + cursor: 'pointer', + } + }) + .addTo(map); L.control.custom({ position: 'bottomright', @@ -675,6 +689,8 @@ }else{ $("#pred_age").text("Predictions: " + pred_data_age.toFixed(1)+"s"); } + + updateTimeSeqClock(); }, age_update_rate); // Start connection to Sondehub Websockets. @@ -1028,7 +1044,7 @@
- Active Time (s)
+ Active Time (s)
Cycle Time (s)
From 2081bc8de3ff9410b36039015bd61326cf02fda4 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 18 May 2025 19:17:52 +0930 Subject: [PATCH 5/7] Show sequenced fox state at bottom of oclock entry --- templates/oclock.html | 89 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/templates/oclock.html b/templates/oclock.html index def110a..fd7a97b 100644 --- a/templates/oclock.html +++ b/templates/oclock.html @@ -55,6 +55,76 @@ var bearing_length = 20000; + + // 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]; + + var chase_config = {}; + + function handleSettings(data){ + chase_config = data; + + timeSeqEnabled = chase_config.time_seq_enabled; + timeSeqActive = chase_config.time_seq_active; + timeSeqCycle = chase_config.time_seq_cycle; + timeSeqTimes = chase_config.time_seq_times; + } + + 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 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').html("

Fox " + _current_seq + ": " + _seqtime.toFixed(1) + "

"); + + } else { + $('#timeseq_notice').html(""); + } + + } else { + $('#timeseq_notice').html(""); + } + } + // Leaflet map instance. var map; @@ -79,7 +149,7 @@ dataType: 'json', async: false, // Yes, this is deprecated... success: function(data) { - serverSettingsUpdate(data); + handleSettings(data); } }); @@ -90,6 +160,10 @@ // This will cause the server to emit a few messages telling us to fetch data. }); + socket.on('server_settings_update', function(data){ + handleSettings(data); + }); + function handleOclockClick(value){ console.log(value); @@ -146,6 +220,12 @@ }); + window.setInterval(function () { + + + updateTimeSeqClock(); + }, 500); + }); @@ -176,7 +256,12 @@
-
Last Bearing:
Last Time:
+
Last Bearing:
Last Time:
+
+
+
+
+
From 4f2b34f411f46ce50050438409cbe40bdb5b4541 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Fri, 15 Aug 2025 14:40:07 +0930 Subject: [PATCH 6/7] Make GPSD test script take IP from argument --- chasemapper/gpsd.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/chasemapper/gpsd.py b/chasemapper/gpsd.py index 07493a9..c69194d 100644 --- a/chasemapper/gpsd.py +++ b/chasemapper/gpsd.py @@ -419,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) @@ -427,16 +432,7 @@ if __name__ == "__main__": format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG ) - _gpsd = GPSDAdaptor(callback=print_dict, hostname="172.17.0.3") + _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) From ffb63b8a1290d5bc47136f001bee4c570396d318 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Fri, 15 Aug 2025 14:42:25 +0930 Subject: [PATCH 7/7] Add note on foxtrotgps and GPSD --- README.md | 1 + 1 file changed, 1 insertion(+) 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.