kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
Added changes to support 400 MHz LMS6 sondes.
rodzic
31e8bb8a76
commit
8c7f22890f
|
@ -444,8 +444,8 @@ def telemetry_filter(telemetry):
|
|||
# Regex to check DFM06/09/15/17 callsigns.
|
||||
dfm_callsign_valid = re.match(r'DFM[01][5679]-\d{6}', _serial)
|
||||
|
||||
# If Vaisala or DFMs, check the callsigns are valid. If M10 or iMet, just pass it through.
|
||||
if vaisala_callsign_valid or dfm_callsign_valid or ('M10' in telemetry['type']) or ('MK2LMS' in telemetry['type']) or ('iMet' in telemetry['type']):
|
||||
# If Vaisala or DFMs, check the callsigns are valid. If M10, iMet or LMS6, just pass it through.
|
||||
if vaisala_callsign_valid or dfm_callsign_valid or ('M10' in telemetry['type']) or ('MK2LMS' in telemetry['type']) or ('LMS6' in telemetry['type']) or ('iMet' in telemetry['type']):
|
||||
return True
|
||||
else:
|
||||
_id_msg = "Payload ID %s is invalid." % telemetry['id']
|
||||
|
|
|
@ -17,7 +17,7 @@ except ImportError:
|
|||
# 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.1.3-beta1"
|
||||
__version__ = "1.1.3-beta2"
|
||||
|
||||
|
||||
# Global Variables
|
||||
|
|
|
@ -67,7 +67,7 @@ def telemetry_to_aprs_position(sonde_data, object_name="<id>", aprs_comment="BOM
|
|||
# Use the last 5 characters of the unique ID we have generated.
|
||||
_object_name = "IMET" + sonde_data['id'][-5:]
|
||||
|
||||
elif 'MK2LMS' in sonde_data['type']:
|
||||
elif ('MK2LMS' in sonde_data['type']) or ('LMS6' in sonde_data['type']):
|
||||
# Use the last 5 hex digits of the sonde ID.
|
||||
_id_suffix = int(sonde_data['id'].split('-')[1])
|
||||
_id_hex = hex(_id_suffix).upper()
|
||||
|
|
|
@ -258,7 +258,7 @@ def read_auto_rx_config(filename):
|
|||
|
||||
# New demod tweaks - Added 2019-04-23
|
||||
# Default to all experimental decoders off.
|
||||
auto_rx_config['experimental_decoders'] = {'RS41': False, 'RS92': False, 'DFM': False, 'M10': False, 'iMet': False, 'LMS6': False, 'MK2LMS': False}
|
||||
auto_rx_config['experimental_decoders'] = {'RS41': False, 'RS92': False, 'DFM': False, 'M10': False, 'iMet': False, 'LMS6': True, 'MK2LMS': False}
|
||||
auto_rx_config['rs41_drift_tweak'] = config.getboolean('advanced', 'drift_tweak')
|
||||
auto_rx_config['decoder_spacing_limit'] = config.getint('advanced', 'decoder_spacing_limit')
|
||||
auto_rx_config['decoder_stats'] = config.getboolean('advanced', 'enable_stats')
|
||||
|
@ -266,6 +266,7 @@ def read_auto_rx_config(filename):
|
|||
auto_rx_config['experimental_decoders']['RS92'] = config.getboolean('advanced', 'rs92_experimental')
|
||||
auto_rx_config['experimental_decoders']['M10'] = config.getboolean('advanced', 'm10_experimental')
|
||||
auto_rx_config['experimental_decoders']['DFM'] = config.getboolean('advanced', 'dfm_experimental')
|
||||
auto_rx_config['experimental_decoders']['LMS6'] = config.getboolean('advanced', 'lms6-400_experimental')
|
||||
# When LMS6 support is added, that will have to be added in here.
|
||||
|
||||
try:
|
||||
|
|
|
@ -20,7 +20,7 @@ from .gps import get_ephemeris, get_almanac
|
|||
from .sonde_specific import *
|
||||
|
||||
# Global valid sonde types list.
|
||||
VALID_SONDE_TYPES = ['RS92', 'RS41', 'DFM', 'M10', 'iMet', 'MK2LMS']
|
||||
VALID_SONDE_TYPES = ['RS92', 'RS41', 'DFM', 'M10', 'iMet', 'MK2LMS', 'LMS6']
|
||||
|
||||
# Known 'Drifty' Radiosonde types
|
||||
# NOTE: Due to observed adjacent channel detections of RS41s, the adjacent channel decoder restriction
|
||||
|
@ -68,7 +68,7 @@ class SondeDecoder(object):
|
|||
'heading' : 0.0
|
||||
}
|
||||
|
||||
VALID_SONDE_TYPES = ['RS92', 'RS41', 'DFM', 'M10', 'iMet', 'MK2LMS']
|
||||
VALID_SONDE_TYPES = ['RS92', 'RS41', 'DFM', 'M10', 'iMet', 'MK2LMS', 'LMS6']
|
||||
|
||||
def __init__(self,
|
||||
sonde_type="None",
|
||||
|
@ -373,6 +373,28 @@ class SondeDecoder(object):
|
|||
else:
|
||||
decode_cmd += "./mk2a_lms1680 --json 2>/dev/null"
|
||||
|
||||
elif self.sonde_type == "LMS6":
|
||||
# LMS6 Decoder command.
|
||||
# rtl_fm -p 0 -g -1 -M fm -F9 -s 15k -f 405500000 | sox -t raw -r 15k -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - lowpass 2600 2>/dev/null | ./rs41ecc --crc --ecc --ptu
|
||||
# Note: Have removed a 'highpass 20' filter from the sox line, will need to re-evaluate if adding that is useful in the future.
|
||||
decode_cmd = "%s %s-p %d -d %s %s-M fm -F9 -s 15k -f %d 2>/dev/null | " % (self.sdr_fm, bias_option, int(self.ppm), str(self.device_idx), gain_param, self.sonde_freq)
|
||||
|
||||
# If selected by the user, we can add a highpass filter into the sox command. This helps handle up to about 5ppm of receiver drift
|
||||
# before performance becomes significantly degraded. By default this is off, as it is not required with TCXO RTLSDRs, and actually
|
||||
# slightly degrades performance.
|
||||
if self.rs41_drift_tweak:
|
||||
_highpass = "highpass 20 "
|
||||
else:
|
||||
_highpass = ""
|
||||
|
||||
decode_cmd += "sox -t raw -r 15k -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - %slowpass 2600 2>/dev/null | " % _highpass
|
||||
|
||||
# Add in tee command to save audio to disk if debugging is enabled.
|
||||
if self.save_decode_audio:
|
||||
decode_cmd += " tee decode_%s.wav |" % str(self.device_idx)
|
||||
|
||||
decode_cmd += "./lms6mod --json 2>/dev/null"
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -551,6 +573,30 @@ class SondeDecoder(object):
|
|||
# iMet-4 (IMET1RS) decoder
|
||||
decode_cmd += "./imet1rs_dft --json 2>/dev/null"
|
||||
|
||||
elif self.sonde_type == "LMS6":
|
||||
# LMS6 (400 MHz variant) Decoder command.
|
||||
_sdr_rate = 48000 # IQ rate. Lower rate = lower CPU usage, but less frequency tracking ability.
|
||||
_output_rate = 48000
|
||||
_baud_rate = 4800
|
||||
_offset = 0.25 # Place the sonde frequency in the centre of the passband.
|
||||
_lower = int(0.025 * _sdr_rate) # Limit the frequency estimation window to not include the passband edges.
|
||||
_upper = int(0.475 * _sdr_rate)
|
||||
_freq = int(self.sonde_freq - _sdr_rate*_offset)
|
||||
|
||||
decode_cmd = "%s %s-p %d -d %s %s-M raw -F9 -s %d -f %d 2>/dev/null |" % (self.sdr_fm, bias_option, int(self.ppm), str(self.device_idx), gain_param, _sdr_rate, _freq)
|
||||
# Add in tee command to save IQ to disk if debugging is enabled.
|
||||
if self.save_decode_iq:
|
||||
decode_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx)
|
||||
|
||||
decode_cmd += "./fsk_demod --cs16 -b %d -u %d %s2 %d %d - - %s |" % (_lower, _upper, _stats_command_1, _sdr_rate, _baud_rate, _stats_command_2)
|
||||
decode_cmd += " python ./test/bit_to_samples.py %d %d | sox -t raw -r %d -e unsigned-integer -b 8 -c 1 - -r %d -b 8 -t wav - 2>/dev/null|" % (_output_rate, _baud_rate, _output_rate, _output_rate)
|
||||
|
||||
# Add in tee command to save audio to disk if debugging is enabled.
|
||||
if self.save_decode_audio:
|
||||
decode_cmd += " tee decode_%s.wav |" % str(self.device_idx)
|
||||
|
||||
decode_cmd += "./lms6mod --json 2>/dev/null"
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -726,8 +772,8 @@ class SondeDecoder(object):
|
|||
_telemetry['station_code'] = self.imet_location
|
||||
|
||||
|
||||
# LMS6-1680 Specific Actions
|
||||
if self.sonde_type == 'MK2LMS':
|
||||
# LMS6 Specific Actions
|
||||
if self.sonde_type == 'MK2LMS' or self.sonde_type == 'LMS6':
|
||||
# We are only provided with HH:MM:SS, so the timestamp needs to be fixed, just like with the iMet sondes
|
||||
_telemetry['datetime_dt'] = fix_datetime(_telemetry['datetime'])
|
||||
|
||||
|
|
|
@ -322,13 +322,13 @@ def detect_sonde(frequency, rs_path="./", dwell_time=10, sdr_fm='rtl_fm', device
|
|||
logging.debug("Scanner #%s - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)" % (str(device_idx), _type, _score))
|
||||
return _type
|
||||
elif 'LMS6' in _type:
|
||||
logging.debug("Scanner #%s - Detected a LMS6 Sonde! (Unsupported) (Score: %.2f)" % (str(device_idx), _score))
|
||||
logging.debug("Scanner #%s - Detected a LMS6 Sonde! (Score: %.2f)" % (str(device_idx), _score))
|
||||
return 'LMS6'
|
||||
elif 'C34' in _type:
|
||||
logging.debug("Scanner #%s - Detected a Meteolabor C34/C50 Sonde! (Unsupported) (Score: %.2f)" % (str(device_idx), _score))
|
||||
return 'C34C50'
|
||||
elif 'MK2LMS' in _type:
|
||||
logging.debug("Scanner #%s - Detected a 1680 MHz LMS6 Sonde! (Score: %.2f)" % (str(device_idx), _score))
|
||||
logging.debug("Scanner #%s - Detected a 1680 MHz LMS6 Sonde (MK2A Telemetry)! (Score: %.2f)" % (str(device_idx), _score))
|
||||
if _score < 0:
|
||||
return '-MK2LMS'
|
||||
else:
|
||||
|
|
|
@ -30,7 +30,7 @@ except ImportError:
|
|||
|
||||
|
||||
# List of binaries we check for on startup
|
||||
REQUIRED_RS_UTILS = ['dft_detect', 'dfm09mod', 'm10', 'imet1rs_dft', 'rs41mod', 'rs92mod', 'fsk_demod', 'mk2a_lms1680']
|
||||
REQUIRED_RS_UTILS = ['dft_detect', 'dfm09mod', 'm10', 'imet1rs_dft', 'rs41mod', 'rs92mod', 'fsk_demod', 'mk2a_lms1680', 'lms6mod']
|
||||
|
||||
def check_rs_utils():
|
||||
""" Check the required RS decoder binaries exist
|
||||
|
|
|
@ -52,9 +52,16 @@ bias = False
|
|||
# Minimum and maximum search frequencies, in MHz.
|
||||
# Australia: Use 400.05 - 403 MHz
|
||||
# Europe: Use 400.05 - 406 MHz
|
||||
# US: Somewhere around 1680 MHz... (Need more information!)
|
||||
# US:
|
||||
# Some areas have transitioned to the 400.05 - 406 MHz band.
|
||||
# Some areas are using ~1680 MHZ sondes, which use channels of 1676, 1678, 1680 and 1682 MHz (Thanks Tory!)
|
||||
# In these areas I suggest using the whitelist feature below instead of using the peak-detect search.
|
||||
# You may also need to apply a small offset to the frequency for decoding reliability (i.e. 1676.025 MHz) as
|
||||
# the sondes are often off-frequency. For now, check in something like GQRX to get an idea of what offset is required.
|
||||
|
||||
min_freq = 400.05
|
||||
max_freq = 403.0
|
||||
|
||||
# Have the decoder timeout after X seconds of no valid data.
|
||||
rx_timeout = 180
|
||||
|
||||
|
@ -369,8 +376,8 @@ sdr_power_path = rtl_power
|
|||
# DEMODULATOR / DECODER TWEAKS #
|
||||
################################
|
||||
|
||||
# RS41 Drift Tweak (Regular demod chain only)
|
||||
# This tweak adds a high-pass filter into the RS41 demod chain. This extends the frequency offset range
|
||||
# RS41 / LMS6 Drift Tweak (Regular demod chain only)
|
||||
# This tweak adds a high-pass filter into the RS41 and LMS6 demod chains. This extends the frequency offset range
|
||||
# the demod chain can handle, with some performance penalty. This is only required when using RTLSDRs
|
||||
# with worse than 2-3 PPM drift, and doesn't need to be enabled for TCXO SDRs.
|
||||
drift_tweak = False
|
||||
|
@ -386,6 +393,8 @@ rs41_experimental = False
|
|||
rs92_experimental = False
|
||||
dfm_experimental = False
|
||||
m10_experimental = False
|
||||
# 400 MHz LMS6 sondes decode best with the fsk_demod decode chain, so we use this by default.
|
||||
lms6-400_experimental = True
|
||||
# Note: As iMet sondes use AFSK, using fsk_demod does not give any advantage, so there is no experimental decoder for them.
|
||||
|
||||
# Dump FSK Demod statistics to stats_<device_ID>.txt while the modem is running.
|
||||
|
@ -398,7 +407,7 @@ enable_stats = False
|
|||
# Optimize 1680 MHz Scanning for RS92-NGP Sondes
|
||||
# This flag sets the use of a narrower FM receiver bandwidth when scanning for 1680 MHz RS92-NGP sondes,
|
||||
# which should improve detection considerably.
|
||||
# Set this to True if you are know that only RS92-NGPs are flying in your area.
|
||||
# Set this to True if you are sure that only RS92-NGPs are flying in your area.
|
||||
ngp_tweak = False
|
||||
|
||||
|
||||
|
|
|
@ -127,7 +127,6 @@ processing_type = {
|
|||
},
|
||||
# # RS92 Decoding
|
||||
'rs92_fsk_demod': {
|
||||
# Not currently working - need to resolve segfault in dfk_demod when using 96 kHz Fs ans 2400 Rb
|
||||
# Shift up to ~24 khz, and then pass into fsk_demod.
|
||||
'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|",
|
||||
|
||||
|
@ -138,7 +137,6 @@ processing_type = {
|
|||
'files' : "./generated/rs92*"
|
||||
},
|
||||
'm10_fsk_demod': {
|
||||
# Not currently working due to weird baud rate (9614). Doesnt work even with fractional resampling (slow down signal to make it appear to be 9600 baud).
|
||||
# Shift up to ~24 khz, and then pass into fsk_demod.
|
||||
'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ",
|
||||
'decode': "../m10 -b -b2 2>/dev/null",
|
||||
|
@ -157,7 +155,35 @@ processing_type = {
|
|||
"post_process" : " | grep frame | wc -l", # ECC
|
||||
#"post_process" : "| grep -o '\[OK\]' | wc -l", # No ECC
|
||||
'files' : "./generated/dfm*.bin"
|
||||
}
|
||||
},
|
||||
|
||||
# LMS6-400 Decoding
|
||||
'lms6-400_fsk_demod': {
|
||||
# Shift up to ~24 khz, and then pass into fsk_demod.
|
||||
'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|",
|
||||
|
||||
# Decode using rs41ecc
|
||||
'decode': "../lms6mod --vit -v 2>/dev/null",
|
||||
# Count the number of telemetry lines.
|
||||
"post_process" : "| wc -l",
|
||||
'files' : "./generated/lms6-400*"
|
||||
},
|
||||
|
||||
'lms6-1680_fsk_demod': {
|
||||
# This is a weird one.
|
||||
# The baud rate is ~9616 Baud, but the deviation is *huge* (~170 kHz occupied bandwidth).
|
||||
# The recording bandwidth needs to be correspondingly huge, with ~480 kHz sample rate required to capture the signal.
|
||||
# We need to resample up to a multiple of 9616 Hz to be able to get fsk_demod to decode.
|
||||
# fsk_demod does not decode these types reliably at the moment.
|
||||
'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 1.00166666 | ../fsk_demod --cs16 -b 5000 -u 230000 --stats=100 2 480800 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null|",
|
||||
|
||||
# Decode using rs41ecc
|
||||
'decode': "../mk2a_lms1680 -i --json 2>/dev/null",
|
||||
# Count the number of telemetry lines.
|
||||
"post_process" : " | grep frame | wc -l",
|
||||
# No low-SNR samples for this sonde available yet.
|
||||
'files' : "./generated/lms6-1680*"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -361,6 +387,67 @@ processing_type['rs41_rtlfm'] = {
|
|||
# }
|
||||
|
||||
|
||||
# LMS6 - 400 MHz version
|
||||
_fm_rate = 22000
|
||||
# Calculate the necessary conversions
|
||||
_rtlfm_oversampling = 8.0 # Viproz's hacked rtl_fm oversamples by 8x.
|
||||
_shift = -2.0*_fm_rate/_sample_fs # rtl_fm tunes 'up' by rate*2, so we need to shift the signal down by this amount.
|
||||
|
||||
_resample = (_fm_rate*_rtlfm_oversampling)/_sample_fs
|
||||
|
||||
if _resample != 1.0:
|
||||
# We will need to resample.
|
||||
_resample_command = "csdr convert_f_s16 | ./tsrc - - %.4f | csdr convert_s16_f |" % _resample
|
||||
_shift = (-2.0*_fm_rate)/(_sample_fs*_resample)
|
||||
else:
|
||||
_resample_command = ""
|
||||
|
||||
_demod_command = "| %s csdr shift_addition_cc %.5f 2>/dev/null | csdr convert_f_u8 |" % (_resample_command, _shift)
|
||||
_demod_command += " ./rtl_fm_stdin -M fm -f 401000000 -F9 -s %d 2>/dev/null|" % (int(_fm_rate))
|
||||
_demod_command += " sox -t raw -r %d -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - highpass 20 2>/dev/null |" % int(_fm_rate)
|
||||
|
||||
|
||||
processing_type['lms6-400_rtlfm'] = {
|
||||
'demod': _demod_command,
|
||||
# Decode using rs92ecc
|
||||
'decode': "../lms6mod 2>/dev/null",
|
||||
#'decode': "../rs92ecc -vx -v --crc --ecc -r --vel 2>/dev/null", # For measuring No-ECC performance
|
||||
# Count the number of telemetry lines.
|
||||
"post_process" : " | wc -l",
|
||||
#"post_process" : " | grep \"errors: 0\" | wc -l",
|
||||
'files' : "./generated/lms6-400*.bin"
|
||||
}
|
||||
|
||||
|
||||
# # LMS6 - 1680
|
||||
_fm_rate = 200000
|
||||
_sample_fs = 480000
|
||||
# Calculate the necessary conversions
|
||||
_rtlfm_oversampling = 8.0 # Viproz's hacked rtl_fm oversamples by 8x.
|
||||
_shift = -2.0*_fm_rate/_sample_fs # rtl_fm tunes 'up' by rate*2, so we need to shift the signal down by this amount.
|
||||
|
||||
_resample = (_fm_rate*_rtlfm_oversampling)/_sample_fs
|
||||
|
||||
if _resample != 1.0:
|
||||
# We will need to resample.
|
||||
_resample_command = "csdr convert_f_s16 | ./tsrc - - %.4f | csdr convert_s16_f |" % _resample
|
||||
_shift = (-2.0*_fm_rate)/(_sample_fs*_resample)
|
||||
else:
|
||||
_resample_command = ""
|
||||
|
||||
_demod_command = "| %s csdr shift_addition_cc %.5f 2>/dev/null | csdr convert_f_u8 |" % (_resample_command, _shift)
|
||||
_demod_command += " ./rtl_fm_stdin -M fm -f 401000000 -F9 -s %d 2>/dev/null|" % (int(_fm_rate))
|
||||
_demod_command += " sox -t raw -r %d -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - highpass 20 2>/dev/null |" % int(_fm_rate)
|
||||
|
||||
processing_type['lms6-1680_rtlfm'] = {
|
||||
'demod': _demod_command,
|
||||
'decode': "../mk2a_lms1680 --json -i 2>/dev/null",
|
||||
# Count the number of telemetry lines.
|
||||
"post_process" : "| grep frame | wc -l",
|
||||
'files' : "./generated/lms6-1680*.bin"
|
||||
}
|
||||
|
||||
|
||||
# # RS_Detect
|
||||
# _fm_rate = 22000
|
||||
# #_fm_rate = 15000
|
||||
|
@ -389,33 +476,34 @@ processing_type['rs41_rtlfm'] = {
|
|||
# 'files' : "./generated/*.bin"
|
||||
# }
|
||||
|
||||
# # DFT_Detect
|
||||
# _fm_rate = 22000
|
||||
# #_fm_rate = 15000
|
||||
# # Calculate the necessary conversions
|
||||
# _rtlfm_oversampling = 8.0 # Viproz's hacked rtl_fm oversamples by 8x.
|
||||
# _shift = -2.0*_fm_rate/_sample_fs # rtl_fm tunes 'up' by rate*2, so we need to shift the signal down by this amount.
|
||||
# DFT_Detect
|
||||
_fm_rate = 22000
|
||||
#_fm_rate = 15000
|
||||
_sample_fs = 96000
|
||||
# Calculate the necessary conversions
|
||||
_rtlfm_oversampling = 8.0 # Viproz's hacked rtl_fm oversamples by 8x.
|
||||
_shift = -2.0*_fm_rate/_sample_fs # rtl_fm tunes 'up' by rate*2, so we need to shift the signal down by this amount.
|
||||
|
||||
# _resample = (_fm_rate*_rtlfm_oversampling)/_sample_fs
|
||||
_resample = (_fm_rate*_rtlfm_oversampling)/_sample_fs
|
||||
|
||||
# if _resample != 1.0:
|
||||
# # We will need to resample.
|
||||
# _resample_command = "csdr convert_f_s16 | ./tsrc - - %.4f | csdr convert_s16_f |" % _resample
|
||||
# _shift = (-2.0*_fm_rate)/(_sample_fs*_resample)
|
||||
# else:
|
||||
# _resample_command = ""
|
||||
if _resample != 1.0:
|
||||
# We will need to resample.
|
||||
_resample_command = "csdr convert_f_s16 | ./tsrc - - %.4f | csdr convert_s16_f |" % _resample
|
||||
_shift = (-2.0*_fm_rate)/(_sample_fs*_resample)
|
||||
else:
|
||||
_resample_command = ""
|
||||
|
||||
# _demod_command = "| %s csdr shift_addition_cc %.5f 2>/dev/null | csdr convert_f_u8 |" % (_resample_command, _shift)
|
||||
# _demod_command += " ./rtl_fm_stdin -M fm -f 401000000 -F9 -s %d 2>/dev/null|" % (int(_fm_rate))
|
||||
# _demod_command += " sox -t raw -r %d -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - highpass 20 2>/dev/null |" % int(_fm_rate)
|
||||
_demod_command = "| %s csdr shift_addition_cc %.5f 2>/dev/null | csdr convert_f_u8 |" % (_resample_command, _shift)
|
||||
_demod_command += " ./rtl_fm_stdin -M fm -f 401000000 -F9 -s %d 2>/dev/null|" % (int(_fm_rate))
|
||||
_demod_command += " sox -t raw -r %d -e s -b 16 -c 1 - -r 48000 -b 8 -t wav - highpass 20 2>/dev/null |" % int(_fm_rate)
|
||||
|
||||
# processing_type['dft_detect_rtlfm'] = {
|
||||
# 'demod': _demod_command,
|
||||
# 'decode': "../dft_detect 2>/dev/null",
|
||||
# # Grep out the line containing the detected sonde type.
|
||||
# "post_process" : " | grep \:",
|
||||
# 'files' : "./generated/*.bin"
|
||||
# }
|
||||
processing_type['dft_detect_rtlfm'] = {
|
||||
'demod': _demod_command,
|
||||
'decode': "../dft_detect 2>/dev/null",
|
||||
# Grep out the line containing the detected sonde type.
|
||||
"post_process" : " | grep \:",
|
||||
'files' : "./generated/*.bin"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
# Copyright (C) 2019 Mark Jessop <vk5qi@rfhead.net>
|
||||
# Released under GNU GPL v3 or later
|
||||
#
|
||||
# Compares the auto_rx calcuated temperature & humidity values with
|
||||
# truth data produced by a Vaisala ground station.
|
||||
# Compares the auto_rx calcuated temperature & humidity values with
|
||||
# truth data produced by a Vaisala ground station.
|
||||
#
|
||||
# The 'truth' data must be in vaisalas 'metdata' tab-delimited text format.
|
||||
# The 'truth' data must be in vaisalas 'metdata' tab-delimited text format.
|
||||
#
|
||||
# Run with:
|
||||
# python3 compare_vaisala.py originalmetdata_20190521_0418_R0230900.txt 20190521-042102_R0230900_RS41_402200_sonde.log
|
||||
# Run with:
|
||||
# python3 compare_vaisala.py originalmetdata_20190521_0418_R0230900.txt 20190521-042102_R0230900_RS41_402200_sonde.log
|
||||
#
|
||||
# TODO:
|
||||
# [ ] Calculate temp/rh error vs altitude
|
||||
|
@ -37,42 +37,42 @@ from metpy.plots import SkewT
|
|||
from metpy.units import units
|
||||
|
||||
def read_vaisala_metdata(filename):
|
||||
""" Read in a Vaisala 'metdata' tab-delimtied text file, as produced by the MW32 ground station """
|
||||
""" Read in a Vaisala 'metdata' tab-delimtied text file, as produced by the MW32 ground station """
|
||||
|
||||
_f = open(filename, 'r')
|
||||
_f = open(filename, 'r')
|
||||
|
||||
# Skip past the header.
|
||||
for i in range(22):
|
||||
_f.readline()
|
||||
# Skip past the header.
|
||||
for i in range(22):
|
||||
_f.readline()
|
||||
|
||||
|
||||
output = []
|
||||
# Read in lines of data.
|
||||
# n Elapsed time HeightMSL Pc Pm Temp RH VirT Lat Lon HeightE Speed Dir
|
||||
for line in _f:
|
||||
try:
|
||||
_fields = line.split('\t')
|
||||
_count = int(_fields[0])
|
||||
_flight_time = int(_fields[1])
|
||||
_height_msl = int(_fields[2])
|
||||
_pressure_calc = float(_fields[3])
|
||||
_pressure_meas = float(_fields[4])
|
||||
_temp = float(_fields[5])
|
||||
_relhum = int(_fields[6])
|
||||
_virt = float(_fields[7])
|
||||
_lat = float(_fields[8])
|
||||
_lon = float(_fields[9])
|
||||
_alt = float(_fields[10])
|
||||
_vel_h = float(_fields[11])
|
||||
_heading = float(_fields[12])
|
||||
output = []
|
||||
# Read in lines of data.
|
||||
# n Elapsed time HeightMSL Pc Pm Temp RH VirT Lat Lon HeightE Speed Dir
|
||||
for line in _f:
|
||||
try:
|
||||
_fields = line.split('\t')
|
||||
_count = int(_fields[0])
|
||||
_flight_time = int(_fields[1])
|
||||
_height_msl = int(_fields[2])
|
||||
_pressure_calc = float(_fields[3])
|
||||
_pressure_meas = float(_fields[4])
|
||||
_temp = float(_fields[5])
|
||||
_relhum = int(_fields[6])
|
||||
_virt = float(_fields[7])
|
||||
_lat = float(_fields[8])
|
||||
_lon = float(_fields[9])
|
||||
_alt = float(_fields[10])
|
||||
_vel_h = float(_fields[11])
|
||||
_heading = float(_fields[12])
|
||||
|
||||
output.append([_count, _flight_time, _height_msl, _pressure_calc, _pressure_meas, _temp, _relhum, _virt, _lat, _lon, _alt, _vel_h, _heading])
|
||||
output.append([_count, _flight_time, _height_msl, _pressure_calc, _pressure_meas, _temp, _relhum, _virt, _lat, _lon, _alt, _vel_h, _heading])
|
||||
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
return np.array(output)
|
||||
return np.array(output)
|
||||
|
||||
|
||||
# Earthmaths code by Daniel Richman (thanks!)
|
||||
|
@ -250,43 +250,87 @@ def read_log_file(filename, decimation=10, min_altitude=100):
|
|||
return (np.array(_output), _burst, _startalt, times[-1])
|
||||
|
||||
|
||||
def comparison_plots(vaisala_data, autorx_data):
|
||||
_vaisala_alt = vaisala_data[:,2]
|
||||
_vaisala_temp = vaisala_data[:,5]
|
||||
_vaisala_rh = vaisala_data[:,6]
|
||||
def comparison_plots(vaisala_data, autorx_data, serial):
|
||||
_vaisala_alt = vaisala_data[:,2]
|
||||
_vaisala_temp = vaisala_data[:,5]
|
||||
_vaisala_rh = vaisala_data[:,6]
|
||||
|
||||
_autorx_alt = autorx_data[:,0]
|
||||
_autorx_temp = autorx_data[:,3]
|
||||
_autorx_rh = autorx_data[:,5]
|
||||
_autorx_alt = autorx_data[:,0]
|
||||
_autorx_temp = autorx_data[:,3]
|
||||
_autorx_rh = autorx_data[:,5]
|
||||
|
||||
# Interpolation
|
||||
_interp_min_alt = max(np.min(_vaisala_alt), np.min(_autorx_alt))
|
||||
_interp_max_alt = min(np.max(_vaisala_alt), np.max(_autorx_alt))
|
||||
# Define the altitude range we interpolate over.
|
||||
_interp_x = np.linspace(_interp_min_alt, _interp_max_alt, 2000)
|
||||
|
||||
# Produce interpolated temperature and humidity data.
|
||||
_vaisala_interp_temp = np.interp(_interp_x, _vaisala_alt, _vaisala_temp)
|
||||
_vaisala_interp_rh = np.interp(_interp_x, _vaisala_alt, _vaisala_rh)
|
||||
_autorx_interp_temp = np.interp(_interp_x, _autorx_alt, _autorx_temp)
|
||||
_autorx_interp_rh = np.interp(_interp_x, _autorx_alt, _autorx_rh)
|
||||
|
||||
# Calculate the error in auto_rx's calculations.
|
||||
_autorx_temp_error = _autorx_interp_temp - _vaisala_interp_temp
|
||||
_autorx_rh_error = _autorx_interp_rh - _vaisala_interp_rh
|
||||
|
||||
|
||||
plt.figure()
|
||||
plt.plot(_vaisala_alt, _vaisala_temp, label="Vaisala")
|
||||
plt.plot(_autorx_alt, _autorx_temp, label="auto_rx")
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Temperature (degC)")
|
||||
plt.title("Temperature")
|
||||
plt.legend()
|
||||
plt.figure()
|
||||
plt.plot(_vaisala_alt, _vaisala_temp, label="Vaisala")
|
||||
plt.plot(_autorx_alt, _autorx_temp, label="auto_rx")
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Temperature (degC)")
|
||||
plt.title("Temperature - %s" % serial)
|
||||
plt.legend()
|
||||
plt.grid()
|
||||
|
||||
plt.figure()
|
||||
plt.plot(_vaisala_alt, _vaisala_rh, label="Vaisala")
|
||||
plt.plot(_autorx_alt, _autorx_rh, label="auto_rx")
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Relative Humidity (%)")
|
||||
plt.title("Relative Humidity")
|
||||
plt.legend()
|
||||
plt.show()
|
||||
plt.figure()
|
||||
plt.plot(_vaisala_alt, _vaisala_rh, label="Vaisala")
|
||||
plt.plot(_autorx_alt, _autorx_rh, label="auto_rx")
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Relative Humidity (%)")
|
||||
plt.title("Relative Humidity - %s" % serial)
|
||||
plt.legend()
|
||||
plt.grid()
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
plt.figure()
|
||||
plt.plot(_interp_x, _autorx_temp_error)
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Error (degC)")
|
||||
plt.title("auto_rx RS41 Temperature Calculation Error - %s" % serial)
|
||||
plt.grid()
|
||||
|
||||
|
||||
plt.figure()
|
||||
plt.plot(_interp_x, _autorx_rh_error)
|
||||
plt.xlabel("Altitude (m)")
|
||||
plt.ylabel("Error (% RH)")
|
||||
plt.title("auto_rx RS41 Humidity Calculation Error - %s" % serial)
|
||||
plt.grid()
|
||||
|
||||
|
||||
plt.figure()
|
||||
plt.plot(_vaisala_interp_temp, _autorx_rh_error)
|
||||
plt.xlabel("Temperature (degC)")
|
||||
plt.ylabel("Error (% RH)")
|
||||
plt.title("auto_rx RS41 Humidity Calculation Error - %s" % serial)
|
||||
plt.grid()
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_vaisala_filename = sys.argv[1]
|
||||
_autorx_filename = sys.argv[2]
|
||||
_vaisala_filename = sys.argv[1]
|
||||
_autorx_filename = sys.argv[2]
|
||||
|
||||
vaisala_data = read_vaisala_metdata(_vaisala_filename)
|
||||
_serial = _autorx_filename.split('_')[1]
|
||||
|
||||
(autorx_data, burst, startalt, lasttime) = read_log_file(_autorx_filename, decimation=1)
|
||||
vaisala_data = read_vaisala_metdata(_vaisala_filename)
|
||||
|
||||
comparison_plots(vaisala_data, autorx_data)
|
||||
(autorx_data, burst, startalt, lasttime) = read_log_file(_autorx_filename, decimation=1)
|
||||
|
||||
comparison_plots(vaisala_data, autorx_data, _serial)
|
||||
|
|
|
@ -628,7 +628,7 @@ static void print_frame(gpx_t *gpx, int crc_err, int len) {
|
|||
// Print JSON output required by auto_rx.
|
||||
if (crc_err==0) { // CRC-OK
|
||||
// UTC oder GPS?
|
||||
printf("{ \"frame\": %d, \"id\": \"%d\", \"time\": \"%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f }\n",
|
||||
printf("{ \"frame\": %d, \"id\": \"LMS6-%d\", \"datetime\": \"%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f }\n",
|
||||
gpx->frnr, gpx->sn, gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vV );
|
||||
printf("\n");
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue