kopia lustrzana https://github.com/projecthorus/horus-gui
Add GQRX UDP input
rodzic
8d0e0ea544
commit
b935f82e6d
|
@ -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?
|
||||
|
||||
|
|
|
@ -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=[],
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.6"
|
||||
__version__ = "0.1.7"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "horusgui"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
description = ""
|
||||
authors = ["Mark Jessop <vk5qi@rfhead.net>"]
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue