Skip to content

Commit

Permalink
Use pipe for communication with soapy_power process, to avoid reading…
Browse files Browse the repository at this point in the history
… garbage from stdout (some SoapySDR drivers like LimeSDR always outputs something to stdout)
  • Loading branch information
xmikos committed Mar 13, 2017
1 parent 47ea0b9 commit d1c6ca4
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 15 deletions.
2 changes: 1 addition & 1 deletion PKGBUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Maintainer: Michal Krenek (Mikos) <[email protected]>
pkgname=qspectrumanalyzer
pkgver=1.5.0
pkgver=1.6.0
pkgrel=1
pkgdesc="Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, rx_power, rtl_power, hackrf_sweep and other backends)"
arch=('any')
Expand Down
18 changes: 10 additions & 8 deletions qspectrumanalyzer/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os, glob, subprocess
import os, glob, subprocess, threading

from PyQt4 import QtCore

Expand Down Expand Up @@ -53,6 +53,7 @@ def __init__(self, data_storage, parent=None):
self.data_storage = data_storage
self.alive = False
self.process = None
self._shutdown_lock = threading.Lock()

def stop(self):
"""Stop power process thread"""
Expand All @@ -71,13 +72,14 @@ def process_start(self):

def process_stop(self):
"""Terminate power process"""
if self.process:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None
with self._shutdown_lock:
if self.process:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None

def parse_output(self, line):
"""Parse one line of output from power process"""
Expand Down
62 changes: 57 additions & 5 deletions qspectrumanalyzer/backends/soapy_power.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import subprocess, pprint, sys, shlex
import os, subprocess, pprint, sys, shlex

import numpy as np
from PyQt4 import QtCore

from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
from soapypower.writer import SoapyPowerBinFormat

if sys.platform == 'win32':
import msvcrt
import _winapi

def _make_inheritable_handle(fd):
"""Return a duplicate of handle, which is inheritable"""
h = _winapi.DuplicateHandle(
_winapi.GetCurrentProcess(),
msvcrt.get_osfhandle(fd),
_winapi.GetCurrentProcess(), 0, 1,
_winapi.DUPLICATE_SAME_ACCESS
)
return subprocess.Handle(h)

formatter = SoapyPowerBinFormat()


Expand Down Expand Up @@ -41,13 +55,28 @@ def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.min_freq = 0

self.pipe_read = None
self.pipe_read_fd = None
self.pipe_write_fd = None
self.pipe_write_handle = None

print("soapy_power params:")
pprint.pprint(self.params)
print()

def process_start(self):
"""Start soapy_power process"""
if not self.process and self.params:
# Create pipe used for communication with soapy_power process
self.pipe_read_fd, self.pipe_write_fd = os.pipe()
self.pipe_read = open(self.pipe_read_fd, 'rb')
os.set_inheritable(self.pipe_write_fd, True)

if sys.platform == 'win32':
self.pipe_write_handle = _make_inheritable_handle(self.pipe_write_fd)
self.pipe_write_fd = int(self.pipe_write_handle)

# Prepare soapy_power cmdline parameters
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "soapy_power"),
Expand All @@ -59,6 +88,7 @@ def process_start(self):
"-r", "{}".format(self.params["sample_rate"]),
"-p", "{}".format(self.params["ppm"]),
"-F", "soapy_power_bin",
"--output-fd", "{}".format(self.pipe_write_fd),
]

if self.params["gain"] >= 0:
Expand All @@ -72,8 +102,30 @@ def process_start(self):
if additional_params:
cmdline.extend(shlex.split(additional_params))

self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=False)
# Start soapy_power process and close write part of pipe
self.process = subprocess.Popen(cmdline, close_fds=False, universal_newlines=False)
os.close(self.pipe_write_fd)

def process_stop(self):
"""Stop soapy_power process"""
with self._shutdown_lock:
if self.process:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None

# Close pipe used for communication with soapy_power process
self.pipe_read.close()
if sys.platform == 'win32':
self.pipe_write_handle.Close()

self.pipe_read = None
self.pipe_read_fd = None
self.pipe_write_fd = None
self.pipe_write_handle = None

def parse_output(self, data):
"""Parse data from soapy_power"""
Expand Down Expand Up @@ -111,10 +163,10 @@ def run(self):

while self.alive:
try:
data = formatter.read(self.process.stdout)
data = formatter.read(self.pipe_read)
except ValueError as e:
print(e, file=sys.stderr)
break
continue

if data:
self.parse_output(data)
Expand Down
2 changes: 1 addition & 1 deletion qspectrumanalyzer/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.5.0"
__version__ = "1.6.0"

0 comments on commit d1c6ca4

Please sign in to comment.