+
-
diff --git a/htdocs/lib/DemodulatorPanel.js b/htdocs/lib/DemodulatorPanel.js
index 35cea9c6..b6f460cc 100644
--- a/htdocs/lib/DemodulatorPanel.js
+++ b/htdocs/lib/DemodulatorPanel.js
@@ -217,6 +217,7 @@ DemodulatorPanel.prototype._apply = function(params) {
this.getDemodulator().set_offset_frequency(params.offset_frequency);
this.getDemodulator().setSquelch(params.squelch_level);
this.updateButtons();
+ this.setMagicKey(params.magic_key);
};
DemodulatorPanel.prototype.setInitialParams = function(params) {
@@ -227,6 +228,14 @@ DemodulatorPanel.prototype.resetInitialParams = function() {
this.initialParams = {};
};
+DemodulatorPanel.prototype.setMagicKey = function(key) {
+ this.magic_key = key;
+};
+
+DemodulatorPanel.prototype.getMagicKey = function() {
+ return this.magic_key;
+};
+
DemodulatorPanel.prototype.onHashChange = function() {
this._apply(this.transformHashParams(this.parseHash()));
};
@@ -238,6 +247,7 @@ DemodulatorPanel.prototype.transformHashParams = function(params) {
if (typeof(params.secondary_mod) !== 'undefined') ret.secondary_mod = params.secondary_mod;
if (typeof(params.offset_frequency) !== 'undefined') ret.offset_frequency = params.offset_frequency;
if (typeof(params.sql) !== 'undefined') ret.squelch_level = parseInt(params.sql);
+ if (typeof(params.key) !== 'undefined') ret.magic_key = params.key;
return ret;
};
@@ -340,10 +350,13 @@ DemodulatorPanel.prototype.updateHash = function() {
freq: demod.get_offset_frequency() + self.center_freq,
mod: demod.get_modulation(),
secondary_mod: demod.get_secondary_demod(),
- sql: demod.getSquelch()
+ sql: demod.getSquelch(),
+ key: self.magic_key
}, function(value, key){
- if (typeof(value) === 'undefined' || value === false) return undefined;
- return key + '=' + value;
+ if (typeof(value) === 'undefined' || value === false || value === '')
+ return undefined;
+ else
+ return key + '=' + value;
}).filter(function(v) {
return !!v;
}).join(',');
diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js
index 7c2133b4..e5393032 100644
--- a/htdocs/openwebrx.js
+++ b/htdocs/openwebrx.js
@@ -151,6 +151,17 @@ function tuneBySteps(steps) {
}
}
+function jumpBySteps(steps) {
+ steps = Math.round(steps);
+ if (steps != 0) {
+ var key = $('#openwebrx-panel-receiver').demodulatorPanel().getMagicKey();
+ var f = center_freq + steps * bandwidth / 4;
+ ws.send(JSON.stringify({
+ "type": "setfrequency", "params": { "frequency": f, "key": key }
+ }));
+ }
+}
+
var waterfall_min_level;
var waterfall_max_level;
var waterfall_min_level_default;
@@ -1080,6 +1091,11 @@ function on_ws_recv(evt) {
$('#openwebrx-panel-receiver').css('border', x? '2px solid':'');
}
+ if ('allow_audio_recording' in config) {
+ var x = config['allow_audio_recording'];
+ $('.openwebrx-record-button').css('display', x? '':'none');
+ }
+
break;
case "secondary_config":
var s = json['value'];
@@ -1149,11 +1165,11 @@ function on_ws_recv(evt) {
case 'secondary_demod':
var value = json['value'];
var panels = [
- $("#openwebrx-panel-wsjt-message").wsjtMessagePanel(),
- $('#openwebrx-panel-packet-message').packetMessagePanel(),
- $('#openwebrx-panel-pocsag-message').pocsagMessagePanel(),
- $('#openwebrx-panel-sstv-message').sstvMessagePanel(),
- $('#openwebrx-panel-fax-message').faxMessagePanel(),
+ $("#openwebrx-panel-wsjt-message").wsjtMessagePanel(),
+ $('#openwebrx-panel-packet-message').packetMessagePanel(),
+ $('#openwebrx-panel-pocsag-message').pocsagMessagePanel(),
+ $('#openwebrx-panel-sstv-message').sstvMessagePanel(),
+ $('#openwebrx-panel-fax-message').faxMessagePanel(),
$("#openwebrx-panel-js8-message").js8()
];
if (!panels.some(function(panel) {
diff --git a/owrx/config/defaults.py b/owrx/config/defaults.py
index 8b01bfde..6b71ce02 100644
--- a/owrx/config/defaults.py
+++ b/owrx/config/defaults.py
@@ -157,6 +157,9 @@ defaultConfig = PropertyLayer(
waterfall_auto_min_range=50,
ui_opacity=100,
ui_frame=False,
+ magic_key="memagic",
+ allow_center_freq_changes=False,
+ allow_audio_recording=True,
tuning_precision=2,
squelch_auto_margin=10,
google_maps_api_key="",
diff --git a/owrx/connection.py b/owrx/connection.py
index 13e66696..27635e98 100644
--- a/owrx/connection.py
+++ b/owrx/connection.py
@@ -139,6 +139,9 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
"tuning_precision",
"ui_opacity",
"ui_frame",
+ "allow_center_freq_changes",
+ "allow_audio_recording",
+ "magic_key",
]
def __init__(self, conn):
@@ -295,6 +298,15 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
profile = message["params"]["profile"].split("|")
self.setSdr(profile[0])
self.sdr.activateProfile(profile[1])
+ elif message["type"] == "setfrequency":
+ # If the magic key is set in the settings, only allow
+ # changes if it matches the received key
+ if "params" in message and self.stack["allow_center_freq_changes"]:
+ magic_key = self.stack["magic_key"]
+ params = message["params"]
+ if magic_key == "" or ("key" in params and params["key"] == magic_key):
+ if "frequency" in params:
+ self.sdr.setCenterFreq(params["frequency"])
elif message["type"] == "connectionproperties":
if "params" in message:
self.connectionProperties = message["params"]
diff --git a/owrx/controllers/settings/general.py b/owrx/controllers/settings/general.py
index 574ecefb..c4e46e82 100644
--- a/owrx/controllers/settings/general.py
+++ b/owrx/controllers/settings/general.py
@@ -91,6 +91,20 @@ class GeneralSettingsController(SettingsFormController):
infotext="Specifies web page describing receiver usage policy "
+ "and shown when a client session times out.",
),
+ CheckboxInput(
+ "allow_audio_recording",
+ "Allow users to record received audio",
+ ),
+ CheckboxInput(
+ "allow_center_freq_changes",
+ "Allow users to change center frequency",
+ ),
+ TextInput(
+ "magic_key",
+ "Magic key",
+ infotext="Enter a key the user has to supply to change center frequency."
+ + " Leave empty if you do not want to protect frequency changes with a key.",
+ ),
),
Section(
"Receiver listings",
diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py
index e3e79d9a..acc8bf92 100644
--- a/owrx/source/__init__.py
+++ b/owrx/source/__init__.py
@@ -116,21 +116,25 @@ class SdrSource(ABC):
self.buffer = None
self.props = PropertyStack()
-
- # layer 0 reserved for profile properties
self.profileCarousel = SdrProfileCarousel(props)
+
+ # layer 0 contains center_freq so that it can be changed
+ # independently of the profile
+ self.props.addLayer(0, PropertyLayer())
+
+ # layer 1 reserved for profile properties
# prevent profile names from overriding the device name
- self.props.addLayer(0, PropertyFilter(self.profileCarousel, ByLambda(lambda x: x != "name")))
+ self.props.addLayer(1, PropertyFilter(self.profileCarousel, ByLambda(lambda x: x != "name")))
# props from our device config
- self.props.addLayer(1, props)
+ self.props.addLayer(2, props)
# the sdr_id is constant, so we put it in a separate layer
# this is used to detect device changes, that are then sent to the client
- self.props.addLayer(2, PropertyLayer(sdr_id=id).readonly())
+ self.props.addLayer(3, PropertyLayer(sdr_id=id).readonly())
# finally, accept global config properties from the top-level config
- self.props.addLayer(3, Config.get())
+ self.props.addLayer(4, Config.get())
self.sdrProps = self.props.filter(*self.getEventNames())
@@ -228,9 +232,13 @@ class SdrSource(ABC):
logger.debug("activating profile {0} for {1}".format(profile_id, self.getId()))
try:
self.profileCarousel.switch(profile_id)
+ self.setCenterFreq(self.profileCarousel["center_freq"])
except KeyError:
logger.warning("invalid profile %s for sdr %s. ignoring", profile_id, self.getId())
+ def setCenterFreq(self, frequency):
+ self.props["center_freq"] = frequency
+
def getId(self):
return self.id