kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
939 wiersze
38 KiB
Python
939 wiersze
38 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# radiosonde_auto_rx - Configuration File Reader
|
|
#
|
|
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
|
|
# Released under GNU GPL v3 or later
|
|
#
|
|
|
|
import copy
|
|
import logging
|
|
import os
|
|
import traceback
|
|
import json
|
|
from configparser import RawConfigParser
|
|
from .sdr_wrappers import test_sdr
|
|
|
|
# Dummy initial config with some parameters we need to make the web interface happy.
|
|
global_config = {
|
|
"min_freq": 400.0,
|
|
"max_freq": 403.0,
|
|
"snr_threshold": 10,
|
|
"station_lat": 0.0,
|
|
"station_lon": 0.0,
|
|
"station_alt": 0.0,
|
|
}
|
|
|
|
# Web interface credentials
|
|
web_password = "none"
|
|
|
|
# Fixed minimum update rates for APRS & Habitat
|
|
# These are set to avoid congestion on the APRS-IS network, and on the Habitat server
|
|
# Please respect other users of these networks and leave these settings as they are.
|
|
MINIMUM_APRS_UPDATE_RATE = 30
|
|
MINIMUM_HABITAT_UPDATE_RATE = 30
|
|
|
|
|
|
def read_auto_rx_config(filename, no_sdr_test=False):
|
|
"""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.
|
|
no_sdr_test (bool): Skip testing the SDRs (used for some unit tests)
|
|
|
|
Returns:
|
|
auto_rx_config (dict): The configuration dictionary.
|
|
sdr_config (dict): A dictionary with SDR parameters.
|
|
"""
|
|
global global_config, web_password
|
|
# Configuration Defaults:
|
|
auto_rx_config = {
|
|
# Log Settings
|
|
"per_sonde_log": True,
|
|
# Email Settings
|
|
"email_enabled": False,
|
|
#'email_error_notifications': False,
|
|
"email_smtp_server": "localhost",
|
|
"email_smtp_port": 25,
|
|
"email_smtp_authentication": "None",
|
|
"email_smtp_login": "None",
|
|
"email_smtp_password": "None",
|
|
"email_from": "sonde@localhost",
|
|
"email_to": None,
|
|
"email_subject": "<type> Sonde launch detected on <freq>: <id>",
|
|
"email_nearby_landing_subject": "Nearby Radiosonde Landing Detected - <id>",
|
|
# SDR Settings
|
|
"sdr_type": "RTLSDR",
|
|
"sdr_hostname": "localhost",
|
|
"sdr_port": 5555,
|
|
"sdr_fm": "rtl_fm",
|
|
"sdr_power": "rtl_power",
|
|
"ss_iq_path": "./ss_iq",
|
|
"ss_power_path": "./ss_power",
|
|
"sdr_quantity": 1,
|
|
# Search Parameters
|
|
"min_freq": 400.4,
|
|
"max_freq": 404.0,
|
|
"rx_timeout": 120,
|
|
"only_scan": [],
|
|
"never_scan": [],
|
|
"always_scan": [],
|
|
"always_decode": [],
|
|
# Location Settings
|
|
"station_lat": 0.0,
|
|
"station_lon": 0.0,
|
|
"station_alt": 0.0,
|
|
"station_code": "SONDE", # NOTE: This will not be read from the config file, but will be left in place for now
|
|
# as a default setting.
|
|
"gpsd_enabled": False,
|
|
"gpsd_host": "localhost",
|
|
"gpsd_port": 2947,
|
|
# Position Filter Settings
|
|
"max_altitude": 50000,
|
|
"max_radius_km": 1000,
|
|
"min_radius_km": 0,
|
|
"radius_temporary_block": False,
|
|
# "sonde_time_threshold": 3, # Commented out to ensure warning message is shown.
|
|
# Habitat Settings
|
|
"habitat_enabled": False,
|
|
"habitat_upload_rate": 30,
|
|
"habitat_uploader_callsign": "SONDE_AUTO_RX",
|
|
"habitat_uploader_antenna": "1/4-wave",
|
|
"habitat_upload_listener_position": False,
|
|
"habitat_payload_callsign": "<id>",
|
|
# 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_use_custom_object_id': False,
|
|
"aprs_custom_comment": "Radiosonde Auto-RX <freq>",
|
|
"aprs_position_report": False,
|
|
"station_beacon_enabled": False,
|
|
"station_beacon_rate": 30,
|
|
"station_beacon_comment": "radiosonde_auto_rx SondeGate v<version>",
|
|
"station_beacon_icon": "/r",
|
|
# Web Settings,
|
|
"web_host": "0.0.0.0",
|
|
"web_port": 5000,
|
|
"web_archive_age": 120,
|
|
"web_control": False,
|
|
# "web_password": "none", # Commented out to ensure warning message is shown
|
|
#'kml_refresh_rate': 10,
|
|
# Advanced Parameters
|
|
"search_step": 800,
|
|
"snr_threshold": 10,
|
|
"min_distance": 1000,
|
|
"dwell_time": 10,
|
|
"max_peaks": 10,
|
|
"quantization": 10000,
|
|
"decoder_spacing_limit": 15000,
|
|
"synchronous_upload": False,
|
|
"scan_dwell_time": 20,
|
|
"detect_dwell_time": 5,
|
|
"scan_delay": 10,
|
|
"payload_id_valid": 5,
|
|
"temporary_block_time": 60,
|
|
"rs41_drift_tweak": False,
|
|
"decoder_stats": False,
|
|
"ngp_tweak": False,
|
|
# Rotator Settings
|
|
"enable_rotator": False,
|
|
"rotator_update_rate": 30,
|
|
"rotator_hostname": "127.0.0.1",
|
|
"rotator_port": 4533,
|
|
"rotation_threshold": 5.0,
|
|
"rotator_homing_enabled": False,
|
|
"rotator_homing_delay": 10,
|
|
"rotator_home_azimuth": 0,
|
|
"rotator_home_elevation": 0,
|
|
# OziExplorer Settings
|
|
"ozi_enabled": False,
|
|
"ozi_update_rate": 5,
|
|
"ozi_port": 55681,
|
|
"payload_summary_enabled": False,
|
|
"payload_summary_port": 55672,
|
|
# Debugging settings
|
|
"save_detection_audio": False,
|
|
"save_decode_audio": False,
|
|
"save_decode_iq": False,
|
|
"save_raw_hex": False,
|
|
"save_system_log": False,
|
|
"enable_debug_logging": 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.
|
|
# For now, sondehub.org just acts as a proxy to habhub.org.
|
|
# This setting is not exposed to users as it's only used for unit/int testing
|
|
"habitat_url": "https://habitat.sondehub.org/",
|
|
# New Sondehub DB Settings
|
|
"sondehub_enabled": True,
|
|
"sondehub_upload_rate": 30,
|
|
# "sondehub_contact_email": "none@none.com" # Commented out to ensure a warning message is shown on startup
|
|
"wideband_sondes": False, # Wideband sonde detection / decoding
|
|
}
|
|
|
|
try:
|
|
|
|
# Check the file exists.
|
|
if not os.path.isfile(filename):
|
|
logging.critical("Config file %s does not exist!" % filename)
|
|
return None
|
|
|
|
config = RawConfigParser(auto_rx_config)
|
|
config.read(filename)
|
|
|
|
# Log Settings
|
|
auto_rx_config["per_sonde_log"] = config.getboolean("logging", "per_sonde_log")
|
|
|
|
# Email Settings
|
|
if config.has_option("email", "email_enabled"):
|
|
try:
|
|
auto_rx_config["email_enabled"] = config.getboolean(
|
|
"email", "email_enabled"
|
|
)
|
|
auto_rx_config["email_smtp_server"] = config.get("email", "smtp_server")
|
|
auto_rx_config["email_smtp_port"] = config.get("email", "smtp_port")
|
|
auto_rx_config["email_smtp_authentication"] = config.get(
|
|
"email", "smtp_authentication"
|
|
)
|
|
auto_rx_config["email_smtp_login"] = config.get("email", "smtp_login")
|
|
auto_rx_config["email_smtp_password"] = config.get(
|
|
"email", "smtp_password"
|
|
)
|
|
auto_rx_config["email_from"] = config.get("email", "from")
|
|
auto_rx_config["email_to"] = config.get("email", "to")
|
|
auto_rx_config["email_subject"] = config.get("email", "subject")
|
|
|
|
if auto_rx_config["email_smtp_authentication"] not in [
|
|
"None",
|
|
"TLS",
|
|
"SSL",
|
|
]:
|
|
logging.error(
|
|
"Config - Invalid email authentication setting. Must be None, TLS or SSL."
|
|
)
|
|
return None
|
|
|
|
except:
|
|
logging.error("Config - Invalid or missing email settings. Disabling.")
|
|
auto_rx_config["email_enabled"] = False
|
|
|
|
# SDR Settings
|
|
auto_rx_config["sdr_fm"] = config.get("advanced", "sdr_fm_path")
|
|
auto_rx_config["sdr_power"] = config.get("advanced", "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["rx_timeout"] = config.getint("search_params", "rx_timeout")
|
|
|
|
if (
|
|
config.has_option("search_params", "only_scan")
|
|
and config.get("search_params", "only_scan") != ""
|
|
): # check if user has new name for scan lists
|
|
auto_rx_config["only_scan"] = json.loads(
|
|
config.get("search_params", "only_scan")
|
|
)
|
|
else:
|
|
logging.warning(
|
|
"Config - whitelist configuration has been deprecated and replaced with only_scan list"
|
|
)
|
|
auto_rx_config["only_scan"] = json.loads(
|
|
config.get("search_params", "whitelist")
|
|
)
|
|
|
|
if (
|
|
config.has_option("search_params", "never_scan")
|
|
and config.get("search_params", "never_scan") != ""
|
|
): # check if user has new name for scan lists
|
|
auto_rx_config["never_scan"] = json.loads(
|
|
config.get("search_params", "never_scan")
|
|
)
|
|
else:
|
|
logging.warning(
|
|
"Config - blacklist configuration has been deprecated and replaced with never_scan list"
|
|
)
|
|
auto_rx_config["never_scan"] = json.loads(
|
|
config.get("search_params", "blacklist")
|
|
)
|
|
|
|
if (
|
|
config.has_option("search_params", "always_scan")
|
|
and config.get("search_params", "always_scan") != ""
|
|
): # check if user has new name for scan lists
|
|
auto_rx_config["always_scan"] = json.loads(
|
|
config.get("search_params", "always_scan")
|
|
)
|
|
else:
|
|
logging.warning(
|
|
"Config - greylist configuration has been deprecated and replaced with always_scan list"
|
|
)
|
|
auto_rx_config["always_scan"] = 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")
|
|
|
|
if auto_rx_config["station_lat"] > 90.0 or auto_rx_config["station_lat"] < -90.0:
|
|
logging.critical("Config - Invalid Station Latitude! (Outside +/- 90 degrees!)")
|
|
return None
|
|
|
|
if auto_rx_config["station_lon"] > 180.0 or auto_rx_config["station_lon"] < -180.0:
|
|
logging.critical("Config - Invalid Station Longitude! (Outside +/- 180 degrees!)")
|
|
return None
|
|
|
|
|
|
# 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
|
|
# Deprecated from v1.5.0
|
|
# 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_uploader_callsign"] = config.get(
|
|
"habitat", "uploader_callsign"
|
|
)
|
|
auto_rx_config["habitat_upload_listener_position"] = config.getboolean(
|
|
"habitat", "upload_listener_position"
|
|
)
|
|
auto_rx_config["habitat_uploader_antenna"] = config.get(
|
|
"habitat", "uploader_antenna"
|
|
).strip()
|
|
|
|
# try: # Use the default configuration if not found
|
|
# auto_rx_config["habitat_url"] = config.get("habitat", "url")
|
|
# except:
|
|
# pass
|
|
|
|
# Deprecated from v1.5.0
|
|
# if auto_rx_config["habitat_upload_rate"] < MINIMUM_HABITAT_UPDATE_RATE:
|
|
# logging.warning(
|
|
# "Config - Habitat Update Rate clipped to minimum of %d seconds. Please be respectful of other users of Habitat."
|
|
# % MINIMUM_HABITAT_UPDATE_RATE
|
|
# )
|
|
# auto_rx_config["habitat_upload_rate"] = MINIMUM_HABITAT_UPDATE_RATE
|
|
|
|
# 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"
|
|
)
|
|
# 2021-08-08 - Disable option for producing APRS position reports.
|
|
#auto_rx_config["aprs_position_report"] = config.getboolean(
|
|
# "aprs", "aprs_position_report"
|
|
#)
|
|
auto_rx_config["aprs_position_report"] = False
|
|
auto_rx_config["station_beacon_enabled"] = config.getboolean(
|
|
"aprs", "station_beacon_enabled"
|
|
)
|
|
auto_rx_config["station_beacon_rate"] = config.getint(
|
|
"aprs", "station_beacon_rate"
|
|
)
|
|
auto_rx_config["station_beacon_comment"] = config.get(
|
|
"aprs", "station_beacon_comment"
|
|
)
|
|
auto_rx_config["station_beacon_icon"] = config.get(
|
|
"aprs", "station_beacon_icon"
|
|
)
|
|
|
|
if auto_rx_config["aprs_upload_rate"] < MINIMUM_APRS_UPDATE_RATE:
|
|
logging.warning(
|
|
"Config - APRS Update Rate clipped to minimum of %d seconds."
|
|
% MINIMUM_APRS_UPDATE_RATE
|
|
)
|
|
auto_rx_config["aprs_upload_rate"] = MINIMUM_APRS_UPDATE_RATE
|
|
|
|
# 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"
|
|
)
|
|
|
|
# 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["scan_delay"] = config.getint("advanced", "scan_delay")
|
|
auto_rx_config["payload_id_valid"] = config.getint(
|
|
"advanced", "payload_id_valid"
|
|
)
|
|
auto_rx_config["synchronous_upload"] = config.getboolean(
|
|
"advanced", "synchronous_upload"
|
|
)
|
|
|
|
# Rotator Settings
|
|
auto_rx_config["rotator_enabled"] = config.getboolean(
|
|
"rotator", "rotator_enabled"
|
|
)
|
|
auto_rx_config["rotator_update_rate"] = config.getint("rotator", "update_rate")
|
|
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"
|
|
)
|
|
auto_rx_config["rotator_homing_delay"] = config.getint(
|
|
"rotator", "rotator_homing_delay"
|
|
)
|
|
auto_rx_config["rotation_threshold"] = config.getfloat(
|
|
"rotator", "rotation_threshold"
|
|
)
|
|
|
|
# Web interface settings.
|
|
auto_rx_config["web_host"] = config.get("web", "web_host")
|
|
auto_rx_config["web_port"] = config.getint("web", "web_port")
|
|
auto_rx_config["web_archive_age"] = config.getint("web", "archive_age")
|
|
|
|
auto_rx_config["save_detection_audio"] = config.getboolean(
|
|
"debugging", "save_detection_audio"
|
|
)
|
|
auto_rx_config["save_decode_audio"] = config.getboolean(
|
|
"debugging", "save_decode_audio"
|
|
)
|
|
auto_rx_config["save_decode_iq"] = config.getboolean(
|
|
"debugging", "save_decode_iq"
|
|
)
|
|
|
|
# NOTE 2019-09-21: The station code will now be fixed at the default to avoid multiple iMet callsign issues.
|
|
# auto_rx_config['station_code'] = config.get('location', 'station_code')
|
|
# if len(auto_rx_config['station_code']) > 5:
|
|
# auto_rx_config['station_code'] = auto_rx_config['station_code'][:5]
|
|
# logging.warning("Config - Clipped station code to 5 digits: %s" % auto_rx_config['station_code'])
|
|
|
|
auto_rx_config["temporary_block_time"] = config.getint(
|
|
"advanced", "temporary_block_time"
|
|
)
|
|
|
|
# New demod tweaks - Added 2019-04-23
|
|
# Default to experimental decoders on for FSK/GFSK sondes...
|
|
auto_rx_config["experimental_decoders"] = {
|
|
"RS41": True,
|
|
"RS92": True,
|
|
"DFM": True,
|
|
"M10": True,
|
|
"M20": True,
|
|
"IMET": False,
|
|
"IMET5": True,
|
|
"LMS6": True,
|
|
"MK2LMS": False,
|
|
"MEISEI": True,
|
|
"MTS01": False, # Until we test it
|
|
"MRZ": False, # .... except for the MRZ, until we know it works.
|
|
"WXR301": False, # No fsk_demod chain for this yet.
|
|
"UDP": False,
|
|
}
|
|
|
|
auto_rx_config["decoder_spacing_limit"] = config.getint(
|
|
"advanced", "decoder_spacing_limit"
|
|
)
|
|
# Use 'experimental' (not really, anymore!) decoders for RS41, RS92, M10, DFM and LMS6-400.
|
|
# Don't allow overriding to the FM based decoders.
|
|
# auto_rx_config["experimental_decoders"]["RS41"] = config.getboolean(
|
|
# "advanced", "rs41_experimental"
|
|
# )
|
|
# 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"
|
|
# )
|
|
|
|
try:
|
|
auto_rx_config["web_control"] = config.getboolean("web", "web_control")
|
|
auto_rx_config["ngp_tweak"] = config.getboolean("advanced", "ngp_tweak")
|
|
auto_rx_config["gpsd_enabled"] = config.getboolean(
|
|
"location", "gpsd_enabled"
|
|
)
|
|
auto_rx_config["gpsd_host"] = config.get("location", "gpsd_host")
|
|
auto_rx_config["gpsd_port"] = config.getint("location", "gpsd_port")
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find web control / ngp_tweak / gpsd options, using defaults (disabled)"
|
|
)
|
|
auto_rx_config["web_control"] = False
|
|
auto_rx_config["ngp_tweak"] = False
|
|
auto_rx_config["gpsd_enabled"] = False
|
|
|
|
try:
|
|
auto_rx_config["min_radius_km"] = config.getint(
|
|
"filtering", "min_radius_km"
|
|
)
|
|
auto_rx_config["radius_temporary_block"] = config.getboolean(
|
|
"filtering", "radius_temporary_block"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find minimum radius filter setting, using default (0km)."
|
|
)
|
|
auto_rx_config["min_radius_km"] = 0
|
|
auto_rx_config["radius_temporary_block"] = False
|
|
|
|
try:
|
|
auto_rx_config["aprs_use_custom_object_id"] = config.getboolean(
|
|
"aprs", "aprs_use_custom_object_id"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find aprs_use_custom_object_id setting, using default (False)"
|
|
)
|
|
auto_rx_config["aprs_use_custom_object_id"] = False
|
|
|
|
try:
|
|
auto_rx_config["aprs_port"] = config.getint("aprs", "aprs_port")
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find aprs_port setting - using default of 14590."
|
|
)
|
|
auto_rx_config["aprs_port"] = 14590
|
|
|
|
try:
|
|
auto_rx_config["email_error_notifications"] = config.getboolean(
|
|
"email", "error_notifications"
|
|
)
|
|
auto_rx_config["email_launch_notifications"] = config.getboolean(
|
|
"email", "launch_notifications"
|
|
)
|
|
auto_rx_config["email_landing_notifications"] = config.getboolean(
|
|
"email", "landing_notifications"
|
|
)
|
|
auto_rx_config["email_landing_range_threshold"] = config.getfloat(
|
|
"email", "landing_range_threshold"
|
|
)
|
|
auto_rx_config["email_landing_altitude_threshold"] = config.getfloat(
|
|
"email", "landing_altitude_threshold"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find new email settings (v1.3.3), using defaults"
|
|
)
|
|
auto_rx_config["email_error_notifications"] = False
|
|
auto_rx_config["email_launch_notifications"] = True
|
|
auto_rx_config["email_landing_notifications"] = True
|
|
auto_rx_config["email_landing_range_threshold"] = 30
|
|
auto_rx_config["email_landing_altitude_threshold"] = 1000
|
|
|
|
try:
|
|
auto_rx_config["kml_refresh_rate"] = config.getint(
|
|
"web", "kml_refresh_rate"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find kml_refresh_rate setting, using default (10 seconds)."
|
|
)
|
|
auto_rx_config["kml_refresh_rate"] = 11
|
|
|
|
# New Sondehub db Settings
|
|
try:
|
|
auto_rx_config["sondehub_enabled"] = config.getboolean(
|
|
"sondehub", "sondehub_enabled"
|
|
)
|
|
auto_rx_config["sondehub_upload_rate"] = config.getint(
|
|
"sondehub", "sondehub_upload_rate"
|
|
)
|
|
if auto_rx_config["sondehub_upload_rate"] < 10:
|
|
logging.warning(
|
|
"Config - Clipped Sondehub update rate to lower limit of 10 seconds"
|
|
)
|
|
auto_rx_config["sondehub_upload_rate"] = 10
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find sondehub_enabled setting, using default (enabled / 15 seconds)."
|
|
)
|
|
auto_rx_config["sondehub_enabled"] = True
|
|
auto_rx_config["sondehub_upload_rate"] = 15
|
|
|
|
try:
|
|
auto_rx_config["experimental_decoders"]["MRZ"] = config.getboolean(
|
|
"advanced", "mrz_experimental"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find MRZ decoder experimental decoder setting, using default (disabled)."
|
|
)
|
|
auto_rx_config["experimental_decoders"]["MRZ"] = False
|
|
|
|
try:
|
|
auto_rx_config["experimental_decoders"]["IMET5"] = config.getboolean(
|
|
"advanced", "imet54_experimental"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find iMet-54 decoder experimental decoder setting, using default (enabled)."
|
|
)
|
|
auto_rx_config["experimental_decoders"]["IMET5"] = True
|
|
|
|
# Sondehub Contact email (1.5.1)
|
|
try:
|
|
auto_rx_config["sondehub_contact_email"] = config.get(
|
|
"sondehub", "sondehub_contact_email"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find Sondehub contact e-mail setting, using default (none)."
|
|
)
|
|
auto_rx_config["sondehub_contact_email"] = "none@none.com"
|
|
|
|
# Sonde time threshold (1.5.1)
|
|
try:
|
|
auto_rx_config["sonde_time_threshold"] = config.getfloat(
|
|
"filtering", "sonde_time_threshold"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find Sonde Time Threshold, using default (3 hrs)."
|
|
)
|
|
auto_rx_config["sonde_time_threshold"] = 3
|
|
|
|
# Web control password
|
|
try:
|
|
auto_rx_config["web_password"] = config.get("web", "web_password")
|
|
if auto_rx_config["web_password"] == "none":
|
|
logging.warning("Config - Web Password not set, disabling web control")
|
|
auto_rx_config["web_control"] = True
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find Web Password setting, using default (web control disabled)"
|
|
)
|
|
auto_rx_config["web_control"] = False
|
|
auto_rx_config["web_password"] = "none"
|
|
|
|
try:
|
|
auto_rx_config["save_raw_hex"] = config.getboolean(
|
|
"debugging", "save_raw_hex"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find save_raw_hex setting, using default (disabled)"
|
|
)
|
|
auto_rx_config["save_raw_hex"] = False
|
|
|
|
try:
|
|
auto_rx_config["experimental_decoders"]["MK2LMS"] = config.getboolean(
|
|
"advanced", "lms6-1680_experimental"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find lms6-1680_experimental setting, using default (disabled)"
|
|
)
|
|
auto_rx_config["experimental_decoders"]["MK2LMS"] = False
|
|
|
|
try:
|
|
auto_rx_config["email_nearby_landing_subject"] = config.get(
|
|
"email", "nearby_landing_subject"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find email_nearby_landing_subject setting, using default"
|
|
)
|
|
auto_rx_config["email_nearby_landing_subject"] = "Nearby Radiosonde Landing Detected - <id>"
|
|
|
|
|
|
# As of auto_rx version 1.5.10, we are limiting APRS output to only radiosondy.info,
|
|
# and only on the non-forwarding port.
|
|
# This decision was not made lightly, and is a result of the considerable amount of
|
|
# non-amateur traffic that radiosonde flights are causing within the APRS-IS network.
|
|
# Until some form of common format can be agreed to amongst the developers of *all*
|
|
# radiosonde tracking software to enable radiosonde telemetry to be de-duped,
|
|
# I have decided to help reduce the impact on the wider APRS-IS network by restricting
|
|
# the allowed servers and ports.
|
|
# If you are using another APRS-IS server that *does not* forward to the wider APRS-IS
|
|
# network and want it allowed, then please raise an issue at
|
|
# https://github.com/projecthorus/radiosonde_auto_rx/issues
|
|
#
|
|
# You are of course free to fork and modify this codebase as you wish, but please be aware
|
|
# that this goes against the wishes of the radiosonde_auto_rx developers to not be part
|
|
# of the bigger problem of APRS-IS congestion.
|
|
|
|
ALLOWED_APRS_SERVERS = ["radiosondy.info", "wettersonde.net", "localhost"]
|
|
ALLOWED_APRS_PORTS = [14580, 14590]
|
|
|
|
if auto_rx_config["aprs_server"] not in ALLOWED_APRS_SERVERS:
|
|
logging.warning(
|
|
"Please do not upload to servers which forward to the wider APRS-IS network and cause network congestion. Switching to default server of radiosondy.info. If you believe this to be in error, please raise an issue at https://github.com/projecthorus/radiosonde_auto_rx/issues"
|
|
)
|
|
auto_rx_config["aprs_server"] = "radiosondy.info"
|
|
|
|
if auto_rx_config["aprs_port"] not in ALLOWED_APRS_PORTS:
|
|
logging.warning(
|
|
"Please do not use APRS ports which forward data out to the wider APRS-IS network and cause network congestion. Switching to default port of 14590. If you believe this to be in error, please raise an issue at https://github.com/projecthorus/radiosonde_auto_rx/issues"
|
|
)
|
|
auto_rx_config["aprs_port"] = 14590
|
|
|
|
|
|
# 1.6.0 - New SDR options
|
|
if not config.has_option("sdr", "sdr_type"):
|
|
logging.warning(
|
|
"Config - Missing sdr_type configuration option, defaulting to RTLSDR."
|
|
)
|
|
auto_rx_config["sdr_type"] = "RTLSDR"
|
|
else:
|
|
auto_rx_config["sdr_type"] = config.get("sdr", "sdr_type")
|
|
|
|
try:
|
|
auto_rx_config["sdr_hostname"] = config.get("sdr", "sdr_hostname")
|
|
auto_rx_config["sdr_port"] = config.getint("sdr", "sdr_port")
|
|
auto_rx_config["ss_iq_path"] = config.get("advanced", "ss_iq_path")
|
|
auto_rx_config["ss_power_path"] = config.get("advanced", "ss_power_path")
|
|
except:
|
|
logging.debug("Config - Did not find new sdr_type associated options.")
|
|
|
|
try:
|
|
auto_rx_config["always_decode"] = json.loads(
|
|
config.get("search_params", "always_decode")
|
|
)
|
|
except:
|
|
logging.debug(
|
|
"Config - No always_decode settings, defaulting to none."
|
|
)
|
|
auto_rx_config["always_decode"] = []
|
|
|
|
try:
|
|
auto_rx_config["experimental_decoders"]["MEISEI"] = config.getboolean(
|
|
"advanced", "meisei_experimental"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find meisei_experimental setting, using default (enabled)"
|
|
)
|
|
auto_rx_config["experimental_decoders"]["MEISEI"] = True
|
|
|
|
try:
|
|
auto_rx_config["save_system_log"] = config.getboolean(
|
|
"logging", "save_system_log"
|
|
)
|
|
auto_rx_config["enable_debug_logging"] = config.getboolean(
|
|
"logging", "enable_debug_logging"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find system / debug logging options, using defaults (disabled, unless set as a command-line option.)"
|
|
)
|
|
|
|
# 1.6.2 - Encrypted Sonde Email Notifications
|
|
try:
|
|
auto_rx_config["email_encrypted_sonde_notifications"] = config.getboolean(
|
|
"email", "encrypted_sonde_notifications"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Did not find encrypted_sonde_notifications setting (new in v1.6.2), using default (True)"
|
|
)
|
|
auto_rx_config["email_encrypted_sonde_notifications"] = True
|
|
|
|
|
|
# 1.6.3 - Weathex WXR301d support
|
|
try:
|
|
auto_rx_config["wideband_sondes"] = config.getboolean(
|
|
"advanced", "wideband_sondes"
|
|
)
|
|
except:
|
|
logging.warning(
|
|
"Config - Missing wideband_sondes option (new in v1.6.3), using default (False)"
|
|
)
|
|
auto_rx_config["wideband_sondes"] = 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
|
|
|
|
# Now we enumerate our SDRs.
|
|
auto_rx_config["sdr_settings"] = {}
|
|
|
|
if auto_rx_config["sdr_type"] == "RTLSDR":
|
|
# Multiple RTLSDRs in use - we need to read in each SDRs 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 = round(config.getfloat(_section, "ppm"))
|
|
_gain = config.getfloat(_section, "gain")
|
|
_bias = config.getboolean(_section, "bias")
|
|
|
|
if (auto_rx_config["sdr_quantity"] > 1) and (_device_idx == "0"):
|
|
logging.critical(
|
|
"Config - RTLSDR 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 = test_sdr(sdr_type = "RTLSDR", rtl_device_idx = _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 RTLSDR #%s OK" % _device_idx)
|
|
else:
|
|
logging.warning("Config - RTLSDR #%s invalid." % _device_idx)
|
|
except Exception as e:
|
|
logging.error(
|
|
"Config - Error parsing RTLSDR %d config - %s" % (_n, str(e))
|
|
)
|
|
continue
|
|
|
|
elif auto_rx_config["sdr_type"] == "SpyServer":
|
|
# Test access to the SpyServer
|
|
_sdr_ok = test_sdr(
|
|
sdr_type=auto_rx_config["sdr_type"],
|
|
sdr_hostname=auto_rx_config["sdr_hostname"],
|
|
sdr_port=auto_rx_config["sdr_port"],
|
|
ss_iq_path=auto_rx_config["ss_iq_path"],
|
|
ss_power_path=auto_rx_config["ss_power_path"],
|
|
check_freq=1e6*(auto_rx_config["max_freq"]+auto_rx_config["min_freq"])/2.0,
|
|
)
|
|
|
|
if not _sdr_ok:
|
|
logging.critical(f"Config - Could not contact SpyServer {auto_rx_config['sdr_hostname']}:{auto_rx_config['sdr_port']}. Exiting.")
|
|
return None
|
|
|
|
for _n in range(1, auto_rx_config["sdr_quantity"] + 1):
|
|
_sdr_name = f"SPY{_n:02d}"
|
|
auto_rx_config["sdr_settings"][_sdr_name] = {
|
|
"ppm": 0,
|
|
"gain": 0,
|
|
"bias": 0,
|
|
"in_use": False,
|
|
"task": None,
|
|
}
|
|
|
|
elif auto_rx_config["sdr_type"] == "KA9Q":
|
|
# Test access to the SpyServer
|
|
_sdr_ok = test_sdr(
|
|
sdr_type=auto_rx_config["sdr_type"],
|
|
sdr_hostname=auto_rx_config["sdr_hostname"],
|
|
sdr_port=auto_rx_config["sdr_port"]
|
|
)
|
|
|
|
if not _sdr_ok:
|
|
logging.critical(f"Config - Could not contact KA9Q Server {auto_rx_config['sdr_hostname']}:{auto_rx_config['sdr_port']}. Exiting.")
|
|
return None
|
|
|
|
for _n in range(1, auto_rx_config["sdr_quantity"] + 1):
|
|
_sdr_name = f"KA9Q{_n:02d}"
|
|
auto_rx_config["sdr_settings"][_sdr_name] = {
|
|
"ppm": 0,
|
|
"gain": 0,
|
|
"bias": 0,
|
|
"in_use": False,
|
|
"task": None,
|
|
}
|
|
|
|
logging.critical("Config - KA9Q SDR Support not implemented yet - exiting.")
|
|
return None
|
|
|
|
else:
|
|
logging.critical(f"Config - Unknown SDR Type {auto_rx_config['sdr_type']} - exiting.")
|
|
return None
|
|
|
|
# Sanity checks when using more than one SDR
|
|
if (len(auto_rx_config["sdr_settings"].keys()) > 1) and (
|
|
auto_rx_config["aprs_object_id"] != "<id>"
|
|
):
|
|
logging.critical(
|
|
"Fixed APRS object ID used in a multi-SDR configuration. Go read the warnings in the config file!"
|
|
)
|
|
return None
|
|
|
|
if (len(auto_rx_config["sdr_settings"].keys()) > 1) and (
|
|
auto_rx_config["rotator_enabled"]
|
|
):
|
|
logging.critical(
|
|
"Rotator enabled in a multi-SDR configuration. Go read the warnings in the config file!"
|
|
)
|
|
return None
|
|
|
|
# TODO: Revisit this limitation once the OziPlotter output sub-module is complete.
|
|
if (len(auto_rx_config["sdr_settings"].keys()) > 1) and auto_rx_config[
|
|
"ozi_enabled"
|
|
]:
|
|
logging.critical("Oziplotter output enabled in a multi-SDR configuration.")
|
|
return None
|
|
|
|
if len(auto_rx_config["sdr_settings"].keys()) == 0:
|
|
# We have no SDRs to use!!
|
|
logging.error("Config - No working SDRs! Cannot run...")
|
|
raise SystemError("No working SDRs!")
|
|
else:
|
|
# Create a global copy of the configuration file at this point
|
|
global_config = copy.deepcopy(auto_rx_config)
|
|
|
|
# Excise some sensitive parameters from the global config.
|
|
global_config.pop("email_smtp_login")
|
|
global_config.pop("email_smtp_password")
|
|
global_config.pop("email_smtp_server")
|
|
global_config.pop("email_smtp_port")
|
|
global_config.pop("email_from")
|
|
global_config.pop("email_to")
|
|
global_config.pop("email_smtp_authentication")
|
|
global_config.pop("sondehub_contact_email")
|
|
global_config.pop("web_password")
|
|
|
|
web_password = auto_rx_config["web_password"]
|
|
|
|
return auto_rx_config
|
|
except SystemError as e:
|
|
raise e
|
|
except:
|
|
traceback.print_exc()
|
|
logging.error("Could not parse config file.")
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
"""Quick test script to attempt to read in a config file."""
|
|
import sys, pprint
|
|
|
|
logging.basicConfig(
|
|
format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG
|
|
)
|
|
|
|
config = read_auto_rx_config(sys.argv[1])
|
|
|
|
pprint.pprint(global_config)
|