add preliminary parsing and display of M17 metadata

This commit is contained in:
Jakob Ketterl 2021-12-21 21:18:17 +01:00
parent 81b8f183c2
commit 40c68933e1
6 changed files with 117 additions and 8 deletions

View File

@ -1,18 +1,19 @@
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, MetaProvider
from csdr.module.m17 import M17Module from csdr.module.m17 import M17Module
from pycsdr.modules import FmDemod, Limit, Convert from pycsdr.modules import FmDemod, Limit, Convert, Writer
from pycsdr.types import Format from pycsdr.types import Format
from digiham.modules import DcBlock from digiham.modules import DcBlock
class M17(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain): class M17(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, MetaProvider):
def __init__(self): def __init__(self):
self.module = M17Module()
workers = [ workers = [
FmDemod(), FmDemod(),
DcBlock(), DcBlock(),
Limit(), Limit(),
Convert(Format.FLOAT, Format.SHORT), Convert(Format.FLOAT, Format.SHORT),
M17Module(), self.module,
] ]
super().__init__(workers) super().__init__(workers)
@ -24,3 +25,6 @@ class M17(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain):
def supportsSquelch(self) -> bool: def supportsSquelch(self) -> bool:
return False return False
def setMetaWriter(self, writer: Writer) -> None:
self.module.setMetaWriter(writer)

View File

@ -118,8 +118,11 @@ class PopenModule(AutoStartModule, metaclass=ABCMeta):
def getCommand(self): def getCommand(self):
pass pass
def _getProcess(self):
return Popen(self.getCommand(), stdin=PIPE, stdout=PIPE)
def start(self): def start(self):
self.process = Popen(self.getCommand(), stdin=PIPE, stdout=PIPE) self.process = self._getProcess()
# resume in case the reader has been stop()ed before # resume in case the reader has been stop()ed before
self.reader.resume() self.reader.resume()
Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start() Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start()

View File

@ -1,8 +1,20 @@
from csdr.module import PopenModule from csdr.module import PopenModule
from pycsdr.types import Format from pycsdr.types import Format
from pycsdr.modules import Writer
from subprocess import Popen, PIPE
from threading import Thread
import re
import pickle
class M17Module(PopenModule): class M17Module(PopenModule):
lsfRegex = re.compile("SRC: ([a-zA-Z0-9]+), DEST: ([a-zA-Z0-9]+)")
def __init__(self):
super().__init__()
self.metawriter = None
def getInputFormat(self) -> Format: def getInputFormat(self) -> Format:
return Format.SHORT return Format.SHORT
@ -10,4 +22,37 @@ class M17Module(PopenModule):
return Format.SHORT return Format.SHORT
def getCommand(self): def getCommand(self):
return ["m17-demod"] return ["m17-demod", "-l"]
def _getProcess(self):
return Popen(self.getCommand(), stdin=PIPE, stdout=PIPE, stderr=PIPE)
def start(self):
super().start()
Thread(target=self._readOutput).start()
def _readOutput(self):
while True:
line = self.process.stderr.readline()
if not line:
break
self.parseOutput(line.decode())
def parseOutput(self, line):
if self.metawriter is None:
return
matches = self.lsfRegex.match(line)
msg = {"protocol": "M17"}
if matches:
# fake sync
msg["sync"] = "voice"
msg["source"] = matches.group(1)
msg["destination"] = matches.group(2)
elif line.startswith("EOS"):
pass
else:
return
self.metawriter.write(pickle.dumps(msg))
def setMetaWriter(self, writer: Writer) -> None:
self.metawriter = writer

View File

