From 20f425c6f3b67d1c02c546f64c4fff7736e99f2e Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Thu, 16 Feb 2023 13:27:50 -0500 Subject: [PATCH] Enabling SSTV background service. --- csdr/chain/digimodes.py | 4 +-- owrx/modes.py | 2 ++ owrx/service/__init__.py | 3 ++ owrx/sstv.py | 63 +++++++++++++++++++++++++++++++++++----- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 0629b401..7be5c4f2 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -115,12 +115,12 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): class SstvDemodulator(ServiceDemodulator): - def __init__(self): + def __init__(self, service: bool = False): self.sampleRate = 48000 workers = [ Agc(Format.COMPLEX_FLOAT), SstvDecoder(self.sampleRate), - SstvParser() + SstvParser(service=service) ] super().__init__(workers) diff --git a/owrx/modes.py b/owrx/modes.py index 5929d2ba..7a6b1fdb 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -146,6 +146,8 @@ class Modes(object): "SSTV", underlying=["usb", "lsb"], bandpass=Bandpass(0, 3000), + service=True, + squelch=False, ), ] diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index e0ad540a..ffe2d9bf 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -291,6 +291,9 @@ class ServiceHandler(SdrSourceEventClient): elif mod == "packet": from csdr.chain.digimodes import PacketDemodulator return PacketDemodulator(service=True) + elif mod == "sstv": + from csdr.chain.digimodes import SstvDemodulator + return SstvDemodulator(service=True) return None diff --git a/owrx/sstv.py b/owrx/sstv.py index f4d4357d..8e01fb91 100644 --- a/owrx/sstv.py +++ b/owrx/sstv.py @@ -3,6 +3,7 @@ from pycsdr.types import Format from datetime import datetime import base64 import pickle +import os import logging @@ -55,14 +56,48 @@ modeNames = { } class SstvParser(ThreadModule): - def __init__(self): - self.data = bytearray(b'') - self.width = 0 - self.height = 0 - self.line = 0 - self.mode = 0 + def __init__(self, service: bool = False): + self.service = service + self.file = None + self.data = bytearray(b'') + self.width = 0 + self.height = 0 + self.line = 0 + self.mode = 0 super().__init__() + def __del__(self): + # Close currently open file, if any + self.closeFile() + + def closeFile(self): + if self.file is not None: + try: + logger.debug("Closing bitmap file '%s'." % self.fileName) + self.file.close() + self.file = None + if self.line==0: + logger.debug("Deleting empty bitmap file '%s'." % self.fileName) + os.unlink(self.fileName) + except Exception: + self.file = None + + def newFile(self, fileName): + self.closeFile() + try: + self.fileName = "/tmp/%s.bmp" % fileName + logger.debug("Opening bitmap file '%s'..." % self.fileName) + self.file = open(self.fileName, "wb") + except Exception: + self.file = None + + def writeFile(self, data): + if self.file is not None: + try: + self.file.write(data) + except Exception: + pass + def getInputFormat(self) -> Format: return Format.CHAR @@ -98,12 +133,17 @@ class SstvParser(ThreadModule): # SSTV mode is passed via reserved area at offset 6 self.mode = self.data[6] self.line = 0 - # Remove parsed data - del self.data[0:54] # Find mode name and time modeName = modeNames.get(self.mode) if self.mode in modeNames else "Unknown Mode" timeStamp = datetime.now().strftime("%H:%M:%S") fileName = datetime.now().strftime("SSTV-%y%m%d-%H%M%S") + # If running as a service... + if self.service: + # Create a new image file and write BMP header + self.newFile(fileName) + self.writeFile(self.data[0:54]) + # Remove parsed data + del self.data[0:54] # Return parsed values return { "mode": "SSTV", @@ -146,6 +186,13 @@ class SstvParser(ThreadModule): } # Advance scanline self.line = self.line + 1 + # If running as a service... + if self.service: + # Write a scanline into open image file + self.writeFile(self.data[0:w]) + # Close once the last scanline reached + if self.line>=self.height: + self.closeFile() # If we reached the end of frame, finish scan if self.line>=self.height: self.width = 0