add support for DMR locations
This commit is contained in:
parent
6fbe6b4983
commit
78dcdd5715
@ -115,7 +115,7 @@
|
|||||||
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
|
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
|
||||||
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
|
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-dmr-id"></div>
|
<div class="openwebrx-dmr-id"><span class="location"></span><span class="dmr-id"></span></div>
|
||||||
<div class="openwebrx-dmr-name"></div>
|
<div class="openwebrx-dmr-name"></div>
|
||||||
<div class="openwebrx-dmr-target"></div>
|
<div class="openwebrx-dmr-target"></div>
|
||||||
<div class="mute">
|
<div class="mute">
|
||||||
@ -128,7 +128,7 @@
|
|||||||
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
|
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
|
||||||
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
|
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-dmr-id"></div>
|
<div class="openwebrx-dmr-id"><span class="location"></span><span class="dmr-id"></span></div>
|
||||||
<div class="openwebrx-dmr-name"></div>
|
<div class="openwebrx-dmr-name"></div>
|
||||||
<div class="openwebrx-dmr-target"></div>
|
<div class="openwebrx-dmr-target"></div>
|
||||||
<div class="mute">
|
<div class="mute">
|
||||||
|
@ -26,16 +26,27 @@ DmrMetaSlot.prototype.update = function(data) {
|
|||||||
this.setName(data['additional'] && data['additional']['fname']);
|
this.setName(data['additional'] && data['additional']['fname']);
|
||||||
this.setMode(['group', 'direct'].includes(data['type']) ? data['type'] : undefined);
|
this.setMode(['group', 'direct'].includes(data['type']) ? data['type'] : undefined);
|
||||||
this.setTarget(data['target']);
|
this.setTarget(data['target']);
|
||||||
|
this.setLocation(data['lat'], data['lon'], this.getCallsign(data));
|
||||||
this.el.addClass("active");
|
this.el.addClass("active");
|
||||||
} else {
|
} else {
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DmrMetaSlot.prototype.getCallsign = function(data) {
|
||||||
|
if ('additional' in data) {
|
||||||
|
return data['additional']['callsign'];
|
||||||
|
}
|
||||||
|
if ('talkeralias' in data) {
|
||||||
|
var matches = /^([A-Z0-9]+)(\s.*)?$/.exec(data['talkeralias']);
|
||||||
|
if (matches) return matches[1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
DmrMetaSlot.prototype.setId = function(id) {
|
DmrMetaSlot.prototype.setId = function(id) {
|
||||||
if (this.id === id) return;
|
if (this.id === id) return;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.el.find('.openwebrx-dmr-id').text(id || '');
|
this.el.find('.openwebrx-dmr-id .dmr-id').text(id || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
DmrMetaSlot.prototype.setName = function(name) {
|
DmrMetaSlot.prototype.setName = function(name) {
|
||||||
@ -59,11 +70,23 @@ DmrMetaSlot.prototype.setTarget = function(target) {
|
|||||||
this.el.find('.openwebrx-dmr-target').text(target || '');
|
this.el.find('.openwebrx-dmr-target').text(target || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DmrMetaSlot.prototype.setLocation = function(lat, lon, callsign) {
|
||||||
|
var hasLocation = lat && lon && callsign && callsign != '';
|
||||||
|
if (hasLocation === this.hasLocation && this.callsign === callsign) return;
|
||||||
|
this.hasLocation = hasLocation; this.callsign = callsign;
|
||||||
|
var html = '';
|
||||||
|
if (hasLocation) {
|
||||||
|
html = '<a class="openwebrx-maps-pin" href="map?callsign=' + encodeURIComponent(callsign) + '" target="_blank"><svg viewBox="0 0 20 35"><use xlink:href="static/gfx/svg-defs.svg#maps-pin"></use></svg></a>';
|
||||||
|
}
|
||||||
|
this.el.find('.openwebrx-dmr-id .location').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
DmrMetaSlot.prototype.clear = function() {
|
DmrMetaSlot.prototype.clear = function() {
|
||||||
this.setId();
|
this.setId();
|
||||||
this.setName();
|
this.setName();
|
||||||
this.setMode();
|
this.setMode();
|
||||||
this.setTarget();
|
this.setTarget();
|
||||||
|
this.setLocation();
|
||||||
this.el.removeClass("active");
|
this.el.removeClass("active");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -250,7 +273,7 @@ NxdnMetaPanel.prototype = new MetaPanel();
|
|||||||
NxdnMetaPanel.prototype.update = function(data) {
|
NxdnMetaPanel.prototype.update = function(data) {
|
||||||
if (!this.isSupported(data)) return;
|
if (!this.isSupported(data)) return;
|
||||||
|
|
||||||
if (data['sync'] && data['sync'] == 'voice') {
|
if (data['sync'] && data['sync'] === 'voice') {
|
||||||
this.el.find(".openwebrx-meta-slot").addClass("active");
|
this.el.find(".openwebrx-meta-slot").addClass("active");
|
||||||
this.setSource(data['additional'] && data['additional']['callsign'] || data['source']);
|
this.setSource(data['additional'] && data['additional']['callsign'] || data['source']);
|
||||||
this.setName(data['additional'] && data['additional']['fname']);
|
this.setName(data['additional'] && data['additional']['fname']);
|
||||||
|
@ -1266,6 +1266,9 @@ function digimodes_init() {
|
|||||||
$('.openwebrx-dmr-timeslot-panel').click(function (e) {
|
$('.openwebrx-dmr-timeslot-panel').click(function (e) {
|
||||||
$(e.currentTarget).toggleClass("muted");
|
$(e.currentTarget).toggleClass("muted");
|
||||||
update_dmr_timeslot_filtering();
|
update_dmr_timeslot_filtering();
|
||||||
|
// don't mute when the location icon is clicked
|
||||||
|
}).find('.location').click(function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.openwebrx-meta-panel').metaPanel();
|
$('.openwebrx-meta-panel').metaPanel();
|
||||||
|
76
owrx/meta.py
76
owrx/meta.py
@ -2,7 +2,8 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import pickle
|
import pickle
|
||||||
from abc import ABC, abstractmethod
|
import re
|
||||||
|
from abc import ABC, ABCMeta, abstractmethod
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from urllib import request
|
from urllib import request
|
||||||
from urllib.error import HTTPError
|
from urllib.error import HTTPError
|
||||||
@ -120,28 +121,71 @@ class RadioIDEnricher(Enricher):
|
|||||||
return meta
|
return meta
|
||||||
|
|
||||||
|
|
||||||
class YsfMetaEnricher(Enricher):
|
class DigihamEnricher(Enricher, metaclass=ABCMeta):
|
||||||
def enrich(self, meta, callback):
|
def parseCoordinate(self, meta, mode):
|
||||||
for key in ["source", "up", "down", "target"]:
|
|
||||||
if key in meta:
|
|
||||||
meta[key] = meta[key].strip()
|
|
||||||
for key in ["lat", "lon"]:
|
for key in ["lat", "lon"]:
|
||||||
if key in meta:
|
if key in meta:
|
||||||
meta[key] = float(meta[key])
|
meta[key] = float(meta[key])
|
||||||
if "source" in meta and "lat" in meta and "lon" in meta:
|
callsign = self.getCallsign(meta)
|
||||||
|
if callsign is not None and "lat" in meta and "lon" in meta:
|
||||||
loc = LatLngLocation(meta["lat"], meta["lon"])
|
loc = LatLngLocation(meta["lat"], meta["lon"])
|
||||||
Map.getSharedInstance().updateLocation(meta["source"], loc, "YSF", self.parser.getBand())
|
Map.getSharedInstance().updateLocation(callsign, loc, mode, self.parser.getBand())
|
||||||
|
return meta
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def getCallsign(self, meta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DmrEnricher(DigihamEnricher, RadioIDEnricher):
|
||||||
|
# callsign must be uppercase alphanumeric and at the beginning
|
||||||
|
# if there's anything after the callsign, it must be separated by a whitespace
|
||||||
|
talkerAliasRegex = re.compile("^([A-Z0-9]+)(\\s.*)?$")
|
||||||
|
|
||||||
|
def __init__(self, parser):
|
||||||
|
super().__init__("dmr", parser)
|
||||||
|
|
||||||
|
def getCallsign(self, meta):
|
||||||
|
# there's no explicit callsign data in dmr, so we can only rely on one of the following:
|
||||||
|
# a) a callsign provided by a radioid lookup
|
||||||
|
if "additional" in meta and "callsign" in meta["additional"]:
|
||||||
|
return meta["additional"]["callsign"]
|
||||||
|
# b) a callsign in the talker alias
|
||||||
|
if "talkeralias" in meta:
|
||||||
|
matches = DmrEnricher.talkerAliasRegex.match(meta["talkeralias"])
|
||||||
|
if matches:
|
||||||
|
return matches.group(1)
|
||||||
|
|
||||||
|
def enrich(self, meta, callback):
|
||||||
|
def asyncParse(meta):
|
||||||
|
self.parseCoordinate(meta, "DMR")
|
||||||
|
callback(meta)
|
||||||
|
meta = super().enrich(meta, asyncParse)
|
||||||
|
meta = self.parseCoordinate(meta, "DMR")
|
||||||
return meta
|
return meta
|
||||||
|
|
||||||
|
|
||||||
class DStarEnricher(Enricher):
|
class YsfMetaEnricher(DigihamEnricher):
|
||||||
|
def getCallsign(self, meta):
|
||||||
|
if "source" in meta:
|
||||||
|
return meta["source"]
|
||||||
|
|
||||||
def enrich(self, meta, callback):
|
def enrich(self, meta, callback):
|
||||||
for key in ["lat", "lon"]:
|
meta = self.parseCoordinate(meta, "YSF")
|
||||||
if key in meta:
|
return meta
|
||||||
meta[key] = float(meta[key])
|
|
||||||
if "ourcall" in meta and "lat" in meta and "lon" in meta:
|
|
||||||
loc = LatLngLocation(meta["lat"], meta["lon"])
|
class DStarEnricher(DigihamEnricher):
|
||||||
Map.getSharedInstance().updateLocation(meta["ourcall"], loc, "D-Star", self.parser.getBand())
|
def getCallsign(self, meta):
|
||||||
|
if "ourcall" in meta:
|
||||||
|
return meta["ourcall"]
|
||||||
|
|
||||||
|
def enrich(self, meta, callback):
|
||||||
|
meta = self.parseCoordinate(meta, "D-Star")
|
||||||
|
meta = self.parseDprs(meta)
|
||||||
|
return meta
|
||||||
|
|
||||||
|
def parseDprs(self, meta):
|
||||||
if "dprs" in meta:
|
if "dprs" in meta:
|
||||||
try:
|
try:
|
||||||
# we can send the DPRS stuff through our APRS parser to extract the information
|
# we can send the DPRS stuff through our APRS parser to extract the information
|
||||||
@ -168,7 +212,7 @@ class DStarEnricher(Enricher):
|
|||||||
class MetaParser(PickleModule):
|
class MetaParser(PickleModule):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.enrichers = {
|
self.enrichers = {
|
||||||
"DMR": RadioIDEnricher("dmr", self),
|
"DMR": DmrEnricher(self),
|
||||||
"YSF": YsfMetaEnricher(self),
|
"YSF": YsfMetaEnricher(self),
|
||||||
"DSTAR": DStarEnricher(self),
|
"DSTAR": DStarEnricher(self),
|
||||||
"NXDN": RadioIDEnricher("nxdn", self),
|
"NXDN": RadioIDEnricher("nxdn", self),
|
||||||
|
Loading…
Reference in New Issue
Block a user