def write_mpad_repeater_data_to_disc( mpad_repeatermap_json: str, mpad_repeatermap_filename: str = "mpad_repeater_data.json", ): """ writes the processed repeatermap data in enriched MPAD format to disc and returns the operation's status Parameters ========== mpad_repeatermap_json: 'str' json string which contains the enriched repeatermap data mpad_repeatermap_filename: 'str' file name of the native MPAD repeatermap data Returns ======= success: 'bool' True if operation was successful """ success = False absolute_path_filename = build_full_pathname( file_name=mpad_repeatermap_filename) try: with open(f"{absolute_path_filename}", "w") as f: f.write(mpad_repeatermap_json) f.close() success = True except: logger.info( msg= f"Cannot write native repeatermap data to local disc file '{absolute_path_filename}'" ) return success
def update_local_airport_stations_file( airport_stations_filename: str = "airport_stations.txt", ): """ Imports the ICAO/IATA data from the web and saves it to a local file. Parameters ========== airport_stations_filename : 'str' This local file will hold the content from https://www.aviationweather.gov/docs/metar/stations.txt. Default filename is "airport_stations.txt" Returns ======= success: 'bool' True if operation was successful """ # This is the fixed name of the URL Source that we are going to download file_url = "https://www.aviationweather.gov/docs/metar/stations.txt" success: bool = False absolute_path_filename = build_full_pathname( file_name=airport_stations_filename) # try to get the file try: r = requests.get(file_url) except: logger.info(msg=f"Cannot download airport data from '{file_url}'") r = None if r: if r.status_code == 200: try: with open(absolute_path_filename, "wb") as f: f.write(r.content) f.close() success = True except: logger.info( msg= f"Cannot update airport data to local file '{absolute_path_filename}'" ) return success
def read_mpad_satellite_data_from_disc( mpad_satellite_filename: str = "mpad_satellite_data.json", ): """ reads the pre-processed satellite data in enriched MPAD format (JSON) from disc, then returns the operation's status and a dict object which contains the JSON data Enriched format contains TLE data plus frequencies Parameters ========== mpad_satellite_filename: 'str' file name of the native MPAD satellite data Returns ======= success: 'bool' True if operation was successful mpad_satellite_data: 'dict' dictionary, containing the enriched satellite data """ success = False absolute_path_filename = build_full_pathname( file_name=mpad_satellite_filename) mpad_satellite_data = {} # create empty dict if check_if_file_exists(absolute_path_filename): try: with open(f"{absolute_path_filename}", "r") as f: if f.mode == "r": mpad_satellite_data_json = f.read() f.close() mpad_satellite_data = json.loads(mpad_satellite_data_json) success = True except: logger.info( msg= f"Cannot read MPAD satellite data file '{absolute_path_filename}' from disc" ) success = False else: logger.info( msg= f"MPAD satellite data file '{absolute_path_filename}' does not exist" ) return success, mpad_satellite_data
def read_mpad_repeatermap_data_from_disc( mpad_repeatermap_filename: str = "mpad_repeater_data.json", ): """ Read the MPAD preprocessed repeatermap file from disc and return the JSON string if the file was found Parameters ========== mpad_repeatermap_filename: 'str' file name of the MPAD-enriched repeatermap data Returns ======= success: 'bool' True if operation was successful mpad_repeatermap: 'dict' dictionary which contains the preprocessed repeatermap data (or empty dictionary if nothing was found) """ success = False absolute_path_filename = build_full_pathname( file_name=mpad_repeatermap_filename) mpad_repeatermap = {} # create empty dict if check_if_file_exists(absolute_path_filename): try: with open(f"{absolute_path_filename}", "r") as f: if f.mode == "r": mpad_repeatermap_json = f.read() f.close() mpad_repeatermap = json.loads(mpad_repeatermap_json) success = True except: logger.info( msg= f"Cannot read MPAD repeatermap data file '{absolute_path_filename}' from disc" ) success = False else: logger.info( msg= f"MPAD repeatermap data file '{absolute_path_filename}' does not exist!" ) return success, mpad_repeatermap
def download_and_write_local_satfreq_file( satfreq_filename: str = "satellite_frequencies.csv", ): """ Download the amateur radio satellite frequency data and save it to a local file. Parameters ========== satfreq_filename : 'str' This local CSV file will hold the content from http://www.ne.jp/asahi/hamradio/je9pel/satslist.csv Default name is "satellite_frequencies.csv" Returns ======= success: 'bool' True if operation was successful """ file_url = "http://www.ne.jp/asahi/hamradio/je9pel/satslist.csv" success = False absolute_path_filename = build_full_pathname(file_name=satfreq_filename) try: r = requests.get(file_url) except: logger.info( msg=f"Cannot download satellite frequency data from {file_url}") r = None if r: if r.status_code == 200: try: with open(absolute_path_filename, "wb") as f: f.write(r.content) f.close() success = True except: logger.info( msg= f"Cannot write satellite frequency csv file {absolute_path_filename} to disc" ) return success
def download_and_write_local_tle_file( tle_filename: str = "tle_amateur_satellites.txt"): """ Download the amateur radio satellite TLE data and save it to a local file. Parameters ========== tle_filename : 'str' This local file will hold the content from http://www.celestrak.com/NORAD/elements/amateur.txt Default is "tle_amateur_satellites.txt" Returns ======= success: 'bool' True if operation was successful """ # This is the fixed name of the file that we are going to download tle_data_file_url = "http://www.celestrak.com/NORAD/elements/amateur.txt" absolute_path_filename = build_full_pathname(file_name=tle_filename) success: bool = False # try to get the file try: r = requests.get(tle_data_file_url) except: logger.info(msg=f"Cannot download TLE data from {tle_data_file_url}") r = None if r: if r.status_code == 200: try: with open(absolute_path_filename, "wb") as f: f.write(r.content) f.close() success = True except: logger.info( msg= f"Cannot update TLE data to file {absolute_path_filename}") return success
def read_hearham_raw_data_from_disk( hearham_raw_data_file: str = "hearham_raw_data.json", ): """ Read the repeatermap.de raw data from disc. Return the file's content to the user as a JSON string for further processing Parameters ========== hearham_raw_data_file: 'str' Filename of the file which contains the raw data from hearham.de Returns ======= success: 'bool' True if operation was successful repeatermap_raw_json_content: 'str' Contains the file's raw JSON content (otherwise 'None') """ success = False hearham_raw_json_content = None absolute_path_filename = build_full_pathname( file_name=hearham_raw_data_file) if check_if_file_exists(absolute_path_filename): try: with open(f"{absolute_path_filename}", "r") as f: if f.mode == "r": hearham_raw_json_content = f.read() f.close() success = True except: logger.info( msg=f"Cannot read '{absolute_path_filename}' from disc") else: logger.info( msg= f"Hearham.com raw data file '{absolute_path_filename}' does not exist!" ) return success, hearham_raw_json_content
def download_hearham_raw_data_to_local_file( url: str = "https://hearham.com/api/repeaters/v1", hearham_raw_data_file: str = "hearham_raw_data.json", ): """ Downloads the repeatermap.de data and write it to a file 'as is' That file needs to be post-processed at a later point in time Parameters ========== url: 'str' Source URL where we will get the data from. hearham_raw_data_file: 'str' Filename of the target file which will hold the raw data Returns ======= success: 'bool' True if operation was successful """ success = False absolute_path_filename = build_full_pathname( file_name=hearham_raw_data_file) resp = requests.get(url) if resp.status_code == 200: try: with open(f"{absolute_path_filename}", "w") as f: f.write(resp.text) f.close() success = True except: logger.info( msg= f"Cannot write hearham.com data to local disc file '{absolute_path_filename}'" ) return success
def read_local_airport_data_file( airport_stations_filename: str = "airport_stations.txt", ): """ Imports the ICAO/IATA data from a local file. Creates dictionaries for IATA-ICAO mapping and IATA-lat/lon mapping Parameters ========== airport_stations_filename : 'str' local file that is to be parsed. File format: see https://www.aviationweather.gov/docs/metar/stations.txt default filename: "airport_stations.txt" Returns ======= iata_dict: 'dict' dictionary mapping between ICAO-Code (3 chars) and IATA-Code (4 chars) for all cases where such a mapping exists icao_dict: 'dict' Dictionary. References IATA-Code (4 chars) to lat/lon, METAR capability and the actual airport name """ icao_dict = {} # create empty dict iata_dict = {} # create empty dict absolute_path_filename = build_full_pathname( file_name=airport_stations_filename) lines = None # Open the local file and read it if check_if_file_exists(absolute_path_filename): try: with open(f"{absolute_path_filename}", "r") as f: if f.mode == "r": lines = f.readlines() f.close() except: lines = None else: logger.info( msg=f"Airport data file '{absolute_path_filename}' does not exist") # If the file did contain content, then parse it if lines: # Start to parse the file content. The data that we want to digest # comes in lines of at least 63 chars and does not start with an # exclamation mark. Apart from that, everything is fixed file format for line in lines: line = line.rstrip() if len(line) > 63: if line[0] != "!" and line[0:11] != "CD STATION": # Extract all fields icao = line[20:24] icao = icao.strip() iata = line[26:29] iata = iata.strip() latdeg = line[39:41] latmin = line[42:44] latns = line[44:45] londeg = line[47:50] lonmin = line[51:53] lonns = line[53:54] airport_name = line[3:19] metar = line[62:63] # set to 'METAR capable if content is present if metar in ["X", "Z"]: metar = True else: metar = False # Convert DMS coordinates latitude latitude = int(latdeg) + int(latmin) * (1 / 60) if latns == "S": latitude = latitude * -1 # Convert DMS coordinates latitude longitude = int(londeg) + int(lonmin) * (1 / 60) if lonns == "W": longitude = longitude * -1 # Create IATA - ICAO relationship in dictionary # if ICAO entry exists if len(iata) != 0: iata_dict[f"{iata}"] = {"icao": f"{icao}"} # Create an entry for the IATA data if len(icao) != 0: icao_dict[f"{icao}"] = { "latitude": latitude, "longitude": longitude, "metar_capable": metar, "airport_name": airport_name, } return iata_dict, icao_dict
def read_local_satfreq_file( satfreq_filename: str = "satellite_frequencies.csv"): """ Reads the local amateur radio satellite frequency data from disc, transforms the data and creates a dictionary out of it. Parameters ========== satfreq_filename : 'str' This local CSV file holds the content from http://www.ne.jp/asahi/hamradio/je9pel/satslist.csv Default name is "satellite_frequencies.csv" Returns ======= success: 'bool' True if operation was successful satellite_dictionary: 'dict' satellite dictionary, containing all satellites that we have found data for. Each satellite can have 1..n entries. Each entry can have different data fields. Primary key = satellite ID (not satellite name) """ success = False absolute_path_filename = build_full_pathname(file_name=satfreq_filename) satellite_dictionary = {} if check_if_file_exists(absolute_path_filename): try: with open(absolute_path_filename) as csvfile: csv_object = csv.reader(csvfile, delimiter=";") for row in csv_object: # Some rows from the CSV cannot be imported; we skip those if len(row) > 6: satellite_name = row[0].strip() # Find the satellite NAME (contained in the brackets). If # found, remove it from the string. Remainder is the # satellit ID. This solution works for the majority # of the entries in the list regex_string = r"(\()(.*)(\))$" matches = re.findall( pattern=regex_string, string=satellite_name, flags=re.IGNORECASE, ) if matches: # This solution is far from ideal but the source file has way too many different formats # so we'll try to get at least some of that data satellite_key = re.sub( pattern=regex_string, repl="", string=satellite_name, flags=re.IGNORECASE, ).strip() else: # if brackets are missing, assime satellite ID = satellite name satellite_key = satellite_name.replace(" ", "-") # Convert to UpperCase. This file may provide some # sats in upper/lowercase; TLE data however is always in uppercase satellite_key = satellite_key.upper() # extract uplink/downloing frequencies etc # do not convert to float but treat them as # string; some entries contain more than one # frequency value for e.g. uplink uplink = row[2].strip() uplink = uplink if len(uplink) != 0 else None downlink = row[3].strip() downlink = downlink if len(downlink) != 0 else None beacon = row[4].strip() beacon = beacon if len(beacon) != 0 else None satellite_mode = row[5].strip() satellite_mode = (satellite_mode if len(satellite_mode) != 0 else None) # Create a dictionary entry for that single uplink/downlink entry # Each satellite can have 1..n of such entries satellite_element = { # "satellite_name": satellite_name, "uplink": uplink, "downlink": downlink, "beacon": beacon, "satellite_mode": satellite_mode, } # We store these entries in a list # if our satellite already exists in the final # dictionary, get the existing list so we can # add the new entry to it. Otherwise, create a # new empty list. # Finally, add the new entry if satellite_key in satellite_dictionary: satellite_data = satellite_dictionary[ satellite_key] else: satellite_data = [] satellite_data.append(satellite_element) satellite_dictionary[satellite_key] = satellite_data success = True except: success = False else: logger.info( msg= f"Satellite frequency data file '{absolute_path_filename}' does not exist" ) return success, satellite_dictionary
def read_local_tle_file(tle_filename: str = "tle_amateur_satellites.txt"): """ Imports the Celestrak TLE data from a local file. Create dictionary based on given data. TLE data consists of three lines: Line 1 - identifier (aka satellite name) Line 2 - TLE Data Line 1 Line 3 - TLE Data Line 2 The identifier will be provided in two different formats: satellite name (satellite ID) OR satellite name If the satellite ID is present, this function will prefer the satellite ID over the satellite name. If only the satellite name (but no ID) is present, then all space characters will be replaced with dashes. Parameters ========== tle_filename : 'str' local file that is to be parsed. File format: see http://www.celestrak.com/NORAD/elements/amateur.txt Returns ======= success: 'bool' True if operation was successful tle_data: 'dict' dictionary which contains the parsed data """ success: bool = False tle_data = {} absolute_path_filename = build_full_pathname(file_name=tle_filename) lines = None # Open the local file and read it if check_if_file_exists(absolute_path_filename): try: with open(f"{absolute_path_filename}", "r") as f: if f.mode == "r": lines = f.readlines() f.close() except: lines = None else: logger.info( msg=f"Celestrak TLE file '{absolute_path_filename}' does not exist" ) if lines: if len(lines) % 3 != 0: logger.info( msg=f"Invalid TLE file structure for file {tle_filename}") success = False return success, tle_data lc = 1 # Retrieve the data and create the dictionary for tle_satellite in lines[0::3]: # Process the key. Try to extract the ID (if present). # Otherwise, replace all blanks with dashes tle_satellite = tle_satellite.rstrip() matches = re.search(r"^.*(\()(.*)(\))$", tle_satellite) if matches: tle_key = matches[2] else: tle_key = tle_satellite.replace(" ", "-") # Ensure that the key is always in uppercase tle_key = tle_key.upper() # Convenience mapping :-) if tle_key == "ZARYA": tle_key = "ISS" # Get the actual TLE data tle_line1 = lines[lc].rstrip() tle_line2 = lines[lc + 1].rstrip() lc += 3 tle_data[f"{tle_key}"] = { "satellite_name": tle_satellite, "tle_line1": tle_line1, "tle_line2": tle_line2, } # Did we manage to download something? success: bool = True if len(tle_data) != 0 else False return success, tle_data