2024-08-16 23:20:58 -06:00
import logging
import os
import sys
import time
2024-08-17 07:08:43 -06:00
from datetime import datetime
2024-08-16 23:20:58 -06:00
import numpy as np
import paho . mqtt . client as mqtt
from lib . cddis_fetch import fetch_latest_ionex
2024-08-17 07:08:43 -06:00
from lib . tecmap import get_tecmaps , plot_tec_map , parse_ionex_datetime
2024-08-16 23:20:58 -06:00
logging . basicConfig ( level = logging . INFO )
MQTT_BROKER_HOST = os . getenv ( ' MQTT_BROKER_HOST ' , " " )
MQTT_BROKER_PORT = int ( os . getenv ( ' MQTT_BROKER_PORT ' , 1883 ) )
MQTT_CLIENT_ID = os . getenv ( ' MQTT_CLIENT_ID ' , " space_weather " )
MQTT_USERNAME = os . getenv ( ' MQTT_USERNAME ' , " " )
MQTT_PASSWORD = os . getenv ( ' MQTT_PASSWORD ' , " " )
MQTT_TOPIC_PREFIX = os . getenv ( ' MQTT_TOPIC_PREFIX ' , " space-weather " )
LAT_RANGE_MIN = os . getenv ( ' LAT_RANGE_MIN ' )
LAT_RANGE_MAX = os . getenv ( ' LAT_RANGE_MAX ' )
LON_RANGE_MIN = os . getenv ( ' LON_RANGE_MIN ' )
LON_RANGE_MAX = os . getenv ( ' LON_RANGE_MAX ' )
if not LAT_RANGE_MIN or not LAT_RANGE_MAX or not LON_RANGE_MIN or not LON_RANGE_MAX :
logging . critical ( ' Must set LAT_RANGE_MIN, LAT_RANGE_MAX, LON_RANGE_MIN, and LON_RANGE_MAX environment variables ' )
sys . exit ( 1 )
CDDIS_USERNAME = os . getenv ( ' CDDIS_USERNAME ' )
CDDIS_PASSWORD = os . getenv ( ' CDDIS_PASSWORD ' )
if not CDDIS_USERNAME or not CDDIS_PASSWORD :
logging . critical ( ' Must set CDDIS_USERNAME and CDDIS_PASSWORD environment variables ' )
sys . exit ( 1 )
client = mqtt . Client ( client_id = MQTT_CLIENT_ID )
if MQTT_USERNAME and MQTT_PASSWORD :
client . username_pw_set ( MQTT_USERNAME , MQTT_PASSWORD )
client . will_set ( MQTT_TOPIC_PREFIX + " /status " , payload = " Offline " , qos = 1 , retain = True ) # set LWT
client . connect ( MQTT_BROKER_HOST , port = MQTT_BROKER_PORT )
client . loop_start ( )
def publish ( topic : str , msg ) :
topic_expanded = MQTT_TOPIC_PREFIX + ' / ' + topic
2024-08-18 14:54:40 -06:00
retries = 10
for i in range ( retries ) : # retry
result = client . publish ( topic_expanded , msg )
status = result [ 0 ]
if status == 0 :
logging . info ( f " Sent { msg } to topic { topic_expanded } " )
return
else :
logging . warning ( f " Failed to send message to topic { topic_expanded } : { result } . Retry { i + 1 } / { retries } " )
time . sleep ( 10 )
logging . error ( f " Failed to send message to topic { topic_expanded } . " )
2024-08-16 23:20:58 -06:00
def main ( ) :
while True :
2024-08-17 07:08:43 -06:00
# TODO: tick every second and execute runs if it's time.
# TODO: get TEC map every 15 min and serve from URL https://services.swpc.noaa.gov/images/animations/natec-ustec/ustec_tec/latest.png?time=1716232652000
utc_hr = datetime . utcnow ( ) . hour
2024-08-16 23:20:58 -06:00
logging . info ( ' Fetching latest IONEX data ' )
2024-08-17 07:08:43 -06:00
logging . info ( f ' Using hour { utc_hr } ' )
2024-08-16 23:20:58 -06:00
ionex_data = fetch_latest_ionex ( CDDIS_USERNAME , CDDIS_PASSWORD )
2024-08-17 07:08:43 -06:00
avg_tec = None
2024-08-16 23:20:58 -06:00
for tecmap , epoch in get_tecmaps ( ionex_data ) :
2024-08-18 20:59:09 -06:00
parsed_dt = parse_ionex_datetime ( epoch )
if parsed_dt . hour == utc_hr :
2024-08-17 07:08:43 -06:00
avg_tec = np . mean ( plot_tec_map ( tecmap , [ float ( LON_RANGE_MIN ) , float ( LON_RANGE_MAX ) ] , [ float ( LAT_RANGE_MIN ) , float ( LAT_RANGE_MAX ) ] ) )
2024-08-18 20:59:09 -06:00
logging . info ( f ' Data timestamp: { parsed_dt . isoformat ( ) } ' )
2024-08-17 07:08:43 -06:00
break
latest = round ( avg_tec , 1 )
2024-08-16 23:54:04 -06:00
publish ( ' vtec ' , latest )
2024-08-19 14:03:43 -06:00
time . sleep ( 900 )
2024-08-16 23:20:58 -06:00
if __name__ == ' __main__ ' :
main ( )