Skip to content

Commit 145979e

Browse files
authored
Merge pull request #820 from projecthorus/testing
v1.7.1 - Bugfixes, RS41 Mainboard type
2 parents 18ab0a2 + f93229a commit 145979e

File tree

12 files changed

+229
-15
lines changed

12 files changed

+229
-15
lines changed

auto_rx/auto_rx.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,10 @@ def main():
891891
# Start our exporter options
892892
# Telemetry Logger
893893
if config["per_sonde_log"]:
894-
_logger = TelemetryLogger(log_directory=logging_path)
894+
_logger = TelemetryLogger(
895+
log_directory=logging_path,
896+
save_cal_data=config["save_cal_data"]
897+
)
895898
exporter_objects.append(_logger)
896899
exporter_functions.append(_logger.add)
897900

auto_rx/autorx/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus.
1313
# PATCH - Small changes, or minor feature additions.
1414

15-
__version__ = "1.7.0"
15+
__version__ = "1.7.1"
1616

1717

1818
# Global Variables

auto_rx/autorx/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ def read_auto_rx_config(filename, no_sdr_test=False):
165165
"save_raw_hex": False,
166166
"save_system_log": False,
167167
"enable_debug_logging": False,
168+
"save_cal_data": False,
168169
# URL for the Habitat DB Server.
169170
# As of July 2018 we send via sondehub.org, which will allow us to eventually transition away
170171
# 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):
775776
)
776777
auto_rx_config["wideband_sondes"] = False
777778

779+
# 1.7.1 - Save RS41 Calibration Data
780+
try:
781+
auto_rx_config["save_cal_data"] = config.getboolean(
782+
"logging", "save_cal_data"
783+
)
784+
except:
785+
logging.warning(
786+
"Config - Missing save_cal_data option (new in v1.7.1), using default (False)"
787+
)
788+
auto_rx_config["save_cal_data"] = False
789+
778790
# If we are being called as part of a unit test, just return the config now.
779791
if no_sdr_test:
780792
return auto_rx_config

auto_rx/autorx/decode.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ def __init__(
246246
self.imet_prev_time = None
247247
self.imet_prev_frame = None
248248

249+
# Keep a record of which RS41 serials we have uploaded complete subframe data for.
250+
self.rs41_subframe_uploads = []
251+
249252
# This will become our decoder thread.
250253
self.decoder = None
251254

@@ -394,7 +397,7 @@ def generate_decoder_command(self):
394397
if self.save_decode_audio:
395398
decode_cmd += f" tee {self.save_decode_audio_path} |"
396399

397-
decode_cmd += "./rs41mod --ptu2 --json 2>/dev/null"
400+
decode_cmd += "./rs41mod --ptu2 --json --jsnsubfrm1 2>/dev/null"
398401

399402
elif self.sonde_type == "RS92":
400403
# Decoding a RS92 requires either an ephemeris or an almanac file.
@@ -830,7 +833,7 @@ def generate_decoder_command_experimental(self):
830833
_baud_rate,
831834
)
832835

833-
decode_cmd = f"./rs41mod --ptu2 --json --softin -i {self.raw_file_option} 2>/dev/null"
836+
decode_cmd = f"./rs41mod --ptu2 --json --jsnsubfrm1 --softin -i {self.raw_file_option} 2>/dev/null"
834837

835838
# RS41s transmit pulsed beacons - average over the last 2 frames, and use a peak-hold
836839
demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=True)
@@ -1602,8 +1605,9 @@ def handle_decoder_line(self, data):
16021605
# which is most likely an Ozone sensor (though could be something different!)
16031606
# We append -Ozone to the sonde type field to indicate this.
16041607
# TODO: Decode device ID from aux field to indicate what the aux payload actually is?
1605-
if "aux" in _telemetry:
1606-
_telemetry["type"] += "-Ozone"
1608+
# 2023-10 - disabled this addition. Can be too misleading. -XDATA now appended on the web interface only.
1609+
# if "aux" in _telemetry:
1610+
# _telemetry["type"] += "-Ozone"
16071611

16081612
# iMet Specific actions
16091613
if self.sonde_type == "IMET":
@@ -1704,6 +1708,19 @@ def handle_decoder_line(self, data):
17041708
"%Y-%m-%dT%H:%M:%SZ"
17051709
)
17061710

