diff --git a/README.md b/README.md index 9038528..5b49de6 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,21 @@ continually update this file as messages come in. It requires you to let Log4OM ## Usage -Place the script on a system that can hear UDP from Log4OM and has a way of putting the resulting HTML file on a webserver -as fast as possible. Edit the script as required (IP, port, output location, HTML formatting). +This is not something you can just toss on your computer and get it to work. It's designed to run on a web-server; and +even worse the environment I designed it for has a public httpd running on my local LAN. + +The script takes UDP packets from Log4OM and will then write an HTML file. So there are two requirements: + +- Send/recieve UDP messages to/from Log4OM +- Serve the HTML file. + +There are a number of ways this can be accomplished. You can run it on a local machine and upload the HTML file +to your webserver every few seconds, or minutes. You can run it on your hosted server and just blast the packets over +the internet every 5 seconds. + +I have plans to write a version that's less real-time for cases where you want to send the UDP over the internet. There +are also ideas for hacking together an SSH tunneling solution...maybe. I would love for as many people to run this; but +at the end of the day, getting this to work will require a higher-than-average skill level. ## Basic Operation @@ -21,11 +34,19 @@ the following in a very basic way: This now will determine if the radio is off or if Log4OM is not loaded by trying to request the Alive command over remote control. It will assue Log4OM is active if a response is received. In an effort to make the thing a bit less chatty, it -backs down to 60 second checks when not receiving data automatically. +backs down to 60 second checks when not receiving data automatically. Some modes (FT8, JS8, FT4) might use split, but who +cares if you say it; it's almost implied. So now we just ignore split for those modes. + +## Running As A Service -Code could likely use more optimization. ChatGPT has been used to some degree but currently has issues giving complete -output. +I have run this as a systemd service to great success. For the 11 months I was off the air it largely sat in the background +trying to ask my PC if Log4OM was alive. Granted I rebooted the thing about 40 times in that 11 months; I forgot to deactivate +and turn it off. During that 11 months I saw absolutely no negative impacts on my system. It consumed almost no CPU and just +made a lot of UDP requests to my IP. After getting back on the air, updating Log4OM, and getting CAT control working; it +immedately began updating like nothing had happened. +I've included a systemd service file, as that's what I'm using. If you write one for something else; please submit it given +you will allow it to be released and distributed under the FBSD3 license. ## Examples This has been implemented on the sidebar/menu of [nq4t.com](https://nq4t.com). The actual webpage that's updated is served @@ -36,6 +57,7 @@ from my [QTH's webserver](https://log.nq4t.com/radio.html). ``` 02-FEB-2023: Initial Version. Shows status and basic offline message. 04-APR-2023: Second Version. Now shows more percise offline message. Removes threads. +08-JUN-2024: "Back On The Air" Update. Minor cleanup. Code commenting. Split exclusion for FT/JT modes. ``` ## License @@ -43,7 +65,7 @@ from my [QTH's webserver](https://log.nq4t.com/radio.html). ``` BSD 3-Clause License -Copyright (c) 2023, Jay Moore +Copyright (c) 2024, Jay Moore Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/log4om.py b/log4om.py index 9dbf13e..f53ef6b 100644 --- a/log4om.py +++ b/log4om.py @@ -1,27 +1,40 @@ +#!/usr/bin/env python3 + # LOG4OM Web Statuts -# Version: 5-APR-2023 - Jay Moore/NQ4T +# Version: 8-JUN-2024 - Jay Moore/NQ4T # https://git.pickmy.org/nq4t/log4om-webstatus # https://nq4t.com/software/log4omudp/ # FreeBSD 3-Clause License (see LICENSE) +# Please read comments for configuration. +# Script must be configured for your setup. + + import time import xml.etree.ElementTree as ET import socket import select -UDP_IP = "0.0.0.0" +UDP_IP = "0.0.0.0" # Binds to "Any" For Broadcast Packets UDP_PORT = 2242 # Default Log4OM UDP Out Port -UDP_C_IP = "192.168.1.70" # Set to IP running Log4OM -UDP_C_PORT = 2241 +UDP_C_IP = "0.0.0.0" # Set to IP running Log4OM +UDP_C_PORT = 2241 # Default Log4OM Remote Port -tot = 1 +# Global Variable Definitions and Initializations +tot = time.time() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, UDP_PORT)) - check = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -def checkrig(): +# These modes should ignore split mode indication + +ignored_modes = ["FT8", "FT4", "JT65", "JT9", "JS8", "MSK144"] + + +# Script Functions + +def checkrig(): # Checks if it's radio off or Log4OM off check = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) check.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) check.bind((UDP_IP, UDP_C_PORT)) @@ -32,40 +45,67 @@ def checkrig(): """ check.sendto(msg.encode(), (UDP_C_IP, UDP_C_PORT)) status = select.select([check], [], [], 2) + # It's simple; we either get a response, or we don't. + # It doesn't matter what it is. if status[0]: writehtml(f"Radio Off", False) else: writehtml(f"Log4OM Down", False) check.close() -def writehtml(rs, t = "true"): +def writehtml(rs, t = "true"): # Takes data as argument from loop, writes HTML file. header = """\n\nFT-1000MP Status \n\n""" - # if you use CSS then modify for your stylesheet URI + # if you use CSS then modify for your stylesheet URI css = """ """ + # you may not need this div portion, it's part of nq4t.com's integration div = "
" + # you will need the footer though footer = "\n
\n" + # you can change it to "BEING A LID" if you want isonair = "ON THE AIR
\n" - with open("/var/www/log/radio.html", "w") as html: # Modifiy file location as needed. - if t == True: # Checks for on-air - html.write(header + css + div + isonair + rs + footer) # If not using CSS, remove it. + # HTML Output: Modify the file path. Also modify html.write to remove css or div if not needed. + # isonair can be placed before or after 'rs' + with open("/var/www/log/radio.html", "w") as html: + if t == True: + html.write(header + css + div + isonair + rs + footer) else: html.write(header + css + div + rs + footer) +checkrig() # Run this once to ensure things are initalized. + +# Script Loop + +# Log4OM sends it's packets every five seconds. So we wait six to see if a packet comes in. +# If it does, we parse the XML and send it as arguments to writehtml() +# +# If it doesn't.... +# +# Then we create the sleepy variable by subtracting tot from time.time(). If it's more than +# a minute; we run checkrig(). If it's not, we go away. After a minute has passed, we check +# again and reset the counter. +# +# This largely is done so we can daemon the script and have it pick back up when things change. +# +# You also probably shouldn't modify anything here unless you know what you're doing. + while True: - ready = select.select([sock], [], [], 6) # Just give it an extra second + ready = select.select([sock], [], [], 6) if ready[0]: data, addr = sock.recvfrom(1024) root = ET.fromstring(data.decode("utf-8")) - freq = int(root.find("Freq").text) - tx_freq = int(root.find("TXFreq").text) - mode = root.find("Mode").text - onair = (root.find("IsTransmitting").text == "true") + freq = int(root.find("Freq").text) # Frequency + tx_freq = int(root.find("TXFreq").text) # TX Frequency + mode = root.find("Mode").text # Operating Mode + onair = (root.find("IsTransmitting").text == "true") # TX Status f = freq / 100 tf = tx_freq / 100 - sv = tf - f # Determines split value + if mode in ignored_modes: + sv = 0 + else: + sv = tf - f if sv > 0: writehtml(f"Frequency: {f}kHz {mode}
Tx Split · Up: {sv:.2f} kHz", onair) elif sv < 0: @@ -77,5 +117,5 @@ while True: else: sleepy = time.time() - tot if sleepy > 60: # How often to check in Off/Down state - checkrig() - tot = time.time() + checkrig() + tot = time.time() # Reset tot. diff --git a/log4omudp.service b/log4omudp.service new file mode 100644 index 0000000..ccae7e1 --- /dev/null +++ b/log4omudp.service @@ -0,0 +1,13 @@ +[Unit] +Description=Log4OM UDP Status Display +After=network.target +StartLimitIntervalSec=0 +[Service] +Type=simple +Restart=always +RestartSec=1 +User=dewdude +ExecStart=/usr/bin/python3 /path/to/log4om.py + +[Install] +WantedBy=multi-user.target