Adding all current customizations to the original forked code.
This commit is contained in:
parent
e20d94e241
commit
e3780f6aea
@ -1,15 +1,18 @@
|
|||||||
from csdr.chain import Chain
|
from csdr.chain import Chain
|
||||||
from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, Limit
|
from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, Limit, NoiseFilter
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
|
|
||||||
|
|
||||||
class Converter(Chain):
|
class Converter(Chain):
|
||||||
def __init__(self, format: Format, inputRate: int, clientRate: int):
|
def __init__(self, format: Format, inputRate: int, clientRate: int, nrEnabled: bool, nrThreshold: int):
|
||||||
workers = []
|
workers = []
|
||||||
if inputRate != clientRate:
|
# we only have an audio resampler and noise filter for float ATM,
|
||||||
# we only have an audio resampler for float ATM so if we need to resample, we need to convert
|
# so if we need to resample or remove noise, we need to convert
|
||||||
if format != Format.FLOAT:
|
if (inputRate != clientRate or nrEnabled) and format != Format.FLOAT:
|
||||||
workers += [Convert(format, Format.FLOAT)]
|
workers += [Convert(format, Format.FLOAT)]
|
||||||
|
if nrEnabled:
|
||||||
|
workers += [NoiseFilter(nrThreshold)]
|
||||||
|
if inputRate != clientRate:
|
||||||
workers += [AudioResampler(inputRate, clientRate), Limit(), Convert(Format.FLOAT, Format.SHORT)]
|
workers += [AudioResampler(inputRate, clientRate), Limit(), Convert(Format.FLOAT, Format.SHORT)]
|
||||||
elif format != Format.SHORT:
|
elif format != Format.SHORT:
|
||||||
workers += [Convert(format, Format.SHORT)]
|
workers += [Convert(format, Format.SHORT)]
|
||||||
@ -17,10 +20,12 @@ class Converter(Chain):
|
|||||||
|
|
||||||
|
|
||||||
class ClientAudioChain(Chain):
|
class ClientAudioChain(Chain):
|
||||||
def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str):
|
def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str, nrEnabled: bool, nrThreshold: int):
|
||||||
self.format = format
|
self.format = format
|
||||||
self.inputRate = inputRate
|
self.inputRate = inputRate
|
||||||
self.clientRate = clientRate
|
self.clientRate = clientRate
|
||||||
|
self.nrEnabled = nrEnabled
|
||||||
|
self.nrThreshold = nrThreshold
|
||||||
workers = []
|
workers = []
|
||||||
converter = self._buildConverter()
|
converter = self._buildConverter()
|
||||||
if not converter.empty():
|
if not converter.empty():
|
||||||
@ -30,7 +35,7 @@ class ClientAudioChain(Chain):
|
|||||||
super().__init__(workers)
|
super().__init__(workers)
|
||||||
|
|
||||||
def _buildConverter(self):
|
def _buildConverter(self):
|
||||||
return Converter(self.format, self.inputRate, self.clientRate)
|
return Converter(self.format, self.inputRate, self.clientRate, self.nrEnabled, self.nrThreshold)
|
||||||
|
|
||||||
def _updateConverter(self):
|
def _updateConverter(self):
|
||||||
converter = self._buildConverter()
|
converter = self._buildConverter()
|
||||||
@ -70,3 +75,15 @@ class ClientAudioChain(Chain):
|
|||||||
else:
|
else:
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
self.remove(index)
|
self.remove(index)
|
||||||
|
|
||||||
|
def setNrEnabled(self, nrEnabled: bool) -> None:
|
||||||
|
if nrEnabled == self.nrEnabled:
|
||||||
|
return
|
||||||
|
self.nrEnabled = nrEnabled
|
||||||
|
self._updateConverter()
|
||||||
|
|
||||||
|
def setNrThreshold(self, nrThreshold: int) -> None:
|
||||||
|
if nrThreshold == self.nrThreshold:
|
||||||
|
return
|
||||||
|
self.nrThreshold = nrThreshold
|
||||||
|
self._updateConverter()
|
||||||
|
@ -24,5 +24,7 @@
|
|||||||
<g id="play-button"><circle cx="350" cy="350" r="330" fill="none" stroke="#fff" stroke-width="36"/><path d="M195 211v278l366-139z" fill="#fff"/></g>
|
<g id="play-button"><circle cx="350" cy="350" r="330" fill="none" stroke="#fff" stroke-width="36"/><path d="M195 211v278l366-139z" fill="#fff"/></g>
|
||||||
<g id="meta-mute" stroke="#fff" stroke-width="5"><path stroke-linejoin="round" style="paint-order:fill" fill="none" d="m21.989 47.699 17.4 15.051V13.769L22.235 28.606H6v19.093z" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/><path d="m48.652 50.27 20.743-24.299M69.395 50.27 48.652 25.971" stroke-linecap="round" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/></g>
|
<g id="meta-mute" stroke="#fff" stroke-width="5"><path stroke-linejoin="round" style="paint-order:fill" fill="none" d="m21.989 47.699 17.4 15.051V13.769L22.235 28.606H6v19.093z" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/><path d="m48.652 50.27 20.743-24.299M69.395 50.27 48.652 25.971" stroke-linecap="round" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/></g>
|
||||||
<g id="waterfall-continuous"><g stroke="#fff" stroke-width="8"><path d="M5 40A35 35 0 0 1 26.606 7.664a35 35 0 0 1 38.143 7.587" fill="none"/><path d="m68.284 11.716 2.828 9.9-9.899-2.829z" fill="#fff"/></g><path d="m48.008 48.144 2.816 8.624h9.035L45.075 14h-9.739L20.141 56.768h8.976l2.875-8.624zm-2.405-7.333H34.456l5.573-16.72z" fill="#fff" aria-label="A"/><g stroke="#fff" stroke-width="8"><path d="M75 40a35 35 0 0 1-21.606 32.336 35 35 0 0 1-38.143-7.587" fill="none"/><path d="m11.716 68.284-2.828-9.9 9.899 2.829z" fill="#fff"/></g></g>
|
<g id="waterfall-continuous"><g stroke="#fff" stroke-width="8"><path d="M5 40A35 35 0 0 1 26.606 7.664a35 35 0 0 1 38.143 7.587" fill="none"/><path d="m68.284 11.716 2.828 9.9-9.899-2.829z" fill="#fff"/></g><path d="m48.008 48.144 2.816 8.624h9.035L45.075 14h-9.739L20.141 56.768h8.976l2.875-8.624zm-2.405-7.333H34.456l5.573-16.72z" fill="#fff" aria-label="A"/><g stroke="#fff" stroke-width="8"><path d="M75 40a35 35 0 0 1-21.606 32.336 35 35 0 0 1-38.143-7.587" fill="none"/><path d="m11.716 68.284-2.828-9.9 9.899 2.829z" fill="#fff"/></g></g>
|
||||||
|
<g id="noise-reduce" fill="#fff" stroke-width=".767"><path d="m 36.606611,74.237282 -9.008789,0 L 11.11833,30.951149 q 0.659179,3.164062 0.659179,4.96582 l 0,38.320313 -10.0195308,0 0,-68.7744143 9.0087888,0 16.479492,42.4072263 Q 26.58708,44.706032 26.58708,42.904274 l 0,-37.4414063 10.019531,0 0,68.7744143 z"/><path d="m 80.200361,74.237282 -10.722656,0 L 58.139814,42.28904 q 0,0 0,-6.196289 l 10.107422,0 0,-21.796875 -12.963867,0 0,59.941406 -10.283203,0 0,-68.7744143 27.290039,0 q 6.196289,0 6.196289,6.3281253 l 0,25.004883 q 0,4.350585 -2.944336,5.625 -1.582031,0.659179 -6.679688,0.659179 l 11.337891,31.157227 z"/></g>
|
||||||
|
<g id="disabled" fill="#fff" stroke-width=".767"><path d="M 59.826868,-0.58229828 28.318079,81.155983 l -7.602539,0 31.376953,-81.73828128 7.734375,0 z"/></g>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
@ -201,6 +201,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
|
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="openwebrx-panel-line">
|
||||||
|
<div title="Noise reduction on/off" class="openwebrx-nr-toggle openwebrx-button openwebrx-slider-button" onclick="toggleNR();">
|
||||||
|
<svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#noise-reduce"></use></svg>
|
||||||
|
</div>
|
||||||
|
<input title="Noise reduction level" disabled id="openwebrx-panel-nr" class="openwebrx-panel-slider" type="range" min="-10" max="10" value="0" step="1" onchange="updateNR()" oninput="updateNR()">
|
||||||
|
</div>
|
||||||
<div class="openwebrx-panel-line">
|
<div class="openwebrx-panel-line">
|
||||||
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomInOneStep();" title="Zoom in one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-in"></use></svg></div>
|
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomInOneStep();" title="Zoom in one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-in"></use></svg></div>
|
||||||
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomOutOneStep();" title="Zoom out one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-out"></use></svg></div>
|
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomOutOneStep();" title="Zoom out one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-out"></use></svg></div>
|
||||||
|
@ -81,6 +81,12 @@ Envelope.prototype.draw = function(visible_range){
|
|||||||
scale_ctx.fill();
|
scale_ctx.fill();
|
||||||
scale_ctx.globalAlpha = 1;
|
scale_ctx.globalAlpha = 1;
|
||||||
scale_ctx.stroke();
|
scale_ctx.stroke();
|
||||||
|
scale_ctx.lineWidth = 1;
|
||||||
|
scale_ctx.textAlign = "left";
|
||||||
|
scale_ctx.fillText(this.demodulator.high_cut.toString(), to_px + env_att_w, env_h2);
|
||||||
|
scale_ctx.textAlign = "right";
|
||||||
|
scale_ctx.fillText(this.demodulator.low_cut.toString(), from_px - env_att_w, env_h2);
|
||||||
|
scale_ctx.lineWidth = 3;
|
||||||
}
|
}
|
||||||
if (typeof line !== "undefined") // out of screen?
|
if (typeof line !== "undefined") // out of screen?
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,7 @@ $(function(){
|
|||||||
var retention_time = 2 * 60 * 60 * 1000;
|
var retention_time = 2 * 60 * 60 * 1000;
|
||||||
var strokeOpacity = 0.8;
|
var strokeOpacity = 0.8;
|
||||||
var fillOpacity = 0.35;
|
var fillOpacity = 0.35;
|
||||||
|
var callsign_url = null;
|
||||||
|
|
||||||
var colorKeys = {};
|
var colorKeys = {};
|
||||||
var colorScale = chroma.scale(['red', 'blue', 'green']).mode('hsl');
|
var colorScale = chroma.scale(['red', 'blue', 'green']).mode('hsl');
|
||||||
@ -286,6 +287,9 @@ $(function(){
|
|||||||
if ('map_position_retention_time' in config) {
|
if ('map_position_retention_time' in config) {
|
||||||
retention_time = config.map_position_retention_time * 1000;
|
retention_time = config.map_position_retention_time * 1000;
|
||||||
}
|
}
|
||||||
|
if ('callsign_url' in config) {
|
||||||
|
callsign_url = config['callsign_url'];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
processUpdates(json.value);
|
processUpdates(json.value);
|
||||||
@ -340,6 +344,32 @@ $(function(){
|
|||||||
return infowindow;
|
return infowindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var linkifyCallsign = function(callsign) {
|
||||||
|
if ((callsign_url == null) || (callsign_url == ''))
|
||||||
|
return callsign;
|
||||||
|
else
|
||||||
|
return '<a target="callsign_info" href="' +
|
||||||
|
callsign_url.replaceAll('{}', callsign.replace(new RegExp('-.*$'), '')) +
|
||||||
|
'">' + callsign + '</a>';
|
||||||
|
};
|
||||||
|
|
||||||
|
var distanceKm = function(p1, p2) {
|
||||||
|
// Earth radius in km
|
||||||
|
var R = 6371.0;
|
||||||
|
// Convert degrees to radians
|
||||||
|
var rlat1 = p1.lat() * (Math.PI/180);
|
||||||
|
var rlat2 = p2.lat() * (Math.PI/180);
|
||||||
|
// Compute difference in radians
|
||||||
|
var difflat = rlat2-rlat1;
|
||||||
|
var difflon = (p2.lng()-p1.lng()) * (Math.PI/180);
|
||||||
|
// Compute distance
|
||||||
|
d = 2 * R * Math.asin(Math.sqrt(
|
||||||
|
Math.sin(difflat/2) * Math.sin(difflat/2) +
|
||||||
|
Math.cos(rlat1) * Math.cos(rlat2) * Math.sin(difflon/2) * Math.sin(difflon/2)
|
||||||
|
));
|
||||||
|
return Math.round(d);
|
||||||
|
}
|
||||||
|
|
||||||
var infowindow;
|
var infowindow;
|
||||||
var showLocatorInfoWindow = function(locator, pos) {
|
var showLocatorInfoWindow = function(locator, pos) {
|
||||||
var infowindow = getInfoWindow();
|
var infowindow = getInfoWindow();
|
||||||
@ -351,13 +381,15 @@ $(function(){
|
|||||||
}).sort(function(a, b){
|
}).sort(function(a, b){
|
||||||
return b.lastseen - a.lastseen;
|
return b.lastseen - a.lastseen;
|
||||||
});
|
});
|
||||||
|
var distance = receiverMarker?
|
||||||
|
" at " + distanceKm(receiverMarker.position, pos) + " km" : "";
|
||||||
infowindow.setContent(
|
infowindow.setContent(
|
||||||
'<h3>Locator: ' + locator + '</h3>' +
|
'<h3>Locator: ' + locator + distance + '</h3>' +
|
||||||
'<div>Active Callsigns:</div>' +
|
'<div>Active Callsigns:</div>' +
|
||||||
'<ul>' +
|
'<ul>' +
|
||||||
inLocator.map(function(i){
|
inLocator.map(function(i){
|
||||||
var timestring = moment(i.lastseen).fromNow();
|
var timestring = moment(i.lastseen).fromNow();
|
||||||
var message = i.callsign + ' (' + timestring + ' using ' + i.mode;
|
var message = linkifyCallsign(i.callsign) + ' (' + timestring + ' using ' + i.mode;
|
||||||
if (i.band) message += ' on ' + i.band;
|
if (i.band) message += ' on ' + i.band;
|
||||||
message += ')';
|
message += ')';
|
||||||
return '<li>' + message + '</li>'
|
return '<li>' + message + '</li>'
|
||||||
@ -374,11 +406,15 @@ $(function(){
|
|||||||
var marker = markers[callsign];
|
var marker = markers[callsign];
|
||||||
var timestring = moment(marker.lastseen).fromNow();
|
var timestring = moment(marker.lastseen).fromNow();
|
||||||
var commentString = "";
|
var commentString = "";
|
||||||
|
var distance = "";
|
||||||
if (marker.comment) {
|
if (marker.comment) {
|
||||||
commentString = '<div>' + marker.comment + '</div>';
|
commentString = '<div>' + marker.comment + '</div>';
|
||||||
}
|
}
|
||||||
|
if (receiverMarker) {
|
||||||
|
distance = " at " + distanceKm(receiverMarker.position, marker.position) + " km";
|
||||||
|
}
|
||||||
infowindow.setContent(
|
infowindow.setContent(
|
||||||
'<h3>' + callsign + '</h3>' +
|
'<h3>' + linkifyCallsign(callsign) + distance + '</h3>' +
|
||||||
'<div>' + timestring + ' using ' + marker.mode + ( marker.band ? ' on ' + marker.band : '' ) + '</div>' +
|
'<div>' + timestring + ' using ' + marker.mode + ( marker.band ? ' on ' + marker.band : '' ) + '</div>' +
|
||||||
commentString
|
commentString
|
||||||
);
|
);
|
||||||
|
@ -31,6 +31,9 @@ var fft_compression = "none";
|
|||||||
var fft_codec;
|
var fft_codec;
|
||||||
var waterfall_setup_done = 0;
|
var waterfall_setup_done = 0;
|
||||||
var secondary_fft_size;
|
var secondary_fft_size;
|
||||||
|
var tuning_step = 1;
|
||||||
|
var nr_enabled = false;
|
||||||
|
var nr_threshold = 0;
|
||||||
|
|
||||||
function updateVolume() {
|
function updateVolume() {
|
||||||
audioEngine.setVolume(parseFloat($("#openwebrx-panel-volume").val()) / 100);
|
audioEngine.setVolume(parseFloat($("#openwebrx-panel-volume").val()) / 100);
|
||||||
@ -51,6 +54,28 @@ function toggleMute() {
|
|||||||
updateVolume();
|
updateVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateNR() {
|
||||||
|
var $nrPanel = $('#openwebrx-panel-nr');
|
||||||
|
|
||||||
|
nr_threshold = Math.round(parseFloat($nrPanel.val()));
|
||||||
|
$nrPanel.attr('title', 'Noise level (' + nr_threshold + ' dB)');
|
||||||
|
nr_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleNR() {
|
||||||
|
var $nrPanel = $('#openwebrx-panel-nr');
|
||||||
|
|
||||||
|
if ($nrPanel.prop('disabled')) {
|
||||||
|
$nrPanel.prop('disabled', false);
|
||||||
|
nr_enabled = true;
|
||||||
|
} else {
|
||||||
|
$nrPanel.prop('disabled', true);
|
||||||
|
nr_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_changed();
|
||||||
|
}
|
||||||
|
|
||||||
function zoomInOneStep() {
|
function zoomInOneStep() {
|
||||||
zoom_set(zoom_level + 1);
|
zoom_set(zoom_level + 1);
|
||||||
}
|
}
|
||||||
@ -257,18 +282,23 @@ var scale_canvas_drag_params = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function scale_canvas_mousedown(evt) {
|
function scale_canvas_mousedown(evt) {
|
||||||
|
// Left button only
|
||||||
|
if (evt.button == 0) {
|
||||||
scale_canvas_drag_params.mouse_down = true;
|
scale_canvas_drag_params.mouse_down = true;
|
||||||
scale_canvas_drag_params.drag = false;
|
scale_canvas_drag_params.drag = false;
|
||||||
scale_canvas_drag_params.start_x = evt.pageX;
|
scale_canvas_drag_params.start_x = evt.pageX;
|
||||||
scale_canvas_drag_params.key_modifiers.shiftKey = evt.shiftKey;
|
scale_canvas_drag_params.key_modifiers.shiftKey = evt.shiftKey;
|
||||||
scale_canvas_drag_params.key_modifiers.altKey = evt.altKey;
|
scale_canvas_drag_params.key_modifiers.altKey = evt.altKey;
|
||||||
scale_canvas_drag_params.key_modifiers.ctrlKey = evt.ctrlKey;
|
scale_canvas_drag_params.key_modifiers.ctrlKey = evt.ctrlKey;
|
||||||
|
}
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
function scale_offset_freq_from_px(x, visible_range) {
|
function scale_offset_freq_from_px(x, visible_range) {
|
||||||
if (typeof visible_range === "undefined") visible_range = get_visible_freq_range();
|
if (typeof visible_range === "undefined") visible_range = get_visible_freq_range();
|
||||||
return (visible_range.start + visible_range.bw * (x / waterfallWidth())) - center_freq;
|
|
||||||
|
var f = (visible_range.start + visible_range.bw * (x / waterfallWidth())) - center_freq;
|
||||||
|
return tuning_step>0? Math.round(f / tuning_step) * tuning_step : f;
|
||||||
}
|
}
|
||||||
|
|
||||||
function scale_canvas_mousemove(evt) {
|
function scale_canvas_mousemove(evt) {
|
||||||
@ -307,6 +337,7 @@ function scale_canvas_end_drag(x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function scale_canvas_mouseup(evt) {
|
function scale_canvas_mouseup(evt) {
|
||||||
|
if (evt.button == 0)
|
||||||
scale_canvas_end_drag(evt.pageX);
|
scale_canvas_end_drag(evt.pageX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,11 +544,16 @@ function resize_scale() {
|
|||||||
|
|
||||||
function canvas_get_freq_offset(relativeX) {
|
function canvas_get_freq_offset(relativeX) {
|
||||||
var rel = (relativeX / canvas_container.clientWidth);
|
var rel = (relativeX / canvas_container.clientWidth);
|
||||||
return Math.round((bandwidth * rel) - (bandwidth / 2));
|
var off = (bandwidth * rel) - (bandwidth / 2);
|
||||||
|
|
||||||
|
return tuning_step>0?
|
||||||
|
Math.round(off / tuning_step) * tuning_step : Math.round(off);
|
||||||
}
|
}
|
||||||
|
|
||||||
function canvas_get_frequency(relativeX) {
|
function canvas_get_frequency(relativeX) {
|
||||||
return center_freq + canvas_get_freq_offset(relativeX);
|
var f = center_freq + canvas_get_freq_offset(relativeX);
|
||||||
|
|
||||||
|
return tuning_step>0? Math.round(f / tuning_step) * tuning_step : f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -535,16 +571,23 @@ function format_frequency(format, freq_hz, pre_divide, decimals) {
|
|||||||
var canvas_drag = false;
|
var canvas_drag = false;
|
||||||
var canvas_drag_min_delta = 1;
|
var canvas_drag_min_delta = 1;
|
||||||
var canvas_mouse_down = false;
|
var canvas_mouse_down = false;
|
||||||
|
var canvas_mouse2_down = 0;
|
||||||
var canvas_drag_last_x;
|
var canvas_drag_last_x;
|
||||||
var canvas_drag_last_y;
|
var canvas_drag_last_y;
|
||||||
var canvas_drag_start_x;
|
var canvas_drag_start_x;
|
||||||
var canvas_drag_start_y;
|
var canvas_drag_start_y;
|
||||||
|
|
||||||
function canvas_mousedown(evt) {
|
function canvas_mousedown(evt) {
|
||||||
|
if (evt.button > 0)
|
||||||
|
if (canvas_mouse2_down == 0)
|
||||||
|
canvas_mouse2_down = evt.button;
|
||||||
|
else {
|
||||||
canvas_mouse_down = true;
|
canvas_mouse_down = true;
|
||||||
canvas_drag = false;
|
canvas_drag = false;
|
||||||
canvas_drag_last_x = canvas_drag_start_x = evt.pageX;
|
canvas_drag_last_x = canvas_drag_start_x = evt.pageX;
|
||||||
canvas_drag_last_y = canvas_drag_start_y = evt.pageY;
|
canvas_drag_last_y = canvas_drag_start_y = evt.pageY;
|
||||||
|
}
|
||||||
|
|
||||||
evt.preventDefault(); //don't show text selection mouse pointer
|
evt.preventDefault(); //don't show text selection mouse pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,6 +624,10 @@ function canvas_container_mouseleave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canvas_mouseup(evt) {
|
function canvas_mouseup(evt) {
|
||||||
|
if (evt.button > 0) {
|
||||||
|
if (evt.button == canvas_mouse2_down)
|
||||||
|
canvas_mouse2_down = 0;
|
||||||
|
} else {
|
||||||
if (!waterfall_setup_done) return;
|
if (!waterfall_setup_done) return;
|
||||||
var relativeX = get_relative_x(evt);
|
var relativeX = get_relative_x(evt);
|
||||||
|
|
||||||
@ -591,6 +638,7 @@ function canvas_mouseup(evt) {
|
|||||||
canvas_end_drag();
|
canvas_end_drag();
|
||||||
}
|
}
|
||||||
canvas_mouse_down = false;
|
canvas_mouse_down = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function canvas_end_drag() {
|
function canvas_end_drag() {
|
||||||
@ -618,7 +666,16 @@ function canvas_mousewheel(evt) {
|
|||||||
if (!waterfall_setup_done) return;
|
if (!waterfall_setup_done) return;
|
||||||
var relativeX = get_relative_x(evt);
|
var relativeX = get_relative_x(evt);
|
||||||
var dir = (evt.deltaY / Math.abs(evt.deltaY)) > 0;
|
var dir = (evt.deltaY / Math.abs(evt.deltaY)) > 0;
|
||||||
|
|
||||||
|
// Zoom when mouse button down, tune otherwise
|
||||||
|
if (canvas_mouse2_down > 0) {
|
||||||
zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX));
|
zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX));
|
||||||
|
} else {
|
||||||
|
var f = $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().get_offset_frequency();
|
||||||
|
f += dir? -tuning_step : tuning_step;
|
||||||
|
$('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().set_offset_frequency(f);
|
||||||
|
}
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,11 +828,15 @@ function on_ws_recv(evt) {
|
|||||||
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
||||||
|
|
||||||
waterfall_clear();
|
waterfall_clear();
|
||||||
|
zoom_set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('tuning_precision' in config)
|
if ('tuning_precision' in config)
|
||||||
$('#openwebrx-panel-receiver').demodulatorPanel().setTuningPrecision(config['tuning_precision']);
|
$('#openwebrx-panel-receiver').demodulatorPanel().setTuningPrecision(config['tuning_precision']);
|
||||||
|
|
||||||
|
if ('tuning_step' in config)
|
||||||
|
tuning_step = config['tuning_step'];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "secondary_config":
|
case "secondary_config":
|
||||||
var s = json['value'];
|
var s = json['value'];
|
||||||
@ -935,9 +996,15 @@ var waterfall_measure_minmax_now = false;
|
|||||||
var waterfall_measure_minmax_continuous = false;
|
var waterfall_measure_minmax_continuous = false;
|
||||||
|
|
||||||
function waterfall_measure_minmax_do(what) {
|
function waterfall_measure_minmax_do(what) {
|
||||||
|
// Get visible range
|
||||||
|
var range = get_visible_freq_range();
|
||||||
|
var start = center_freq - bandwidth / 2;
|
||||||
|
|
||||||
// this is based on an oversampling factor of about 1,25
|
// this is based on an oversampling factor of about 1,25
|
||||||
var ignored = .1 * what.length;
|
range.start = Math.max(0.1, (range.start - start) / bandwidth);
|
||||||
var data = what.slice(ignored, -ignored);
|
range.end = Math.min(0.9, (range.end - start) / bandwidth);
|
||||||
|
|
||||||
|
var data = what.slice(range.start * what.length, range.end * what.length);
|
||||||
return {
|
return {
|
||||||
min: Math.min.apply(Math, data),
|
min: Math.min.apply(Math, data),
|
||||||
max: Math.max.apply(Math, data)
|
max: Math.max.apply(Math, data)
|
||||||
@ -1556,3 +1623,13 @@ function sdr_profile_changed() {
|
|||||||
var value = $('#openwebrx-sdr-profiles-listbox').val();
|
var value = $('#openwebrx-sdr-profiles-listbox').val();
|
||||||
ws.send(JSON.stringify({type: "selectprofile", params: {profile: value}}));
|
ws.send(JSON.stringify({type: "selectprofile", params: {profile: value}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nr_changed() {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
"type": "connectionproperties",
|
||||||
|
"params": {
|
||||||
|
"nr_enabled": nr_enabled,
|
||||||
|
"nr_threshold": nr_threshold
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=2400000,
|
samp_rate=2400000,
|
||||||
start_freq=439275000,
|
start_freq=439275000,
|
||||||
start_mod="nfm",
|
start_mod="nfm",
|
||||||
|
tuning_step="1000",
|
||||||
),
|
),
|
||||||
"2m": PropertyLayer(
|
"2m": PropertyLayer(
|
||||||
name="2m",
|
name="2m",
|
||||||
@ -41,6 +42,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=2048000,
|
samp_rate=2048000,
|
||||||
start_freq=145725000,
|
start_freq=145725000,
|
||||||
start_mod="nfm",
|
start_mod="nfm",
|
||||||
|
tuning_step="1000",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -57,6 +59,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=384000,
|
samp_rate=384000,
|
||||||
start_freq=14070000,
|
start_freq=14070000,
|
||||||
start_mod="usb",
|
start_mod="usb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"30m": PropertyLayer(
|
"30m": PropertyLayer(
|
||||||
name="30m",
|
name="30m",
|
||||||
@ -64,6 +67,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=192000,
|
samp_rate=192000,
|
||||||
start_freq=10142000,
|
start_freq=10142000,
|
||||||
start_mod="usb",
|
start_mod="usb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"40m": PropertyLayer(
|
"40m": PropertyLayer(
|
||||||
name="40m",
|
name="40m",
|
||||||
@ -71,6 +75,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=256000,
|
samp_rate=256000,
|
||||||
start_freq=7070000,
|
start_freq=7070000,
|
||||||
start_mod="lsb",
|
start_mod="lsb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"80m": PropertyLayer(
|
"80m": PropertyLayer(
|
||||||
name="80m",
|
name="80m",
|
||||||
@ -78,6 +83,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=384000,
|
samp_rate=384000,
|
||||||
start_freq=3570000,
|
start_freq=3570000,
|
||||||
start_mod="lsb",
|
start_mod="lsb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"49m": PropertyLayer(
|
"49m": PropertyLayer(
|
||||||
name="49m Broadcast",
|
name="49m Broadcast",
|
||||||
@ -85,6 +91,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=384000,
|
samp_rate=384000,
|
||||||
start_freq=6070000,
|
start_freq=6070000,
|
||||||
start_mod="am",
|
start_mod="am",
|
||||||
|
tuning_step="1000",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -102,6 +109,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=500000,
|
samp_rate=500000,
|
||||||
start_freq=14070000,
|
start_freq=14070000,
|
||||||
start_mod="usb",
|
start_mod="usb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"30m": PropertyLayer(
|
"30m": PropertyLayer(
|
||||||
name="30m",
|
name="30m",
|
||||||
@ -110,6 +118,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=250000,
|
samp_rate=250000,
|
||||||
start_freq=10142000,
|
start_freq=10142000,
|
||||||
start_mod="usb",
|
start_mod="usb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"40m": PropertyLayer(
|
"40m": PropertyLayer(
|
||||||
name="40m",
|
name="40m",
|
||||||
@ -118,6 +127,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=500000,
|
samp_rate=500000,
|
||||||
start_freq=7070000,
|
start_freq=7070000,
|
||||||
start_mod="lsb",
|
start_mod="lsb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"80m": PropertyLayer(
|
"80m": PropertyLayer(
|
||||||
name="80m",
|
name="80m",
|
||||||
@ -126,6 +136,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=500000,
|
samp_rate=500000,
|
||||||
start_freq=3570000,
|
start_freq=3570000,
|
||||||
start_mod="lsb",
|
start_mod="lsb",
|
||||||
|
tuning_step="500",
|
||||||
),
|
),
|
||||||
"49m": PropertyLayer(
|
"49m": PropertyLayer(
|
||||||
name="49m Broadcast",
|
name="49m Broadcast",
|
||||||
@ -134,6 +145,7 @@ defaultConfig = PropertyLayer(
|
|||||||
samp_rate=500000,
|
samp_rate=500000,
|
||||||
start_freq=6070000,
|
start_freq=6070000,
|
||||||
start_mod="am",
|
start_mod="am",
|
||||||
|
tuning_step="1000",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -147,6 +159,7 @@ defaultConfig = PropertyLayer(
|
|||||||
squelch_auto_margin=10,
|
squelch_auto_margin=10,
|
||||||
google_maps_api_key="",
|
google_maps_api_key="",
|
||||||
map_position_retention_time=2 * 60 * 60,
|
map_position_retention_time=2 * 60 * 60,
|
||||||
|
callsign_url="https://www.qrzcq.com/call/{}",
|
||||||
decoding_queue_workers=2,
|
decoding_queue_workers=2,
|
||||||
decoding_queue_length=10,
|
decoding_queue_length=10,
|
||||||
wsjt_decoding_depth=3,
|
wsjt_decoding_depth=3,
|
||||||
|
@ -120,6 +120,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
|
|||||||
"start_mod",
|
"start_mod",
|
||||||
"start_freq",
|
"start_freq",
|
||||||
"center_freq",
|
"center_freq",
|
||||||
|
"tuning_step",
|
||||||
"initial_squelch_level",
|
"initial_squelch_level",
|
||||||
"sdr_id",
|
"sdr_id",
|
||||||
"profile_id",
|
"profile_id",
|
||||||
@ -456,6 +457,7 @@ class MapConnection(OpenWebRxClient):
|
|||||||
"google_maps_api_key",
|
"google_maps_api_key",
|
||||||
"receiver_gps",
|
"receiver_gps",
|
||||||
"map_position_retention_time",
|
"map_position_retention_time",
|
||||||
|
"callsign_url",
|
||||||
"receiver_name",
|
"receiver_name",
|
||||||
)
|
)
|
||||||
filtered_config.wire(self.write_config)
|
filtered_config.wire(self.write_config)
|
||||||
|
@ -168,6 +168,13 @@ class GeneralSettingsController(SettingsFormController):
|
|||||||
infotext="Specifies how log markers / grids will remain visible on the map",
|
infotext="Specifies how log markers / grids will remain visible on the map",
|
||||||
append="s",
|
append="s",
|
||||||
),
|
),
|
||||||
|
TextInput(
|
||||||
|
"callsign_url",
|
||||||
|
"Callsign database URL",
|
||||||
|
infotext="Specifies callsign lookup URL, such as QRZ.COM "
|
||||||
|
+ "or QRZCQ.COM. Place curly brackers ({}) where callsign "
|
||||||
|
+ "is supposed to be.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
20
owrx/dsp.py
20
owrx/dsp.py
@ -34,10 +34,12 @@ class ClientDemodulatorSecondaryDspEventClient(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class ClientDemodulatorChain(Chain):
|
class ClientDemodulatorChain(Chain):
|
||||||
def __init__(self, demod: BaseDemodulatorChain, sampleRate: int, outputRate: int, hdOutputRate: int, audioCompression: str, secondaryDspEventReceiver: ClientDemodulatorSecondaryDspEventClient):
|
def __init__(self, demod: BaseDemodulatorChain, sampleRate: int, outputRate: int, hdOutputRate: int, audioCompression: str, nrEnabled: bool, nrThreshold: int, secondaryDspEventReceiver: ClientDemodulatorSecondaryDspEventClient):
|
||||||
self.sampleRate = sampleRate
|
self.sampleRate = sampleRate
|
||||||
self.outputRate = outputRate
|
self.outputRate = outputRate
|
||||||
self.hdOutputRate = hdOutputRate
|
self.hdOutputRate = hdOutputRate
|
||||||
|
self.nrEnabled = nrEnabled
|
||||||
|
self.nrThreshold = nrThreshold
|
||||||
self.secondaryDspEventReceiver = secondaryDspEventReceiver
|
self.secondaryDspEventReceiver = secondaryDspEventReceiver
|
||||||
self.selector = Selector(sampleRate, outputRate)
|
self.selector = Selector(sampleRate, outputRate)
|
||||||
self.selector.setBandpass(-4000, 4000)
|
self.selector.setBandpass(-4000, 4000)
|
||||||
@ -50,7 +52,7 @@ class ClientDemodulatorChain(Chain):
|
|||||||
self.wfmDeemphasisTau = 50e-6
|
self.wfmDeemphasisTau = 50e-6
|
||||||
inputRate = demod.getFixedAudioRate() if isinstance(demod, FixedAudioRateChain) else outputRate
|
inputRate = demod.getFixedAudioRate() if isinstance(demod, FixedAudioRateChain) else outputRate
|
||||||
oRate = hdOutputRate if isinstance(demod, HdAudio) else outputRate
|
oRate = hdOutputRate if isinstance(demod, HdAudio) else outputRate
|
||||||
self.clientAudioChain = ClientAudioChain(demod.getOutputFormat(), inputRate, oRate, audioCompression)
|
self.clientAudioChain = ClientAudioChain(demod.getOutputFormat(), inputRate, oRate, audioCompression, nrEnabled, nrThreshold)
|
||||||
self.secondaryFftSize = 2048
|
self.secondaryFftSize = 2048
|
||||||
self.secondaryFftOverlapFactor = 0.3
|
self.secondaryFftOverlapFactor = 0.3
|
||||||
self.secondaryFftFps = 9
|
self.secondaryFftFps = 9
|
||||||
@ -251,6 +253,12 @@ class ClientDemodulatorChain(Chain):
|
|||||||
def setAudioCompression(self, compression: str) -> None:
|
def setAudioCompression(self, compression: str) -> None:
|
||||||
self.clientAudioChain.setAudioCompression(compression)
|
self.clientAudioChain.setAudioCompression(compression)
|
||||||
|
|
||||||
|
def setNrEnabled(self, nrEnabled: bool) -> None:
|
||||||
|
self.clientAudioChain.setNrEnabled(nrEnabled)
|
||||||
|
|
||||||
|
def setNrThreshold(self, nrThreshold: int) -> None:
|
||||||
|
self.clientAudioChain.setNrThreshold(nrThreshold)
|
||||||
|
|
||||||
def setSquelchLevel(self, level: float) -> None:
|
def setSquelchLevel(self, level: float) -> None:
|
||||||
if level == self.squelchLevel:
|
if level == self.squelchLevel:
|
||||||
return
|
return
|
||||||
@ -409,6 +417,8 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||||||
"mod": ModulationValidator(),
|
"mod": ModulationValidator(),
|
||||||
"secondary_offset_freq": "int",
|
"secondary_offset_freq": "int",
|
||||||
"dmr_filter": "int",
|
"dmr_filter": "int",
|
||||||
|
"nr_enabled": "bool",
|
||||||
|
"nr_threshold": "int",
|
||||||
}
|
}
|
||||||
self.localProps = PropertyValidator(PropertyLayer().filter(*validators.keys()), validators)
|
self.localProps = PropertyValidator(PropertyLayer().filter(*validators.keys()), validators)
|
||||||
|
|
||||||
@ -436,6 +446,8 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||||||
output_rate=12000,
|
output_rate=12000,
|
||||||
hd_output_rate=48000,
|
hd_output_rate=48000,
|
||||||
digital_voice_codecserver="",
|
digital_voice_codecserver="",
|
||||||
|
nr_enabled=False,
|
||||||
|
nr_threshold=0
|
||||||
).readonly()
|
).readonly()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -445,6 +457,8 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||||||
self.props["output_rate"],
|
self.props["output_rate"],
|
||||||
self.props["hd_output_rate"],
|
self.props["hd_output_rate"],
|
||||||
self.props["audio_compression"],
|
self.props["audio_compression"],
|
||||||
|
self.props["nr_enabled"],
|
||||||
|
self.props["nr_threshold"],
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -487,6 +501,8 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||||||
self.props.wireProperty("wfm_deemphasis_tau", self.chain.setWfmDeemphasisTau),
|
self.props.wireProperty("wfm_deemphasis_tau", self.chain.setWfmDeemphasisTau),
|
||||||
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
||||||
self.props.wireProperty("secondary_offset_freq", self.chain.setSecondaryFrequencyOffset),
|
self.props.wireProperty("secondary_offset_freq", self.chain.setSecondaryFrequencyOffset),
|
||||||
|
self.props.wireProperty("nr_enabled", self.chain.setNrEnabled),
|
||||||
|
self.props.wireProperty("nr_threshold", self.chain.setNrThreshold),
|
||||||
]
|
]
|
||||||
|
|
||||||
# wire power level output
|
# wire power level output
|
||||||
|
@ -94,4 +94,5 @@ validator_types = {
|
|||||||
"int": IntegerValidator,
|
"int": IntegerValidator,
|
||||||
"number": NumberValidator,
|
"number": NumberValidator,
|
||||||
"num": NumberValidator,
|
"num": NumberValidator,
|
||||||
|
"bool": BoolValidator,
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ from owrx.command import CommandMapper
|
|||||||
from owrx.socket import getAvailablePort
|
from owrx.socket import getAvailablePort
|
||||||
from owrx.property import PropertyStack, PropertyLayer, PropertyFilter, PropertyCarousel, PropertyDeleted
|
from owrx.property import PropertyStack, PropertyLayer, PropertyFilter, PropertyCarousel, PropertyDeleted
|
||||||
from owrx.property.filter import ByLambda
|
from owrx.property.filter import ByLambda
|
||||||
from owrx.form.input import Input, TextInput, NumberInput, CheckboxInput, ModesInput, ExponentialInput
|
from owrx.form.input import Input, TextInput, NumberInput, CheckboxInput, ModesInput, ExponentialInput, DropdownInput, Option
|
||||||
from owrx.form.input.converter import OptionalConverter
|
from owrx.form.input.converter import OptionalConverter, IntConverter
|
||||||
from owrx.form.input.device import GainInput, SchedulerInput, WaterfallLevelsInput
|
from owrx.form.input.device import GainInput, SchedulerInput, WaterfallLevelsInput
|
||||||
from owrx.form.input.validator import RequiredValidator
|
from owrx.form.input.validator import RequiredValidator
|
||||||
from owrx.form.section import OptionalSection
|
from owrx.form.section import OptionalSection
|
||||||
@ -277,8 +277,9 @@ class SdrSource(ABC):
|
|||||||
if self.monitor:
|
if self.monitor:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.isFailed():
|
# @@@
|
||||||
return
|
# if self.isFailed():
|
||||||
|
# return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.preStart()
|
self.preStart()
|
||||||
@ -568,6 +569,12 @@ class SdrDeviceDescription(object):
|
|||||||
ExponentialInput("samp_rate", "Sample rate", "S/s"),
|
ExponentialInput("samp_rate", "Sample rate", "S/s"),
|
||||||
ExponentialInput("start_freq", "Initial frequency", "Hz"),
|
ExponentialInput("start_freq", "Initial frequency", "Hz"),
|
||||||
ModesInput("start_mod", "Initial modulation"),
|
ModesInput("start_mod", "Initial modulation"),
|
||||||
|
DropdownInput(
|
||||||
|
"tuning_step",
|
||||||
|
"Tuning step",
|
||||||
|
options=[Option(str(i), "{} Hz".format(i)) for i in [1, 100, 500, 1000, 2500, 3000, 5000, 6000, 10000, 12000, 50000]],
|
||||||
|
converter=IntConverter(),
|
||||||
|
),
|
||||||
NumberInput("initial_squelch_level", "Initial squelch level", append="dBFS"),
|
NumberInput("initial_squelch_level", "Initial squelch level", append="dBFS"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -592,7 +599,7 @@ class SdrDeviceDescription(object):
|
|||||||
return keys
|
return keys
|
||||||
|
|
||||||
def getProfileMandatoryKeys(self):
|
def getProfileMandatoryKeys(self):
|
||||||
return ["name", "center_freq", "samp_rate", "start_freq", "start_mod"]
|
return ["name", "center_freq", "samp_rate", "start_freq", "start_mod", "tuning_step"]
|
||||||
|
|
||||||
def getProfileOptionalKeys(self):
|
def getProfileOptionalKeys(self):
|
||||||
return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_levels"]
|
return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_levels"]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
|
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
|
||||||
from owrx.form.input import Input, CheckboxInput, DropdownInput, DropdownEnum
|
from owrx.form.input import Input, CheckboxInput, DropdownInput, NumberInput, DropdownEnum
|
||||||
from owrx.form.input.device import BiasTeeInput
|
from owrx.form.input.device import BiasTeeInput, GainInput
|
||||||
|
from owrx.form.input.validator import RangeValidator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ class SdrplaySource(SoapyConnectorSource):
|
|||||||
"dab_notch": "dabnotch_ctrl",
|
"dab_notch": "dabnotch_ctrl",
|
||||||
"if_mode": "if_mode",
|
"if_mode": "if_mode",
|
||||||
"external_reference": "extref_ctrl",
|
"external_reference": "extref_ctrl",
|
||||||
|
"rfgain_sel": "rfgain_sel",
|
||||||
|
"agc_setpoint": "agc_setpoint",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return mappings
|
return mappings
|
||||||
@ -36,9 +39,6 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
|
|||||||
def getName(self):
|
def getName(self):
|
||||||
return "SDRPlay device (RSP1, RSP2, RSPDuo, RSPDx)"
|
return "SDRPlay device (RSP1, RSP2, RSPDuo, RSPDx)"
|
||||||
|
|
||||||
def getGainStages(self):
|
|
||||||
return ["RFGR", "IFGR"]
|
|
||||||
|
|
||||||
def getInputs(self) -> List[Input]:
|
def getInputs(self) -> List[Input]:
|
||||||
return super().getInputs() + [
|
return super().getInputs() + [
|
||||||
BiasTeeInput(),
|
BiasTeeInput(),
|
||||||
@ -55,10 +55,26 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
|
|||||||
"IF Mode",
|
"IF Mode",
|
||||||
IfModeOptions,
|
IfModeOptions,
|
||||||
),
|
),
|
||||||
|
NumberInput(
|
||||||
|
"rfgain_sel",
|
||||||
|
"RF gain reduction",
|
||||||
|
validator=RangeValidator(0, 32),
|
||||||
|
),
|
||||||
|
NumberInput(
|
||||||
|
"agc_setpoint",
|
||||||
|
"AGC setpoint",
|
||||||
|
append="dBFS",
|
||||||
|
validator=RangeValidator(-60, 0),
|
||||||
|
),
|
||||||
|
GainInput(
|
||||||
|
"rf_gain",
|
||||||
|
"IF gain reduction",
|
||||||
|
has_agc=self.hasAgc(),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def getDeviceOptionalKeys(self):
|
def getDeviceOptionalKeys(self):
|
||||||
return super().getDeviceOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
|
return super().getDeviceOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode", "rfgain_sel", "agc_setpoint"]
|
||||||
|
|
||||||
def getProfileOptionalKeys(self):
|
def getProfileOptionalKeys(self):
|
||||||
return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
|
return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode", "rfgain_sel", "agc_setpoint"]
|
||||||
|
Loading…
Reference in New Issue
Block a user