mirror of
https://github.com/jketterl/openwebrx.git
synced 2026-01-14 04:30:32 +01:00
Updating with the latest OWRX+ HDRadio changes.
This commit is contained in:
parent
5b7397da46
commit
5fa7e9d695
|
|
@ -1,5 +1,5 @@
|
|||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, \
|
||||
MetaProvider, DabServiceSelector, DialFrequencyReceiver
|
||||
MetaProvider, AudioServiceSelector, DialFrequencyReceiver
|
||||
from csdr.module import PickleModule
|
||||
from csdreti.modules import EtiDecoder
|
||||
from owrx.dab.dablin import DablinModule
|
||||
|
|
@ -58,7 +58,7 @@ class MetaProcessor(PickleModule):
|
|||
self.shifter.setRate(0)
|
||||
|
||||
|
||||
class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, MetaProvider, DabServiceSelector, DialFrequencyReceiver):
|
||||
class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, MetaProvider, AudioServiceSelector, DialFrequencyReceiver):
|
||||
def __init__(self):
|
||||
shift = Shift(0)
|
||||
self.decoder = EtiDecoder()
|
||||
|
|
@ -99,7 +99,7 @@ class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain,
|
|||
def setMetaWriter(self, writer: Writer) -> None:
|
||||
self.processor.setWriter(writer)
|
||||
|
||||
def setDabServiceId(self, serviceId: int) -> None:
|
||||
def setAudioServiceId(self, serviceId: int) -> None:
|
||||
self.decoder.setServiceIdFilter([serviceId])
|
||||
self.dablin.setDabServiceId(serviceId)
|
||||
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ class RdsChain(ABC):
|
|||
pass
|
||||
|
||||
|
||||
class DabServiceSelector(ABC):
|
||||
class AudioServiceSelector(ABC):
|
||||
@abstractmethod
|
||||
def setDabServiceId(self, serviceId: int) -> None:
|
||||
def setAudioServiceId(self, serviceId: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,50 @@
|
|||
from csdr.chain.demodulator import FixedIfSampleRateChain, BaseDemodulatorChain, FixedAudioRateChain, DialFrequencyReceiver
|
||||
from csdr.chain.demodulator import FixedIfSampleRateChain, BaseDemodulatorChain, FixedAudioRateChain, DialFrequencyReceiver, HdAudio, MetaProvider, AudioServiceSelector
|
||||
from csdr.module.hdradio import HdRadioModule
|
||||
from pycsdr.modules import Convert, Agc, Downmix, Writer
|
||||
from pycsdr.modules import Convert, Agc, Downmix, Writer, Buffer, Throttle
|
||||
from pycsdr.types import Format
|
||||
from typing import Optional
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
class HdRadio(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, DialFrequencyReceiver):
|
||||
class HdRadio(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, MetaProvider, DialFrequencyReceiver, AudioServiceSelector):
|
||||
def __init__(self, program: int = 0):
|
||||
self.hdradio = HdRadioModule(program = program)
|
||||
workers = [
|
||||
Agc(Format.COMPLEX_FLOAT),
|
||||
Convert(Format.COMPLEX_FLOAT, Format.COMPLEX_SHORT),
|
||||
self.hdradio,
|
||||
Throttle(Format.SHORT, 44100 * 2),
|
||||
Downmix(Format.SHORT),
|
||||
]
|
||||
super().__init__(workers)
|
||||
|
||||
def getFixedIfSampleRate(self):
|
||||
def getFixedIfSampleRate(self) -> int:
|
||||
return self.hdradio.getFixedAudioRate()
|
||||
|
||||
def getFixedAudioRate(self):
|
||||
def getFixedAudioRate(self) -> int:
|
||||
return 44100
|
||||
|
||||
def supportsSquelch(self) -> bool:
|
||||
return False
|
||||
|
||||
# Set metadata consumer
|
||||
def setMetaWriter(self, writer: Writer) -> None:
|
||||
self.hdradio.setMetaWriter(writer)
|
||||
|
||||
# Change program
|
||||
def setProgram(self, program: int) -> None:
|
||||
self.hdradio.setProgram(program)
|
||||
def setAudioServiceId(self, serviceId: int) -> None:
|
||||
self.hdradio.setProgram(serviceId)
|
||||
|
||||
def setDialFrequency(self, frequency: int) -> None:
|
||||
# Clear station metadata when changing frequency
|
||||
pass
|
||||
self.hdradio.setFrequency(frequency)
|
||||
|
||||
def _connect(self, w1, w2, buffer: Optional[Buffer] = None) -> None:
|
||||
if isinstance(w2, Throttle):
|
||||
# Audio data comes in in bursts, so we use a throttle
|
||||
# and 10x the default buffer size here
|
||||
buffer = Buffer(Format.SHORT, 2621440)
|
||||
return super()._connect(w1, w2, buffer)
|
||||
|
|
|
|||
|
|
@ -2,23 +2,56 @@ from csdr.module.nrsc5 import NRSC5, Mode, EventType, ComponentType, Access
|
|||
from csdr.module import ThreadModule
|
||||
from pycsdr.modules import Writer
|
||||
from pycsdr.types import Format
|
||||
from owrx.map import Map, LatLngLocation
|
||||
|
||||
import logging
|
||||
import threading
|
||||
import pickle
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
class StationLocation(LatLngLocation):
|
||||
def __init__(self, data):
|
||||
super().__init__(data["lat"], data["lon"])
|
||||
# Complete station data
|
||||
self.data = data
|
||||
|
||||
def getSymbolData(self, symbol, table):
|
||||
return {"symbol": symbol, "table": table, "index": ord(symbol) - 33, "tableindex": ord(table) - 33}
|
||||
|
||||
def __dict__(self):
|
||||
# Return APRS-like dictionary object with "antenna tower" symbol
|
||||
res = super(StationLocation, self).__dict__()
|
||||
res["symbol"] = self.getSymbolData('r', '/')
|
||||
res.update(self.data)
|
||||
return res
|
||||
|
||||
|
||||
class HdRadioModule(ThreadModule):
|
||||
def __init__(self, program: int = 0, amMode: bool = False):
|
||||
self.program = program
|
||||
self.frequency = 0
|
||||
self.metaLock = threading.Lock()
|
||||
self.metaWriter = None
|
||||
self.program = program
|
||||
self.radio = NRSC5(lambda evt_type, evt: self.callback(evt_type, evt))
|
||||
self.meta = {}
|
||||
self._clearMeta()
|
||||
# Initialize and start NRSC5 decoder
|
||||
self.radio = NRSC5(lambda evt_type, evt: self.callback(evt_type, evt))
|
||||
self.radio.open_pipe()
|
||||
self.radio.start()
|
||||
# Crashes things?
|
||||
# self.radio.set_mode(Mode.AM if amMode else Mode.FM)
|
||||
super().__init__()
|
||||
|
||||
def __del__(self):
|
||||
# Make sure NRSC5 object is truly destroyed
|
||||
if self.radio is not None:
|
||||
self.radio.stop()
|
||||
self.radio.close()
|
||||
self.radio = None
|
||||
|
||||
def getInputFormat(self) -> Format:
|
||||
return Format.COMPLEX_SHORT
|
||||
|
||||
|
|
@ -30,53 +63,101 @@ class HdRadioModule(ThreadModule):
|
|||
|
||||
# Change program
|
||||
def setProgram(self, program: int) -> None:
|
||||
self.program = program
|
||||
if program != self.program:
|
||||
self.program = program
|
||||
logger.info("Now playing program #{0}".format(self.program))
|
||||
# Clear program metadata
|
||||
with self.metaLock:
|
||||
self.meta["program"] = self.program
|
||||
if "title" in self.meta:
|
||||
del self.meta["title"]
|
||||
if "artist" in self.meta:
|
||||
del self.meta["artist"]
|
||||
if "album" in self.meta:
|
||||
del self.meta["album"]
|
||||
if "genre" in self.meta:
|
||||
del self.meta["genre"]
|
||||
self._writeMeta()
|
||||
|
||||
# Change frequency
|
||||
def setFrequency(self, frequency: int) -> None:
|
||||
if frequency != self.frequency:
|
||||
self.frequency = frequency
|
||||
self.program = 0
|
||||
logger.info("Now playing program #{0} at {1}MHz".format(self.program, self.frequency / 1000000))
|
||||
self._clearMeta()
|
||||
|
||||
# Set metadata consumer
|
||||
def setMetaWriter(self, writer: Writer) -> None:
|
||||
self.metaWriter = writer
|
||||
|
||||
# Write metadata
|
||||
def _writeMeta(self, data) -> None:
|
||||
if data and self.metaWriter:
|
||||
self.metaWriter.write(data)
|
||||
def _writeMeta(self) -> None:
|
||||
if self.meta and self.metaWriter:
|
||||
logger.debug("Metadata: {0}".format(self.meta))
|
||||
self.metaWriter.write(pickle.dumps(self.meta))
|
||||
|
||||
# Clear all metadata
|
||||
def _clearMeta(self) -> None:
|
||||
with self.metaLock:
|
||||
self.meta = {
|
||||
"mode" : "HDR",
|
||||
"frequency" : self.frequency,
|
||||
"program" : self.program
|
||||
}
|
||||
self._writeMeta()
|
||||
|
||||
# Update existing metadata
|
||||
def _updateMeta(self, data) -> None:
|
||||
# Update station location on the map
|
||||
if "station" in data and "lat" in data and "lon" in data:
|
||||
loc = StationLocation(data)
|
||||
Map.getSharedInstance().updateLocation(data["station"], loc, "HDR")
|
||||
# Update any new or different values
|
||||
with self.metaLock:
|
||||
changes = 0
|
||||
for key in data.keys():
|
||||
if key not in self.meta or self.meta[key] != data[key]:
|
||||
self.meta[key] = data[key]
|
||||
changes = changes + 1
|
||||
# If anything changed, write metadata to the buffer
|
||||
if changes > 0:
|
||||
self._writeMeta()
|
||||
|
||||
def run(self):
|
||||
# Start NRSC5 decoder
|
||||
logger.debug("Starting NRSC5 decoder...")
|
||||
self.radio.open_pipe()
|
||||
self.radio.start()
|
||||
|
||||
# Main loop
|
||||
logger.debug("Running the loop...")
|
||||
while self.doRun:
|
||||
data = self.reader.read()
|
||||
if data is None:
|
||||
if data is None or len(data) == 0:
|
||||
self.doRun = False
|
||||
break
|
||||
try:
|
||||
self.radio.pipe_samples_cs16(data.tobytes())
|
||||
except Exception as exptn:
|
||||
logger.debug("Exception: %s" % str(exptn))
|
||||
else:
|
||||
try:
|
||||
self.radio.pipe_samples_cs16(data.tobytes())
|
||||
except Exception as exptn:
|
||||
logger.debug("Exception: %s" % str(exptn))
|
||||
|
||||
# Stop NRSC5 decoder
|
||||
logger.debug("Stopping NRSC5 decoder...")
|
||||
self.radio.stop()
|
||||
self.radio.close()
|
||||
self.radio = None
|
||||
logger.debug("DONE.")
|
||||
|
||||
def callback(self, evt_type, evt):
|
||||
if evt_type == EventType.AUDIO:
|
||||
if evt_type == EventType.LOST_DEVICE:
|
||||
logger.info("Lost device")
|
||||
self.doRun = False
|
||||
elif evt_type == EventType.AUDIO:
|
||||
if evt.program == self.program:
|
||||
#logger.info("Audio data for program %d", evt.program)
|
||||
self.writer.write(evt.data)
|
||||
elif evt_type == EventType.HDC:
|
||||
if evt.program == self.program:
|
||||
#logger.info("HDC data for program %d", evt.program)
|
||||
pass
|
||||
elif evt_type == EventType.LOST_DEVICE:
|
||||
logger.info("Lost device")
|
||||
self.doRun = False
|
||||
elif evt_type == EventType.IQ:
|
||||
logger.info("IQ data")
|
||||
elif evt_type == EventType.SYNC:
|
||||
|
|
@ -89,7 +170,7 @@ class HdRadioModule(ThreadModule):
|
|||
logger.info("BER: %.6f", evt.cber)
|
||||
elif evt_type == EventType.ID3:
|
||||
if evt.program == self.program:
|
||||
# Collect metadata
|
||||
# Collect new metadata
|
||||
meta = {}
|
||||
if evt.title:
|
||||
meta["title"] = evt.title
|
||||
|
|
@ -98,13 +179,13 @@ class HdRadioModule(ThreadModule):
|
|||
if evt.album:
|
||||
meta["album"] = evt.album
|
||||
if evt.genre:
|
||||
meta["genre"] = evt.album
|
||||
meta["genre"] = evt.genre
|
||||
if evt.ufid:
|
||||
logger.info("Unique file identifier: %s %s", evt.ufid.owner, evt.ufid.id)
|
||||
if evt.xhdr:
|
||||
logger.info("XHDR: param=%s mime=%s lot=%s", evt.xhdr.param, evt.xhdr.mime, evt.xhdr.lot)
|
||||
# Output collected metadata
|
||||
self._writeMeta(meta)
|
||||
# Update existing metadata
|
||||
self._updateMeta(meta)
|
||||
elif evt_type == EventType.SIG:
|
||||
for service in evt:
|
||||
logger.info("SIG Service: type=%s number=%s name=%s",
|
||||
|
|
@ -130,8 +211,11 @@ class HdRadioModule(ThreadModule):
|
|||
logger.info("LOT file: port=%04X lot=%s name=%s size=%s mime=%s expiry=%s",
|
||||
evt.port, evt.lot, evt.name, len(evt.data), evt.mime, time_str)
|
||||
elif evt_type == EventType.SIS:
|
||||
# Collect metadata
|
||||
meta = {}
|
||||
# Collect new metadata
|
||||
meta = {
|
||||
"audio_services" : [],
|
||||
"data_services" : []
|
||||
}
|
||||
if evt.country_code:
|
||||
meta["country"] = evt.country_code
|
||||
meta["fcc_id"] = evt.fcc_facility_id
|
||||
|
|
@ -146,17 +230,30 @@ class HdRadioModule(ThreadModule):
|
|||
if evt.latitude:
|
||||
meta["lat"] = evt.latitude
|
||||
meta["lon"] = evt.longitude
|
||||
meta["alt"] = evt.altitude
|
||||
meta["altitude"] = round(evt.altitude)
|
||||
for audio_service in evt.audio_services:
|
||||
logger.info("Audio program %s: %s, type: %s, sound experience %s",
|
||||
audio_service.program,
|
||||
"public" if audio_service.access == Access.PUBLIC else "restricted",
|
||||
self.radio.program_type_name(audio_service.type),
|
||||
audio_service.sound_exp)
|
||||
#logger.info("Audio program %s: %s, type: %s, sound experience %s",
|
||||
# audio_service.program,
|
||||
# "public" if audio_service.access == Access.PUBLIC else "restricted",
|
||||
# self.radio.program_type_name(audio_service.type),
|
||||
# audio_service.sound_exp)
|
||||
meta["audio_services"] += [{
|
||||
"id" : audio_service.program,
|
||||
"type" : audio_service.type.value,
|
||||
"name" : self.radio.program_type_name(audio_service.type),
|
||||
"public" : audio_service.access == Access.PUBLIC,
|
||||
"experience" : audio_service.sound_exp
|
||||
}]
|
||||
for data_service in evt.data_services:
|
||||
logger.info("Data service: %s, type: %s, MIME type %03x",
|
||||
"public" if data_service.access == Access.PUBLIC else "restricted",
|
||||
self.radio.service_data_type_name(data_service.type),
|
||||
data_service.mime_type)
|
||||
# Output collected metadata
|
||||
self._writeMeta(meta)
|
||||
#logger.info("Data service: %s, type: %s, MIME type %03x",
|
||||
# "public" if data_service.access == Access.PUBLIC else "restricted",
|
||||
# self.radio.service_data_type_name(data_service.type),
|
||||
# data_service.mime_type)
|
||||
meta["data_services"] += [{
|
||||
"mime" : data_service.mime_type,
|
||||
"type" : data_service.type.value,
|
||||
"name" : self.radio.service_data_type_name(data_service.type),
|
||||
"public" : data_service.access == Access.PUBLIC
|
||||
}]
|
||||
# Update existing metadata
|
||||
self._updateMeta(meta)
|
||||
|
|
|
|||
|
|
@ -1381,6 +1381,52 @@ img.openwebrx-mirror-img
|
|||
content: "🔗 ";
|
||||
}
|
||||
|
||||
#openwebrx-panel-metadata-hdr {
|
||||
width: 350px;
|
||||
max-height: 300px;
|
||||
padding: 10px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.hdr-container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-station {
|
||||
font-weight: bold;
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-top-line,
|
||||
.hdr-container .hdr-bottom-line,
|
||||
.hdr-container .hdr-station,
|
||||
.hdr-container .hdr-message {
|
||||
min-height: 1lh;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-top-line {
|
||||
padding: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-bottom-line {
|
||||
padding: 5px 0 0 0;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-station,
|
||||
.hdr-container .hdr-message {
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-selector,
|
||||
.hdr-container .hdr-genre {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.hdr-container .hdr-identifier {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#openwebrx-panel-metadata-dab {
|
||||
width: 300px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="openwebrx-panel openwebrx-meta-panel disabled" id="openwebrx-panel-metadata-wfm" style="display: none;" data-panel-name="metadata-wfm"></div>
|
||||
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-hdr" style="display: none;" data-panel-name="metadata-hdr"></div>
|
||||
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-dab" style="display: none;" data-panel-name="metadata-dab"></div>
|
||||
<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" style="width: 619px;">
|
||||
<div class="openwebrx-panel-inner nano" id="openwebrx-log-scroll">
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ function Demodulator(offset_frequency, modulation) {
|
|||
this.filter = new Filter(this);
|
||||
this.squelch_level = -150;
|
||||
this.dmr_filter = 3;
|
||||
this.dab_service_id = 0;
|
||||
this.audio_service_id = 0;
|
||||
this.started = false;
|
||||
this.state = {};
|
||||
this.secondary_demod = false;
|
||||
|
|
@ -328,7 +328,7 @@ Demodulator.prototype.set = function () { //this function sends demodulator par
|
|||
"offset_freq": this.offset_frequency,
|
||||
"mod": this.modulation,
|
||||
"dmr_filter": this.dmr_filter,
|
||||
"dab_service_id": this.dab_service_id,
|
||||
"audio_service_id": this.audio_service_id,
|
||||
"squelch_level": this.squelch_level,
|
||||
"secondary_mod": this.secondary_demod,
|
||||
"secondary_offset_freq": this.secondary_offset_freq
|
||||
|
|
@ -366,8 +366,8 @@ Demodulator.prototype.setDmrFilter = function(dmr_filter) {
|
|||
this.set();
|
||||
};
|
||||
|
||||
Demodulator.prototype.setDabServiceId = function(dab_service_id) {
|
||||
this.dab_service_id = dab_service_id;
|
||||
Demodulator.prototype.setAudioServiceId = function(audio_service_id) {
|
||||
this.audio_service_id = audio_service_id;
|
||||
this.set();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -471,7 +471,6 @@ WfmMetaPanel.prototype.update = function(data) {
|
|||
if ('info.weather' in tags) {
|
||||
this.radiotext_plus.weather = tags['info.weather'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ('radiotext' in data && !this.radiotext_plus) {
|
||||
|
|
@ -550,6 +549,80 @@ WfmMetaPanel.prototype.clear = function() {
|
|||
this.radiotext_plus = false;
|
||||
};
|
||||
|
||||
function HdrMetaPanel(el) {
|
||||
MetaPanel.call(this, el);
|
||||
this.modes = ['HDR'];
|
||||
|
||||
// Create info panel
|
||||
var $container = $(
|
||||
'<div class="hdr-container">' +
|
||||
'<div class="hdr-top-line">' +
|
||||
'<select id="hdr-program-id" class="hdr-selector"></select>' +
|
||||
'<span class="hdr-identifier"></span>' +
|
||||
'</div>' +
|
||||
'<div class="hdr-station"></div>' +
|
||||
'<div class="hdr-message"></div>' +
|
||||
'<div class="hdr-title"></div>' +
|
||||
'<div class="hdr-artist"></div>' +
|
||||
'<div class="hdr-album"></div>' +
|
||||
'<div class="hdr-bottom-line">' +
|
||||
'<span class="hdr-genre"></span>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
$(this.el).append($container);
|
||||
|
||||
var $select = $('#hdr-program-id');
|
||||
$select.hide();
|
||||
$select.on("change", function() {
|
||||
var id = parseInt($(this).val());
|
||||
$('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().setAudioServiceId(id);
|
||||
});
|
||||
}
|
||||
|
||||
HdrMetaPanel.prototype = new MetaPanel();
|
||||
|
||||
HdrMetaPanel.prototype.update = function(data) {
|
||||
if (!this.isSupported(data)) return;
|
||||
|
||||
// Convert FCC ID to hexadecimal
|
||||
var fcc_id = '';
|
||||
if ('fcc_id' in data) {
|
||||
fcc_id = data.fcc_id.toString(16).toUpperCase();
|
||||
fcc_id = '0x' + ('0000' + fcc_id).slice(-4);
|
||||
fcc_id = ('country' in data? data.country + ':' : '') + fcc_id;
|
||||
}
|
||||
|
||||
// Update panel
|
||||
var $el = $(this.el);
|
||||
$el.find('.hdr-identifier').text(fcc_id);
|
||||
$el.find('.hdr-station').text(data.station || '');
|
||||
$el.find('.hdr-message').text(data.alert || data.message || data.slogan || '');
|
||||
$el.find('.hdr-title').text(data.title || '');
|
||||
$el.find('.hdr-artist').text(data.artist || '');
|
||||
$el.find('.hdr-genre').text(data.genre || '');
|
||||
$el.find('.hdr-album').text(data.album || '');
|
||||
|
||||
// Update program selector
|
||||
var $select = $('#hdr-program-id');
|
||||
if (data.audio_services && data.audio_services.length) {
|
||||
$select.html(data.audio_services.map(function(pgm) {
|
||||
var selected = data.program == pgm.id? ' selected' : '';
|
||||
return '<option value="' + pgm.id + '"' + selected + '>P' +
|
||||
(pgm.id + 1) + ' - ' + pgm.name + '</option>';
|
||||
}).join());
|
||||
$select.show();
|
||||
} else {
|
||||
$select.html('');
|
||||
$select.hide();
|
||||
}
|
||||
};
|
||||
|
||||
HdrMetaPanel.prototype.isSupported = function(data) {
|
||||
return this.modes.includes(data.mode);
|
||||
};
|
||||
|
||||
function DabMetaPanel(el) {
|
||||
MetaPanel.call(this, el);
|
||||
var me = this;
|
||||
|
|
@ -558,7 +631,7 @@ function DabMetaPanel(el) {
|
|||
this.$select = $('<select id="dab-service-id"></select>');
|
||||
this.$select.on("change", function() {
|
||||
me.service_id = parseInt($(this).val());
|
||||
$('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().setDabServiceId(me.service_id);
|
||||
$('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().setAudioServiceId(me.service_id);
|
||||
});
|
||||
var $container = $(
|
||||
'<div class="dab-container">' +
|
||||
|
|
@ -580,7 +653,6 @@ DabMetaPanel.prototype.isSupported = function(data) {
|
|||
return this.modes.includes(data.mode);
|
||||
}
|
||||
|
||||
|
||||
DabMetaPanel.prototype.update = function(data) {
|
||||
if (!this.isSupported(data)) return;
|
||||
|
||||
|
|
@ -633,6 +705,7 @@ MetaPanel.types = {
|
|||
m17: M17MetaPanel,
|
||||
wfm: WfmMetaPanel,
|
||||
dab: DabMetaPanel,
|
||||
hdr: HdrMetaPanel,
|
||||
};
|
||||
|
||||
$.fn.metaPanel = function() {
|
||||
|
|
|
|||
25
owrx/dsp.py
25
owrx/dsp.py
|
|
@ -5,7 +5,7 @@ from owrx.modes import Modes, DigitalMode
|
|||
from csdr.chain import Chain
|
||||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, \
|
||||
SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain, SecondarySelectorChain, \
|
||||
DeemphasisTauChain, DemodulatorError, RdsChain, DabServiceSelector
|
||||
DeemphasisTauChain, DemodulatorError, RdsChain, AudioServiceSelector
|
||||
from csdr.chain.selector import Selector, SecondarySelector
|
||||
from csdr.chain.clientaudio import ClientAudioChain
|
||||
from csdr.chain.fft import FftChain
|
||||
|
|
@ -330,10 +330,10 @@ class ClientDemodulatorChain(Chain):
|
|||
return
|
||||
self.demodulator.setSlotFilter(filter)
|
||||
|
||||
def setDabServiceId(self, serviceId: int) -> None:
|
||||
if not isinstance(self.demodulator, DabServiceSelector):
|
||||
def setAudioServiceId(self, serviceId: int) -> None:
|
||||
if not isinstance(self.demodulator, AudioServiceSelector):
|
||||
return
|
||||
self.demodulator.setDabServiceId(serviceId)
|
||||
self.demodulator.setAudioServiceId(serviceId)
|
||||
|
||||
def setSecondaryFftSize(self, size: int) -> None:
|
||||
if size == self.secondaryFftSize:
|
||||
|
|
@ -429,7 +429,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
"mod": ModulationValidator(),
|
||||
"secondary_offset_freq": "int",
|
||||
"dmr_filter": "int",
|
||||
"dab_service_id": "int",
|
||||
"audio_service_id": "int",
|
||||
}
|
||||
self.localProps = PropertyValidator(PropertyLayer().filter(*validators.keys()), validators)
|
||||
|
||||
|
|
@ -510,7 +510,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
self.props.wireProperty("high_cut", self.setHighCut),
|
||||
self.props.wireProperty("mod", self.setDemodulator),
|
||||
self.props.wireProperty("dmr_filter", self.chain.setSlotFilter),
|
||||
self.props.wireProperty("dab_service_id", self.chain.setDabServiceId),
|
||||
self.props.wireProperty("audio_service_id", self.chain.setAudioServiceId),
|
||||
self.props.wireProperty("wfm_deemphasis_tau", self.chain.setWfmDeemphasisTau),
|
||||
self.props.wireProperty("wfm_rds_rbds", self.chain.setRdsRbds),
|
||||
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
||||
|
|
@ -573,18 +573,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
elif demod == "nxdn":
|
||||
from csdr.chain.digiham import Nxdn
|
||||
return Nxdn(self.props["digital_voice_codecserver"])
|
||||
elif demod == "hdr1":
|
||||
elif demod == "hdr":
|
||||
from csdr.chain.hdradio import HdRadio
|
||||
return HdRadio(program = 0)
|
||||
elif demod == "hdr2":
|
||||
from csdr.chain.hdradio import HdRadio
|
||||
return HdRadio(program = 1)
|
||||
elif demod == "hdr3":
|
||||
from csdr.chain.hdradio import HdRadio
|
||||
return HdRadio(program = 2)
|
||||
elif demod == "hdr4":
|
||||
from csdr.chain.hdradio import HdRadio
|
||||
return HdRadio(program = 3)
|
||||
return HdRadio()
|
||||
elif demod == "m17":
|
||||
from csdr.chain.m17 import M17
|
||||
return M17()
|
||||
|
|
|
|||
|
|
@ -134,11 +134,8 @@ class Modes(object):
|
|||
"freedv", "FreeDV", bandpass=Bandpass(300, 3000), requirements=["digital_voice_freedv"], squelch=False
|
||||
),
|
||||
AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False),
|
||||
AnalogMode("dab", "DAB", bandpass=None, ifRate=2.048e6, requirements=["dab"], squelch=False),
|
||||
AnalogMode("hdr1", "HDR1", bandpass=None, ifRate=744188, requirements=["hdradio"], squelch=False),
|
||||
AnalogMode("hdr2", "HDR2", bandpass=Bandpass(-200000, 200000), requirements=["hdradio"], squelch=False),
|
||||
AnalogMode("hdr3", "HDR3", bandpass=Bandpass(-200000, 200000), requirements=["hdradio"], squelch=False),
|
||||
AnalogMode("hdr4", "HDR4", bandpass=Bandpass(-200000, 200000), requirements=["hdradio"], squelch=False),
|
||||
AnalogMode("dab", "DAB", bandpass=None, ifRate=2048000, requirements=["dab"], squelch=False),
|
||||
AnalogMode("hdr", "HDR", bandpass=Bandpass(-200000, 200000), requirements=["hdradio"], squelch=False),
|
||||
DigitalMode("bpsk31", "BPSK31", underlying=["usb"]),
|
||||
DigitalMode("bpsk63", "BPSK63", underlying=["usb"]),
|
||||
DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]),
|
||||
|
|
|
|||
Loading…
Reference in a new issue