kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
Initial works of autorx v2 main loop.
rodzic
feba7351f1
commit
ccba90388b
|
|
@ -5,4 +5,4 @@
|
|||
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
|
||||
# Released under GNU GPL v3 or later
|
||||
#
|
||||
__version__ = "20180507"
|
||||
__version__ = "20180525"
|
||||
|
|
@ -10,142 +10,203 @@ import ConfigParser
|
|||
import logging
|
||||
import traceback
|
||||
import json
|
||||
from .utils import rtlsdr_test
|
||||
|
||||
def read_auto_rx_config(filename):
|
||||
""" Read an Auto-RX Station Configuration File
|
||||
""" Read an Auto-RX v2 Station Configuration File.
|
||||
|
||||
This function will attempt to parse a configuration file.
|
||||
It will also confirm the accessibility of any SDRs specified in the config file.
|
||||
|
||||
Args:
|
||||
filename (str): Filename of the configuration file to read.
|
||||
|
||||
Returns:
|
||||
auto_rx_config (dict): The configuration dictionary.
|
||||
sdr_config (dict): A dictionary with SDR parameters.
|
||||
"""
|
||||
# Configuration Defaults:
|
||||
auto_rx_config = {
|
||||
# Log Settings
|
||||
'per_sonde_log' : True,
|
||||
'sdr_fm_path': 'rtl_fm',
|
||||
'sdr_power_path': 'rtl_power',
|
||||
'sdr_ppm' : 0,
|
||||
'sdr_gain' : -1,
|
||||
'sdr_bias' : False,
|
||||
'search_attempts': 5,
|
||||
'search_delay' : 10,
|
||||
# SDR Settings
|
||||
'sdr_fm': 'rtl_fm',
|
||||
'sdr_power': 'rtl_power',
|
||||
'sdr_quantity': 1,
|
||||
# Search Parameters
|
||||
'min_freq' : 400.4,
|
||||
'max_freq' : 404.0,
|
||||
'search_step' : 800,
|
||||
'min_snr' : 10,
|
||||
'min_distance' : 1000,
|
||||
'dwell_time' : 10,
|
||||
'quantization' : 10000,
|
||||
'rx_timeout' : 120,
|
||||
'whitelist' : [],
|
||||
'blacklist' : [],
|
||||
'greylist' : [],
|
||||
# Location Settings
|
||||
'station_lat' : 0.0,
|
||||
'station_lon' : 0.0,
|
||||
'station_alt' : 0.0,
|
||||
'upload_rate' : 30,
|
||||
'synchronous_upload' : False,
|
||||
'enable_aprs' : False,
|
||||
'enable_habitat': False,
|
||||
# Position Filter Settings
|
||||
'max_altitude' : 50000,
|
||||
'max_radius_km' : 1000,
|
||||
# Habitat Settings
|
||||
'habitat_enabled': False,
|
||||
'habitat_upload_rate': 30,
|
||||
'habitat_uploader_callsign': 'SONDE_AUTO_RX',
|
||||
'habitat_upload_listener_position': False,
|
||||
'habitat_payload_callsign': '<id>',
|
||||
'habitat_payload_description': 'Meteorological Radiosonde',
|
||||
# APRS Settings
|
||||
'aprs_enabled' : False,
|
||||
'aprs_upload_rate': 30,
|
||||
'aprs_user' : 'N0CALL',
|
||||
'aprs_pass' : '00000',
|
||||
'aprs_server' : 'rotate.aprs2.net',
|
||||
'aprs_object_id': '<id>',
|
||||
'aprs_custom_comment': 'Radiosonde Auto-RX <freq>',
|
||||
'payload_callsign': '<id>',
|
||||
'payload_description': 'Meteorological Radiosonde',
|
||||
'uploader_callsign': 'SONDE_AUTO_RX',
|
||||
'upload_listener_position': False,
|
||||
# Advanced Parameters
|
||||
'search_step' : 800,
|
||||
'snr_threshold' : 10,
|
||||
'min_distance' : 1000,
|
||||
'dwell_time' : 10,
|
||||
'max_peaks' : 10,
|
||||
'quantization' : 10000,
|
||||
'synchronous_upload' : False,
|
||||
'scan_dwell_time' : 20,
|
||||
'detect_dwell_time' : 5,
|
||||
'payload_id_valid' : 5,
|
||||
# Rotator Settings
|
||||
'enable_rotator': False,
|
||||
'rotator_hostname': '127.0.0.1',
|
||||
'rotator_port' : 4533,
|
||||
'rotator_homing_enabled': False,
|
||||
'rotator_home_azimuth': 0,
|
||||
'rotator_home_elevation': 0,
|
||||
# OziExplorer Settings
|
||||
'ozi_enabled' : False,
|
||||
'ozi_update_rate': 5,
|
||||
'ozi_hostname' : '127.0.0.1',
|
||||
'ozi_port' : 55681,
|
||||
'mqtt_enabled' : False,
|
||||
'mqtt_hostname' : '127.0.0.1',
|
||||
'mqtt_port' : 1883,
|
||||
'payload_summary_enabled': False,
|
||||
'payload_summary_port' : 55672,
|
||||
'whitelist' : [],
|
||||
'blacklist' : [],
|
||||
'greylist' : [],
|
||||
'max_altitude' : 50000,
|
||||
'max_radius_km' : 1000,
|
||||
'payload_id_valid' : 5 # TODO: Add this to config file in next bulk update.
|
||||
'payload_summary_port' : 55672
|
||||
}
|
||||
|
||||
sdr_settings = {}#'0':{'ppm':0, 'gain':-1, 'bias': False}}
|
||||
|
||||
try:
|
||||
config = ConfigParser.RawConfigParser(auto_rx_config)
|
||||
config.read(filename)
|
||||
|
||||
# Log Settings
|
||||
auto_rx_config['per_sonde_log'] = config.getboolean('logging', 'per_sonde_log')
|
||||
auto_rx_config['sdr_fm_path'] = config.get('sdr','sdr_fm_path')
|
||||
auto_rx_config['sdr_power_path'] = config.get('sdr','sdr_power_path')
|
||||
auto_rx_config['sdr_ppm'] = int(config.getfloat('sdr', 'sdr_ppm'))
|
||||
auto_rx_config['sdr_gain'] = config.getfloat('sdr', 'sdr_gain')
|
||||
auto_rx_config['sdr_bias'] = config.getboolean('sdr', 'sdr_bias')
|
||||
auto_rx_config['search_attempts'] = config.getint('search_params', 'search_attempts')
|
||||
auto_rx_config['search_delay'] = config.getint('search_params', 'search_delay')
|
||||
|
||||
# SDR Settings
|
||||
auto_rx_config['sdr_fm'] = config.get('sdr', 'sdr_fm_path')
|
||||
auto_rx_config['sdr_power'] = config.get('sdr', 'sdr_power_path')
|
||||
auto_rx_config['sdr_quantity'] = config.getint('sdr', 'sdr_quantity')
|
||||
|
||||
# Search Parameters
|
||||
auto_rx_config['min_freq'] = config.getfloat('search_params', 'min_freq')
|
||||
auto_rx_config['max_freq'] = config.getfloat('search_params', 'max_freq')
|
||||
auto_rx_config['search_step'] = config.getfloat('search_params', 'search_step')
|
||||
auto_rx_config['min_snr'] = config.getfloat('search_params', 'min_snr')
|
||||
auto_rx_config['min_distance'] = config.getfloat('search_params', 'min_distance')
|
||||
auto_rx_config['dwell_time'] = config.getint('search_params', 'dwell_time')
|
||||
auto_rx_config['quantization'] = config.getint('search_params', 'quantization')
|
||||
auto_rx_config['rx_timeout'] = config.getint('search_params', 'rx_timeout')
|
||||
auto_rx_config['whitelist'] = json.loads(config.get('search_params', 'whitelist'))
|
||||
auto_rx_config['blacklist'] = json.loads(config.get('search_params', 'blacklist'))
|
||||
auto_rx_config['greylist'] = json.loads(config.get('search_params', 'greylist'))
|
||||
|
||||
# Location Settings
|
||||
auto_rx_config['station_lat'] = config.getfloat('location', 'station_lat')
|
||||
auto_rx_config['station_lon'] = config.getfloat('location', 'station_lon')
|
||||
auto_rx_config['station_alt'] = config.getfloat('location', 'station_alt')
|
||||
auto_rx_config['upload_rate'] = config.getint('upload', 'upload_rate')
|
||||
auto_rx_config['synchronous_upload'] = config.getboolean('upload','synchronous_upload')
|
||||
auto_rx_config['enable_aprs'] = config.getboolean('upload', 'enable_aprs')
|
||||
auto_rx_config['enable_habitat'] = config.getboolean('upload', 'enable_habitat')
|
||||
|
||||
# Position Filtering
|
||||
auto_rx_config['max_altitude'] = config.getint('filtering', 'max_altitude')
|
||||
auto_rx_config['max_radius_km'] = config.getint('filtering', 'max_radius_km')
|
||||
|
||||
# Habitat Settings
|
||||
auto_rx_config['habitat_enabled'] = config.getboolean('habitat', 'habitat_enabled')
|
||||
auto_rx_config['habitat_upload_rate'] = config.getint('habitat', 'upload_rate')
|
||||
auto_rx_config['habitat_payload_callsign'] = config.get('habitat', 'payload_callsign')
|
||||
auto_rx_config['habitat_payload_description'] = config.get('habitat', 'payload_description')
|
||||
auto_rx_config['habitat_uploader_callsign'] = config.get('habitat', 'uploader_callsign')
|
||||
auto_rx_config['habitat_upload_listener_position'] = config.getboolean('habitat','upload_listener_position')
|
||||
|
||||
# APRS Settings
|
||||
auto_rx_config['aprs_enabled'] = config.getboolean('aprs', 'aprs_enabled')
|
||||
auto_rx_config['aprs_upload_rate'] = config.getint('aprs', 'upload_rate')
|
||||
auto_rx_config['aprs_user'] = config.get('aprs', 'aprs_user')
|
||||
auto_rx_config['aprs_pass'] = config.get('aprs', 'aprs_pass')
|
||||
auto_rx_config['aprs_server'] = config.get('aprs', 'aprs_server')
|
||||
auto_rx_config['aprs_object_id'] = config.get('aprs', 'aprs_object_id')
|
||||
auto_rx_config['aprs_custom_comment'] = config.get('aprs', 'aprs_custom_comment')
|
||||
auto_rx_config['payload_callsign'] = config.get('habitat', 'payload_callsign')
|
||||
auto_rx_config['payload_description'] = config.get('habitat', 'payload_description')
|
||||
auto_rx_config['uploader_callsign'] = config.get('habitat', 'uploader_callsign')
|
||||
auto_rx_config['upload_listener_position'] = config.getboolean('habitat','upload_listener_position')
|
||||
auto_rx_config['enable_rotator'] = config.getboolean('rotator','enable_rotator')
|
||||
auto_rx_config['rotator_hostname'] = config.get('rotator', 'rotator_hostname')
|
||||
auto_rx_config['rotator_port'] = config.getint('rotator', 'rotator_port')
|
||||
auto_rx_config['rotator_homing_enabled'] = config.getboolean('rotator', 'rotator_homing_enabled')
|
||||
auto_rx_config['rotator_home_azimuth'] = config.getfloat('rotator', 'rotator_home_azimuth')
|
||||
auto_rx_config['rotator_home_elevation'] = config.getfloat('rotator', 'rotator_home_elevation')
|
||||
|
||||
# OziPlotter Settings
|
||||
auto_rx_config['ozi_enabled'] = config.getboolean('oziplotter', 'ozi_enabled')
|
||||
auto_rx_config['ozi_update_rate'] = config.getint('oziplotter', 'ozi_update_rate')
|
||||
auto_rx_config['ozi_port'] = config.getint('oziplotter', 'ozi_port')
|
||||
auto_rx_config['payload_summary_enabled'] = config.getboolean('oziplotter', 'payload_summary_enabled')
|
||||
auto_rx_config['payload_summary_port'] = config.getint('oziplotter', 'payload_summary_port')
|
||||
|
||||
# Read in lists using a JSON parser.
|
||||
auto_rx_config['whitelist'] = json.loads(config.get('search_params', 'whitelist'))
|
||||
auto_rx_config['blacklist'] = json.loads(config.get('search_params', 'blacklist'))
|
||||
auto_rx_config['greylist'] = json.loads(config.get('search_params', 'greylist'))
|
||||
# Advanced Settings
|
||||
auto_rx_config['search_step'] = config.getfloat('advanced', 'search_step')
|
||||
auto_rx_config['snr_threshold'] = config.getfloat('advanced', 'snr_threshold')
|
||||
auto_rx_config['min_distance'] = config.getfloat('advanced', 'min_distance')
|
||||
auto_rx_config['dwell_time'] = config.getint('advanced', 'dwell_time')
|
||||
auto_rx_config['quantization'] = config.getint('advanced', 'quantization')
|
||||
auto_rx_config['max_peaks'] = config.getint('advanced', 'max_peaks')
|
||||
auto_rx_config['scan_dwell_time'] = config.getint('advanced', 'scan_dwell_time')
|
||||
auto_rx_config['detect_dwell_time'] = config.getint('advanced', 'detect_dwell_time')
|
||||
auto_rx_config['payload_id_valid'] = config.getint('advanced', 'payload_id_valid')
|
||||
auto_rx_config['synchronous_upload'] = config.getboolean('advanced', 'synchronous_upload')
|
||||
|
||||
# Position Filtering
|
||||
auto_rx_config['max_altitude'] = config.getint('filtering', 'max_altitude')
|
||||
auto_rx_config['max_radius_km'] = config.getint('filtering', 'max_radius_km')
|
||||
# Rotator Settings (TBC)
|
||||
auto_rx_config['rotator_enabled'] = config.getboolean('rotator','rotator_enabled')
|
||||
auto_rx_config['rotator_hostname'] = config.get('rotator', 'rotator_hostname')
|
||||
auto_rx_config['rotator_port'] = config.getint('rotator', 'rotator_port')
|
||||
auto_rx_config['rotator_homing_enabled'] = config.getboolean('rotator', 'rotator_homing_enabled')
|
||||
auto_rx_config['rotator_home_azimuth'] = config.getfloat('rotator', 'rotator_home_azimuth')
|
||||
auto_rx_config['rotator_home_elevation'] = config.getfloat('rotator', 'rotator_home_elevation')
|
||||
|
||||
# MQTT settings
|
||||
auto_rx_config['mqtt_enabled'] = config.getboolean('mqtt', 'mqtt_enabled')
|
||||
auto_rx_config['mqtt_hostname'] = config.get('mqtt', 'mqtt_hostname')
|
||||
auto_rx_config['mqtt_port'] = config.getint('mqtt', 'mqtt_port')
|
||||
# Now we attempt to read in the individual SDR parameters.
|
||||
auto_rx_config['sdr_settings'] = {}
|
||||
|
||||
for _n in range(1,auto_rx_config['sdr_quantity']+1):
|
||||
_section = "sdr_%d" % _n
|
||||
try:
|
||||
_device_idx = config.get(_section,'device_idx')
|
||||
_ppm = config.getint(_section, 'ppm')
|
||||
_gain = config.getfloat(_section, 'gain')
|
||||
_bias = config.getboolean(_section, 'bias')
|
||||
|
||||
if (auto_rx_config['sdr_quantity'] > 1) and (_device_idx == '0'):
|
||||
logging.error("Config - SDR Device ID of 0 used with a multi-SDR configuration. Go read the warning in the config file!")
|
||||
return None
|
||||
|
||||
# See if the SDR exists.
|
||||
_sdr_valid = rtlsdr_test(_device_idx)
|
||||
if _sdr_valid:
|
||||
auto_rx_config['sdr_settings'][_device_idx] = {'ppm':_ppm, 'gain':_gain, 'bias':_bias, 'in_use': False, 'task': None}
|
||||
logging.info('Config - Tested SDR #%s OK' % _device_idx)
|
||||
else:
|
||||
logging.warning("Config - SDR #%s invalid." % _device_idx)
|
||||
except Exception as e:
|
||||
logging.error("Config - Error parsing SDR %d config - %s" % (_n,str(e)))
|
||||
continue
|
||||
|
||||
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
|
||||
else:
|
||||
return auto_rx_config
|
||||
|
||||
return auto_rx_config
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error("Could not parse config file, using defaults.")
|
||||
return auto_rx_config
|
||||
logging.error("Could not parse config file.")
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
''' Quick test script to attempt to read in a config file. '''
|
||||
import sys
|
||||
print(read_auto_rx_config(sys.argv[1]))
|
||||
config = read_auto_rx_config(sys.argv[1])
|
||||
|
||||
print(config)
|
||||
|
|
@ -63,7 +63,7 @@ class SondeDecoder(object):
|
|||
|
||||
def __init__(self,
|
||||
sonde_type="None",
|
||||
sonde_freq=400000000,
|
||||
sonde_freq=400000000.0,
|
||||
rs_path = "./",
|
||||
sdr_fm = "rtl_fm",
|
||||
device_idx = 0,
|
||||
|
|
@ -97,6 +97,9 @@ class SondeDecoder(object):
|
|||
|
||||
rs92_ephemeris (str): OPTIONAL - A fixed ephemeris file to use if decoding a RS92. If not supplied, one will be downloaded.
|
||||
"""
|
||||
# Thread running flag
|
||||
self.decoder_running = True
|
||||
|
||||
# Local copy of init arguments
|
||||
self.sonde_type = sonde_type
|
||||
self.sonde_freq = sonde_freq
|
||||
|
|
@ -112,15 +115,14 @@ class SondeDecoder(object):
|
|||
self.timeout = timeout
|
||||
self.rs92_ephemeris = rs92_ephemeris
|
||||
|
||||
# Thread running flag
|
||||
self.decoder_running = False
|
||||
# This will become out decoder thread.
|
||||
# This will become our decoder thread.
|
||||
self.decoder = None
|
||||
|
||||
# Check if the sonde type is valid.
|
||||
if self.sonde_type not in self.VALID_SONDE_TYPES:
|
||||
self.log_error("Unsupported sonde type: %s" % self.sonde_type)
|
||||
raise ValueError("Unsupported sonde type: %s." % self.sonde_type)
|
||||
self.decoder_running = False
|
||||
return
|
||||
|
||||
# Test if the supplied RTLSDR is working.
|
||||
_rtlsdr_ok = rtlsdr_test(device_idx)
|
||||
|
|
@ -128,8 +130,8 @@ class SondeDecoder(object):
|
|||
# TODO: How should this error be handled?
|
||||
if not _rtlsdr_ok:
|
||||
self.log_error("RTLSDR #%s non-functional - exiting." % device_idx)
|
||||
self.decoder_running = False
|
||||
return
|
||||
#raise IOError("Could not open RTLSDR #%d" % device_idx)
|
||||
|
||||
# We can accept a few different types in the exporter argument..
|
||||
# Nothing...
|
||||
|
|
@ -159,6 +161,7 @@ class SondeDecoder(object):
|
|||
|
||||
if self.decoder_command is None:
|
||||
self.log_error("Could not generate decoder command. Not starting decoder.")
|
||||
self.decoder_running = False
|
||||
else:
|
||||
# Start up the decoder thread.
|
||||
self.decode_process = None
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import os
|
|||
import platform
|
||||
import subprocess
|
||||
import time
|
||||
import traceback
|
||||
from threading import Thread
|
||||
from types import FunctionType, MethodType
|
||||
from .utils import detect_peaks, rtlsdr_test, rtlsdr_reset
|
||||
|
|
@ -187,9 +188,9 @@ def detect_sonde(frequency, rs_path="./", dwell_time=10, sdr_fm='rtl_fm', device
|
|||
else:
|
||||
gain_param = ''
|
||||
|
||||
rx_test_command = "timeout %ds %s %s-p %d -d %d %s-M fm -F9 -s 15k -f %d 2>/dev/null |" % (dwell_time, sdr_fm, bias_option, int(ppm), str(device_idx), gain_param, frequency)
|
||||
rx_test_command = "timeout %ds %s %s-p %d -d %s %s-M fm -F9 -s 15k -f %d 2>/dev/null |" % (dwell_time, sdr_fm, bias_option, int(ppm), str(device_idx), gain_param, frequency)
|
||||
rx_test_command += "sox -t raw -r 15k -e s -b 16 -c 1 - -r 48000 -t wav - highpass 20 2>/dev/null |"
|
||||
rx_test_command += os.path.join(rs_path,"rs_detect") + " -z -t 8 2>/dev/null"
|
||||
rx_test_command += os.path.join(rs_path,"rs_detect") + " -z -t 8 2>/dev/null >/dev/null"
|
||||
|
||||
logging.info("Scanner - Attempting sonde detection on %.3f MHz" % (frequency/1e6))
|
||||
logging.debug("Scanner - Running command: %s" % rx_test_command)
|
||||
|
|
@ -254,6 +255,7 @@ class SondeScanner(object):
|
|||
|
||||
def __init__(self,
|
||||
callback = None,
|
||||
auto_start = True,
|
||||
min_freq = 400.0,
|
||||
max_freq = 403.0,
|
||||
search_step = 800.0,
|
||||
|
|
@ -279,7 +281,7 @@ class SondeScanner(object):
|
|||
|
||||
Args:
|
||||
callback (function): A function to pass results from the sonde scanner to (when a sonde is found).
|
||||
|
||||
auto_start (bool): Start up the scanner automatically.
|
||||
min_freq (float): Minimum search frequency, in MHz.
|
||||
max_freq (float): Maximum search frequency, in MHz.
|
||||
search_step (float): Search step, in *Hz*. Defaults to 800 Hz, which seems to work well.
|
||||
|
|
@ -303,6 +305,9 @@ class SondeScanner(object):
|
|||
bias (bool): If True, enable the bias tee on the SDR.
|
||||
"""
|
||||
|
||||
# Thread flag. This is set to True when a scan is running.
|
||||
self.sonde_scanner_running = True
|
||||
|
||||
# Copy parameters
|
||||
|
||||
self.min_freq = min_freq
|
||||
|
|
@ -330,9 +335,6 @@ class SondeScanner(object):
|
|||
# Error counter.
|
||||
self.error_retries = 0
|
||||
|
||||
# Thread flag. This is set to True when a scan is running.
|
||||
self.sonde_scanner_running = False
|
||||
|
||||
# This will become our scanner thread.
|
||||
self.sonde_scan_thread = None
|
||||
|
||||
|
|
@ -341,14 +343,16 @@ class SondeScanner(object):
|
|||
|
||||
# TODO: How should this error be handled?
|
||||
if not _rtlsdr_ok:
|
||||
self.log_error("RTLSDR #%d non-functional - exiting." % device_idx)
|
||||
raise IOError("Could not open RTLSDR #%d" % device_idx)
|
||||
|
||||
self.log_error("RTLSDR #%s non-functional - exiting." % device_idx)
|
||||
self.sonde_scanner_running = False
|
||||
return
|
||||
|
||||
if auto_start:
|
||||
self.start()
|
||||
|
||||
def start(self):
|
||||
# Start the scan loop (if not already running)
|
||||
if self.sonde_scanner_running == False:
|
||||
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()
|
||||
|
|
@ -379,6 +383,7 @@ class SondeScanner(object):
|
|||
time.sleep(10)
|
||||
continue
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self.log_error("Caught other error: %s" % str(e))
|
||||
time.sleep(10)
|
||||
else:
|
||||
|
|
@ -426,7 +431,7 @@ class SondeScanner(object):
|
|||
run_rtl_power(self.min_freq*1e6,
|
||||
self.max_freq*1e6,
|
||||
self.search_step,
|
||||
filename="log_power_%d.csv" % self.device_idx,
|
||||
filename="log_power_%s.csv" % self.device_idx,
|
||||
dwell=self.scan_dwell_time,
|
||||
sdr_power=self.sdr_power,
|
||||
device_idx=self.device_idx,
|
||||
|
|
@ -440,7 +445,7 @@ class SondeScanner(object):
|
|||
|
||||
# Read in result.
|
||||
# This step will throw an IOError if the file does not exist.
|
||||
(freq, power, step) = read_rtl_power("log_power_%d.csv" % self.device_idx)
|
||||
(freq, power, step) = read_rtl_power("log_power_%s.csv" % self.device_idx)
|
||||
# Sanity check results.
|
||||
if step == 0 or len(freq)==0 or len(power)==0:
|
||||
# Otherwise, if a file has been written but contains no data, it can indicate
|
||||
|
|
@ -570,7 +575,7 @@ class SondeScanner(object):
|
|||
Args:
|
||||
line (str): Message to be logged.
|
||||
"""
|
||||
logging.debug("Scanner - %s" % line)
|
||||
logging.debug("Scanner #%s - %s" % (self.device_idx,line))
|
||||
|
||||
|
||||
def log_info(self, line):
|
||||
|
|
@ -578,7 +583,7 @@ class SondeScanner(object):
|
|||
Args:
|
||||
line (str): Message to be logged.
|
||||
"""
|
||||
logging.info("Scanner - %s" % line)
|
||||
logging.info("Scanner #%s - %s" % (self.device_idx,line))
|
||||
|
||||
|
||||
def log_error(self, line):
|
||||
|
|
@ -586,14 +591,14 @@ class SondeScanner(object):
|
|||
Args:
|
||||
line (str): Message to be logged.
|
||||
"""
|
||||
logging.error("Scanner - %s" % line)
|
||||
logging.error("Scanner #%s - %s" % (self.device_idx,line))
|
||||
|
||||
def log_warning(self, line):
|
||||
""" Helper function to log a warning message with a descriptive heading.
|
||||
Args:
|
||||
line (str): Message to be logged.
|
||||
"""
|
||||
logging.warning("Scanner - %s" % line)
|
||||
logging.warning("Scanner #%s - %s" % (self.device_idx,line))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from __future__ import division, print_function
|
|||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import numpy as np
|
||||
from math import radians, degrees, sin, cos, atan2, sqrt, pi
|
||||
try:
|
||||
|
|
@ -274,8 +275,10 @@ def rtlsdr_test(device_idx=0, rtl_sdr_path="rtl_sdr"):
|
|||
FNULL.close()
|
||||
except subprocess.CalledProcessError:
|
||||
# This exception means the subprocess has returned an error code of one.
|
||||
time.sleep(1)
|
||||
return False
|
||||
else:
|
||||
time.sleep(1)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,370 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Radiosonde Auto RX Service - V2.0
|
||||
#
|
||||
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
|
||||
# Released under GNU GPL v3 or later
|
||||
#
|
||||
# Refer github page for instructions on setup and usage.
|
||||
# https://github.com/projecthorus/radiosonde_auto_rx/
|
||||
#
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from autorx.scan import SondeScanner
|
||||
from autorx.decode import SondeDecoder
|
||||
from autorx.logger import TelemetryLogger
|
||||
from autorx.habitat import HabitatUploader
|
||||
from autorx.utils import rtlsdr_test
|
||||
from autorx.config import read_auto_rx_config
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
from Queue import Queue
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from queue import Queue
|
||||
|
||||
|
||||
# Logging level
|
||||
# INFO = Basic status messages
|
||||
# DEBUG = Adds detailed information on submodule operations.
|
||||
logging_level = logging.DEBUG
|
||||
|
||||
|
||||
#
|
||||
# Global Variables
|
||||
#
|
||||
|
||||
RS_PATH = "./"
|
||||
|
||||
# Optional override for RS92 ephemeris data.
|
||||
rs92_ephemeris = None
|
||||
|
||||
# Global configuration dictionary
|
||||
config = None
|
||||
|
||||
# Exporter Lists
|
||||
exporter_objects = [] # This list will hold references to each exporter instance that is created.
|
||||
exporter_functions = [] # This list will hold references to the exporter add functions, which will be passed onto the decoders.
|
||||
|
||||
# RTLSDR Usage Register - This dictionary holds information about each SDR and its currently running Decoder / Scanner
|
||||
# Key = SDR device index / ID
|
||||
# 'device_idx': {
|
||||
# 'in_use' (bool) : True if the SDR is currently in-use by a decoder or scanner.
|
||||
# 'task' (class) : If this SDR is in use, a reference to the task.
|
||||
# 'bias' (bool) : True if the bias-tee should be enabled on this SDR, False otherwise.
|
||||
# 'ppm' (int) : The PPM offset for this SDR.
|
||||
# 'gain' (float) : The gain setting to use with this SDR. A setting of -1 turns on hardware AGC.
|
||||
# }
|
||||
#
|
||||
#
|
||||
sdr_list = {}
|
||||
|
||||
# Currently running task register.
|
||||
# Keys will either be 'SCAN' (only one scanner shall be running at a time), or a sonde frequency in MHz.
|
||||
# Each element contains:
|
||||
# 'task' : (class) Reference to the currently running task.
|
||||
# 'device_idx' (str): The allocated SDR.
|
||||
#
|
||||
task_list = {}
|
||||
|
||||
|
||||
# Scan Result Queue
|
||||
# Scan results are processed asynchronously from the main scanner object.
|
||||
scan_results = Queue()
|
||||
|
||||
|
||||
def allocate_sdr(check_only = False):
|
||||
""" Allocate an un-used SDR for a task.
|
||||
|
||||
Args:
|
||||
check_only (bool) : If True, don't set the free SDR as in-use. Used to check if there are any free SDRs.
|
||||
|
||||
Returns:
|
||||
(str): The device index/serial number of the free/allocated SDR, if one is free, else None.
|
||||
"""
|
||||
global sdr_list
|
||||
|
||||
for _idx in sdr_list.keys():
|
||||
if sdr_list[_idx]['in_use'] == False:
|
||||
# Found a free SDR!
|
||||
if check_only:
|
||||
# If we are just checking to see if there are any SDRs free, we don't allocate it.
|
||||
pass
|
||||
else:
|
||||
# Otherwise, set the SDR as in-use.
|
||||
sdr_list[_idx]['in_use'] = True
|
||||
logging.info("SDR #%s has been allocated." % str(_idx))
|
||||
|
||||
return _idx
|
||||
|
||||
# Otherwise, no SDRs are free.
|
||||
return None
|
||||
|
||||
|
||||
def clean_task_list():
|
||||
""" Routinely run to check the task list to see if any tasks have stopped running. If so, release the associated SDR """
|
||||
global task_list, sdr_list
|
||||
|
||||
for _key in task_list.keys():
|
||||
# Attempt to get the state of the task
|
||||
try:
|
||||
_running = task_list[_key]['task'].running()
|
||||
_task_sdr = task_list[_key]['device_idx']
|
||||
except Exception as e:
|
||||
logging.error("Task Manager - Error getting task %s state - %s" % (str(_key),str(e)))
|
||||
continue
|
||||
|
||||
if _running == False:
|
||||
# This task has stopped. Release it's associated SDR.
|
||||
sdr_list[_task_sdr]['in_use'] = False
|
||||
sdr_list[_task_sdr]['task'] = None
|
||||
# Pop the task from the task list.
|
||||
task_list.pop(_key)
|
||||
|
||||
# Check if there is a scanner thread still running. If not, and if there is a SDR free, start one up again.
|
||||
if ('SCAN' not in task_list) and (allocate_sdr(check_only=True) is not None):
|
||||
# We have a SDR free, and we are not running a scan thread. Start one.
|
||||
start_scanner()
|
||||
|
||||
|
||||
|
||||
def start_scanner():
|
||||
""" Start a scanner thread on the first available SDR """
|
||||
global task_list, sdr_list, config, scan_results, RS_PATH
|
||||
|
||||
if 'SCAN' in task_list:
|
||||
# Already a scanner running! Return.
|
||||
logging.debug("Task Manager - Attempted to start a scanner, but one already running.")
|
||||
return
|
||||
|
||||
# Attempt to allocate a SDR.
|
||||
_device_idx = allocate_sdr()
|
||||
if _device_idx is None:
|
||||
logging.debug("Task Manager - No SDRs free to run Scanner.")
|
||||
return
|
||||
else:
|
||||
# Create entry in task list.
|
||||
task_list['SCAN'] = {'device_idx': _device_idx, 'task': None}
|
||||
|
||||
# Init Scanner using settings from the global config.
|
||||
|
||||
task_list['SCAN']['task'] = SondeScanner(
|
||||
callback = scan_results.put,
|
||||
auto_start = True,
|
||||
min_freq = config['min_freq'],
|
||||
max_freq = config['max_freq'],
|
||||
search_step = config['search_step'],
|
||||
whitelist = config['whitelist'],
|
||||
greylist = config['greylist'],
|
||||
blacklist = config['blacklist'],
|
||||
snr_threshold = config['snr_threshold'],
|
||||
min_distance = config['min_distance'],
|
||||
quantization = config['quantization'],
|
||||
scan_dwell_time = config['scan_dwell_time'],
|
||||
detect_dwell_time = config['detect_dwell_time'],
|
||||
max_peaks = config['max_peaks'],
|
||||
rs_path = RS_PATH,
|
||||
sdr_power = config['sdr_power'],
|
||||
sdr_fm = config['sdr_fm'],
|
||||
device_idx = _device_idx,
|
||||
gain = sdr_list[_device_idx]['gain'],
|
||||
ppm = sdr_list[_device_idx]['ppm'],
|
||||
bias = sdr_list[_device_idx]['bias']
|
||||
)
|
||||
|
||||
# Add a reference into the sdr_list entry
|
||||
sdr_list[_device_idx]['task'] = task_list['SCAN']['task']
|
||||
|
||||
|
||||
def stop_scanner():
|
||||
""" Stop a currently running scan thread, and release the SDR it was using. """
|
||||
global task_list, sdr_list
|
||||
|
||||
if 'SCAN' not in task_list:
|
||||
# No scanner thread running!
|
||||
# This means we likely have a SDR free already.
|
||||
return
|
||||
else:
|
||||
logging.info("Halting Scanner to decode detected radiosonde.")
|
||||
_scan_sdr = task_list['SCAN']['device_idx']
|
||||
# Stop the scanner.
|
||||
task_list['SCAN']['task'].stop()
|
||||
# Relase the SDR.
|
||||
sdr_list[_scan_sdr]['in_use'] = False
|
||||
sdr_list[_scan_sdr]['task'] = None
|
||||
# Remove the scanner task from the task list
|
||||
task_list.pop('SCAN')
|
||||
|
||||
|
||||
def start_decoder(freq, sonde_type):
|
||||
""" Attempt to start a decoder thread """
|
||||
global config, task_list, sdr_list, RS_PATH, exporter_functions, rs92_ephemeris
|
||||
|
||||
# Allocate a SDR.
|
||||
_device_idx = allocate_sdr()
|
||||
|
||||
if _device_idx is None:
|
||||
logging.error("Could not allocate SDR for decoder!")
|
||||
return
|
||||
else:
|
||||
# Add an entry to the task list
|
||||
task_list[freq] = {'device_idx': _device_idx, 'task': None}
|
||||
|
||||
# Set the SDR to in-use
|
||||
sdr_list[_device_idx]['in_use'] = True
|
||||
|
||||
# Initialise a decoder.
|
||||
task_list[freq]['task'] = SondeDecoder(
|
||||
sonde_type = sonde_type,
|
||||
sonde_freq = freq,
|
||||
rs_path = RS_PATH,
|
||||
sdr_fm = config['sdr_fm'],
|
||||
device_idx = _device_idx,
|
||||
gain = sdr_list[_device_idx]['gain'],
|
||||
ppm = sdr_list[_device_idx]['ppm'],
|
||||
bias = sdr_list[_device_idx]['bias'],
|
||||
exporter = exporter_functions,
|
||||
timeout = config['rx_timeout'],
|
||||
telem_filter = telemetry_filter,
|
||||
rs92_ephemeris = rs92_ephemeris
|
||||
)
|
||||
sdr_list[_device_idx]['task'] = task_list[freq]['task']
|
||||
|
||||
|
||||
|
||||
def handle_scan_results():
|
||||
""" Read in Scan results via the scan results Queue.
|
||||
|
||||
Depending on how many SDRs are available, two things can happen:
|
||||
- If there is a free SDR, allocate it to a decoder.
|
||||
- If there is no free SDR, but a scanner is running, stop the scanner and start decoding.
|
||||
"""
|
||||
global scan_results, task_list, sdr_list
|
||||
if scan_results.qsize() > 0:
|
||||
_scan_data = scan_results.get()
|
||||
for _sonde in _scan_data:
|
||||
_freq = _sonde[0]
|
||||
_type = _sonde[1]
|
||||
|
||||
if _freq in task_list:
|
||||
# Already decoding this sonde, continue.
|
||||
continue
|
||||
else:
|
||||
|
||||
if allocate_sdr(check_only=True) is not None :
|
||||
# There is a SDR free! Start the decoder on that SDR
|
||||
start_decoder(_freq, _type)
|
||||
|
||||
elif (allocate_sdr(check_only=True) is None) and ('SCAN' in task_list):
|
||||
# We have run out of SDRs, but a scan thread is running.
|
||||
# Stop the scan thread and take that receiver!
|
||||
stop_scanner()
|
||||
start_decoder(_freq, _type)
|
||||
else:
|
||||
# We have no SDRs free
|
||||
pass
|
||||
|
||||
|
||||
def stop_all():
|
||||
""" Shut-down all decoders, scanners, and exporters. """
|
||||
global task_list, exporter_objects
|
||||
logging.info("Starting shutdown of all threads.")
|
||||
for _task in task_list.keys():
|
||||
try:
|
||||
task_list[_task]['task'].stop()
|
||||
except Exception as e:
|
||||
logging.error("Error stopping task - %s" % str(e))
|
||||
|
||||
for _exporter in exporter_objects:
|
||||
try:
|
||||
_exporter.close()
|
||||
except Exception as e:
|
||||
logging.error("Error stopping exporter - %s" % str(e))
|
||||
|
||||
|
||||
def telemetry_filter(telemetry):
|
||||
""" Filter incoming radiosonde telemetry based on distance from the receiver """
|
||||
global config
|
||||
|
||||
# TODO
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
""" Main Loop """
|
||||
global config, sdr_list, exporter_objects, exporter_functions
|
||||
|
||||
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', filename=datetime.datetime.utcnow().strftime("log/%Y%m%d-%H%M%S_system.log"), level=logging_level)
|
||||
stdout_format = logging.Formatter('%(asctime)s %(levelname)s:%(message)s')
|
||||
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||
stdout_handler.setFormatter(stdout_format)
|
||||
logging.getLogger().addHandler(stdout_handler)
|
||||
|
||||
# Set the requests logger to only display WARNING messages or higher.
|
||||
requests_log = logging.getLogger("requests")
|
||||
requests_log.setLevel(logging.CRITICAL)
|
||||
urllib3_log = logging.getLogger("urllib3")
|
||||
urllib3_log.setLevel(logging.CRITICAL)
|
||||
|
||||
# Command line arguments.
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c" ,"--config", default="station_new.cfg", help="Receive Station Configuration File")
|
||||
parser.add_argument("-f", "--frequency", type=float, default=0.0, help="Sonde Frequency (MHz) (bypass scan step, and quit if no sonde found).")
|
||||
parser.add_argument("-e", "--ephemeris", type=str, default="None", help="Use a manually obtained ephemeris file.")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Attempt to read in config file
|
||||
logging.info("Reading configuration file...")
|
||||
_temp_cfg = read_auto_rx_config(args.config)
|
||||
if _temp_cfg is None:
|
||||
logging.critical("Error in configuration file! Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
config = _temp_cfg
|
||||
sdr_list = config['sdr_settings']
|
||||
|
||||
# If we have been supplied a frequency via the command line, override the whitelist settings.
|
||||
if args.frequency != 0.0:
|
||||
config['whitelist'] = [args.frequency]
|
||||
|
||||
|
||||
# Start our exporter options
|
||||
if config['per_sonde_log']:
|
||||
_logger = TelemetryLogger(log_directory="./testlog/")
|
||||
exporter_objects.append(_logger)
|
||||
exporter_functions.append(_logger.add)
|
||||
|
||||
|
||||
# Habitat
|
||||
|
||||
# APRS
|
||||
|
||||
# OziExplorer
|
||||
|
||||
|
||||
while True:
|
||||
clean_task_list()
|
||||
handle_scan_results()
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
stop_all()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print("Main Loop Error - %s" % str(e))
|
||||
stop_all()
|
||||
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
#
|
||||
# Radiosonde Auto RX Station Configuration File
|
||||
#
|
||||
# Copy this file to station.cfg and modify as required.
|
||||
#
|
||||
|
||||
# Logging Settings
|
||||
[logging]
|
||||
# If enabled, a log file will be written to ./log/ for each detected radiosonde.
|
||||
per_sonde_log = True
|
||||
|
||||
# SDR Receiver Settings
|
||||
[sdr]
|
||||
# Paths to the rtl_fm and rtl_power utilities. If these are on your system path, then you don't need to change this.
|
||||
sdr_fm_path = rtl_fm
|
||||
sdr_power_path = rtl_power
|
||||
|
||||
# Number of RTLSDRs to use.
|
||||
# If more than one RTLSDR is in use, multiple [sdr_X] sections must be populated below
|
||||
sdr_quantity = 2
|
||||
|
||||
# Individual SDR Settings.
|
||||
[sdr_1]
|
||||
# Device Index / Serial
|
||||
# If using a single RTLSDR, set this value to 0
|
||||
# If using multiple SDRs, you MUST allocate each SDR a unique serial number using rtl_eeprom
|
||||
# i.e. to set the serial number of a (single) connected RTLSDR: rtl_eeprom -s 00000002
|
||||
# Then set the device_idx below to 00000002, and repeat for the other [sdr_n] sections below
|
||||
device_idx = 00000001
|
||||
|
||||
# Frequency Correction (ppm offset)
|
||||
# Refer here for a method of determining this correction: https://gist.github.com/darksidelemm/b517e6a9b821c50c170f1b9b7d65b824
|
||||
ppm = 0
|
||||
|
||||
# SDR Gain Setting
|
||||
# Gain settings can generally range between 0dB and 40dB depending on the tuner in use.
|
||||
# Run rtl_test to confirm what gain settings are available, or use a value of -1 to use automatic gain control.
|
||||
# Note that this is an overall gain value, not an individual mixer/tuner gain. This is a limitation of the rtl_power/rtl_fm utils.
|
||||
gain = -1
|
||||
|
||||
# Bias Tee - Enable the bias tee in the RTLSDR v3 Dongles.
|
||||
bias = False
|
||||
|
||||
[sdr_2]
|
||||
# As above, for the next SDR. Note the warning about serial numbers.
|
||||
device_idx = 00000002
|
||||
ppm = 0
|
||||
gain = -1
|
||||
bias = False
|
||||
|
||||
# Add more SDR definitions here.
|
||||
|
||||
|
||||
# Radiosonde Search Parameters
|
||||
[search_params]
|
||||
# Minimum and maximum search frequencies, in MHz.
|
||||
# Australia: Use 400.05 - 403 MHz
|
||||
# Europe: Use 400.05 - 406 MHz
|
||||
min_freq = 400.05
|
||||
max_freq = 403.0
|
||||
# Have the decoder timeout after X seconds of no valid data.
|
||||
rx_timeout = 20
|
||||
|
||||
# Frequency Lists - These must be provided as JSON-compatible lists of floats (in MHz), i.e. [400.50, 401.520, 403.200]
|
||||
|
||||
# White-List - Add values to this list to *only* scan on these frequencies.
|
||||
# This is for when you only want to monitor a small set of launch frequencies.
|
||||
whitelist = []
|
||||
|
||||
# Black-List - Any values added to this list will be removed from the list of detected peaks.
|
||||
# This is used to remove known spurs or other interferers from the scan list, potentially speeding up detection of a sonde.
|
||||
blacklist = []
|
||||
|
||||
# Grey-List - Any values in this list will be added to the start every scan run.
|
||||
# This is useful when you know the regular frequency of a local sonde, but still want to allow detections on other frequencies.
|
||||
greylist = []
|
||||
|
||||
|
||||
|
||||
# Settings for uploading to the Habitat HAB tracking database ( https://tracker.habhub.org/ )
|
||||
# Note that the habitat upload will use a fixed string format of:
|
||||
# `$$<payload_callsign>,<sequence number>,<time>,<lat>,<lon>,<alt>,<speed>,<temp>,<humidity>,<comment>*<CRC16>`
|
||||
# Where callsign is set below. Temp values are only supported on the RS41 at this time.
|
||||
# If you use a custom payload callsign, you will need to create an appropriate payload document for it to appear on the map
|
||||
#
|
||||
[habitat]
|
||||
habitat_enabled = False
|
||||
|
||||
# Uploader callsign, as shown above. PLEASE CHANGE THIS TO SOMETHING UNIQUE.
|
||||
uploader_callsign = SONDE_AUTO_RX
|
||||
|
||||
# Upload listener position to Habitat? (So you show up on the map)
|
||||
upload_listener_position = True
|
||||
|
||||
# Upload Rate - Upload a packet every X seconds.
|
||||
upload_rate = 30
|
||||
|
||||
# Payload callsign - if set to <id> will use the serial number of the sonde and create a payload document automatically
|
||||
payload_callsign = <id>
|
||||
payload_description = Meteorological Radiosonde
|
||||
|
||||
|
||||
# Station Location (optional). Used by the Habitat Uploader, and by Rotator Control
|
||||
[location]
|
||||
station_lat = 0.0
|
||||
station_lon = 0.0
|
||||
station_alt = 0.0
|
||||
|
||||
|
||||
# Settings for uploading to APRS-IS
|
||||
[aprs]
|
||||
aprs_enabled = False
|
||||
# APRS-IS Login Information
|
||||
aprs_user = N0CALL
|
||||
aprs_pass = 00000
|
||||
|
||||
# Upload Rate - Upload a packet every X seconds.
|
||||
upload_rate = 30
|
||||
|
||||
# APRS-IS server to upload to.
|
||||
aprs_server = rotate.aprs2.net
|
||||
|
||||
# Object name to be used when uploading to APRS-IS (Max 9 chars)
|
||||
# Should be either a callsign with a -11 or -12 suffix (i.e. N0CALL-12),
|
||||
# or <id>, which will be replaced with the radiosondes serial number
|
||||
aprs_object_id = <id>
|
||||
|
||||
# The APRS-IS beacon comment. The following fields can be included:
|
||||
# <freq> - Sonde Frequency, i.e. 401.520 MHz
|
||||
# <type> - Sonde Type (RS94/RS41)
|
||||
# <id> - Sonde Serial Number (i.e. M1234567)
|
||||
# <vel_v> - Sonde Vertical Velocity (i.e. -5.1m/s)
|
||||
# <temp> - Sonde reported temperature. If no temp data available, this will report -273 degC. Only works for RS41s.
|
||||
aprs_custom_comment = Radiosonde Auto-RX Testing
|
||||
|
||||
|
||||
# Settings for pushing data into OziPlotter
|
||||
# Oziplotter receives data via a basic CSV format, via UDP.
|
||||
[oziplotter]
|
||||
ozi_enabled = False
|
||||
ozi_update_rate = 5
|
||||
ozi_hostname = 127.0.0.1
|
||||
ozi_port = 55681
|
||||
# Payload summary output, which can be used by a few of the Horus Ground Station tools
|
||||
payload_summary_enabled = False
|
||||
payload_summary_port = 55672
|
||||
|
||||
|
||||
# Position Filtering Options
|
||||
# These are used to discard positions which are clearly bad, such as where the payload has jumped halfway around the world,
|
||||
# or has suddenly ended up in orbit.
|
||||
# Adjust only if absolutely necessary.
|
||||
[filtering]
|
||||
# Discard positions with an altitude greater than 50000 metres.
|
||||
max_altitude = 50000
|
||||
# Discard positions more than 1000 km from the observation station location (if set)
|
||||
max_radius_km = 1000
|
||||
|
||||
# Advanced Settings
|
||||
# These control low-level settings within various modules.
|
||||
# Playing with them may result in odd behaviour.
|
||||
[advanced]
|
||||
# Scanner - Receive bin width (Hz)
|
||||
search_step = 800
|
||||
# Scanner - Minimum SNR for a peak to be detected. The lower the number, the more peaks detected.
|
||||
snr_threshold = 10
|
||||
# Scanner - Maximum number of peaks to search through during a scan pass.
|
||||
# Increase this if you have lots of spurious signals, though this will increase scan times.
|
||||
max_peaks = 10
|
||||
# Scanner - Minimum distance between peaks (Hz)
|
||||
min_distance = 1000
|
||||
# Scanner - Scan Dwell Time - How long to observe the specified spectrum for.
|
||||
scan_dwell_time = 20
|
||||
# Scanner - Detection Dwell time - How long to wait for a sonde detection on each peak.
|
||||
detect_dwell_time = 5
|
||||
# Quantize search results to x Hz steps. Useful as most sondes are on 10 kHz frequency steps.
|
||||
quantization = 10000
|
||||
# Upload when (seconds_since_utc_epoch%upload_rate) == 0. Otherwise just delay upload_rate seconds between uploads.
|
||||
# Setting this to True with multple uploaders should give a higher chance of all uploaders uploading the same frame,
|
||||
# however the upload_rate should not be set too low, else there may be a chance of missing upload slots.
|
||||
synchronous_upload = True
|
||||
# Only accept a payload ID as valid until it has been seen N times.
|
||||
# This helps avoid corrupted callsigns getting onto the map.
|
||||
payload_id_valid = 5
|
||||
|
||||
|
||||
# Rotator Settings
|
||||
# auto_rx can communicate with an instance of rotctld, on either the local machine or elsewhere on the network.
|
||||
# The update rate is tied to the upload_rate setting above, though internet upload does not need to be enabled
|
||||
# for the rotator to be updated.
|
||||
[rotator]
|
||||
rotator_enabled = False
|
||||
# Hostname / Port of the rotctld instance.
|
||||
rotator_hostname = 127.0.0.1
|
||||
rotator_port = 4533
|
||||
# Rotator Homing.
|
||||
# If enabled, turn to this location when scanning for sondes.
|
||||
rotator_homing_enabled = False
|
||||
rotator_home_azimuth = 0
|
||||
rotator_home_elevation = 0
|
||||
Ładowanie…
Reference in New Issue