diff --git a/htdocs/css/files.css b/htdocs/css/files.css index 3d921a49..7ad44f57 100644 --- a/htdocs/css/files.css +++ b/htdocs/css/files.css @@ -31,3 +31,9 @@ td { img { width: 100%; } + +.file-title { + text-align: center; + font-size: 80%; +} + diff --git a/htdocs/lib/MessagePanel.js b/htdocs/lib/MessagePanel.js index 33f2b3c8..bcaaab7d 100644 --- a/htdocs/lib/MessagePanel.js +++ b/htdocs/lib/MessagePanel.js @@ -301,8 +301,9 @@ SstvMessagePanel.prototype.pushMessage = function(msg) { // $b.scrollTop($b[0].scrollHeight); } else if(msg.width>0 && msg.height>0 && !msg.hasOwnProperty('line')) { + var f = msg.frequency>0? ' at ' + Math.floor(msg.frequency/1000) + 'kHz' : ''; var h = '
' + msg.timestamp + ' ' + msg.width + 'x' + msg.height + - ' ' + msg.sstvMode + '
'; + ' ' + msg.sstvMode + f + ''; var c = '
' + '' + ('' % (files[i], files[i])) + ('' % (files[i], files[i])) + - ('

%s

' % files[i]) + + ('

%s

' % files[i]) + '
\n') # Finish a row if i % 3 == 2: diff --git a/owrx/http.py b/owrx/http.py index ebb35b70..12995cac 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -23,6 +23,7 @@ from owrx.controllers.session import SessionController from owrx.controllers.profile import ProfileController from owrx.controllers.imageupload import ImageUploadController from owrx.controllers.robots import RobotsController +from owrx.storage import Storage from http.server import BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs import re @@ -98,7 +99,7 @@ class Router(object): StaticRoute("/policy", PolicyController), StaticRoute("/features", FeatureController), StaticRoute("/files", FilesController), - RegexRoute("^/files/(SSTV-[0-9]+-[0-9]+\.bmp)$", FileController), + RegexRoute("^/files/(%s)$" % Storage().getNamePattern(), FileController), StaticRoute("/api/features", ApiController), StaticRoute("/metrics", MetricsController, options={"action": "prometheusAction"}), StaticRoute("/metrics.json", MetricsController), diff --git a/owrx/sstv.py b/owrx/sstv.py index 8dfda80a..eab424ed 100644 --- a/owrx/sstv.py +++ b/owrx/sstv.py @@ -17,7 +17,7 @@ modeNames = { 44: "Martin 1", 56: "Scottie 2", 60: "Scottie 1", - 76: "Scottie DX", + 75: "Scottie DX", # Unsupported modes 0: "Robot 12", @@ -58,13 +58,14 @@ modeNames = { class SstvParser(ThreadModule): 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 + self.service = service + self.frequency = 0 + self.file = None + self.data = bytearray(b'') + self.width = 0 + self.height = 0 + self.line = 0 + self.mode = 0 super().__init__() def __del__(self): @@ -92,7 +93,7 @@ class SstvParser(ThreadModule): def newFile(self, fileName): self.closeFile() try: - self.fileName = Storage().getStoredFilePath(fileName + ".bmp") + self.fileName = Storage().getFilePath(fileName + ".bmp") logger.debug("Opening bitmap file '%s'..." % self.fileName) self.file = open(self.fileName, "wb") except Exception: @@ -111,6 +112,9 @@ class SstvParser(ThreadModule): def getOutputFormat(self) -> Format: return Format.CHAR + def setDialFrequency(self, frequency: int) -> None: + self.frequency = frequency + def run(self): # Run while there is input data while self.doRun: @@ -141,9 +145,10 @@ class SstvParser(ThreadModule): self.mode = self.data[6] self.line = 0 # Find mode name and time - modeName = modeNames.get(self.mode) if self.mode in modeNames else "Unknown Mode" + modeName = modeNames.get(self.mode) if self.mode in modeNames else "Unknown Mode %d" % self.mode timeStamp = datetime.utcnow().strftime("%H:%M:%S") - fileName = Storage().makeStoredFileName("SSTV-{0}") + fileName = Storage().makeFileName("SSTV-{0}", self.frequency) + logger.debug("Receiving %dx%d %s frame as '%s'." % (self.width, self.height, modeName, fileName)) # If running as a service... if self.service: # Create a new image file and write BMP header @@ -158,7 +163,8 @@ class SstvParser(ThreadModule): "height": self.height, "sstvMode": modeName, "timestamp": timeStamp, - "filename": fileName + "filename": fileName, + "frequency": self.frequency } # Parse debug messages enclosed in ' [...]' diff --git a/owrx/storage.py b/owrx/storage.py index 59e62b18..db2db65e 100644 --- a/owrx/storage.py +++ b/owrx/storage.py @@ -11,16 +11,22 @@ logger = logging.getLogger(__name__) class Storage(object): def __init__(self): - self.filePattern = r'[A-Z]+-[0-9]+-[0-9]+\.bmp' + self.filePattern = r'[A-Z]+-[0-9]+-[0-9]+(-[0-9]+)?\.bmp' + + # Get file name pattern + def getNamePattern(self): + return self.filePattern # Create stored file name by inserting current UTC date # and time into the pattern spot designated with "{0}" - def makeStoredFileName(self, pattern): - return pattern.format(datetime.utcnow().strftime('%y%m%d-%H%M%S')) + def makeFileName(self, pattern: str = '{0}', frequency: int = 0): + d = datetime.utcnow().strftime('%y%m%d-%H%M%S') + f = ('-%d' % (frequency // 1000)) if frequency>0 else '' + return pattern.format(d + f) # Get complete path to a stored file from its filename by # adding folder name - def getStoredFilePath(self, filename): + def getFilePath(self, filename: str): return os.path.join(CoreConfig().get_temporary_directory(), filename) # Get list of stored files, sorted in reverse alphabetic order