From 6b2ce1da94e5cfd8e06626b9d5815b78db37c979 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Thu, 18 May 2023 17:10:13 +0930 Subject: [PATCH 01/32] Rebase M20 demod --- auto_rx/autorx/__init__.py | 2 +- demod/mod/m20mod.c | 121 ++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 31 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index 55fe2949..8bfb6291 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.1" +__version__ = "1.6.2-beta1" # Global Variables diff --git a/demod/mod/m20mod.c b/demod/mod/m20mod.c index 17897ca5..9d496e8c 100644 --- a/demod/mod/m20mod.c +++ b/demod/mod/m20mod.c @@ -104,8 +104,10 @@ typedef struct { double vH; double vD; double vV; double vx; double vy; double vD2; float T; float RH; float TH; float P; + float batV; ui8_t numSV; - ui8_t utc_ofs; + //ui8_t utc_ofs; + ui8_t fwVer; char SN[12+4]; ui8_t SNraw[3]; ui8_t frame_bytes[FRAME_LEN+AUX_LEN+4]; @@ -193,12 +195,12 @@ frame[0x08..0x0A]: GPS altitude frame[0x0B..0x0E]: GPS hor.Vel. (velE,velN) frame[0x0F..0x11]: GPS TOW frame[0x15]: counter -frame[0x16..0x17]: block check - +frame[0x16..0x17]: block check (fwVer < 0x06) ; frame[0x16]: SPI1 P[0] (fwVer >= 0x07), frame[0x17]=0x00 frame[0x18..0x19]: GPS ver.Vel. (velU) frame[0x1A..0x1B]: GPS week frame[0x1C..0x1F]: GPS latitude frame[0x20..0x23]: GPS longitude +frame[0x24..0x25]: SPI1 P[1..2] (if pressure sensor) frame[0x44..0x45]: frame check */ @@ -218,7 +220,8 @@ frame[0x44..0x45]: frame check #define pos_SN 0x12 // 3 byte #define pos_CNT 0x15 // 1 byte #define pos_BlkChk 0x16 // 2 byte -#define pos_Check (stdFLEN-1) // 2 byte +#define pos_stdFW 0x43 // 1 byte +#define pos_stdCheck (stdFLEN-1) // 2 byte #define len_BlkChk 0x16 // frame[0x02..0x17] , incl. chk16 @@ -250,6 +253,10 @@ frame[0x44..0x45]: frame check #define col_CSoo "\x1b[38;5;220m" #define col_CSno "\x1b[38;5;1m" #define col_CNST "\x1b[38;5;58m" // 3 byte +#define col_ptuP "\x1b[38;5;180m" +#define col_ptuT "\x1b[38;5;110m" +#define col_ptuU "\x1b[38;5;120m" +#define col_ptuTH "\x1b[38;5;115m" /* $ for code in {0..255} @@ -701,18 +708,33 @@ static float get_RH(gpx_t *gpx) { } static float get_P(gpx_t *gpx) { -// cf. DF9DQ // float hPa = 0.0f; - ui16_t val = (gpx->frame_bytes[0x25] << 8) | gpx->frame_bytes[0x24]; + ui32_t val = (gpx->frame_bytes[0x25] << 8) | gpx->frame_bytes[0x24]; // cf. DF9DQ + ui8_t p0 = 0x00; + + if (gpx->fwVer >= 0x07) { // SPI1_P[0] + p0 = gpx->frame_bytes[0x16]; + } + val = (val << 8) | p0; if (val > 0) { - hPa = val/16.0f; + hPa = val/(float)(16*256); // 4096=0x1000 } return hPa; } +static float get_BatV(gpx_t *gpx) { + float batV = 0.0f; + ui8_t val = gpx->frame_bytes[0x26]; // cf. DF9DQ + + batV = val * (3.3f/255); // upper 8 bits ADC + + return batV; +} + + /* -------------------------------------------------------------------------- */ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { @@ -741,6 +763,8 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { gpx->P = get_P(gpx); // (optional) pressure } + gpx->batV = get_BatV(gpx); // battery V + if ( !gpx->option.slt ) { if (gpx->option.col) { @@ -763,10 +787,11 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { } if (gpx->option.vbs >= 1) { fprintf(stdout, " # "); - if (bcOK > 0) fprintf(stdout, " "col_CSok"(ok)"col_TXT); - else if (bcOK < 0) fprintf(stdout, " "col_CSoo"(oo)"col_TXT); - else fprintf(stdout, " "col_CSno"(no)"col_TXT); - // + if (gpx->fwVer < 0x07) { + if (bcOK > 0) fprintf(stdout, " "col_CSok"(ok)"col_TXT); + else if (bcOK < 0) fprintf(stdout, " "col_CSoo"(oo)"col_TXT); + else fprintf(stdout, " "col_CSno"(no)"col_TXT); + } if (csOK) fprintf(stdout, " "col_CSok"[OK]"col_TXT); else fprintf(stdout, " "col_CSno"[NO]"col_TXT); } @@ -778,10 +803,14 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { if (gpx->TH > -273.0f) fprintf(stdout, " TH:%.1fC", gpx->TH); } if (gpx->P > 0.0f) { - if (gpx->P < 100.0f) fprintf(stdout, " P=%.2fhPa ", gpx->P); - else fprintf(stdout, " P=%.1fhPa ", gpx->P); + if (gpx->P < 10.0f) fprintf(stdout, " P=%.3fhPa ", gpx->P); + else if (gpx->P < 100.0f) fprintf(stdout, " P=%.2fhPa ", gpx->P); + else fprintf(stdout, " P=%.1fhPa ", gpx->P); } } + if (gpx->option.vbs >= 3 && csOK) { + fprintf(stdout, " (bat:%.2fV)", gpx->batV); + } fprintf(stdout, ANSI_COLOR_RESET""); } else { @@ -803,11 +832,12 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { } if (gpx->option.vbs >= 1) { fprintf(stdout, " # "); - //if (bcOK) fprintf(stdout, " (ok)"); else fprintf(stdout, " (no)"); - if (bcOK > 0) fprintf(stdout, " (ok)"); - else if (bcOK < 0) fprintf(stdout, " (oo)"); - else fprintf(stdout, " (no)"); - // + if (gpx->fwVer < 0x07) { + //if (bcOK) fprintf(stdout, " (ok)"); else fprintf(stdout, " (no)"); + if (bcOK > 0) fprintf(stdout, " (ok)"); + else if (bcOK < 0) fprintf(stdout, " (oo)"); + else fprintf(stdout, " (no)"); + } if (csOK) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]"); } if (gpx->option.ptu && csOK) { @@ -818,10 +848,14 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { if (gpx->TH > -273.0f) fprintf(stdout, " TH:%.1fC", gpx->TH); } if (gpx->P > 0.0f) { - if (gpx->P < 100.0f) fprintf(stdout, " P=%.2fhPa ", gpx->P); - else fprintf(stdout, " P=%.1fhPa ", gpx->P); + if (gpx->P < 10.0f) fprintf(stdout, " P=%.3fhPa ", gpx->P); + else if (gpx->P < 100.0f) fprintf(stdout, " P=%.2fhPa ", gpx->P); + else fprintf(stdout, " P=%.1fhPa ", gpx->P); } } + if (gpx->option.vbs >= 3 && csOK) { + fprintf(stdout, " (bat:%.2fV)", gpx->batV); + } } fprintf(stdout, "\n"); } @@ -846,6 +880,7 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { if (gpx->RH > -0.5f) fprintf(stdout, ", \"humidity\": %.1f", gpx->RH ); if (gpx->P > 0.0f) fprintf(stdout, ", \"pressure\": %.2f", gpx->P ); } + fprintf(stdout, ", \"batt\": %.2f", gpx->batV); fprintf(stdout, ", \"rawid\": \"M20_%02X%02X%02X\"", gpx->frame_bytes[pos_SN], gpx->frame_bytes[pos_SN+1], gpx->frame_bytes[pos_SN+2]); // gpx->type fprintf(stdout, ", \"subtype\": \"0x%02X\"", gpx->type); if (gpx->jsn_freq > 0) { @@ -876,6 +911,8 @@ static int print_frame(gpx_t *gpx, int pos, int b2B) { int cs1, cs2; int bc1, bc2, bc; int flen = stdFLEN; // stdFLEN=0x64, auxFLEN=0x76; M20:0x45 ? + int pos_fw = pos_stdFW; + int pos_check = pos_stdCheck; if (b2B) { bits2bytes(gpx->frame_bits, gpx->frame_bytes); @@ -885,10 +922,21 @@ static int print_frame(gpx_t *gpx, int pos, int b2B) { else { gpx->auxlen = flen - stdFLEN; //if (gpx->auxlen < 0 || gpx->auxlen > AUX_LEN) gpx->auxlen = 0; // 0x43,0x45 + if (gpx->auxlen < 0) { + gpx->auxlen = 0; + pos_fw = flen-2; // only if flen < stdFLEN + } + else if (gpx->auxlen > AUX_LEN) { + gpx->auxlen = AUX_LEN; + flen = stdFLEN+AUX_LEN; + } } + pos_check = flen-1; + gpx->fwVer = gpx->frame_bytes[pos_fw]; + if (gpx->fwVer > 0x20) gpx->fwVer = 0; - cs1 = (gpx->frame_bytes[pos_Check+gpx->auxlen] << 8) | gpx->frame_bytes[pos_Check+gpx->auxlen+1]; - cs2 = checkM10(gpx->frame_bytes, pos_Check+gpx->auxlen); + cs1 = (gpx->frame_bytes[pos_check] << 8) | gpx->frame_bytes[pos_check+1]; + cs2 = checkM10(gpx->frame_bytes, pos_check); bc1 = (gpx->frame_bytes[pos_BlkChk] << 8) | gpx->frame_bytes[pos_BlkChk+1]; bc2 = blk_checkM10(len_BlkChk, gpx->frame_bytes+2); // len(essentialBlock+chk16) = 0x16 @@ -921,16 +969,27 @@ static int print_frame(gpx_t *gpx, int pos, int b2B) { if ((i >= pos_GPSvU) && (i < pos_GPSvU+2)) fprintf(stdout, col_GPSvel); if ((i >= pos_SN) && (i < pos_SN+3)) fprintf(stdout, col_SN); if (i == pos_CNT) fprintf(stdout, col_CNT); - if ((i >= pos_BlkChk) && (i < pos_BlkChk+2)) fprintf(stdout, col_Check); - if ((i >= pos_Check+gpx->auxlen) && (i < pos_Check+gpx->auxlen+2)) fprintf(stdout, col_Check); + if (gpx->fwVer < 0x07) { + if ((i >= pos_BlkChk) && (i < pos_BlkChk+2)) fprintf(stdout, col_Check); + } else { + if ((i >= pos_BlkChk+1) && (i < pos_BlkChk+2)) fprintf(stdout, col_Check); + } + if (i >= 0x02 && i <= 0x03) fprintf(stdout, col_ptuU); + if (i >= 0x04 && i <= 0x05) fprintf(stdout, col_ptuT); + if (i >= 0x06 && i <= 0x07) fprintf(stdout, col_ptuTH); + if (i == 0x16 && gpx->fwVer >= 0x07 || i >= 0x24 && i <= 0x25) fprintf(stdout, col_ptuP); + + if ((i >= pos_check) && (i < pos_check+2)) fprintf(stdout, col_Check); fprintf(stdout, "%02x", byte); fprintf(stdout, col_FRTXT); } if (gpx->option.vbs) { fprintf(stdout, " # "col_Check"%04x"col_FRTXT, cs2); - if (bc > 0) fprintf(stdout, " "col_CSok"(ok)"col_TXT); - else if (bc < 0) fprintf(stdout, " "col_CSoo"(oo)"col_TXT); - else fprintf(stdout, " "col_CSno"(no)"col_TXT); + if (gpx->fwVer < 0x07) { + if (bc > 0) fprintf(stdout, " "col_CSok"(ok)"col_TXT); + else if (bc < 0) fprintf(stdout, " "col_CSoo"(oo)"col_TXT); + else fprintf(stdout, " "col_CSno"(no)"col_TXT); + } if (cs1 == cs2) fprintf(stdout, " "col_CSok"[OK]"col_TXT); else fprintf(stdout, " "col_CSno"[NO]"col_TXT); } @@ -943,9 +1002,11 @@ static int print_frame(gpx_t *gpx, int pos, int b2B) { } if (gpx->option.vbs) { fprintf(stdout, " # %04x", cs2); - if (bc > 0) fprintf(stdout, " (ok)"); - else if (bc < 0) fprintf(stdout, " (oo)"); - else fprintf(stdout, " (no)"); + if (gpx->fwVer < 0x07) { + if (bc > 0) fprintf(stdout, " (ok)"); + else if (bc < 0) fprintf(stdout, " (oo)"); + else fprintf(stdout, " (no)"); + } if (cs1 == cs2) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]"); } fprintf(stdout, "\n"); From 139378b8643c595970413ddf725054f252d90f34 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Thu, 18 May 2023 18:15:47 +0930 Subject: [PATCH 02/32] Add email notification on detection of an encrypted radisonde --- auto_rx/auto_rx.py | 1 + auto_rx/autorx/config.py | 11 +++++ auto_rx/autorx/decode.py | 15 ++++++ auto_rx/autorx/email_notification.py | 69 +++++++++++++++++++++++----- auto_rx/station.cfg.example | 3 ++ auto_rx/station.cfg.example.network | 3 ++ 6 files changed, 91 insertions(+), 11 deletions(-) diff --git a/auto_rx/auto_rx.py b/auto_rx/auto_rx.py index 204374cc..b5f18729 100644 --- a/auto_rx/auto_rx.py +++ b/auto_rx/auto_rx.py @@ -897,6 +897,7 @@ def main(): ), launch_notifications=config["email_launch_notifications"], landing_notifications=config["email_landing_notifications"], + encrypted_sonde_notifications=config["email_encrypted_sonde_notifications"], landing_range_threshold=config["email_landing_range_threshold"], landing_altitude_threshold=config["email_landing_altitude_threshold"], ) diff --git a/auto_rx/autorx/config.py b/auto_rx/autorx/config.py index 8aa77fc7..15a3ec9b 100644 --- a/auto_rx/autorx/config.py +++ b/auto_rx/autorx/config.py @@ -748,6 +748,17 @@ def read_auto_rx_config(filename, no_sdr_test=False): "Config - Did not find system / debug logging options, using defaults (disabled, unless set as a command-line option.)" ) + # 1.6.2 - Encrypted Sonde Email Notifications + try: + auto_rx_config["email_encrypted_sonde_notifications"] = config.getboolean( + "email", "encrypted_sonde_notifications" + ) + except: + logging.warning( + "Config - Did not find encrypted_sonde_notifications setting (new in v1.6.2), using default (True)" + ) + auto_rx_config["email_encrypted_sonde_notifications"] = True + # If we are being called as part of a unit test, just return the config now. if no_sdr_test: diff --git a/auto_rx/autorx/decode.py b/auto_rx/autorx/decode.py index 1e77251b..bf46e39f 100644 --- a/auto_rx/autorx/decode.py +++ b/auto_rx/autorx/decode.py @@ -23,6 +23,7 @@ from .sonde_specific import fix_datetime, imet_unique_id from .fsk_demod import FSKDemodStats from .sdr_wrappers import test_sdr, get_sdr_iq_cmd, get_sdr_fm_cmd, get_sdr_name +from .email_notification import EmailNotification # Global valid sonde types list. VALID_SONDE_TYPES = [ @@ -1424,6 +1425,20 @@ def handle_decoder_line(self, data): "Radiosonde %s has encrypted telemetry (Possible encrypted RS41-SGM)! We cannot decode this, closing decoder." % _telemetry["id"] ) + + # Overwrite the datetime field to make the email notifier happy + _telemetry['datetime_dt'] = datetime.datetime.utcnow() + _telemetry["freq"] = "%.3f MHz" % (self.sonde_freq / 1e6) + + # Send this to only the Email Notifier, if it exists. + for _exporter in self.exporters: + try: + if _exporter.__self__.__module__ == EmailNotification.__module__: + _exporter(_telemetry) + except Exception as e: + self.log_error("Exporter Error %s" % str(e)) + + # Close the decoder. self.exit_state = "Encrypted" self.decoder_running = False return False diff --git a/auto_rx/autorx/email_notification.py b/auto_rx/autorx/email_notification.py index 0b2db5a0..077132de 100644 --- a/auto_rx/autorx/email_notification.py +++ b/auto_rx/autorx/email_notification.py @@ -43,6 +43,7 @@ def __init__( station_position=None, launch_notifications=True, landing_notifications=True, + encrypted_sonde_notifications=True, landing_range_threshold=50, landing_altitude_threshold=1000, landing_descent_trip=10, @@ -60,6 +61,7 @@ def __init__( self.station_position = station_position self.launch_notifications = launch_notifications self.landing_notifications = landing_notifications + self.encrypted_sonde_notifications = encrypted_sonde_notifications self.landing_range_threshold = landing_range_threshold self.landing_altitude_threshold = landing_altitude_threshold self.landing_descent_trip = landing_descent_trip @@ -133,18 +135,44 @@ def process_telemetry(self, telemetry): } ) - if self.launch_notifications: + if "encrypted" in telemetry: + if telemetry["encrypted"] and self.encrypted_sonde_notifications: + try: + # This is a new Encrypted Radiosonde, send an email. + msg = "Encrypted Radiosonde Detected:\n" + msg += "\n" + + if "subtype" in telemetry: + telemetry["type"] = telemetry["subtype"] + + msg += "Serial: %s\n" % _id + msg += "Type: %s\n" % telemetry["type"] + msg += "Frequency: %s\n" % telemetry["freq"] + msg += "Time Detected: %sZ\n" % telemetry["datetime_dt"].isoformat() + + # Construct subject + _subject = self.mail_subject + _subject = _subject.replace("", telemetry["id"]) + _subject = _subject.replace("", telemetry["type"]) + _subject = _subject.replace("", telemetry["freq"]) + + if "encrypted" in telemetry: + if telemetry["encrypted"] == True: + _subject += " - ENCRYPTED SONDE" + + self.send_notification_email(subject=_subject, message=msg) + + except Exception as e: + self.log_error("Error sending E-mail - %s" % str(e)) + + elif self.launch_notifications: try: # This is a new sonde. Send the email. msg = "Sonde launch detected:\n" msg += "\n" - if "encrypted" in telemetry: - if telemetry["encrypted"] == True: - msg += "ENCRYPTED RADIOSONDE DETECTED!\n" - - msg += "Callsign: %s\n" % _id + msg += "Serial: %s\n" % _id msg += "Type: %s\n" % telemetry["type"] msg += "Frequency: %s\n" % telemetry["freq"] msg += "Position: %.5f,%.5f\n" % ( @@ -175,10 +203,6 @@ def process_telemetry(self, telemetry): _subject = _subject.replace("", telemetry["type"]) _subject = _subject.replace("", telemetry["freq"]) - if "encrypted" in telemetry: - if telemetry["encrypted"] == True: - _subject += " - ENCRYPTED SONDE" - self.send_notification_email(subject=_subject, message=msg) except Exception as e: @@ -237,7 +261,7 @@ def process_telemetry(self, telemetry): msg = "Nearby sonde landing detected:\n\n" - msg += "Callsign: %s\n" % _id + msg += "Serial: %s\n" % _id msg += "Type: %s\n" % telemetry["type"] msg += "Frequency: %s\n" % telemetry["freq"] msg += "Position: %.5f,%.5f\n" % ( @@ -433,6 +457,29 @@ def log_error(self, line): } ) + time.sleep(10) + + print("Testing encrypted sonde alert.") + _email_notification.add( + { + "id": "R1234557", + "frame": 10, + "lat": 0.0, + "lon": 0.0, + "alt": 0, + "temp": 1.0, + "type": "RS41", + "subtype": "RS41-SGM", + "freq": "401.520 MHz", + "freq_float": 401.52, + "heading": 0.0, + "vel_h": 5.1, + "vel_v": -5.0, + "datetime_dt": datetime.datetime.utcnow(), + "encrypted": True + } + ) + # Wait a little bit before shutting down. time.sleep(5) diff --git a/auto_rx/station.cfg.example b/auto_rx/station.cfg.example index c5dbca5f..f0541150 100644 --- a/auto_rx/station.cfg.example +++ b/auto_rx/station.cfg.example @@ -362,6 +362,9 @@ launch_notifications = True # Send e-mails when a radiosonde is detected descending near your station location landing_notifications = True +# Send e-mails when an encrypted radiosonde is detected. +encrypted_sonde_notifications = True + # Range threshold for Landing notifications (km from your station location) landing_range_threshold = 30 diff --git a/auto_rx/station.cfg.example.network b/auto_rx/station.cfg.example.network index a0062617..61a8d16b 100644 --- a/auto_rx/station.cfg.example.network +++ b/auto_rx/station.cfg.example.network @@ -363,6 +363,9 @@ launch_notifications = True # Send e-mails when a radiosonde is detected descending near your station location landing_notifications = True +# Send e-mails when an encrypted radiosonde is detected. +encrypted_sonde_notifications = True + # Range threshold for Landing notifications (km from your station location) landing_range_threshold = 30 From 6e098108ab4dd31fe431816f626ba72a0ad81a3e Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Tue, 23 May 2023 17:27:51 +0930 Subject: [PATCH 03/32] Add simple-websocket to requirements --- auto_rx/autorx/__init__.py | 2 +- auto_rx/requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index 8bfb6291..8f2083b3 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta1" +__version__ = "1.6.2-beta2" # Global Variables diff --git a/auto_rx/requirements.txt b/auto_rx/requirements.txt index c9ebba6d..3f1bb376 100644 --- a/auto_rx/requirements.txt +++ b/auto_rx/requirements.txt @@ -6,3 +6,4 @@ numpy requests semver simplekml +simple-websocket From a3aae7a14e0281546cd7c853f5e6b3c1abc6c82d Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 28 May 2023 15:56:49 +0930 Subject: [PATCH 04/32] Only send landing notifications if sonde has gone above the landing alt threshold first. --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/config.py | 2 +- auto_rx/autorx/email_notification.py | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index 8f2083b3..efa39576 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta2" +__version__ = "1.6.2-beta3" # Global Variables diff --git a/auto_rx/autorx/config.py b/auto_rx/autorx/config.py index 15a3ec9b..d35fa3bb 100644 --- a/auto_rx/autorx/config.py +++ b/auto_rx/autorx/config.py @@ -683,7 +683,7 @@ def read_auto_rx_config(filename, no_sdr_test=False): # that this goes against the wishes of the radiosonde_auto_rx developers to not be part # of the bigger problem of APRS-IS congestion. - ALLOWED_APRS_SERVERS = ["radiosondy.info"] + ALLOWED_APRS_SERVERS = ["radiosondy.info", "localhost"] ALLOWED_APRS_PORTS = [14590] if auto_rx_config["aprs_server"] not in ALLOWED_APRS_SERVERS: diff --git a/auto_rx/autorx/email_notification.py b/auto_rx/autorx/email_notification.py index 077132de..4a8f972c 100644 --- a/auto_rx/autorx/email_notification.py +++ b/auto_rx/autorx/email_notification.py @@ -121,6 +121,7 @@ def process_telemetry(self, telemetry): self.sondes[_id] = { "last_time": time.time(), "descending_trip": 0, + "ascent_trip": False, "descent_notified": False, "track": GenericTrack(max_elements=20), } @@ -224,14 +225,21 @@ def process_telemetry(self, telemetry): # We have seen this sonde recently. Let's check it's descending... if self.sondes[_id]["descent_notified"] == False and _sonde_state: + + # Set a flag if the sonde has passed above the landing altitude threshold. + # This is used along with the descending trip to trigger a landing email notification. + if (telemetry["alt"] > self.landing_altitude_threshold): + self.sondes[_id]["ascent_trip"] = True + # If the sonde is below our threshold altitude, *and* is descending at a reasonable rate, increment. if (telemetry["alt"] < self.landing_altitude_threshold) and ( _sonde_state["ascent_rate"] < -2.0 ): self.sondes[_id]["descending_trip"] += 1 - if self.sondes[_id]["descending_trip"] > self.landing_descent_trip: - # We've seen this sonde descending for enough time now. + if (self.sondes[_id]["descending_trip"] > self.landing_descent_trip) and self.sondes[_id]["ascent_trip"]: + # We've seen this sonde descending for enough time now AND we have also seen it go above the landing threshold, + # so it's likely been on a flight and isnt just bouncing around on the ground. # Note that we've passed the descent threshold, so we shouldn't analyze anything from this sonde anymore. self.sondes[_id]["descent_notified"] = True From 38f4141d79f5a2e18dd7d22f540600aef5c71599 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Thu, 15 Jun 2023 07:54:45 +1000 Subject: [PATCH 05/32] Add comment about DFM upload rates --- auto_rx/station.cfg.example | 3 +++ auto_rx/station.cfg.example.network | 3 +++ 2 files changed, 6 insertions(+) diff --git a/auto_rx/station.cfg.example b/auto_rx/station.cfg.example index f0541150..081f3e55 100644 --- a/auto_rx/station.cfg.example +++ b/auto_rx/station.cfg.example @@ -200,6 +200,9 @@ sondehub_enabled = True # How often to push data to the SondeHub Database. (seconds) # All received positions are cached and uploaded every X seconds. # Uploads are gzip compressed, so don't require much data transfer. +# Users receiving Graw DFM sondes may want to set this to 30 to improve +# the chances of uploads not being rejected by our Z-check. +# (Refer: https://github.com/projecthorus/sondehub-infra/wiki/DFM-radiosonde-above-1000-and-not-enough-data-to-perform-z-check ) sondehub_upload_rate = 15 # An optional contact e-mail address. diff --git a/auto_rx/station.cfg.example.network b/auto_rx/station.cfg.example.network index 61a8d16b..d82cebda 100644 --- a/auto_rx/station.cfg.example.network +++ b/auto_rx/station.cfg.example.network @@ -201,6 +201,9 @@ sondehub_enabled = True # How often to push data to the SondeHub Database. (seconds) # All received positions are cached and uploaded every X seconds. # Uploads are gzip compressed, so don't require much data transfer. +# Users receiving Graw DFM sondes may want to set this to 30 to improve +# the chances of uploads not being rejected by our Z-check. +# (Refer: https://github.com/projecthorus/sondehub-infra/wiki/DFM-radiosonde-above-1000-and-not-enough-data-to-perform-z-check ) sondehub_upload_rate = 15 # An optional contact e-mail address. From 1a6dbc0ac7307c9501d22748e930378a849abdac Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sat, 24 Jun 2023 23:22:38 +0930 Subject: [PATCH 06/32] Exit scan loop faster. Add wettersonde.net to APRS allowed list --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/config.py | 4 ++-- auto_rx/autorx/scan.py | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index efa39576..a108510c 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta3" +__version__ = "1.6.2-beta4" # Global Variables diff --git a/auto_rx/autorx/config.py b/auto_rx/autorx/config.py index d35fa3bb..f9b9a56d 100644 --- a/auto_rx/autorx/config.py +++ b/auto_rx/autorx/config.py @@ -683,8 +683,8 @@ def read_auto_rx_config(filename, no_sdr_test=False): # that this goes against the wishes of the radiosonde_auto_rx developers to not be part # of the bigger problem of APRS-IS congestion. - ALLOWED_APRS_SERVERS = ["radiosondy.info", "localhost"] - ALLOWED_APRS_PORTS = [14590] + ALLOWED_APRS_SERVERS = ["radiosondy.info", "wettersonde.net", "localhost"] + ALLOWED_APRS_PORTS = [14580, 14590] if auto_rx_config["aprs_server"] not in ALLOWED_APRS_SERVERS: logging.warning( diff --git a/auto_rx/autorx/scan.py b/auto_rx/autorx/scan.py index e23e317b..1077c5ee 100644 --- a/auto_rx/autorx/scan.py +++ b/auto_rx/autorx/scan.py @@ -866,7 +866,11 @@ def scan_loop(self): self.error_retries = 0 # Sleep before starting the next scan. - time.sleep(self.scan_delay) + for i in range(self.scan_delay): + time.sleep(1) + if self.sonde_scanner_running == False: + self.log_debug("Breaking out of scan loop.") + break self.log_info("Scanner Thread Closed.") self.sonde_scanner_running = False From 40926abad9048567dbaddac35e13b4d053295943 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:44:47 +0300 Subject: [PATCH 07/32] raise exception on config file problems instead of returning quietly --- auto_rx/autorx/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/auto_rx/autorx/config.py b/auto_rx/autorx/config.py index 8aa77fc7..e5108731 100644 --- a/auto_rx/autorx/config.py +++ b/auto_rx/autorx/config.py @@ -872,7 +872,7 @@ def read_auto_rx_config(filename, no_sdr_test=False): if len(auto_rx_config["sdr_settings"].keys()) == 0: # We have no SDRs to use!! logging.error("Config - No working SDRs! Cannot run...") - return None + raise SystemError("No working SDRs!") else: # Create a global copy of the configuration file at this point global_config = copy.deepcopy(auto_rx_config) @@ -891,7 +891,8 @@ def read_auto_rx_config(filename, no_sdr_test=False): web_password = auto_rx_config["web_password"] return auto_rx_config - + except SystemError as e: + raise e except: traceback.print_exc() logging.error("Could not parse config file.") From 16acef5d2c9e1eda05ac4910e39c05c250f5425a Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:45:44 +0300 Subject: [PATCH 08/32] added timeouts for joining threads so that the system will not hang forever if some thread does not join --- auto_rx/autorx/aprs.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/auto_rx/autorx/aprs.py b/auto_rx/autorx/aprs.py index eb864496..747eeaa0 100644 --- a/auto_rx/autorx/aprs.py +++ b/auto_rx/autorx/aprs.py @@ -759,13 +759,19 @@ def close(self): # Wait for all threads to close. if self.upload_thread is not None: - self.upload_thread.join() + self.upload_thread.join(60) + if self.upload_thread.is_alive(): + self.log_error("aprs upload thread failed to join") if self.timer_thread is not None: - self.timer_thread.join() + self.timer_thread.join(60) + if self.timer_thread.is_alive(): + self.log_error("aprs timer thread failed to join") if self.input_thread is not None: - self.input_thread.join() + self.input_thread.join(60) + if self.input_thread.is_alive(): + self.log_error("aprs input thread failed to join") def log_debug(self, line): """ Helper function to log a debug message with a descriptive heading. From f0288365caa6895b2cf5c0e3b69ae6a3db3aa174 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:47:08 +0300 Subject: [PATCH 09/32] added timeout for joining gps thread --- auto_rx/autorx/gpsd.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/gpsd.py b/auto_rx/autorx/gpsd.py index bdd8d8e1..ac19782a 100644 --- a/auto_rx/autorx/gpsd.py +++ b/auto_rx/autorx/gpsd.py @@ -335,7 +335,9 @@ def close(self): self.gpsd_thread_running = False # Wait for the thread to close. if self.gpsd_thread != None: - self.gpsd_thread.join() + self.gpsd_thread.join(60) + if self.gpsd_thread.is_alive(): + logging.error("GPS thread failed to join") def send_to_callback(self, data): """ From 55f9924c34f960868a302849860275a8c62a1866 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:48:03 +0300 Subject: [PATCH 10/32] added timeouts for joining theards to ensure that the system will not hang if some thread will not join --- auto_rx/autorx/habitat.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/auto_rx/autorx/habitat.py b/auto_rx/autorx/habitat.py index ebb4303b..f76c4299 100644 --- a/auto_rx/autorx/habitat.py +++ b/auto_rx/autorx/habitat.py @@ -831,13 +831,20 @@ def close(self): # Wait for all threads to close. if self.upload_thread is not None: - self.upload_thread.join() + self.upload_thread.join(60) + if self.upload_thread.is_alive(): + self.log_error("habitat upload thread failed to join") + if self.timer_thread is not None: - self.timer_thread.join() + self.timer_thread.join(60) + if self.timer_thread.is_alive(): + self.log_error("habitat timer thread failed to join") if self.input_thread is not None: - self.input_thread.join() + self.input_thread.join(60) + if self.input_thread.is_alive(): + self.log_error("habitat input thread failed to join") def log_debug(self, line): """ Helper function to log a debug message with a descriptive heading. From 1dd780bc08e0e09b57e0258f6ea1fbc62e02f7c1 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:48:48 +0300 Subject: [PATCH 11/32] added join timeout for ozimux input thread --- auto_rx/autorx/ozimux.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/ozimux.py b/auto_rx/autorx/ozimux.py index a3c8ce84..685a6910 100644 --- a/auto_rx/autorx/ozimux.py +++ b/auto_rx/autorx/ozimux.py @@ -252,7 +252,9 @@ def close(self): self.input_processing_running = False if self.input_thread is not None: - self.input_thread.join() + self.input_thread.join(60) + if self.input_thread.is_alive(): + self.log_error("ozimux input thread failed to join") def log_debug(self, line): """ Helper function to log a debug message with a descriptive heading. From cb8921c966586664de6b8279c6eb33bfc13d7ceb Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 16:49:22 +0300 Subject: [PATCH 12/32] added join timeout for rotator thread --- auto_rx/autorx/rotator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/rotator.py b/auto_rx/autorx/rotator.py index 5301cf80..82ee374c 100644 --- a/auto_rx/autorx/rotator.py +++ b/auto_rx/autorx/rotator.py @@ -320,7 +320,9 @@ def close(self): self.rotator_thread_running = False if self.rotator_thread is not None: - self.rotator_thread.join() + self.rotator_thread.join(60) + if self.rotator_thread.is_alive(): + self.log_error("rotator control thread failed to join") self.log_debug("Stopped rotator control thread.") From c10524455910759cebcc2574cfe7a60cf818a576 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:01:28 +0300 Subject: [PATCH 13/32] added function to figure out which timeout command to use on non-linux platforms raise exception on SDR reset requests --- auto_rx/autorx/utils.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/auto_rx/autorx/utils.py b/auto_rx/autorx/utils.py index 8e502faa..9a016afc 100644 --- a/auto_rx/autorx/utils.py +++ b/auto_rx/autorx/utils.py @@ -19,6 +19,7 @@ import time import numpy as np import semver +import shutil from dateutil.parser import parse from datetime import datetime, timedelta from math import radians, degrees, sin, cos, atan2, sqrt, pi @@ -45,6 +46,21 @@ "iq_dec" ] +_timeout_cmd = None + +def timeout_cmd(): + global _timeout_cmd + if not _timeout_cmd: + t=shutil.which("gtimeout") + if t: + _timeout_cmd = "gtimeout -k 30 " + else: + if not shutil.which("timeout"): + logging.critical("timeout command-line tool not present in system. try installing gtimeout.") + sys.exit(1) + else: + _timeout_cmd = "timeout -k 30 " + return _timeout_cmd def check_rs_utils(): """ Check the required RS decoder binaries exist @@ -54,7 +70,7 @@ def check_rs_utils(): if not os.path.isfile(_file): logging.critical("Binary %s does not exist - did you run build.sh?" % _file) return False - + _ = timeout_cmd() return True @@ -776,10 +792,10 @@ def is_rtlsdr(vid, pid): def reset_rtlsdr_by_serial(serial): """ Attempt to reset a RTLSDR with a provided serial number """ - # If not Linux, return immediately. + # If not Linux, raise exception and let auto_rx.py convert it to exit status code. if is_not_linux(): logging.debug("RTLSDR - Not a native Linux system, skipping reset attempt.") - return + raise SystemError("SDR unresponsive") lsusb_info = lsusb() bus_num = None @@ -853,10 +869,10 @@ def find_rtlsdr(serial=None): def reset_all_rtlsdrs(): """ Reset all RTLSDR devices found in the lsusb tree """ - # If not Linux, return immediately. + # If not Linux, raise exception and let auto_rx.py convert it to exit status code. if is_not_linux(): logging.debug("RTLSDR - Not a native Linux system, skipping reset attempt.") - return + raise SystemError("SDR unresponsive") lsusb_info = lsusb() bus_num = None @@ -906,11 +922,12 @@ def rtlsdr_test(device_idx="0", rtl_sdr_path="rtl_sdr", retries=5): logging.debug("RTLSDR - TCP Device, skipping RTLSDR test step.") return True - _rtl_cmd = "timeout 5 %s -d %s -n 200000 - > /dev/null" % ( + _rtl_cmd = "%s 5 %s -d %s -f 400000000 -n 200000 - > /dev/null" % ( + timeout_cmd(), rtl_sdr_path, str(device_idx), ) - + # First, check if the RTLSDR with a provided serial number is present. if device_idx == "0": # Check for the presence of any RTLSDRs. From 815ef49c2311aaf15310dd66ee3b232a660f27a0 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:02:52 +0300 Subject: [PATCH 14/32] switched to using system-appropriate timeout command --- auto_rx/autorx/sdr_wrappers.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/auto_rx/autorx/sdr_wrappers.py b/auto_rx/autorx/sdr_wrappers.py index 62af89d1..2b187ad2 100644 --- a/auto_rx/autorx/sdr_wrappers.py +++ b/auto_rx/autorx/sdr_wrappers.py @@ -11,7 +11,7 @@ import subprocess import numpy as np -from .utils import rtlsdr_test, reset_rtlsdr_by_serial, reset_all_rtlsdrs +from .utils import rtlsdr_test, reset_rtlsdr_by_serial, reset_all_rtlsdrs, timeout_cmd def test_sdr( @@ -67,7 +67,7 @@ def test_sdr( return False _cmd = ( - f"timeout 10 " # Add a timeout, because connections to non-existing IPs seem to block. + f"{timeout_cmd()} 10 " # Add a timeout, because connections to non-existing IPs seem to block. f"{ss_iq_path} " f"-f {check_freq} " f"-s 48000 " @@ -480,17 +480,7 @@ def get_power_spectrum( if os.path.exists(_log_filename): os.remove(_log_filename) - - # Add -k 30 option, to SIGKILL rtl_power 30 seconds after the regular timeout expires. - # Note that this only works with the GNU Coreutils version of Timeout, not the IBM version, - # which is provided with OSX (Darwin). - _platform = platform.system() - if "Darwin" in _platform: - _timeout_kill = "" - else: - _timeout_kill = "-k 30 " - - _timeout_cmd = f"timeout {_timeout_kill}{integration_time+10}" + _timeout_cmd = f"{timeout_cmd()} {integration_time+10} " _gain = "" if gain: @@ -564,17 +554,7 @@ def get_power_spectrum( if os.path.exists(_log_filename): os.remove(_log_filename) - - # Add -k 30 option, to SIGKILL rtl_power 30 seconds after the regular timeout expires. - # Note that this only works with the GNU Coreutils version of Timeout, not the IBM version, - # which is provided with OSX (Darwin). - _platform = platform.system() - if "Darwin" in _platform: - _timeout_kill = "" - else: - _timeout_kill = "-k 30 " - - _timeout_cmd = f"timeout {_timeout_kill}{integration_time+10}" + _timeout_cmd = f"{timeout_cmd()} {integration_time+10} " _frequency_centre = int(frequency_start + (frequency_stop-frequency_start)/2.0) From 942c992f837860dd7fef16e02dea081259ad22ed Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:04:22 +0300 Subject: [PATCH 15/32] switched to using system-appropriate timeout command faster and cleaner shutdown --- auto_rx/autorx/scan.py | 57 ++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/auto_rx/autorx/scan.py b/auto_rx/autorx/scan.py index e23e317b..87203843 100644 --- a/auto_rx/autorx/scan.py +++ b/auto_rx/autorx/scan.py @@ -9,6 +9,7 @@ import logging import numpy as np import os +import sys import platform import subprocess import time @@ -22,6 +23,7 @@ reset_rtlsdr_by_serial, reset_all_rtlsdrs, peak_decimation, + timeout_cmd ) from .sdr_wrappers import test_sdr, reset_sdr, get_sdr_name, get_sdr_iq_cmd, get_sdr_fm_cmd, get_power_spectrum @@ -91,18 +93,10 @@ def run_rtl_power( if os.path.exists(filename): os.remove(filename) - # Add -k 30 option, to SIGKILL rtl_power 30 seconds after the regular timeout expires. - # Note that this only works with the GNU Coreutils version of Timeout, not the IBM version, - # which is provided with OSX (Darwin). - if "Darwin" in platform.platform(): - timeout_kill = "" - else: - timeout_kill = "-k 30 " - rtl_power_cmd = ( - "timeout %s%d %s %s-f %d:%d:%d -i %d -1 -c 25%% -p %d -d %s %s%s" + "%s %d %s %s-f %d:%d:%d -i %d -1 -c 25%% -p %d -d %s %s%s" % ( - timeout_kill, + timeout_cmd(), dwell + 10, rtl_power_path, bias_option, @@ -314,7 +308,7 @@ def detect_sonde( if _mode == "IQ": # IQ decoding - rx_test_command = f"timeout {dwell_time * 2} " + rx_test_command = f"{timeout_cmd()} {dwell_time * 2} " rx_test_command += get_sdr_iq_cmd( sdr_type=sdr_type, @@ -331,8 +325,9 @@ def detect_sonde( ) # rx_test_command = ( - # "timeout %ds %s %s-p %d -d %s %s-M raw -F9 -s %d -f %d 2>/dev/null |" + # "%s %ds %s %s-p %d -d %s %s-M raw -F9 -s %d -f %d 2>/dev/null |" # % ( + # timeout_cmd(), # dwell_time * 2, # rtl_fm_path, # bias_option, @@ -360,7 +355,7 @@ def detect_sonde( # Sample Source (rtl_fm) - rx_test_command = f"timeout {dwell_time * 2} " + rx_test_command = f"{timeout_cmd()} {dwell_time * 2} " rx_test_command += get_sdr_fm_cmd( sdr_type=sdr_type, @@ -379,8 +374,9 @@ def detect_sonde( ) # rx_test_command = ( - # "timeout %ds %s %s-p %d -d %s %s-M fm -F9 -s %d -f %d 2>/dev/null |" + # "%s %ds %s %s-p %d -d %s %s-M fm -F9 -s %d -f %d 2>/dev/null |" # % ( + # timeout_cmd(), # dwell_time * 2, # rtl_fm_path, # bias_option, @@ -783,9 +779,9 @@ def __init__( def start(self): # Start the scan loop (if not already running) if self.sonde_scan_thread is None: - self.sonde_scanner_running = True self.sonde_scan_thread = Thread(target=self.scan_loop) self.sonde_scan_thread.start() + self.sonde_scanner_running = True else: self.log_warning("Sonde scan already running!") @@ -854,22 +850,31 @@ def scan_loop(self): sdr_hostname = self.sdr_hostname, sdr_port = self.sdr_port ) - - time.sleep(10) + for _ in range(10): + if not self.sonde_scanner_running: + break + time.sleep(1) continue except Exception as e: traceback.print_exc() self.log_error("Caught other error: %s" % str(e)) - time.sleep(10) + for _ in range(10): + if not self.sonde_scanner_running: + break + time.sleep(1) else: # Scan completed successfuly! Reset the error counter. self.error_retries = 0 # Sleep before starting the next scan. - time.sleep(self.scan_delay) + for _ in range(self.scan_delay): + if not self.sonde_scanner_running: + break + time.sleep(1) self.log_info("Scanner Thread Closed.") self.sonde_scanner_running = False + self.sonde_scanner_thread = None def sonde_search(self, first_only=False): """Perform a frequency scan across a defined frequency range, and test each detected peak for the presence of a radiosonde. @@ -1139,12 +1144,16 @@ def oneshot(self, first_only=False): def stop(self, nowait=False): """Stop the Scan Loop""" - self.log_info("Waiting for current scan to finish...") - self.sonde_scanner_running = False + if self.sonde_scanner_running: + self.log_info("Waiting for current scan to finish...") + self.sonde_scanner_running = False - # Wait for the sonde scanner thread to close, if there is one. - if self.sonde_scan_thread != None and (not nowait): - self.sonde_scan_thread.join() + # Wait for the sonde scanner thread to close, if there is one. + if self.sonde_scan_thread != None and (not nowait): + self.sonde_scan_thread.join(60) + if self.sonde_scan_thread.is_alive(): + self.log_error("Scanning thread did not finish, terminating") + sys.exit(4) def running(self): """Check if the scanner is running""" From 0464b26add5126436ac917dd1778054590353571 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:05:49 +0300 Subject: [PATCH 16/32] added function for checking if flask has been started --- auto_rx/autorx/web.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index 4457d4e7..9911ec34 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -49,7 +49,7 @@ # This thread will hold the currently running flask application thread. flask_app_thread = None # A key that needs to be matched to allow shutdown. -flask_shutdown_key = "temp" +flask_shutdown_key = None # SocketIO instance socketio = SocketIO(app, async_mode="threading") @@ -289,6 +289,9 @@ def flask_get_log_list(): """ Return a list of log files, as a list of objects """ return json.dumps(list_log_files(quicklook=True)) +def flask_running(): + global flask_shutdown_key + return flask_shutdown_key is not None @app.route("/get_log_by_serial/") def flask_get_log_by_serial(serial): From b7d69325acf74e5836266338dc31eca8409963a5 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:06:26 +0300 Subject: [PATCH 17/32] added timout for email notification thread joinging --- auto_rx/autorx/email_notification.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/email_notification.py b/auto_rx/autorx/email_notification.py index 0b2db5a0..8b7c89e0 100644 --- a/auto_rx/autorx/email_notification.py +++ b/auto_rx/autorx/email_notification.py @@ -347,7 +347,9 @@ def close(self): self.input_processing_running = False if self.input_thread is not None: - self.input_thread.join() + self.input_thread.join(60) + if self.input_thread.is_alive(): + self.log_error("email notification input thread failed to join") def running(self): """ Check if the logging thread is running. From bce4b99a539d488f915c6d531961bcdc21db1ce5 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 17:10:58 +0300 Subject: [PATCH 18/32] script to power cycle connected RTL-SDR devices --- auto_rx/sdr_reset.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 auto_rx/sdr_reset.py diff --git a/auto_rx/sdr_reset.py b/auto_rx/sdr_reset.py new file mode 100644 index 00000000..585d4e54 --- /dev/null +++ b/auto_rx/sdr_reset.py @@ -0,0 +1,13 @@ +import time +import serial + +# the SDR devices are attached to USB hub, with power routed though +# switches that are disabled when serial device is opened. So this script +# basically removes power from RTL-SDR devices for 3 seconds, then +# restores the power and waits for 3 more seconds for the USB subsystem to +# find them again + +p=serial.Serial("/dev/tty.usbserial-A5XK3RJT",9600) +time.sleep(3) +p.close() +time.sleep(3) From b16b5dbb61575f5ee91d985142aac5ee7ec029d1 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 19:01:28 +0300 Subject: [PATCH 19/32] running auto_rx.py in a loop, handling exit codes --- auto_rx/auto_rx.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/auto_rx/auto_rx.sh b/auto_rx/auto_rx.sh index a8a1b567..fdfc3fc2 100755 --- a/auto_rx/auto_rx.sh +++ b/auto_rx/auto_rx.sh @@ -15,7 +15,18 @@ cd $(dirname $0) # Clean up old files rm log_power*.csv -# Start auto_rx process with a 3 hour timeout. -# auto_rx will exit after this time. - -python3 auto_rx.py -t 180 +while true +do + python3 auto_rx.py + rc=$? + echo auto_rx.py exited with result code $rc + if [ $rc -gt 2 ] + then + echo "Performing power reset of SDR's" + python3 sdr_reset.py + fi + if [ $rc -eq 0 ] + then + break + fi +done From 29699c02e58aa944939fac69c6d27389a82fe815 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Mon, 3 Jul 2023 19:03:18 +0300 Subject: [PATCH 20/32] clean shutdown with consistent exit status codes --- auto_rx/auto_rx.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/auto_rx/auto_rx.py b/auto_rx/auto_rx.py index 204374cc..c65a4305 100644 --- a/auto_rx/auto_rx.py +++ b/auto_rx/auto_rx.py @@ -8,6 +8,16 @@ # Refer github page for instructions on setup and usage. # https://github.com/projecthorus/radiosonde_auto_rx/ # + +# exit status codes: +# +# 0 - normal termination (ctrl-c) +# 1 - critical error, needs human attention to fix +# 2 - exit because continous running timeout reached +# 3 - exception occurred, can rerun after resetting SDR +# 4 - some of the threads failed to join, SDR reset and restart required +# this is mostly caused by hung external utilities + import argparse import datetime import logging @@ -44,6 +54,7 @@ start_flask, stop_flask, flask_emit_event, + flask_running, WebHandler, WebExporter, ) @@ -322,7 +333,7 @@ def handle_scan_results(): if (type(_key) == int) or (type(_key) == float): # Extract the currently decoded sonde type from the currently running decoder. _decoding_sonde_type = autorx.task_list[_key]["task"].sonde_type - + # Remove any inverted decoder information for the comparison. if _decoding_sonde_type.startswith("-"): _decoding_sonde_type = _decoding_sonde_type[1:] @@ -806,6 +817,11 @@ def main(): logging.getLogger("engineio").setLevel(logging.ERROR) logging.getLogger("geventwebsocket").setLevel(logging.ERROR) + # Check all the RS utilities exist. + logging.debug("Checking if utils exist") + if not check_rs_utils(): + sys.exit(1) + # Attempt to read in config file logging.info("Reading configuration file...") _temp_cfg = read_auto_rx_config(args.config) @@ -844,9 +860,6 @@ def main(): web_handler = WebHandler() logging.getLogger().addHandler(web_handler) - # Check all the RS utilities exist. - if not check_rs_utils(): - sys.exit(1) # If a sonde type has been provided, insert an entry into the scan results, # and immediately start a decoder. This also sets the decoder time to 0, which @@ -1073,7 +1086,7 @@ def main(): logging.info("Shutdown time reached. Closing.") stop_flask(host=config["web_host"], port=config["web_port"]) stop_all() - break + sys.exit(2) if __name__ == "__main__": @@ -1084,9 +1097,13 @@ def main(): # Upon CTRL+C, shutdown all threads and exit. stop_flask(host=config["web_host"], port=config["web_port"]) stop_all() + sys.exit(0) except Exception as e: # Upon exceptions, attempt to shutdown threads and exit. traceback.print_exc() print("Main Loop Error - %s" % str(e)) - stop_flask(host=config["web_host"], port=config["web_port"]) + if flask_running(): + stop_flask(host=config["web_host"], port=config["web_port"]) stop_all() + sys.exit(3) + From 9bec70549f23c0feb2bb9c5ea6305006c2fbac74 Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Tue, 4 Jul 2023 12:20:36 +0300 Subject: [PATCH 21/32] do not swallow exception thrown during scanning --- auto_rx/autorx/log_files.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/auto_rx/autorx/log_files.py b/auto_rx/autorx/log_files.py index 6a595bfc..95eefde0 100644 --- a/auto_rx/autorx/log_files.py +++ b/auto_rx/autorx/log_files.py @@ -455,9 +455,8 @@ def calculate_skewt_data( break except Exception as e: - print(str(e)) - - # Continue through the data.. + logging.exception(f"Exception {str(e)} in calculate_skewt_data") + raise return _skewt From 75fd53bf45e8430a1a9e162dca1c7cb1bc3e99fd Mon Sep 17 00:00:00 2001 From: Madis Kaal Date: Tue, 4 Jul 2023 13:58:02 +0300 Subject: [PATCH 22/32] schematics for SDR power cycling hardware --- auto_rx/sdr_reset.png | Bin 0 -> 11885 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 auto_rx/sdr_reset.png diff --git a/auto_rx/sdr_reset.png b/auto_rx/sdr_reset.png new file mode 100644 index 0000000000000000000000000000000000000000..261fb4e92b9e75179d544fc335f0d06a0fdfe338 GIT binary patch literal 11885 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 Date: Sat, 15 Jul 2023 11:34:17 +0930 Subject: [PATCH 23/32] Reset auto_rx.sh --- auto_rx/auto_rx.sh | 20 ++++---------------- auto_rx/sdr_reset.png | Bin 11885 -> 0 bytes auto_rx/sdr_reset.py | 13 ------------- 3 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 auto_rx/sdr_reset.png delete mode 100644 auto_rx/sdr_reset.py diff --git a/auto_rx/auto_rx.sh b/auto_rx/auto_rx.sh index fdfc3fc2..6c901cd3 100755 --- a/auto_rx/auto_rx.sh +++ b/auto_rx/auto_rx.sh @@ -6,7 +6,9 @@ # NOTE: If running this from crontab, make sure to set the appropriate PATH env-vars, # else utilities like rtl_power and rtl_fm won't be found. # -# WARNING - THIS IS DEPRECATED - PLEASE USE THE SYSTEMD SERVICE +# WARNING - THIS IS DEPRECATED - PLEASE USE THE SYSTEMD SERVICE OR DOCKER IMAGE +# See: https://github.com/projecthorus/radiosonde_auto_rx/wiki#451-option-1---operation-as-a-systemd-service-recommended +# Or: https://github.com/projecthorus/radiosonde_auto_rx/wiki/Docker # # change into appropriate directory @@ -15,18 +17,4 @@ cd $(dirname $0) # Clean up old files rm log_power*.csv -while true -do - python3 auto_rx.py - rc=$? - echo auto_rx.py exited with result code $rc - if [ $rc -gt 2 ] - then - echo "Performing power reset of SDR's" - python3 sdr_reset.py - fi - if [ $rc -eq 0 ] - then - break - fi -done +python3 auto_rx.py -t 180 \ No newline at end of file diff --git a/auto_rx/sdr_reset.png b/auto_rx/sdr_reset.png deleted file mode 100644 index 261fb4e92b9e75179d544fc335f0d06a0fdfe338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11885 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 Date: Sat, 15 Jul 2023 11:38:31 +0930 Subject: [PATCH 24/32] Bump beta version --- auto_rx/autorx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index a108510c..df2b5a84 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta4" +__version__ = "1.6.2-beta5" # Global Variables From 3b3597474c83f42353a0f2e806836e22d1f08c6d Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 16 Jul 2023 13:53:40 +0930 Subject: [PATCH 25/32] Change SDR task list on web interface to block sections. Add on SDR type info. --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/static/css/autorx.css | 15 +++++++++++++++ auto_rx/autorx/static/js/autorxapi.js | 27 ++++++++++++++++++++++++--- auto_rx/autorx/templates/index.html | 2 +- auto_rx/autorx/web.py | 9 +++++++++ 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index df2b5a84..e7065df5 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta5" +__version__ = "1.6.2-beta6" # Global Variables diff --git a/auto_rx/autorx/static/css/autorx.css b/auto_rx/autorx/static/css/autorx.css index fcb0cd0d..2a99920c 100644 --- a/auto_rx/autorx/static/css/autorx.css +++ b/auto_rx/autorx/static/css/autorx.css @@ -60,3 +60,18 @@ .icon-cog:before { content: '\e802'; } /* '' */ .icon-angle-down:before { content: '\f107'; } /* '' */ .icon-history:before { content: '\f1da'; } /* '' */ + + +#task_status { + display: flex; + flex-wrap: wrap; + gap: 2px; +} + +.sdrinfo-element { + margin: 0px 4px; + padding: 4px; + + border: 2px solid rgb(135, 135, 135); + border-radius: 1px; +} \ No newline at end of file diff --git a/auto_rx/autorx/static/js/autorxapi.js b/auto_rx/autorx/static/js/autorxapi.js index 8897eceb..14d5ab6e 100644 --- a/auto_rx/autorx/static/js/autorxapi.js +++ b/auto_rx/autorx/static/js/autorxapi.js @@ -10,8 +10,14 @@ function update_task_list(){ added_decoders = false; for (_task in data){ - // Append the current task to the task list text. - task_info += "SDR #" + _task + ": " + data[_task]["task"] + " "; + // Append the current task to the task list. + if(_task.includes("SPY")){ + task_detail = _task + " - " + }else{ + task_detail = "SDR:" + _task + " - " + } + + if(data[_task]["freq"] > 0.0){ $('#stop-frequency-select') .append($("") @@ -19,7 +25,22 @@ function update_task_list(){ .text( (parseFloat( data[_task]["freq"] )/1e6).toFixed(3))); added_decoders = true; + + task_detail += (parseFloat( data[_task]["freq"] )/1e6).toFixed(3); + + if (data[_task].hasOwnProperty("type")){ + task_detail += " " + data[_task]["type"]; + } + + } else { + if(data[_task]["task"] == "Scanning"){ + task_detail += "Scan"; + } else { + task_detail += "Idle"; + } } + + task_info += "
" + task_detail + "
" } if(added_decoders == false){ @@ -30,7 +51,7 @@ function update_task_list(){ } // Update page with latest task. - $('#task_status').text(task_info); + $('#task_status').html(task_info); setTimeout(resume_web_controls,2000); }); diff --git a/auto_rx/autorx/templates/index.html b/auto_rx/autorx/templates/index.html index b2189ac2..a7be3198 100644 --- a/auto_rx/autorx/templates/index.html +++ b/auto_rx/autorx/templates/index.html @@ -1611,7 +1611,7 @@

Historical View

Station: ???

-

Current Task: ???

+

Tasking:

diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index 9911ec34..b86252e9 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -21,6 +21,7 @@ from autorx.geometry import GenericTrack from autorx.utils import check_autorx_versions from autorx.log_files import list_log_files, read_log_by_serial, zip_log_files +from autorx.decode import SondeDecoder from queue import Queue from threading import Thread import flask @@ -105,6 +106,7 @@ def flask_get_version(): def flask_get_task_list(): """ Return the current list of active SDRs, and their active task names """ + # Read in the task list, index by SDR ID. _task_list = {} for _task in autorx.task_list.keys(): @@ -124,9 +126,16 @@ def flask_get_task_list(): "task": "Decoding (%.3f MHz)" % (_task_list[str(_sdr)] / 1e6), "freq": _task_list[str(_sdr)], } + except: _sdr_list[str(_sdr)] = {"task": "Decoding (?? MHz)", "freq": 0} + # Try and add on sonde type. + try: + _sdr_list[str(_sdr)]['type'] = autorx.task_list[_task_list[str(_sdr)]]['task'].sonde_type + except: + pass + # Convert the task list to a JSON blob, and return. return json.dumps(_sdr_list) From f2d211ec8dc04a12fe390d3cbc274b57e1a3f6ff Mon Sep 17 00:00:00 2001 From: Clayton Smith Date: Tue, 18 Jul 2023 20:30:55 -0400 Subject: [PATCH 26/32] Add iq_dec to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8f1413f5..62ec1fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ auto_rx/dfm09mod auto_rx/dft_detect auto_rx/fsk_demod auto_rx/imet1rs_dft +auto_rx/iq_dec auto_rx/lms6Xmod auto_rx/lms6mod auto_rx/m10mod From b672eaee9bff295acf620e65ff135d596201507e Mon Sep 17 00:00:00 2001 From: Ilias Daradimos Date: Mon, 24 Jul 2023 20:54:38 +0000 Subject: [PATCH 27/32] Fix reverse proxy path handling --- auto_rx/autorx/static/js/autorxapi.js | 6 +++--- auto_rx/autorx/templates/historical.html | 25 ++++++++++++------------ auto_rx/autorx/templates/index.html | 19 +++++++++--------- auto_rx/autorx/web.py | 4 +++- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/auto_rx/autorx/static/js/autorxapi.js b/auto_rx/autorx/static/js/autorxapi.js index 14d5ab6e..43231436 100644 --- a/auto_rx/autorx/static/js/autorxapi.js +++ b/auto_rx/autorx/static/js/autorxapi.js @@ -2,7 +2,7 @@ function update_task_list(){ // Grab the latest task list. - $.getJSON("/get_task_list", function(data){ + $.getJSON("get_task_list", function(data){ var task_info = ""; $('#stop-frequency-select').children().remove(); @@ -117,7 +117,7 @@ function verify_password(){ // Do the request $.post( - "/check_password", + "check_password", {"password": _api_password}, function(data){ // If OK, update the header to indicate the password was OK. @@ -280,4 +280,4 @@ function start_decoder(){ $("#password-header").html("

Incorrect Password

"); } }); -} \ No newline at end of file +} diff --git a/auto_rx/autorx/templates/historical.html b/auto_rx/autorx/templates/historical.html index 03551908..ff9b98e0 100644 --- a/auto_rx/autorx/templates/historical.html +++ b/auto_rx/autorx/templates/historical.html @@ -45,13 +45,12 @@ quickLaunches = false; quickLandings = false; - namespace = '/update_status'; + var socket_path = "{{ url_for("static", filename="") }}".replace('static/', 'socket.io') + var socket = io.connect({'path': socket_path}); - var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace); - $.ajax({ // Get station.cfg file. - url: "/get_config", + url: "get_config", dataType: 'json', async: false, success: function(data) { @@ -64,7 +63,7 @@ $.ajax({ // Get list of sonde. - url: "/get_log_list", + url: "get_log_list", dataType: 'json', async: false, success: function(data) { @@ -462,7 +461,7 @@ highest = i; } $.ajax({ - url: "/get_log_by_serial/" + selectedrows[i]['serial'], + url: "get_log_by_serial/" + selectedrows[i]['serial'], dataType: 'json', async: true, success: function(data) { @@ -731,7 +730,7 @@ table.selectRow(); mymap.eachLayer(function(layer){ try { - if (layer['options']['icon']['options']['iconUrl'] == "/static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "/static/img/launch_marker.png") { + if (layer['options']['icon']['options']['iconUrl'] == "static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "static/img/launch_marker.png") { new_icon = layer['options']['icon']; new_icon.options.iconSize = [20, 20]; new_icon.options.iconAnchor = [10, 10]; @@ -746,7 +745,7 @@ table.deselectRow(); mymap.eachLayer(function(layer){ try { - if (layer['options']['icon']['options']['iconUrl'] == "/static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "/static/img/launch_marker.png") { + if (layer['options']['icon']['options']['iconUrl'] == "static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "static/img/launch_marker.png") { new_icon = layer['options']['icon']; new_icon.options.iconSize = [15, 15]; new_icon.options.iconAnchor = [7.5, 7.5]; @@ -770,7 +769,7 @@ mymap.eachLayer(function(layer){ try { - if (layer['options']['icon']['options']['iconUrl'] == "/static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "/static/img/launch_marker.png") { + if (layer['options']['icon']['options']['iconUrl'] == "static/img/landing_marker.png" || layer['options']['icon']['options']['iconUrl'] == "static/img/launch_marker.png") { if (layer['options']['icon']['options']['iconSize'][0] == 15) { if (!shown.includes(layer['options']['title'])) { mymap.removeLayer(layer); @@ -857,7 +856,7 @@ _serial = selectedrows[selectedrows.length-1]['serial']; _type = selectedrows[selectedrows.length-1]['type']; $.post( - "/get_log_detail", + "get_log_detail", {serial: _serial, decimation:decimation}, async function(data){ try { @@ -947,13 +946,13 @@ if(_serial_list.length == table.getData().length){ // Request all log files - window.open("/export_all_log_files" , '_blank'); + window.open("export_all_log_files" , '_blank'); }else { // Just request the selected ones. // Convert the list to JSON, and then to base64 b64 = btoa(JSON.stringify(_serial_list)); // Make the request in a new tab - window.open("/export_log_files/"+b64 , '_blank'); + window.open("export_log_files/"+b64 , '_blank'); } } } @@ -1529,4 +1528,4 @@

Show Software Version

Live KML

 
- +


@@ -1592,7 +1591,7 @@

Controls

Historical View

 
- +


diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index b86252e9..fa019809 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -27,6 +27,7 @@ import flask from flask import request, abort, make_response, send_file from flask_socketio import SocketIO +from werkzeug.middleware.proxy_fix import ProxyFix import re try: @@ -44,6 +45,7 @@ # Instantiate our Flask app. app = flask.Flask(__name__) +app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_prefix=1) app.config["SECRET_KEY"] = "secret!" app.config["TEMPLATES_AUTO_RELOAD"] = True app.jinja_env.auto_reload = True @@ -69,7 +71,7 @@ # def flask_emit_event(event_name="none", data={}): """ Emit a socketio event to any clients. """ - socketio.emit(event_name, data, namespace="/update_status") + socketio.emit(event_name, data, namespace="update_status") # From f641be9d3efd0e77a9fba677f38e675c23585329 Mon Sep 17 00:00:00 2001 From: Ilias Daradimos Date: Tue, 25 Jul 2023 11:15:27 +0300 Subject: [PATCH 28/32] Fix more absolute paths Signed-off-by: Ilias Daradimos --- auto_rx/autorx/static/js/autorxapi.js | 8 ++++---- auto_rx/autorx/static/js/utils.js | 4 ++-- auto_rx/autorx/templates/skewt_test.html | 2 +- auto_rx/autorx/web.py | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/auto_rx/autorx/static/js/autorxapi.js b/auto_rx/autorx/static/js/autorxapi.js index 43231436..340cfcff 100644 --- a/auto_rx/autorx/static/js/autorxapi.js +++ b/auto_rx/autorx/static/js/autorxapi.js @@ -146,7 +146,7 @@ function disable_scanner(){ // Do the request $.post( - "/disable_scanner", + "disable_scanner", {"password": _api_password}, function(data){ //console.log(data); @@ -183,7 +183,7 @@ function enable_scanner(){ // Do the request $.post( - "/enable_scanner", + "enable_scanner", {"password": _api_password}, function(data){ //console.log(data); @@ -215,7 +215,7 @@ function stop_decoder(){ // Do the request $.post( - "/stop_decoder", + "stop_decoder", {password: _api_password, freq: _decoder}, function(data){ //console.log(data); @@ -266,7 +266,7 @@ function start_decoder(){ // Do the request $.post( - "/start_decoder", + "start_decoder", {password: _api_password, freq: _freq_hz, type: _type}, function(data){ alert("Added requested decoder to results queue.") diff --git a/auto_rx/autorx/static/js/utils.js b/auto_rx/autorx/static/js/utils.js index 8ecb224c..270281cb 100644 --- a/auto_rx/autorx/static/js/utils.js +++ b/auto_rx/autorx/static/js/utils.js @@ -16,12 +16,12 @@ var sondeDescentIcons = {}; // TODO: Make these /static URLS be filled in with templates (or does it not matter?) for (_col in colour_values){ sondeAscentIcons[colour_values[_col]] = L.icon({ - iconUrl: "/static/img/balloon-" + colour_values[_col] + '.png', + iconUrl: "static/img/balloon-" + colour_values[_col] + '.png', iconSize: [46, 85], iconAnchor: [23, 76] }); sondeDescentIcons[colour_values[_col]] = L.icon({ - iconUrl: "/static/img/parachute-" + colour_values[_col] + '.png', + iconUrl: "static/img/parachute-" + colour_values[_col] + '.png', iconSize: [46, 84], iconAnchor: [23, 76] }); diff --git a/auto_rx/autorx/templates/skewt_test.html b/auto_rx/autorx/templates/skewt_test.html index 7fe28d63..ea2ceba9 100644 --- a/auto_rx/autorx/templates/skewt_test.html +++ b/auto_rx/autorx/templates/skewt_test.html @@ -59,7 +59,7 @@

auto_rx SkewT Plot Testing

_decim = $('#decimation-input').val(); $.post( - "/get_log_detail", + "get_log_detail", {serial: _serial, decimation:_decim}, function(data){ console.log(data); diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index fa019809..4afe49ec 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -150,7 +150,7 @@ def flask_get_kml(): kml = Kml() netlink = kml.newnetworklink(name="Radiosonde Auto-RX Live Telemetry") netlink.open = 1 - netlink.link.href = flask.request.host_url + "rs_feed.kml" + netlink.link.href = flask.request.url_root + "rs_feed.kml" try: netlink.link.refreshinterval = _config["kml_refresh_rate"] except KeyError: @@ -173,7 +173,7 @@ def flask_get_kml_feed(): description="AutoRX Ground Station", ) pnt.open = 1 - pnt.iconstyle.icon.href = flask.request.host_url + "static/img/antenna-green.png" + pnt.iconstyle.icon.href = flask.request.url_root + "static/img/antenna-green.png" pnt.coords = [ ( autorx.config.global_config["station_lon"], @@ -200,9 +200,9 @@ def flask_get_kml_feed(): Pressure: {pressure:.1f} hPa """ if flask_telemetry_store[rs_id]["latest_telem"]["vel_v"] > -5: - icon = flask.request.host_url + "static/img/balloon-green.png" + icon = flask.request.url_root + "static/img/balloon-green.png" else: - icon = flask.request.host_url + "static/img/parachute-green.png" + icon = flask.request.url_root + "static/img/parachute-green.png" # Add folder fol = kml.newfolder(name=rs_id) From 3a77bf0c5dd707f800b92629eb94cb110688965d Mon Sep 17 00:00:00 2001 From: Ilias Daradimos Date: Wed, 26 Jul 2023 15:12:41 +0000 Subject: [PATCH 29/32] Fix namespace Signed-off-by: Ilias Daradimos --- auto_rx/autorx/templates/historical.html | 6 ++++-- auto_rx/autorx/templates/index.html | 3 ++- auto_rx/autorx/web.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/auto_rx/autorx/templates/historical.html b/auto_rx/autorx/templates/historical.html index ff9b98e0..95d7b577 100644 --- a/auto_rx/autorx/templates/historical.html +++ b/auto_rx/autorx/templates/historical.html @@ -45,8 +45,10 @@ quickLaunches = false; quickLandings = false; - var socket_path = "{{ url_for("static", filename="") }}".replace('static/', 'socket.io') - var socket = io.connect({'path': socket_path}); + namespace = '/update_status'; + var socket_path = "{{ url_for("static", filename="") }}".replace('static/', 'socket.io') + var socket = io.connect(location.origin+namespace, {'path': socket_path}); + $.ajax({ // Get station.cfg file. diff --git a/auto_rx/autorx/templates/index.html b/auto_rx/autorx/templates/index.html index eac2816c..2bf751db 100644 --- a/auto_rx/autorx/templates/index.html +++ b/auto_rx/autorx/templates/index.html @@ -99,8 +99,9 @@ $( document ).ready(function() { + namespace = '/update_status'; var socket_path = "{{ url_for("static", filename="") }}".replace('static/', 'socket.io') - var socket = io.connect({'path': socket_path}); + var socket = io.connect(location.origin+namespace, {'path': socket_path}); $.ajax({ // Get station.cfg file. diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index 4afe49ec..94a4cda9 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -71,7 +71,7 @@ # def flask_emit_event(event_name="none", data={}): """ Emit a socketio event to any clients. """ - socketio.emit(event_name, data, namespace="update_status") + socketio.emit(event_name, data, namespace="/update_status") # From 007916639b73274b0cf804509266c54c5664ad93 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sat, 29 Jul 2023 16:13:52 +0930 Subject: [PATCH 30/32] Merge in drid/proxy-fix changes --- auto_rx/autorx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index e7065df5..d0c2d927 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta6" +__version__ = "1.6.2-beta7" # Global Variables From 0eed2131c10d728db62448e7ecd145fde4462e46 Mon Sep 17 00:00:00 2001 From: Clayton Smith Date: Tue, 1 Aug 2023 09:53:27 -0400 Subject: [PATCH 31/32] Use fixed-point notation for ascent rate --- auto_rx/autorx/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_rx/autorx/web.py b/auto_rx/autorx/web.py index 94a4cda9..34a40b97 100644 --- a/auto_rx/autorx/web.py +++ b/auto_rx/autorx/web.py @@ -194,7 +194,7 @@ def flask_get_kml_feed(): Altitude: {alt:.1f} m Heading: {heading:.1f} degrees Ground Speed: {vel_h:.2f} m/s - Ascent Rate: {vel_v:.2} m/s + Ascent Rate: {vel_v:.2f} m/s Temperature: {temp:.1f} C Humidity: {humidity:.1f} % Pressure: {pressure:.1f} hPa From fb7258890bf0631a735205543cd4abcf28c99ab6 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Fri, 4 Aug 2023 10:07:33 +0930 Subject: [PATCH 32/32] Update version prior to merge --- auto_rx/autorx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index d0c2d927..375b8a8f 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -12,7 +12,7 @@ # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.6.2-beta7" +__version__ = "1.6.2" # Global Variables