Add GQRX UDP input

pull/13/head
Mark Jessop 2020-07-12 17:09:23 +09:30
rodzic 8d0e0ea544
commit b935f82e6d
7 zmienionych plików z 129 dodań i 18 usunięć

Wyświetl plik

@ -19,11 +19,14 @@ Written by:
![Screenshot](doc/horusgui_screenshot.png)
### Known Issues
* Occasional crash when processing is stopped just as a packet is being processed by horus_api.
* Queue events not processed on OSX when the application is running in the background.
### TODO LIST - Important Stuff
* Better build system via Travis (@xssfox)
### TODO LIST - Extras
* UDP input from GQRX
* Waterfall Display (? Need something GPU accelerated if possible...)
* rotctld rotator control?

Wyświetl plik

@ -4,8 +4,8 @@ block_cipher = None
a = Analysis(['horus-gui.py'],
pathex=['C:\\HAB\\horus-gui'],
binaries=[('libhorus.dll','.')],
pathex=['.'],
binaries=[('libhorus.dll','.'),('libgcc_s_seh-1.dll','.'),('libwinpthread-1.dll','.'),('libstdc++-6.dll','.')],
datas=[],
hiddenimports=['pkg_resources.py2_warn'],
hookspath=[],

Wyświetl plik

@ -1 +1 @@
__version__ = "0.1.6"
__version__ = "0.1.7"

Wyświetl plik

@ -20,6 +20,8 @@ def init_audio(widgets):
# Clear list
widgets["audioDeviceSelector"].clear()
# Add in the 'dummy' GQRX UDP interface
widgets["audioDeviceSelector"].addItem('GQRX UDP')
# Iterate through PyAudio devices
for x in range(0, pyAudio.get_device_count()):
@ -54,6 +56,11 @@ def populate_sample_rates(widgets):
# Get information on current audio device
_dev_name = widgets["audioDeviceSelector"].currentText()
# Add in fixed sample rate for GQRX input.
if _dev_name == 'GQRX UDP':
widgets["audioSampleRateSelector"].addItem(str(48000))
widgets["audioSampleRateSelector"].setCurrentIndex(0)
if _dev_name in audioDevices:
# TODO: Determine valid samples rates. For now, just use the default.
# TODO: Add support for resampling.

Wyświetl plik

@ -25,6 +25,7 @@ from threading import Thread
from .widgets import *
from .audio import *
from .udpaudio import *
from .fft import *
from .modem import *
from .config import *
@ -73,7 +74,7 @@ pg.mkQApp()
win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.setWindowTitle("Horus Telemetry GUI")
win.setWindowTitle(f"Horus Telemetry GUI - v{__version__}")
win.setWindowIcon(getHorusIcon())
# Create multiple dock areas, for displaying our data.
@ -599,8 +600,12 @@ def start_decoding():
if not running:
# Grab settings off widgets
_dev_name = widgets["audioDeviceSelector"].currentText()
_sample_rate = int(widgets["audioSampleRateSelector"].currentText())
_dev_index = audio_devices[_dev_name]["index"]
if _dev_name != 'GQRX UDP':
_sample_rate = int(widgets["audioSampleRateSelector"].currentText())
_dev_index = audio_devices[_dev_name]["index"]
else:
# Override sample rate for GQRX UDP input.
_sample_rate = 48000
# Grab Horus Settings
_modem_name = widgets["horusModemSelector"].currentText()
@ -647,18 +652,29 @@ def start_decoding():
mode=_modem_id,
rate=_modem_rate,
tone_spacing=_modem_tone_spacing,
callback=handle_new_packet
callback=handle_new_packet,
sample_rate=_sample_rate
)
# Setup Audio
audio_stream = AudioStream(
_dev_index,
fs=_sample_rate,
block_size=fft_process.stride,
fft_input=fft_process.add_samples,
modem=horus_modem,
stats_callback=add_stats_update
)
# Setup Audio (or UDP input)
if _dev_name == 'GQRX UDP':
audio_stream = UDPStream(
udp_port=7355,
fs=_sample_rate,
block_size=fft_process.stride,
fft_input=fft_process.add_samples,
modem=horus_modem,
stats_callback=add_stats_update
)
else:
audio_stream = AudioStream(
_dev_index,
fs=_sample_rate,
block_size=fft_process.stride,
fft_input=fft_process.add_samples,
modem=horus_modem,
stats_callback=add_stats_update
)
widgets["startDecodeButton"].setText("Stop")
running = True

Wyświetl plik

@ -0,0 +1,85 @@
# UDP Audio Source (Obtaining audio from GQRX)
import socket
import traceback
from threading import Thread
class UDPStream(object):
""" Listen for UDP Audio data from GQRX (s16, 48kHz), and pass data around to different callbacks """
def __init__(self, udp_port=7355, fs=48000, block_size=8192, fft_input=None, modem=None, stats_callback = None):
self.udp_port = udp_port
self.fs = fs
self.block_size = block_size
self.fft_input = fft_input
self.modem = modem
self.stats_callback = stats_callback
# Start audio stream
self.listen_thread_running = True
self.listen_thread = Thread(target=self.udp_listen_thread)
self.listen_thread.start()
def udp_listen_thread(self):
""" Open a UDP socket and listen for incoming data """
self.s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.s.settimeout(1)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# OSX Specific
try:
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except:
pass
self.s.bind(('',self.udp_port))
while self.listen_thread_running:
try:
m = self.s.recvfrom(65535)
except socket.timeout:
m = None
except:
traceback.print_exc()
if m != None:
self.handle_samples(m[0], len(m[0])//2)
self.s.close()
def handle_samples(self, data, frame_count, time_info="", status_flags=""):
""" Handle incoming samples from pyaudio """
# Pass samples directly into fft.
if self.fft_input:
self.fft_input(data)
if self.modem:
# Add samples to modem
_stats = self.modem.add_samples(data)
# Send any stats data back to the stats callback
if _stats:
if self.stats_callback:
self.stats_callback(_stats)
return (None, None)
def stop(self):
""" Halt stream """
self.listen_thread_running = False
if __name__ == "__main__":
import time
udp = UDPStream()
try:
while True:
time.sleep(5)
except KeyboardInterrupt:
udp.close()

Wyświetl plik

@ -1,6 +1,6 @@
[tool.poetry]
name = "horusgui"
version = "0.1.6"
version = "0.1.7"
description = ""
authors = ["Mark Jessop <vk5qi@rfhead.net>"]