1711+
# RS41 Subframe Data Actions
1712+
# We only upload the subframe data once.
1713+
if 'rs41_calconf51x16' in _telemetry:
1714+
# Remove subframe data if we have already uploaded it once.
1715+
if _telemetry['id'] in self.rs41_subframe_uploads:
1716+
_telemetry.pop('rs41_calconf51x16')
1717+
else:
1718+
self.rs41_subframe_uploads.append(_telemetry['id'])
1719+
self.log_info(f"Received complete calibration dataset for {_telemetry['id']}.")
1720+
_telemetry['rs41_subframe'] = _telemetry['rs41_calconf51x16']
1721+
_telemetry.pop('rs41_calconf51x16')
1722+
1723+
17071724

17081725
# Grab a snapshot of modem statistics, if we are using an experimental decoder.
17091726
if self.demod_stats is not None:

auto_rx/autorx/logger.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Copyright (C) 2018 Mark Jessop <[email protected]>
66
# Released under GNU GPL v3 or later
77
#
8+
import codecs
89
import datetime
910
import glob
1011
import logging
@@ -50,7 +51,9 @@ class TelemetryLogger(object):
5051

5152
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"
5253

53-
def __init__(self, log_directory="./log"):
54+
def __init__(self,
55+
log_directory="./log",
56+
save_cal_data=False):
5457
""" Initialise and start a sonde logger.
5558
5659
Args:
@@ -59,6 +62,7 @@ def __init__(self, log_directory="./log"):
5962
"""
6063

6164
self.log_directory = log_directory
65+
self.save_cal_data = save_cal_data
6266

6367
# Dictionary to contain file handles.
6468
# Each sonde id is added as a unique key. Under each key are the contents:
@@ -199,6 +203,9 @@ def write_telemetry(self, telemetry):
199203
_id = telemetry["id"]
200204
_type = telemetry["type"]
201205

206+
if 'aux' in telemetry:
207+
_type += "-XDATA"
208+
202209
# If there is no log open for the current ID check to see if there is an existing (closed) log file, and open it.
203210
if _id not in self.open_logs:
204211
_search_string = os.path.join(self.log_directory, "*%s_*_sonde.log" % (_id))
@@ -211,6 +218,7 @@ def write_telemetry(self, telemetry):
211218
self.open_logs[_id] = {
212219
"log": open(_log_file_name, "a"),
213220
"last_time": time.time(),
221+
"subframe_saved": False
214222
}
215223
else:
216224
# Create a new log file.
@@ -226,6 +234,7 @@ def write_telemetry(self, telemetry):
226234
self.open_logs[_id] = {
227235
"log": open(_log_file_name, "a"),
228236
"last_time": time.time(),
237+
"subframe_saved": False
229238
}
230239

231240
# Write in a header line.
@@ -241,6 +250,15 @@ def write_telemetry(self, telemetry):
241250
self.open_logs[_id]["last_time"] = time.time()
242251
self.log_debug("Wrote line: %s" % _log_line.strip())
243252

253+
# Save out RS41 subframe data once, if we have it.
254+
if ('rs41_subframe' in telemetry) and self.save_cal_data:
255+
if self.open_logs[_id]['subframe_saved'] == False:
256+
self.open_logs[_id]['subframe_saved'] = self.write_rs41_subframe(telemetry)
257+
258+
259+
260+
261+
244262
def cleanup_logs(self):
245263
""" Close any open logs that have not had telemetry added in X seconds. """
246264

@@ -259,6 +277,42 @@ def cleanup_logs(self):
259277
except Exception as e:
260278
self.log_error("Error closing log for %s - %s" % (_id, str(e)))
261279

280+
def write_rs41_subframe(self, telemetry):
281+
""" Write RS41 subframe data to disk """
282+
283+
_id = telemetry["id"]
284+
_type = telemetry["type"]
285+
286+
if 'aux' in telemetry:
287+
_type += "-XDATA"
288+
289+
_subframe_log_suffix = "%s_%s_%s_%d_subframe.bin" % (
290+
datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S"),
291+
_id,
292+
_type,
293+
int(telemetry["freq_float"] * 1e3), # Convert frequency to kHz
294+
)
295+
_log_file_name = os.path.join(self.log_directory, _subframe_log_suffix)
296+
297+
298+
try:
299+
_subframe_data = codecs.decode(telemetry['rs41_subframe'], 'hex')
300+
except Exception as e:
301+
self.log_error("Error parsing RS41 subframe data")
302+
303+
if _subframe_data:
304+
_subframe_file = open(_log_file_name, 'wb')
305+
_subframe_file.write(_subframe_data)
306+
_subframe_file.close()
307+
308+
self.log_info(f"Wrote subframe data for {telemetry['id']} to {_subframe_log_suffix}")
309+
return True
310+
else:
311+
return False
312+
313+
314+
315+
262316
def close(self):
263317
""" Close input processing thread. """
264318
self.input_processing_running = False

