From 4e8176b0415cbe5e51561983f7b77fda447d6388 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Mon, 2 Oct 2023 09:08:10 +1030 Subject: [PATCH 1/6] Handle IMET1RS as IMET-4, pass rs41_mainboard into to sondehub --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/scan.py | 6 ++++-- auto_rx/autorx/sondehub.py | 6 ++++++ demod/mod/rs41mod.c | 23 ++++++++++++++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index d4a6269b..42ec06e0 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.7.0" +__version__ = "1.7.1-beta1" # Global Variables diff --git a/auto_rx/autorx/scan.py b/auto_rx/autorx/scan.py index 45f84d4a..cc2d791e 100644 --- a/auto_rx/autorx/scan.py +++ b/auto_rx/autorx/scan.py @@ -533,11 +533,13 @@ def detect_sonde( ) _sonde_type = "IMET" elif "IMET1" in _type: + # This could actually be a wideband iMet sonde. We treat this as a IMET4. logging.debug( - "Scanner (%s) - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)" + "Scanner (%s) - Possible detection of a Wideband iMet Sonde! (Type %s) (Score: %.2f)" % (_sdr_name, _type, _score) ) - _sonde_type = "IMET1" + # Override the type to IMET4. + _sonde_type = "IMET" elif "IMETafsk" in _type: logging.debug( "Scanner (%s) - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)" diff --git a/auto_rx/autorx/sondehub.py b/auto_rx/autorx/sondehub.py index fbb3b030..3d241b0d 100644 --- a/auto_rx/autorx/sondehub.py +++ b/auto_rx/autorx/sondehub.py @@ -291,6 +291,12 @@ def reformat_data(self, telemetry): if "ref_datetime" in telemetry: _output["ref_datetime"] = telemetry["ref_datetime"] + if "rs41_mainboard" in telemetry: + _output["rs41_mainboard"] = telemetry["rs41_mainboard"] + + if "rs41_mainboard_fw" in telemetry: + _output["rs41_mainboard_fw"] = str(telemetry["rs41_mainboard_fw"]) + # Handle the additional SNR and frequency estimation if we have it if "snr" in telemetry: _output["snr"] = telemetry["snr"] diff --git a/demod/mod/rs41mod.c b/demod/mod/rs41mod.c index 38a5cc3a..0eb4bab6 100644 --- a/demod/mod/rs41mod.c +++ b/demod/mod/rs41mod.c @@ -139,6 +139,7 @@ typedef struct { ui16_t conf_cd; // kill countdown (sec) (kt or bt) ui8_t conf_bk; // burst kill char rstyp[9]; // RS41-SG, RS41-SGP + char rsm[10]; // RSM421 int aux; char xdata[XDATA_LEN+16]; // xdata: aux_str1#aux_str2 ... option_t option; @@ -330,7 +331,8 @@ GPS chip: ublox UBX-G6010-ST #define pos_Calburst 0x05E // 1 byte, calfr 0x02 // ? #define pos_Caltimer 0x05A // 2 byte, calfr 0x02 ? #define pos_CalRSTyp 0x05B // 8 byte, calfr 0x21 (+2 byte in 0x22?) - // weitere chars in calfr 0x22/0x23; weitere ID + // weitere chars in calfr 0x22/0x23; weitere ID (RSM) +#define pos_CalRSM 0x055 // 6 byte, calfr 0x22 #define crc_PTU (1<<1) #define xor_PTU 0xE388 // ^0x99A2=0x0x7A2A @@ -448,6 +450,7 @@ static int get_SondeID(gpx_t *gpx, int crc, int ofs) { memset(gpx->calfrchk, 0, 51); // 0x00..0x32 // reset conf data memset(gpx->rstyp, 0, 9); + memset(gpx->rsm, 0, 10); gpx->freq = 0; gpx->conf_fw = 0; gpx->conf_bt = 0; @@ -1366,6 +1369,7 @@ static int get_Calconf(gpx_t *gpx, int out, int ofs) { ui16_t fw = 0; int freq = 0, f0 = 0, f1 = 0; char sondetyp[9]; + char rsmtyp[10]; int err = 0; byte = gpx->frame[pos_CalData+ofs]; @@ -1451,6 +1455,17 @@ static int get_Calconf(gpx_t *gpx, int out, int ofs) { } } } + + if (calfr == 0x22) { + for (i = 0; i < 10; i++) rsmtyp[i] = 0; + for (i = 0; i < 8; i++) { + byte = gpx->frame[pos_CalRSM+ofs + i]; + if ((byte >= 0x20) && (byte < 0x7F)) rsmtyp[i] = byte; + else /*if (byte == 0x00)*/ rsmtyp[i] = '\0'; + } + if (out && gpx->option.vbs) fprintf(stdout, ": %s ", rsmtyp); + strcpy(gpx->rsm, rsmtyp); + } } return 0; @@ -2088,6 +2103,12 @@ static int print_position(gpx_t *gpx, int ec) { if (gpx->freq > 0) fq_kHz = gpx->freq; fprintf(stdout, ", \"freq\": %d", fq_kHz); } + if (*gpx->rsm) { // RSM type + fprintf(stdout, ", \"rs41_mainboard\": \"%s\"", gpx->rsm); + } + if (gpx->conf_fw) { // firmware + fprintf(stdout, ", \"rs41_mainboard_fw\": %d", gpx->conf_fw); + } // Include frequency derived from subframe information if available. if (gpx->freq > 0) { From 8d14750c5323f3c393178dda8710a08de447ca03 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Mon, 2 Oct 2023 15:52:27 +1030 Subject: [PATCH 2/6] Fixes for iMet+XDATA. Don't add -Ozone onto the end of types anymore. --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/decode.py | 5 +++-- auto_rx/autorx/templates/index.html | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index 42ec06e0..b1713121 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.7.1-beta1" +__version__ = "1.7.1-beta2" # Global Variables diff --git a/auto_rx/autorx/decode.py b/auto_rx/autorx/decode.py index 58c6792a..9f4d381d 100644 --- a/auto_rx/autorx/decode.py +++ b/auto_rx/autorx/decode.py @@ -1602,8 +1602,9 @@ def handle_decoder_line(self, data): # which is most likely an Ozone sensor (though could be something different!) # We append -Ozone to the sonde type field to indicate this. # TODO: Decode device ID from aux field to indicate what the aux payload actually is? - if "aux" in _telemetry: - _telemetry["type"] += "-Ozone" + # 2023-10 - disabled this addition. Can be too misleading. -XDATA now appended on the web interface only. + # if "aux" in _telemetry: + # _telemetry["type"] += "-Ozone" # iMet Specific actions if self.sonde_type == "IMET": diff --git a/auto_rx/autorx/templates/index.html b/auto_rx/autorx/templates/index.html index 6efba9ac..18e5ae7d 100644 --- a/auto_rx/autorx/templates/index.html +++ b/auto_rx/autorx/templates/index.html @@ -719,6 +719,11 @@ sonde_id_data.other += sonde_id_data.batt.toFixed(1) + " V"; } + if (sonde_id_data.hasOwnProperty('aux')){ + sonde_id_data.type += "-XDATA"; + } + + telem_data.push(sonde_id_data); }); } From 5fa360534ea3df7f8e6af6c000b901accadc83d9 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Mon, 2 Oct 2023 16:14:56 +1030 Subject: [PATCH 3/6] Add -XDATA onto type when logging imet sondes --- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/logger.py | 3 +++ auto_rx/autorx/utils.py | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index b1713121..e7e21f9d 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.7.1-beta2" +__version__ = "1.7.1-beta3" # Global Variables diff --git a/auto_rx/autorx/logger.py b/auto_rx/autorx/logger.py index 1b0fa29f..e00c40d2 100644 --- a/auto_rx/autorx/logger.py +++ b/auto_rx/autorx/logger.py @@ -199,6 +199,9 @@ def write_telemetry(self, telemetry): _id = telemetry["id"] _type = telemetry["type"] + if 'aux' in telemetry: + _type += "-XDATA" + # If there is no log open for the current ID check to see if there is an existing (closed) log file, and open it. if _id not in self.open_logs: _search_string = os.path.join(self.log_directory, "*%s_*_sonde.log" % (_id)) diff --git a/auto_rx/autorx/utils.py b/auto_rx/autorx/utils.py index f9a7cc32..9c789f05 100644 --- a/auto_rx/autorx/utils.py +++ b/auto_rx/autorx/utils.py @@ -196,6 +196,8 @@ def short_type_lookup(type_name): return "Lockheed Martin LMS6-1680" elif type_name == "IMET": return "Intermet Systems iMet-1/4" + elif type_name == "IMET-XDATA": + return "Intermet Systems iMet-1/4 + XDATA" elif type_name == "IMET5": return "Intermet Systems iMet-5x" elif type_name == "MEISEI": @@ -238,6 +240,8 @@ def short_short_type_lookup(type_name): return "LMS6-1680" elif type_name == "IMET": return "iMet-1/4" + elif type_name == "IMET-XDATA": + return "iMet-1/4" elif type_name == "IMET5": return "iMet-5x" elif type_name == "MEISEI": From 3ca10ea536fb1b3e089ea929b9afeef5ef2cdf7a Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Tue, 3 Oct 2023 20:35:08 +1030 Subject: [PATCH 4/6] Show mainboard type on web interface if not RSM412, upload RS41 subframe data to sondehub, and optionally save it to disk. --- auto_rx/auto_rx.py | 5 ++- auto_rx/autorx/__init__.py | 2 +- auto_rx/autorx/config.py | 12 ++++++ auto_rx/autorx/decode.py | 20 ++++++++- auto_rx/autorx/logger.py | 53 ++++++++++++++++++++++- auto_rx/autorx/sondehub.py | 12 ++++++ auto_rx/autorx/templates/index.html | 8 ++++ auto_rx/station.cfg.example | 3 ++ auto_rx/station.cfg.example.network | 4 ++ demod/mod/rs41mod.c | 65 ++++++++++++++++++++++++++--- 10 files changed, 174 insertions(+), 10 deletions(-) diff --git a/auto_rx/auto_rx.py b/auto_rx/auto_rx.py index 1f61c66c..03e7c5a3 100644 --- a/auto_rx/auto_rx.py +++ b/auto_rx/auto_rx.py @@ -891,7 +891,10 @@ def main(): # Start our exporter options # Telemetry Logger if config["per_sonde_log"]: - _logger = TelemetryLogger(log_directory=logging_path) + _logger = TelemetryLogger( + log_directory=logging_path, + save_cal_data=config["save_cal_data"] + ) exporter_objects.append(_logger) exporter_functions.append(_logger.add) diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index e7e21f9d..2f98435b 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.7.1-beta3" +__version__ = "1.7.1-beta4" # Global Variables diff --git a/auto_rx/autorx/config.py b/auto_rx/autorx/config.py index 27ffd17a..07503a81 100644 --- a/auto_rx/autorx/config.py +++ b/auto_rx/autorx/config.py @@ -165,6 +165,7 @@ def read_auto_rx_config(filename, no_sdr_test=False): "save_raw_hex": False, "save_system_log": False, "enable_debug_logging": False, + "save_cal_data": False, # URL for the Habitat DB Server. # As of July 2018 we send via sondehub.org, which will allow us to eventually transition away # from using the habhub.org tracker, and leave it for use by High-Altitude Balloon Hobbyists. @@ -775,6 +776,17 @@ def read_auto_rx_config(filename, no_sdr_test=False): ) auto_rx_config["wideband_sondes"] = False + # 1.7.1 - Save RS41 Calibration Data + try: + auto_rx_config["save_cal_data"] = config.getboolean( + "logging", "save_cal_data" + ) + except: + logging.warning( + "Config - Missing save_cal_data option (new in v1.7.1), using default (False)" + ) + auto_rx_config["save_cal_data"] = False + # If we are being called as part of a unit test, just return the config now. if no_sdr_test: return auto_rx_config diff --git a/auto_rx/autorx/decode.py b/auto_rx/autorx/decode.py index 9f4d381d..17bcd9b8 100644 --- a/auto_rx/autorx/decode.py +++ b/auto_rx/autorx/decode.py @@ -246,6 +246,9 @@ def __init__( self.imet_prev_time = None self.imet_prev_frame = None + # Keep a record of which RS41 serials we have uploaded complete subframe data for. + self.rs41_subframe_uploads = [] + # This will become our decoder thread. self.decoder = None @@ -394,7 +397,7 @@ def generate_decoder_command(self): if self.save_decode_audio: decode_cmd += f" tee {self.save_decode_audio_path} |" - decode_cmd += "./rs41mod --ptu2 --json 2>/dev/null" + decode_cmd += "./rs41mod --ptu2 --json --jsnsubfrm1 2>/dev/null" elif self.sonde_type == "RS92": # Decoding a RS92 requires either an ephemeris or an almanac file. @@ -830,7 +833,7 @@ def generate_decoder_command_experimental(self): _baud_rate, ) - decode_cmd = f"./rs41mod --ptu2 --json --softin -i {self.raw_file_option} 2>/dev/null" + decode_cmd = f"./rs41mod --ptu2 --json --jsnsubfrm1 --softin -i {self.raw_file_option} 2>/dev/null" # RS41s transmit pulsed beacons - average over the last 2 frames, and use a peak-hold demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=True) @@ -1705,6 +1708,19 @@ def handle_decoder_line(self, data): "%Y-%m-%dT%H:%M:%SZ" ) + # RS41 Subframe Data Actions + # We only upload the subframe data once. + if 'rs41_calconf320' in _telemetry: + # Remove subframe data if we have already uploaded it once. + if _telemetry['id'] in self.rs41_subframe_uploads: + _telemetry.pop('rs41_calconf320') + else: + self.rs41_subframe_uploads.append(_telemetry['id']) + self.log_info(f"Received complete calibration dataset for {_telemetry['id']}.") + _telemetry['rs41_subframe'] = _telemetry['rs41_calconf320'] + _telemetry.pop('rs41_calconf320') + + # Grab a snapshot of modem statistics, if we are using an experimental decoder. if self.demod_stats is not None: diff --git a/auto_rx/autorx/logger.py b/auto_rx/autorx/logger.py index e00c40d2..f893b7d5 100644 --- a/auto_rx/autorx/logger.py +++ b/auto_rx/autorx/logger.py @@ -5,6 +5,7 @@ # Copyright (C) 2018 Mark Jessop # Released under GNU GPL v3 or later # +import codecs import datetime import glob import logging @@ -50,7 +51,9 @@ class TelemetryLogger(object): LOG_HEADER = "timestamp,serial,frame,lat,lon,alt,vel_v,vel_h,heading,temp,humidity,pressure,type,freq_mhz,snr,f_error_hz,sats,batt_v,burst_timer,aux_data\n" - def __init__(self, log_directory="./log"): + def __init__(self, + log_directory="./log", + save_cal_data=False): """ Initialise and start a sonde logger. Args: @@ -59,6 +62,7 @@ def __init__(self, log_directory="./log"): """ self.log_directory = log_directory + self.save_cal_data = save_cal_data # Dictionary to contain file handles. # Each sonde id is added as a unique key. Under each key are the contents: @@ -214,6 +218,7 @@ def write_telemetry(self, telemetry): self.open_logs[_id] = { "log": open(_log_file_name, "a"), "last_time": time.time(), + "subframe_saved": False } else: # Create a new log file. @@ -229,6 +234,7 @@ def write_telemetry(self, telemetry): self.open_logs[_id] = { "log": open(_log_file_name, "a"), "last_time": time.time(), + "subframe_saved": False } # Write in a header line. @@ -244,6 +250,15 @@ def write_telemetry(self, telemetry): self.open_logs[_id]["last_time"] = time.time() self.log_debug("Wrote line: %s" % _log_line.strip()) + # Save out RS41 subframe data once, if we have it. + if ('rs41_subframe' in telemetry) and self.save_cal_data: + if self.open_logs[_id]['subframe_saved'] == False: + self.open_logs[_id]['subframe_saved'] = self.write_rs41_subframe(telemetry) + + + + + def cleanup_logs(self): """ Close any open logs that have not had telemetry added in X seconds. """ @@ -262,6 +277,42 @@ def cleanup_logs(self): except Exception as e: self.log_error("Error closing log for %s - %s" % (_id, str(e))) + def write_rs41_subframe(self, telemetry): + """ Write RS41 subframe data to disk """ + + _id = telemetry["id"] + _type = telemetry["type"] + + if 'aux' in telemetry: + _type += "-XDATA" + + _subframe_log_suffix = "%s_%s_%s_%d_subframe.bin" % ( + datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S"), + _id, + _type, + int(telemetry["freq_float"] * 1e3), # Convert frequency to kHz + ) + _log_file_name = os.path.join(self.log_directory, _subframe_log_suffix) + + + try: + _subframe_data = codecs.decode(telemetry['rs41_subframe'], 'hex') + except Exception as e: + self.log_error("Error parsing RS41 subframe data") + + if _subframe_data: + _subframe_file = open(_log_file_name, 'wb') + _subframe_file.write(_subframe_data) + _subframe_file.close() + + self.log_info(f"Wrote subframe data for {telemetry['id']} to {_subframe_log_suffix}") + return True + else: + return False + + + + def close(self): """ Close input processing thread. """ self.input_processing_running = False diff --git a/auto_rx/autorx/sondehub.py b/auto_rx/autorx/sondehub.py index 3d241b0d..a917055f 100644 --- a/auto_rx/autorx/sondehub.py +++ b/auto_rx/autorx/sondehub.py @@ -10,6 +10,8 @@ # Released under GNU GPL v3 or later # import autorx +import base64 +import codecs import datetime import glob import gzip @@ -297,6 +299,16 @@ def reformat_data(self, telemetry): if "rs41_mainboard_fw" in telemetry: _output["rs41_mainboard_fw"] = str(telemetry["rs41_mainboard_fw"]) + if 'rs41_subframe' in telemetry: + # RS41 calibration subframe data. + # We try to base64 encode this. + try: + _calbytes = codecs.decode(telemetry['rs41_subframe'], 'hex') + _output['rs41_subframe'] = base64.b64encode(_calbytes).decode() + except Exception as e: + self.log_error(f"Error handling RS41 subframe data.") + + # Handle the additional SNR and frequency estimation if we have it if "snr" in telemetry: _output["snr"] = telemetry["snr"] diff --git a/auto_rx/autorx/templates/index.html b/auto_rx/autorx/templates/index.html index 18e5ae7d..345c555c 100644 --- a/auto_rx/autorx/templates/index.html +++ b/auto_rx/autorx/templates/index.html @@ -709,6 +709,14 @@ // Add data into the 'other' field. sonde_id_data.other = ""; + + if(sonde_id_data.hasOwnProperty('rs41_mainboard')){ + // Only print mainboard type if it's not the 'original' mainboard. + if(sonde_id_data.rs41_mainboard !== 'RSM412'){ + sonde_id_data.other += sonde_id_data.rs41_mainboard + " "; + } + } + // Burst timer for RS41s if (sonde_id_data.hasOwnProperty('bt')){ if ((sonde_id_data.bt >= 0) && (sonde_id_data.bt < 65535)) { diff --git a/auto_rx/station.cfg.example b/auto_rx/station.cfg.example index 7a8ce8e2..edbde7f9 100644 --- a/auto_rx/station.cfg.example +++ b/auto_rx/station.cfg.example @@ -458,6 +458,9 @@ save_system_log = False # auto_rx operational issues. enable_debug_logging = False +# Enable logging of RS41 Calibration data ('subframe' data) +# This is saved as a binary file with file suffix _subframe.bin +save_cal_data = False ########################### # WEB INTERFACE SETTINNGS # diff --git a/auto_rx/station.cfg.example.network b/auto_rx/station.cfg.example.network index f8ea1f20..4cd46182 100644 --- a/auto_rx/station.cfg.example.network +++ b/auto_rx/station.cfg.example.network @@ -458,6 +458,10 @@ save_system_log = False # auto_rx operational issues. enable_debug_logging = False +# Enable logging of RS41 Calibration data ('subframe' data) +# This is saved as a binary file with file suffix _subframe.bin +save_cal_data = False + ########################### # WEB INTERFACE SETTINNGS # ########################### diff --git a/demod/mod/rs41mod.c b/demod/mod/rs41mod.c index 0eb4bab6..0beb6309 100644 --- a/demod/mod/rs41mod.c +++ b/demod/mod/rs41mod.c @@ -65,6 +65,7 @@ typedef struct { i8_t aut; i8_t jsn; // JSON output (auto_rx) i8_t slt; // silent (only raw/json) + i8_t cal; // json cal/conf } option_t; typedef struct { @@ -117,6 +118,9 @@ typedef struct { ui8_t dfrm_bitscore[FRAME_LEN]; ui8_t calibytes[51*16]; ui8_t calfrchk[51]; + ui8_t calconf_complete; + ui8_t calconf_sent; + ui8_t *calconf_subfrm; // 1+16 byte cal/conf subframe float ptu_Rf1; // ref-resistor f1 (750 Ohm) float ptu_Rf2; // ref-resistor f2 (1100 Ohm) float ptu_co1[3]; // { -243.911 , 0.187654 , 8.2e-06 } @@ -267,15 +271,15 @@ float r4(ui8_t *bytes) { } */ -static int crc16(gpx_t *gpx, int start, int len) { +static int crc16(ui8_t data[], int len) { int crc16poly = 0x1021; int rem = 0xFFFF, i, j; int byte; - if (start+len+2 > FRAME_LEN) return -1; + //if (start+len+2 > FRAME_LEN) return -1; for (i = 0; i < len; i++) { - byte = gpx->frame[start+i]; + byte = data[i]; rem = rem ^ (byte << 8); for (j = 0; j < 8; j++) { if (rem & 0x8000) { @@ -298,7 +302,7 @@ static int check_CRC(gpx_t *gpx, ui32_t pos, ui32_t pck) { crclen = gpx->frame[pos+1]; if (pos + crclen + 4 > FRAME_LEN) return -1; crcdat = u2(gpx->frame+pos+2+crclen); - if ( crcdat != crc16(gpx, pos+2, crclen) ) { + if ( crcdat != crc16(gpx->frame+pos+2, crclen) ) { return 1; // CRC NO } else return 0; // CRC OK @@ -451,6 +455,8 @@ static int get_SondeID(gpx_t *gpx, int crc, int ofs) { // reset conf data memset(gpx->rstyp, 0, 9); memset(gpx->rsm, 0, 10); + gpx->calconf_complete = 0; + gpx->calconf_sent = 0; gpx->freq = 0; gpx->conf_fw = 0; gpx->conf_bt = 0; @@ -504,6 +510,19 @@ static int get_FrameConf(gpx_t *gpx, int ofs) { gpx->ecdat.last_calfrm = calfr; gpx->ecdat.last_calfrm_ts = gpx->ecdat.ts; + + if ( !gpx->calconf_complete ) { + int sum = 0; + for (i = 0; i < 51; i++) { // 0x00..0x32 + sum += gpx->calfrchk[i]; + } + if (sum == 51) { // count all subframes + int calconf_dat = gpx->calibytes[0] | (gpx->calibytes[1]<<8); + int calconf_crc = crc16(gpx->calibytes+2, 50*16-2); // subframe 0x32 not included (variable) + + if (calconf_dat == calconf_crc) gpx->calconf_complete = 1; + } + } } return err; @@ -1299,7 +1318,7 @@ static int get_Aux(gpx_t *gpx, int out, int pos) { auxlen = gpx->frame[pos+1]; auxcrc = gpx->frame[pos+2+auxlen] | (gpx->frame[pos+2+auxlen+1]<<8); - if ( auxcrc == crc16(gpx, pos+2, auxlen) ) { + if ( pos + auxlen + 4 <= FRAME_LEN && auxcrc == crc16(gpx->frame+pos+2, auxlen) ) { if (count7E == 0) { if (out) fprintf(stdout, "\n # xdata = "); } @@ -1372,6 +1391,8 @@ static int get_Calconf(gpx_t *gpx, int out, int ofs) { char rsmtyp[10]; int err = 0; + gpx->calconf_subfrm = gpx->frame+pos_CalData+ofs; + byte = gpx->frame[pos_CalData+ofs]; calfr = byte; err = check_CRC(gpx, pos_FRAME+ofs, pck_FRAME); @@ -2110,6 +2131,31 @@ static int print_position(gpx_t *gpx, int ec) { fprintf(stdout, ", \"rs41_mainboard_fw\": %d", gpx->conf_fw); } + if (gpx->option.cal == 1) { // cal/conf + if ( !gpx->calconf_sent && gpx->calconf_complete ) { + fprintf(stdout, ", \"rs41_calconf320\": \""); + for (int _j = 0; _j < 51*16; _j++) { + fprintf(stdout, "%02X", gpx->calibytes[_j]); + } + fprintf(stdout, "\""); + gpx->calconf_sent = 1; + } + if (gpx->calconf_subfrm[0] == 0x32) { + fprintf(stdout, ", \"rs41_conf0x32\": \""); + for (int _j = 0; _j < 16; _j++) { + fprintf(stdout, "%02X", gpx->calconf_subfrm[1+_j]); + } + fprintf(stdout, "\""); + } + } + if (gpx->option.cal == 2) { // cal/conf + fprintf(stdout, ", \"rs41_subfrm\": \"0x%02X:", gpx->calconf_subfrm[0]); + for (int _j = 0; _j < 16; _j++) { + fprintf(stdout, "%02X", gpx->calconf_subfrm[1+_j]); + } + fprintf(stdout, "\""); + } + // Include frequency derived from subframe information if available. if (gpx->freq > 0) { fprintf(stdout, ", \"tx_frequency\": %d", gpx->freq ); @@ -2444,6 +2490,8 @@ int main(int argc, char *argv[]) { if (frq < 300000000) frq = -1; cfreq = frq; } + else if (strcmp(*argv, "--jsnsubfrm1") == 0) { gpx.option.cal = 1; } // json cal/conf + else if (strcmp(*argv, "--jsnsubfrm2") == 0) { gpx.option.cal = 2; } // json cal/conf else if (strcmp(*argv, "--rawhex") == 0) { rawhex = 2; } // raw hex input else if (strcmp(*argv, "--xorhex") == 0) { rawhex = 2; xorhex = 1; } // raw xor input else if (strcmp(*argv, "-") == 0) { @@ -2495,6 +2543,13 @@ int main(int argc, char *argv[]) { // init gpx memcpy(gpx.frame, rs41_header_bytes, sizeof(rs41_header_bytes)); // 8 header bytes + gpx.calconf_subfrm = gpx.frame+pos_CalData; + if (gpx.option.cal) { + gpx.option.jsn = 1; + gpx.option.ecc = 2; + gpx.option.crc = 1; + } + if (cfreq > 0) gpx.jsn_freq = (cfreq+500)/1000; From 58feaebf9c1a98644305b8c7e20e7b73054ae743 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Wed, 4 Oct 2023 07:49:57 +1030 Subject: [PATCH 5/6] rebase rs41mod, change field name for subframe data --- auto_rx/autorx/decode.py | 8 ++++---- demod/mod/rs41mod.c | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/auto_rx/autorx/decode.py b/auto_rx/autorx/decode.py index 17bcd9b8..75679a28 100644 --- a/auto_rx/autorx/decode.py +++ b/auto_rx/autorx/decode.py @@ -1710,15 +1710,15 @@ def handle_decoder_line(self, data): # RS41 Subframe Data Actions # We only upload the subframe data once. - if 'rs41_calconf320' in _telemetry: + if 'rs41_calconf51x16' in _telemetry: # Remove subframe data if we have already uploaded it once. if _telemetry['id'] in self.rs41_subframe_uploads: - _telemetry.pop('rs41_calconf320') + _telemetry.pop('rs41_calconf51x16') else: self.rs41_subframe_uploads.append(_telemetry['id']) self.log_info(f"Received complete calibration dataset for {_telemetry['id']}.") - _telemetry['rs41_subframe'] = _telemetry['rs41_calconf320'] - _telemetry.pop('rs41_calconf320') + _telemetry['rs41_subframe'] = _telemetry['rs41_calconf51x16'] + _telemetry.pop('rs41_calconf51x16') diff --git a/demod/mod/rs41mod.c b/demod/mod/rs41mod.c index 0beb6309..937f7ed1 100644 --- a/demod/mod/rs41mod.c +++ b/demod/mod/rs41mod.c @@ -2132,9 +2132,16 @@ static int print_position(gpx_t *gpx, int ec) { } if (gpx->option.cal == 1) { // cal/conf + int _j; if ( !gpx->calconf_sent && gpx->calconf_complete ) { - fprintf(stdout, ", \"rs41_calconf320\": \""); - for (int _j = 0; _j < 51*16; _j++) { + /* + fprintf(stdout, ", \"rs41_calconf320h\": \""); // only constant/crc part + for (int _j = 0; _j < 50*16; _j++) { + fprintf(stdout, "%02X", gpx->calibytes[_j]); + } + */ + fprintf(stdout, ", \"rs41_calconf51x16\": \""); + for (_j = 0; _j < 51*16; _j++) { fprintf(stdout, "%02X", gpx->calibytes[_j]); } fprintf(stdout, "\""); @@ -2142,15 +2149,16 @@ static int print_position(gpx_t *gpx, int ec) { } if (gpx->calconf_subfrm[0] == 0x32) { fprintf(stdout, ", \"rs41_conf0x32\": \""); - for (int _j = 0; _j < 16; _j++) { + for (_j = 0; _j < 16; _j++) { fprintf(stdout, "%02X", gpx->calconf_subfrm[1+_j]); } fprintf(stdout, "\""); } } if (gpx->option.cal == 2) { // cal/conf + int _j; fprintf(stdout, ", \"rs41_subfrm\": \"0x%02X:", gpx->calconf_subfrm[0]); - for (int _j = 0; _j < 16; _j++) { + for (_j = 0; _j < 16; _j++) { fprintf(stdout, "%02X", gpx->calconf_subfrm[1+_j]); } fprintf(stdout, "\""); From 58641dfe2ae0e16736924a7136d588f80d17db1f Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sat, 7 Oct 2023 15:57:58 +1030 Subject: [PATCH 6/6] Version number change 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 2f98435b..427e6c17 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.7.1-beta4" +__version__ = "1.7.1" # Global Variables