diff --git a/horusgui/__init__.py b/horusgui/__init__.py
index 74acd0e..fb69db9 100755
--- a/horusgui/__init__.py
+++ b/horusgui/__init__.py
@@ -1 +1 @@
-__version__ = "0.1.12"
+__version__ = "0.1.14"
diff --git a/horusgui/config.py b/horusgui/config.py
index e35f813..3420cd8 100644
--- a/horusgui/config.py
+++ b/horusgui/config.py
@@ -29,6 +29,8 @@ default_config = {
"habitat_radio": "",
"horus_udp_enabled": True,
"horus_udp_port": 55672,
+ "ozimux_enabled": False,
+ "ozimux_udp_port": 55683,
"payload_list": json.dumps(horusdemodlib.payloads.HORUS_PAYLOAD_LIST),
"custom_field_list": json.dumps({})
}
@@ -87,6 +89,8 @@ def read_config(widgets):
# Horus Settings
widgets["horusUploadSelector"].setChecked(ValueToBool(default_config["horus_udp_enabled"]))
widgets["horusUDPEntry"].setText(str(default_config["horus_udp_port"]))
+ widgets["ozimuxUploadSelector"].setChecked(ValueToBool(default_config["ozimux_enabled"]))
+ widgets["ozimuxUDPEntry"].setText(str(default_config["ozimux_udp_port"]))
# Try and set the audio device.
# If the audio device is not in the available list of devices, this will fail silently.
@@ -133,6 +137,8 @@ def save_config(widgets):
default_config["habitat_radio"] = widgets["userRadioEntry"].text()
default_config["horus_udp_enabled"] = widgets["horusUploadSelector"].isChecked()
default_config["horus_udp_port"] = int(widgets["horusUDPEntry"].text())
+ default_config["ozimux_enabled"] = widgets["ozimuxUploadSelector"].isChecked()
+ default_config["ozimux_udp_port"] = int(widgets["ozimuxUDPEntry"].text())
default_config["audio_device"] = widgets["audioDeviceSelector"].currentText()
default_config["audio_sample_rate"] = widgets["audioSampleRateSelector"].currentText()
default_config["modem"] = widgets["horusModemSelector"].currentText()
@@ -150,7 +156,7 @@ def init_payloads():
global default_config
# Attempt to grab the payload list.
- _payload_list = download_latest_payload_id_list()
+ _payload_list = download_latest_payload_id_list(timeout=3)
if _payload_list:
# Sanity check the result
if 0 in _payload_list:
@@ -175,7 +181,7 @@ def init_payloads():
logging.info(f"Payload List contains {len(list(horusdemodlib.payloads.HORUS_PAYLOAD_LIST.keys()))} entries.")
- _custom_fields = download_latest_custom_field_list()
+ _custom_fields = download_latest_custom_field_list(timeout=3)
if _custom_fields:
# Sanity Check
if 'HORUSTEST' in _custom_fields:
diff --git a/horusgui/gui.py b/horusgui/gui.py
index 6665b81..9ccce7c 100644
--- a/horusgui/gui.py
+++ b/horusgui/gui.py
@@ -16,6 +16,7 @@ if sys.version_info < (3, 0):
import datetime
import glob
import logging
+import platform
import pyqtgraph as pg
import numpy as np
from queue import Queue
@@ -35,7 +36,7 @@ from .icon import getHorusIcon
from horusdemodlib.demod import HorusLib, Mode
from horusdemodlib.decoder import decode_packet, parse_ukhas_string
from horusdemodlib.payloads import *
-from horusdemodlib.horusudp import send_payload_summary
+from horusdemodlib.horusudp import send_payload_summary, send_ozimux_message
from . import __version__
# Setup Logging
@@ -125,6 +126,7 @@ d0.addWidget(w1_audio)
w1_modem = pg.LayoutWidget()
+
# Modem Parameters
widgets["horusModemLabel"] = QtGui.QLabel("Mode:")
widgets["horusModemSelector"] = QtGui.QComboBox()
@@ -134,11 +136,28 @@ widgets["horusModemRateSelector"] = QtGui.QComboBox()
widgets["horusMaskEstimatorLabel"] = QtGui.QLabel("Enable Mask Estim.:")
widgets["horusMaskEstimatorSelector"] = QtGui.QCheckBox()
+widgets["horusMaskEstimatorSelector"].setToolTip(
+ "Enable the mask frequency estimator, which makes uses of the \n"\
+ "tone spacing value entered below as extra input to the frequency\n"\
+ "estimator. This can help decode performance in very weak signal conditions."
+)
widgets["horusMaskSpacingLabel"] = QtGui.QLabel("Tone Spacing (Hz):")
widgets["horusMaskSpacingEntry"] = QtGui.QLineEdit("270")
+widgets["horusMaskSpacingEntry"].setToolTip(
+ "If the tone spacing of the transmitter is known, it can be entered here,\n"\
+ "and used with the mask estimator option above. The default tone spacing for\n"\
+ "a RS41-based transmitter is 270 Hz."
+)
widgets["horusManualEstimatorLabel"] = QtGui.QLabel("Manual Estim. Limits:")
widgets["horusManualEstimatorSelector"] = QtGui.QCheckBox()
+widgets["horusManualEstimatorSelector"].setToolTip(
+ "Enables manual selection of the frequency estimator limits. This will enable\n"\
+ "a slidable area on the spectrum display, which can be used to select the frequency\n"\
+ "range of interest, and help stop in-band CW interference from biasing the frequency\n"\
+ "estimator. You can either click-and-drag the entire area, or click-and-drag the edges\n"\
+ "to change the estimator frequency range."
+)
# Start/Stop
widgets["startDecodeButton"] = QtGui.QPushButton("Start")
@@ -168,14 +187,30 @@ widgets["habitatUploadSelector"].setChecked(True)
widgets["userCallLabel"] = QtGui.QLabel("Callsign:")
widgets["userCallEntry"] = QtGui.QLineEdit("N0CALL")
widgets["userCallEntry"].setMaxLength(20)
+widgets["userCallEntry"].setToolTip(
+ "Your station callsign, which doesn't necessarily need to be an\n"\
+ "amateur radio callsign."
+)
widgets["userLocationLabel"] = QtGui.QLabel("Lat/Lon:")
widgets["userLatEntry"] = QtGui.QLineEdit("0.0")
+widgets["userLatEntry"].setToolTip("Station Latitude in Decimal Degrees, e.g. -34.123456")
widgets["userLonEntry"] = QtGui.QLineEdit("0.0")
+widgets["userLonEntry"].setToolTip("Station Longitude in Decimal Degrees, e.g. 138.123456")
widgets["userAntennaLabel"] = QtGui.QLabel("Antenna:")
widgets["userAntennaEntry"] = QtGui.QLineEdit("")
+widgets["userAntennaEntry"].setToolTip("A text description of your station's antenna.")
widgets["userRadioLabel"] = QtGui.QLabel("Radio:")
widgets["userRadioEntry"] = QtGui.QLineEdit("Horus-GUI " + __version__)
+widgets["userRadioEntry"].setToolTip(
+ "A text description of your station's radio setup.\n"\
+ "This field will be automatically prefixed with Horus-GUI."
+)
widgets["habitatUploadPosition"] = QtGui.QPushButton("Upload Position")
+widgets["habitatUploadPosition"].setToolTip(
+ "Manually re-upload your position information to HabHub.\n"\
+ "Note that it can take a few minutes for your new information to\n"\
+ "appear on the map."
+)
widgets["saveSettingsButton"] = QtGui.QPushButton("Save Settings")
w1_habitat.addWidget(widgets["habitatUploadLabel"], 0, 0, 1, 1)
@@ -199,14 +234,38 @@ w1_other = pg.LayoutWidget()
widgets["horusUploadLabel"] = QtGui.QLabel("Enable Horus UDP Output:")
widgets["horusUploadSelector"] = QtGui.QCheckBox()
widgets["horusUploadSelector"].setChecked(True)
+widgets["horusUploadSelector"].setToolTip(
+ "Enable output of 'Horus UDP' JSON messages. These are emitted as a JSON object\n"\
+ "and contain the fields: callsign, time, latitude, longitude, altitude, snr"\
+)
widgets["horusUDPLabel"] = QtGui.QLabel("Horus UDP Port:")
widgets["horusUDPEntry"] = QtGui.QLineEdit("55672")
widgets["horusUDPEntry"].setMaxLength(5)
+widgets["horusUDPEntry"].setToolTip(
+ "UDP Port to output 'Horus UDP' JSON messages to."
+)
+widgets["ozimuxUploadLabel"] = QtGui.QLabel("Enable OziMux UDP Output:")
+widgets["ozimuxUploadSelector"] = QtGui.QCheckBox()
+widgets["ozimuxUploadSelector"].setChecked(False)
+widgets["ozimuxUploadSelector"].setToolTip(
+ "Output OziMux UDP messages. These are of the form:\n"\
+ "'TELEMETRY,HH:MM:SS,lat,lon,alt\\n'"
+)
+widgets["ozimuxUDPLabel"] = QtGui.QLabel("Ozimux UDP Port:")
+widgets["ozimuxUDPEntry"] = QtGui.QLineEdit("55683")
+widgets["ozimuxUDPEntry"].setMaxLength(5)
+widgets["ozimuxUDPEntry"].setToolTip(
+ "UDP Port to output 'OziMux' UDP messages to."
+)
w1_other.addWidget(widgets["horusUploadLabel"], 0, 0, 1, 1)
w1_other.addWidget(widgets["horusUploadSelector"], 0, 1, 1, 1)
w1_other.addWidget(widgets["horusUDPLabel"], 1, 0, 1, 1)
w1_other.addWidget(widgets["horusUDPEntry"], 1, 1, 1, 1)
+w1_other.addWidget(widgets["ozimuxUploadLabel"], 2, 0, 1, 1)
+w1_other.addWidget(widgets["ozimuxUploadSelector"], 2, 1, 1, 1)
+w1_other.addWidget(widgets["ozimuxUDPLabel"], 3, 0, 1, 1)
+w1_other.addWidget(widgets["ozimuxUDPEntry"], 3, 1, 1, 1)
w1_other.layout.setRowStretch(5, 1)
d0_other.addWidget(w1_other)
@@ -308,13 +367,9 @@ w4_data = pg.LayoutWidget()
widgets["latestRawSentenceLabel"] = QtGui.QLabel("Latest Packet (Raw):")
widgets["latestRawSentenceData"] = QtGui.QLineEdit("NO DATA")
widgets["latestRawSentenceData"].setReadOnly(True)
-#widgets["latestRawSentenceData"].setFont(QtGui.QFont("Courier New", 18, QtGui.QFont.Bold))
-#widgets["latestRawSentenceData"].setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
widgets["latestDecodedSentenceLabel"] = QtGui.QLabel("Latest Packet (Decoded):")
widgets["latestDecodedSentenceData"] = QtGui.QLineEdit("NO DATA")
widgets["latestDecodedSentenceData"].setReadOnly(True)
-#widgets["latestDecodedSentenceData"].setFont(QtGui.QFont("Courier New", 18, QtGui.QFont.Bold))
-#widgets["latestDecodedSentenceData"].setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
w4_data.addWidget(widgets["latestRawSentenceLabel"], 0, 0, 1, 1)
w4_data.addWidget(widgets["latestRawSentenceData"], 0, 1, 1, 6)
w4_data.addWidget(widgets["latestDecodedSentenceLabel"], 1, 0, 1, 1)
@@ -322,7 +377,12 @@ w4_data.addWidget(widgets["latestDecodedSentenceData"], 1, 1, 1, 6)
d3_data.addWidget(w4_data)
w4_position = pg.LayoutWidget()
-POSITION_LABEL_FONT_SIZE = 16
+# This font seems to look bigger in Windows... not sure why.
+if 'Windows' in platform.system():
+ POSITION_LABEL_FONT_SIZE = 14
+else:
+ POSITION_LABEL_FONT_SIZE = 16
+
widgets["latestPacketCallsignLabel"] = QtGui.QLabel("Callsign")
widgets["latestPacketCallsignValue"] = QtGui.QLabel("---")
widgets["latestPacketCallsignValue"].setFont(QtGui.QFont("Courier New", POSITION_LABEL_FONT_SIZE, QtGui.QFont.Bold))
@@ -641,6 +701,11 @@ def handle_new_packet(frame):
_decoded['snr'] = _snr
send_payload_summary(_decoded, port=_udp_port)
+
+ # Send data out via OziMux messaging
+ if widgets["ozimuxUploadSelector"].isChecked():
+ _udp_port = int(widgets["ozimuxUDPEntry"].text())
+ send_ozimux_message(_decoded, port=_udp_port)
@@ -743,6 +808,14 @@ def start_decoding():
running = True
logging.info("Started Audio Processing.")
+ # Grey out some selectors, so the user cannot adjust them while we are decoding.
+ widgets["audioDeviceSelector"].setEnabled(False)
+ widgets["audioSampleRateSelector"].setEnabled(False)
+ widgets["horusModemSelector"].setEnabled(False)
+ widgets["horusModemRateSelector"].setEnabled(False)
+ widgets["horusMaskEstimatorSelector"].setEnabled(False) # This should really be editable while running.
+ widgets["horusMaskSpacingEntry"].setEnabled(False) # This should really be editable while running
+
else:
try:
audio_stream.stop()
@@ -768,6 +841,14 @@ def start_decoding():
running = False
logging.info("Stopped Audio Processing.")
+
+ # Re-Activate selectors.
+ widgets["audioDeviceSelector"].setEnabled(True)
+ widgets["audioSampleRateSelector"].setEnabled(True)
+ widgets["horusModemSelector"].setEnabled(True)
+ widgets["horusModemRateSelector"].setEnabled(True)
+ widgets["horusMaskEstimatorSelector"].setEnabled(True)
+ widgets["horusMaskSpacingEntry"].setEnabled(True)
widgets["startDecodeButton"].clicked.connect(start_decoding)
diff --git a/horusgui/modem.py b/horusgui/modem.py
index b4686c1..0e8d6cf 100644
--- a/horusgui/modem.py
+++ b/horusgui/modem.py
@@ -7,7 +7,7 @@ from horusdemodlib.demod import Mode
HORUS_MODEM_LIST = {
"Horus Binary v1 (Legacy)": {
"id": Mode.BINARY_V1,
- "baud_rates": [25, 50, 100, 300],
+ "baud_rates": [50, 100, 300], # Note: 25 Baud removed until issues in underlying modem are fixed.
"default_baud_rate": 100,
"default_tone_spacing": 270,
"use_mask_estimator": False,
diff --git a/pyproject.toml b/pyproject.toml
index 10ea770..0fbd5f7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "horusgui"
-version = "0.1.12"
+version = "0.1.14"
description = ""
authors = ["Mark Jessop "]
@@ -12,7 +12,7 @@ PyQt5 = "^5.13.0"
pyqtgraph = "^0.11.0"
pyaudio = "^0.2.11"
"ruamel.yaml" = "^0.16.10"
-horusdemodlib = "^0.1.1"
+horusdemodlib = "^0.1.19"
[tool.poetry.dev-dependencies]
diff --git a/requirements.txt b/requirements.txt
index 8de2b6f..26602ff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,4 @@ PyQt5
pyqtgraph
ruamel.yaml
requests
-horusdemodlib
\ No newline at end of file
+horusdemodlib>=0.1.20
\ No newline at end of file