diff --git a/radio.py b/radio.py index 5a2a6d6..685d83a 100644 --- a/radio.py +++ b/radio.py @@ -3,6 +3,9 @@ import SoapySDR as soapy import prefixed from formats import * import sys +import os +import subprocess +import struct class Radio: @@ -11,9 +14,6 @@ class Radio: def __init__(self, device_info): self.device_info = device_info - self.stream = None - self.buffer = None - self.port = None self.run = False self.thread = None @@ -21,7 +21,6 @@ class Radio: if self.device is None: raise RuntimeError("Failed to connect to radio device") - def configure(self, frequency): frequency = int(prefixed.Float(frequency)) sample_rate = 384000 @@ -41,31 +40,14 @@ class Radio: 'gain-mode': 'auto' if self.device.getGainMode(soapy.SOAPY_SDR_RX, 0) else 'manual', } - def start_stream(self): if self.thread: raise RuntimeError('Stream thread is already running') - self.buffer = np.array([0] * Radio.SAMPLES * 2, TYPES[Radio.FORMAT]) - self.stream = self.device.setupStream(soapy.SOAPY_SDR_RX, FORMATS[Radio.FORMAT]) - self.device.activateStream(self.stream) - self.run = True self.thread = Thread(target=self._stream_thread, daemon=True, args=()) self.thread.start() - - def _stream_thread(self): - while self.run: - result = self.device.readStream(self.stream, [self.buffer], Radio.SAMPLES) - - if result.ret < 1: - print('Stream read failed, exiting.', file=sys.stderr) - self.keep_going = False - - self._cleanup_stream() - - def end_stream(self): if self.thread is None: raise RuntimeError('No stream thread to terminate') @@ -74,8 +56,72 @@ class Radio: self.thread.join() self.thread = None + def _stream_thread(self): + self._init_stream() + + while self.run: + result = self.device.readStream(self.stream, [self.buffer], Radio.SAMPLES) + + if result.ret < 1: + print('Stream read failed, exiting.', file=sys.stderr) + self.keep_going = False + + read_size = int(result.ret * 2) + self.demod.stdin.write( + struct.pack(PACKINGS[Radio.FORMAT] % read_size, + *self.buffer[:read_size]) + ) + + self._cleanup_stream() + + def _init_stream(self): + self.playback = subprocess.Popen( + ['/usr/bin/sox', '-t', 'raw', '-r', '32000', '-b', '16', '-c', '2', '-L', '-e', 'signed-integer', '-', '-d'], + stdin=subprocess.PIPE, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + + self.demod = subprocess.Popen( + ['/usr/bin/python3', 'fm_demod.py', '-f', 'CS16', '-s', '384k', '-d', '32k'], + stdin=subprocess.PIPE, + stdout=self.playback.stdin, + stderr=subprocess.DEVNULL + ) + + self.buffer = np.array([0] * Radio.SAMPLES * 2, TYPES[Radio.FORMAT]) + self.stream = self.device.setupStream(soapy.SOAPY_SDR_RX, FORMATS[Radio.FORMAT]) + self.device.activateStream(self.stream) def _cleanup_stream(self): - #shutdown the stream + # Cleanup the streaming objects in the reverse order to the _init method. self.device.deactivateStream(self.stream) - self.device.closeStream(self.stream) \ No newline at end of file + self.device.closeStream(self.stream) + self.buffer = None + + self.demod.kill() + self.playback.kill() + + +""" +Quick and dirty test of the Radio class. +""" +if __name__ == '__main__': + import time + + sdr = Radio({'driver': 'sdrplay'}) + + print('Configuring...') + sdr.configure('105.5M') + print('Configured.') + + print('Starting stream...') + sdr.start_stream() + print('Stream started.') + + # Let the stream play for a while + time.sleep(5) + + print('Ending stream...') + sdr.end_stream() + print('Stream ended.') \ No newline at end of file