auto_rx/autorx/scan.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,13 @@ def detect_sonde(
533533
)
534534
_sonde_type = "IMET"
535535
elif "IMET1" in _type:
536+
# This could actually be a wideband iMet sonde. We treat this as a IMET4.
536537
logging.debug(
537-
"Scanner (%s) - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)"
538+
"Scanner (%s) - Possible detection of a Wideband iMet Sonde! (Type %s) (Score: %.2f)"
538539
% (_sdr_name, _type, _score)
539540
)
540-
_sonde_type = "IMET1"
541+
# Override the type to IMET4.
542+
_sonde_type = "IMET"
541543
elif "IMETafsk" in _type:
542544
logging.debug(
543545
"Scanner (%s) - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)"

auto_rx/autorx/sondehub.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# Released under GNU GPL v3 or later
1111
#
1212
import autorx
13+
import base64
14+
import codecs
1315
import datetime
1416
import glob
1517
import gzip
@@ -291,6 +293,22 @@ def reformat_data(self, telemetry):
291293
if "ref_datetime" in telemetry:
292294
_output["ref_datetime"] = telemetry["ref_datetime"]
293295

296+
if "rs41_mainboard" in telemetry:
297+
_output["rs41_mainboard"] = telemetry["rs41_mainboard"]
298+
299+
if "rs41_mainboard_fw" in telemetry:
300+
_output["rs41_mainboard_fw"] = str(telemetry["rs41_mainboard_fw"])
301+
302+
if 'rs41_subframe' in telemetry:
303+
# RS41 calibration subframe data.
304+
# We try to base64 encode this.
305+
try:
306+
_calbytes = codecs.decode(telemetry['rs41_subframe'], 'hex')
307+
_output['rs41_subframe'] = base64.b64encode(_calbytes).decode()
308+
except Exception as e:
309+
self.log_error(f"Error handling RS41 subframe data.")
310+
311+
294312
# Handle the additional SNR and frequency estimation if we have it
295313
if "snr" in telemetry:
296314
_output["snr"] = telemetry["snr"]

auto_rx/autorx/templates/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,14 @@
709709

710710
// Add data into the 'other' field.
711711
sonde_id_data.other = "";
712+
713+
if(sonde_id_data.hasOwnProperty('rs41_mainboard')){
714+
// Only print mainboard type if it's not the 'original' mainboard.
715+
if(sonde_id_data.rs41_mainboard !== 'RSM412'){
716+
sonde_id_data.other += sonde_id_data.rs41_mainboard + " ";
717+
}
718+
}
719+
712720
// Burst timer for RS41s
713721
if (sonde_id_data.hasOwnProperty('bt')){
714722
if ((sonde_id_data.bt >= 0) && (sonde_id_data.bt < 65535)) {
@@ -719,6 +727,11 @@
719727
sonde_id_data.other += sonde_id_data.batt.toFixed(1) + " V";
720728
}
721729

730+
if (sonde_id_data.hasOwnProperty('aux')){
731+
sonde_id_data.type += "-XDATA";
732+
}
733+
734+
722735
telem_data.push(sonde_id_data);
723736
});
724737
}

auto_rx/autorx/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ def short_type_lookup(type_name):
196196
return "Lockheed Martin LMS6-1680"
197197
elif type_name == "IMET":
198198
return "Intermet Systems iMet-1/4"
199+
elif type_name == "IMET-XDATA":
200+
return "Intermet Systems iMet-1/4 + XDATA"
199201
elif type_name == "IMET5":
200202
return "Intermet Systems iMet-5x"
201203
elif type_name == "MEISEI":
@@ -238,6 +240,8 @@ def short_short_type_lookup(type_name):
238240
return "LMS6-1680"
239241
elif type_name == "IMET":
240242
return "iMet-1/4"
243+
elif type_name == "IMET-XDATA":
244+
return "iMet-1/4"
241245
elif type_name == "IMET5":
242246
return "iMet-5x"
243247
elif type_name == "MEISEI":

auto_rx/station.cfg.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,9 @@ save_system_log = False
458458
# auto_rx operational issues.
459459
enable_debug_logging = False
460460

461+
# Enable logging of RS41 Calibration data ('subframe' data)
462+
# This is saved as a binary file with file suffix _subframe.bin
463+
save_cal_data = False
461464

462465
###########################
463466
# WEB INTERFACE SETTINNGS #

0 commit comments

Comments
 (0)