@ -1025,7 +1025,8 @@ img.openwebrx-mirror-img
.openwebrx-meta-slot.active.direct .openwebrx-meta-user-image .directcall, .openwebrx-meta-slot.active.direct .openwebrx-meta-user-image .directcall,
.openwebrx-meta-slot.active.individual .openwebrx-meta-user-image .directcall, .openwebrx-meta-slot.active.individual .openwebrx-meta-user-image .directcall,
#openwebrx-panel-metadata-ysf .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall, #openwebrx-panel-metadata-ysf .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall,
#openwebrx-panel-metadata-dstar .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall { #openwebrx-panel-metadata-dstar .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall,
#openwebrx-panel-metadata-m17 .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall {
display: initial; display: initial;
} }
@ -1059,6 +1060,14 @@ img.openwebrx-mirror-img
content: "Down: "; content: "Down: ";
} }
.openwebrx-m17-source:not(:empty):before {
content: "SRC: ";
}
.openwebrx-m17-destination:not(:empty):before {
content: "DEST: ";
}
.openwebrx-dstar-yourcall:not(:empty):before { .openwebrx-dstar-yourcall:not(:empty):before {
content: "UR: "; content: "UR: ";
} }

View File

@ -74,6 +74,15 @@
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-js8-message" style="display:none; width: 619px;" data-panel-name="js8-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-js8-message" style="display:none; width: 619px;" data-panel-name="js8-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-message"></div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-m17" style="display: none;" data-panel-name="metadata-m17">
<div class="openwebrx-meta-slot">
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
</div>
<div class="openwebrx-m17-source"></div>
<div class="openwebrx-m17-destination"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-ysf" style="display: none;" data-panel-name="metadata-ysf"> <div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-ysf" style="display: none;" data-panel-name="metadata-ysf">
<div class="openwebrx-meta-slot"> <div class="openwebrx-meta-slot">
<div class="openwebrx-ysf-mode"></div> <div class="openwebrx-ysf-mode"></div>

View File

@ -321,18 +321,57 @@ NxdnMetaPanel.prototype.clear = function() {
this.setDestination(); this.setDestination();
}; };
function M17MetaPanel(el) {
MetaPanel.call(this, el);
this.modes = ['M17'];
this.clear();
}
M17MetaPanel.prototype = new MetaPanel();
M17MetaPanel.prototype.update = function(data) {
if (!this.isSupported(data)) return;
if (data['sync'] && data['sync'] === 'voice') {
this.el.find(".openwebrx-meta-slot").addClass("active");
this.setSource(data['source']);
this.setDestination(data['destination']);
} else {
this.clear();
}
};
M17MetaPanel.prototype.setSource = function(source) {
if (this.source === source) return;
this.source = source;
this.el.find('.openwebrx-m17-source').text(source || '');
};
M17MetaPanel.prototype.setDestination = function(destination) {
if (this.destination === destination) return;
this.destination = destination;
this.el.find('.openwebrx-m17-destination').text(destination || '');
};
M17MetaPanel.prototype.clear = function() {
MetaPanel.prototype.clear.call(this);
this.setSource();
this.setDestination();
};
MetaPanel.types = { MetaPanel.types = {
dmr: DmrMetaPanel, dmr: DmrMetaPanel,
ysf: YsfMetaPanel, ysf: YsfMetaPanel,
dstar: DStarMetaPanel, dstar: DStarMetaPanel,
nxdn: NxdnMetaPanel, nxdn: NxdnMetaPanel,
m17: M17MetaPanel,
}; };
$.fn.metaPanel = function() { $.fn.metaPanel = function() {
return this.map(function() { return this.map(function() {
var $self = $(this); var $self = $(this);
if (!$self.data('metapanel')) { if (!$self.data('metapanel')) {
var matches = /^openwebrx-panel-metadata-([a-z]+)$/.exec($self.prop('id')); var matches = /^openwebrx-panel-metadata-([a-z0-9]+)$/.exec($self.prop('id'));
var constructor = matches && MetaPanel.types[matches[1]] || MetaPanel; var constructor = matches && MetaPanel.types[matches[1]] || MetaPanel;
$self.data('metapanel', new constructor($self)); $self.data('metapanel', new constructor($self));
} }