-
![](static/gfx/openwebrx-top-photo.jpg)
+
-
![](static/gfx/openwebrx-top-logo.png)
-
![](static/gfx/openwebrx-avatar.png)
+
![OpenWebRX Logo](static/gfx/openwebrx-top-logo.png)
+
@@ -13,10 +13,10 @@
-
-
-
- ![](static/gfx/openwebrx-panel-map.png)
Map
+
+
+
+ ![Map](static/gfx/openwebrx-panel-map.png)
Map
${settingslink}
diff --git a/htdocs/lib/DemodulatorPanel.js b/htdocs/lib/DemodulatorPanel.js
index bfd3619..439a8e5 100644
--- a/htdocs/lib/DemodulatorPanel.js
+++ b/htdocs/lib/DemodulatorPanel.js
@@ -222,7 +222,7 @@ DemodulatorPanel.prototype.transformHashParams = function(params) {
var ret = {
mod: params.secondary_mod || params.mod
};
- if (params.offset_frequency) ret.offset_frequency = params.offset_frequency;
+ if (typeof(params.offset_frequency) !== 'undefined') ret.offset_frequency = params.offset_frequency;
if (typeof(params.sql) !== 'undefined') ret.squelch_level = parseInt(params.sql);
return ret;
};
diff --git a/htdocs/lib/Header.js b/htdocs/lib/Header.js
index d1c6a30..cced6b6 100644
--- a/htdocs/lib/Header.js
+++ b/htdocs/lib/Header.js
@@ -6,6 +6,7 @@ function Header(el) {
});
this.init_rx_photo();
+ this.download_details();
};
Header.prototype.setDetails = function(details) {
@@ -57,6 +58,13 @@ Header.prototype.toggle_rx_photo = function(ev) {
}
};
+Header.prototype.download_details = function() {
+ var self = this;
+ $.ajax('api/receiverdetails').done(function(data){
+ self.setDetails(data);
+ });
+};
+
$.fn.header = function() {
if (!this.data('header')) {
this.data('header', new Header(this));
diff --git a/htdocs/lib/settings/Input.js b/htdocs/lib/settings/Input.js
new file mode 100644
index 0000000..f638257
--- /dev/null
+++ b/htdocs/lib/settings/Input.js
@@ -0,0 +1,138 @@
+function Input(name, value, options) {
+ this.name = name;
+ this.value = value;
+ this.options = options;
+ this.label = options && options.label || name;
+};
+
+Input.prototype.getClasses = function() {
+ return ['form-control', 'form-control-sm'];
+}
+
+Input.prototype.bootstrapify = function(input) {
+ this.getClasses().forEach(input.addClass.bind(input));
+ return [
+ '
'
+ ].join('');
+};
+
+function TextInput() {
+ Input.apply(this, arguments);
+};
+
+TextInput.prototype = new Input();
+
+TextInput.prototype.render = function() {
+ return this.bootstrapify($('
'));
+}
+
+function NumberInput() {
+ Input.apply(this, arguments);
+};
+
+NumberInput.prototype = new Input();
+
+NumberInput.prototype.render = function() {
+ return this.bootstrapify($('
'));
+};
+
+function SoapyGainInput() {
+ Input.apply(this, arguments);
+}
+
+SoapyGainInput.prototype = new Input();
+
+SoapyGainInput.prototype.getClasses = function() {
+ return [];
+};
+
+SoapyGainInput.prototype.render = function(){
+ var markup = $(
+ '
' +
+ '
' +
+ this.options.gains.map(function(g){
+ return '
';
+ }).join('')
+ );
+ var el = $(this.bootstrapify(markup))
+ var setMode = function(mode){
+ el.find('select').val(mode);
+ el.find('.option').hide();
+ el.find('.gain-mode-' + mode).show();
+ };
+ el.on('change', 'select', function(){
+ var mode = $(this).val();
+ setMode(mode);
+ });
+ if (typeof(this.value) === 'number') {
+ setMode('single');
+ el.find('.gain-mode-single input').val(this.value);
+ } else if (typeof(this.value) === 'string') {
+ if (this.value === 'auto') {
+ setMode('auto');
+ } else {
+ setMode('separate');
+ values = $.extend.apply($, this.value.split(',').map(function(seg){
+ var split = seg.split('=');
+ if (split.length < 2) return;
+ var res = {};
+ res[split[0]] = parseInt(split[1]);
+ return res;
+ }));
+ el.find('.gain-mode-separate input').each(function(){
+ var $input = $(this);
+ var g = $input.data('gain');
+ $input.val(g in values ? values[g] : 0);
+ });
+ }
+ } else {
+ setMode('auto');
+ }
+ return el;
+};
+
+function ProfileInput() {
+ Input.apply(this, arguments);
+};
+
+ProfileInput.prototype = new Input();
+
+ProfileInput.prototype.render = function() {
+ return $('
Profiles
');
+};
+
+function SchedulerInput() {
+ Input.apply(this, arguments);
+};
+
+SchedulerInput.prototype = new Input();
+
+SchedulerInput.prototype.render = function() {
+ return $('
Scheduler
');
+};
diff --git a/htdocs/lib/settings/SdrDevice.js b/htdocs/lib/settings/SdrDevice.js
new file mode 100644
index 0000000..25f85c9
--- /dev/null
+++ b/htdocs/lib/settings/SdrDevice.js
@@ -0,0 +1,252 @@
+function SdrDevice(el, data) {
+ this.el = el;
+ this.data = data;
+ this.inputs = {};
+ this.render();
+
+ var self = this;
+ el.on('click', '.fieldselector .btn', function() {
+ var key = el.find('.fieldselector select').val();
+ self.data[key] = self.getInitialValue(key);
+ self.render();
+ });
+};
+
+SdrDevice.create = function(el) {
+ var data = JSON.parse(decodeURIComponent(el.data('config')));
+ var type = data.type;
+ var constructor = SdrDevice.types[type] || SdrDevice;
+ return new constructor(el, data);
+};
+
+SdrDevice.prototype.getData = function() {
+ return $.extend(new Object(), this.getDefaults(), this.data);
+};
+
+SdrDevice.prototype.getDefaults = function() {
+ var defaults = {}
+ $.each(this.getMappings(), function(k, v) {
+ if (!v.includeInDefault) return;
+ defaults[k] = 'initialValue' in v ? v['initialValue'] : false;
+ });
+ return defaults;
+};
+
+SdrDevice.prototype.getMappings = function() {
+ return {
+ "name": {
+ constructor: TextInput,
+ inputOptions: {
+ label: "Name"
+ },
+ initialValue: "",
+ includeInDefault: true
+ },
+ "type": {
+ constructor: TextInput,
+ inputOptions: {
+ label: "Type"
+ },
+ initialValue: '',
+ includeInDefault: true
+ },
+ "ppm": {
+ constructor: NumberInput,
+ inputOptions: {
+ label: "PPM"
+ },
+ initialValue: 0
+ },
+ "profiles": {
+ constructor: ProfileInput,
+ inputOptions: {
+ label: "Profiles"
+ },
+ initialValue: [],
+ includeInDefault: true,
+ position: 100
+ },
+ "scheduler": {
+ constructor: SchedulerInput,
+ inputOptions: {
+ label: "Scheduler",
+ },
+ initialValue: {},
+ position: 101
+ },
+ "rf_gain": {
+ constructor: TextInput,
+ inputOptions: {
+ label: "Gain",
+ },
+ initialValue: 0
+ }
+ };
+};
+
+SdrDevice.prototype.getMapping = function(key) {
+ var mappings = this.getMappings();
+ return mappings[key];
+};
+
+SdrDevice.prototype.getInputClass = function(key) {
+ var mapping = this.getMapping(key);
+ return mapping && mapping.constructor || TextInput;
+};
+
+SdrDevice.prototype.getInitialValue = function(key) {
+ var mapping = this.getMapping(key);
+ return mapping && ('initialValue' in mapping) ? mapping['initialValue'] : false;
+};
+
+SdrDevice.prototype.getPosition = function(key) {
+ var mapping = this.getMapping(key);
+ return mapping && mapping.position || 10;
+};
+
+SdrDevice.prototype.getInputOptions = function(key) {
+ var mapping = this.getMapping(key);
+ return mapping && mapping.inputOptions || {};
+};
+
+SdrDevice.prototype.getLabel = function(key) {
+ var options = this.getInputOptions(key);
+ return options && options.label || key;
+};
+
+SdrDevice.prototype.render = function() {
+ var self = this;
+ self.el.empty();
+ var data = this.getData();
+ Object.keys(data).sort(function(a, b){
+ return self.getPosition(a) - self.getPosition(b);
+ }).forEach(function(key){
+ var value = data[key];
+ var inputClass = self.getInputClass(key);
+ var input = new inputClass(key, value, self.getInputOptions(key));
+ self.inputs[key] = input;
+ self.el.append(input.render());
+ });
+ self.el.append(this.renderFieldSelector());
+};
+
+SdrDevice.prototype.renderFieldSelector = function() {
+ var self = this;
+ return '
' +
+ '
Add new configuration options' +
+ '' +
+ '
';
+};
+
+RtlSdrDevice = function() {
+ SdrDevice.apply(this, arguments);
+};
+
+RtlSdrDevice.prototype = Object.create(SdrDevice.prototype);
+RtlSdrDevice.prototype.constructor = RtlSdrDevice;
+
+RtlSdrDevice.prototype.getMappings = function() {
+ var mappings = SdrDevice.prototype.getMappings.apply(this, arguments);
+ return $.extend(new Object(), mappings, {
+ "device": {
+ constructor: TextInput,
+ inputOptions:{
+ label: "Serial number"
+ },
+ initialValue: ""
+ }
+ });
+};
+
+SoapySdrDevice = function() {
+ SdrDevice.apply(this, arguments);
+};
+
+SoapySdrDevice.prototype = Object.create(SdrDevice.prototype);
+SoapySdrDevice.prototype.constructor = SoapySdrDevice;
+
+SoapySdrDevice.prototype.getMappings = function() {
+ var mappings = SdrDevice.prototype.getMappings.apply(this, arguments);
+ return $.extend(new Object(), mappings, {
+ "device": {
+ constructor: TextInput,
+ inputOptions:{
+ label: "Soapy device selector"
+ },
+ initialValue: ""
+ },
+ "rf_gain": {
+ constructor: SoapyGainInput,
+ initialValue: 0,
+ inputOptions: {
+ label: "Gain",
+ gains: this.getGains()
+ }
+ }
+ });
+};
+
+SoapySdrDevice.prototype.getGains = function() {
+ return [];
+};
+
+SdrplaySdrDevice = function() {
+ SoapySdrDevice.apply(this, arguments);
+};
+
+SdrplaySdrDevice.prototype = Object.create(SoapySdrDevice.prototype);
+SdrplaySdrDevice.prototype.constructor = SdrplaySdrDevice;
+
+SdrplaySdrDevice.prototype.getGains = function() {
+ return ['RFGR', 'IFGR'];
+};
+
+AirspyHfSdrDevice = function() {
+ SoapySdrDevice.apply(this, arguments);
+};
+
+AirspyHfSdrDevice.prototype = Object.create(SoapySdrDevice.prototype);
+AirspyHfSdrDevice.prototype.constructor = AirspyHfSdrDevice;
+
+AirspyHfSdrDevice.prototype.getGains = function() {
+ return ['RF', 'VGA'];
+};
+
+HackRfSdrDevice = function() {
+ SoapySdrDevice.apply(this, arguments);
+};
+
+HackRfSdrDevice.prototype = Object.create(SoapySdrDevice.prototype);
+HackRfSdrDevice.prototype.constructor = HackRfSdrDevice;
+
+HackRfSdrDevice.prototype.getGains = function() {
+ return ['LNA', 'VGA', 'AMP'];
+};
+
+SdrDevice.types = {
+ 'rtl_sdr': RtlSdrDevice,
+ 'sdrplay': SdrplaySdrDevice,
+ 'airspyhf': AirspyHfSdrDevice,
+ 'hackrf': HackRfSdrDevice
+};
+
+$.fn.sdrdevice = function() {
+ return this.map(function(){
+ var el = $(this);
+ if (!el.data('sdrdevice')) {
+ el.data('sdrdevice', SdrDevice.create(el));
+ }
+ return el.data('sdrdevice');
+ });
+};
diff --git a/htdocs/login.html b/htdocs/login.html
index 9cbeacc..4f4c554 100644
--- a/htdocs/login.html
+++ b/htdocs/login.html
@@ -3,7 +3,7 @@
OpenWebRX Login
-
+
@@ -21,7 +21,7 @@
-
+
\ No newline at end of file
diff --git a/htdocs/sdrsettings.html b/htdocs/sdrsettings.html
index 08664fe..74aa8b7 100644
--- a/htdocs/sdrsettings.html
+++ b/htdocs/sdrsettings.html
@@ -3,11 +3,9 @@
-
-
- device settings go here
-
+ return "".join(self.render_device(key, value) for key, value in Config.get()["sdrs"].items())
+
+ def render_device(self, device_id, config):
+ return """
+
+
- """.format(device_name=config["name"])
- return "".join(render_devicde(key, value) for key, value in Config.get()["sdrs"].items())
+
+ {form}
+
+
+ """.format(device_name=config["name"], form=self.render_form(device_id, config))
+
+ def render_form(self, device_id, config):
+ return """
+
+ """.format(device_id=device_id, formdata=quote(json.dumps(config)))
def indexAction(self):
self.serve_template("sdrsettings.html", **self.template_variables())
@@ -236,18 +244,6 @@ class GeneralSettingsController(AdminController):
infotext="This callsign will be used to send spots to pskreporter.info",
),
),
- Section(
- "sdr.hu",
- TextInput(
- "sdrhu_key",
- "sdr.hu key",
- infotext='Please obtain your personal key on
sdr.hu',
- ),
- CheckboxInput(
- "sdrhu_public_listing", "List on sdr.hu", "List my receiver on sdr.hu"
- ),
- TextInput("server_hostname", "Hostname"),
- ),
]
def render_sections(self):
diff --git a/owrx/controllers/status.py b/owrx/controllers/status.py
index 61f7102..beded28 100644
--- a/owrx/controllers/status.py
+++ b/owrx/controllers/status.py
@@ -9,26 +9,6 @@ import pkg_resources
class StatusController(Controller):
- def indexAction(self):
- pm = Config.get()
- # convert to old format
- gps = (pm["receiver_gps"]["lat"], pm["receiver_gps"]["lon"])
- avatar_path = pkg_resources.resource_filename("htdocs", "gfx/openwebrx-avatar.png")
- # TODO keys that have been left out since they are no longer simple strings: sdr_hw, bands, antenna
- vars = {
- "status": "active",
- "name": pm["receiver_name"],
- "op_email": pm["receiver_admin"],
- "users": ClientRegistry.getSharedInstance().clientCount(),
- "users_max": pm["max_clients"],
- "gps": gps,
- "asl": pm["receiver_asl"],
- "loc": pm["receiver_location"],
- "sw_version": openwebrx_version,
- "avatar_ctime": os.path.getctime(avatar_path),
- }
- self.send_response("\n".join(["{key}={value}".format(key=key, value=value) for key, value in vars.items()]))
-
def getProfileStats(self, profile):
return {
"name": profile["name"],
@@ -45,7 +25,7 @@ class StatusController(Controller):
}
return stats
- def jsonAction(self):
+ def indexAction(self):
pm = Config.get()
status = {
diff --git a/owrx/controllers/template.py b/owrx/controllers/template.py
index 23c1d17..3d57861 100644
--- a/owrx/controllers/template.py
+++ b/owrx/controllers/template.py
@@ -23,7 +23,7 @@ class WebpageController(TemplateController):
settingslink = ""
pm = Config.get()
if "webadmin_enabled" in pm and pm["webadmin_enabled"]:
- settingslink = """
![](static/gfx/openwebrx-panel-settings.png)
Settings"""
+ settingslink = """
![Settings](static/gfx/openwebrx-panel-settings.png)
Settings"""
header = self.render_template("include/header.include.html", settingslink=settingslink)
return {"header": header}
diff --git a/owrx/details.py b/owrx/details.py
new file mode 100644
index 0000000..5bc7253
--- /dev/null
+++ b/owrx/details.py
@@ -0,0 +1,21 @@
+from owrx.config import Config
+from owrx.locator import Locator
+from owrx.property import PropertyFilter
+
+
+class ReceiverDetails(PropertyFilter):
+ def __init__(self):
+ super().__init__(
+ Config.get(),
+ "receiver_name",
+ "receiver_location",
+ "receiver_asl",
+ "receiver_gps",
+ "photo_title",
+ "photo_desc",
+ )
+
+ def __dict__(self):
+ receiver_info = super().__dict__()
+ receiver_info["locator"] = Locator.fromCoordinates(receiver_info["receiver_gps"])
+ return receiver_info
diff --git a/owrx/feature.py b/owrx/feature.py
index 6d5b4fa..b6d83b8 100644
--- a/owrx/feature.py
+++ b/owrx/feature.py
@@ -24,12 +24,12 @@ class FeatureDetector(object):
"rtl_sdr": ["rtl_connector"],
"rtl_sdr_soapy": ["soapy_connector", "soapy_rtl_sdr"],
"sdrplay": ["soapy_connector", "soapy_sdrplay"],
- "hackrf": ["hackrf_transfer"],
+ "hackrf": ["soapy_connector", "soapy_hackrf"],
"perseussdr": ["perseustest"],
"airspy": ["soapy_connector", "soapy_airspy"],
"airspyhf": ["soapy_connector", "soapy_airspyhf"],
"lime_sdr": ["soapy_connector", "soapy_lime_sdr"],
- "fifi_sdr": ["alsa"],
+ "fifi_sdr": ["alsa", "rockprog"],
"pluto_sdr": ["soapy_connector", "soapy_pluto_sdr"],
"soapy_remote": ["soapy_connector", "soapy_remote"],
"uhd": ["soapy_connector", "soapy_uhd"],
@@ -128,26 +128,6 @@ class FeatureDetector(object):
"""
return self.command_is_runnable("nc --help")
- def has_hackrf_transfer(self):
- """
- To use a HackRF, compile the HackRF host tools from its "stdout" branch:
- ```
- git clone https://github.com/mossmann/hackrf/
- cd hackrf
- git fetch
- git checkout origin/stdout
- cd host
- mkdir build
- cd build
- cmake .. -DINSTALL_UDEV_RULES=ON
- make
- sudo make install
- ```
- """
- # TODO i don't have a hackrf, so somebody doublecheck this.
- # TODO also check if it has the stdout feature
- return self.command_is_runnable("hackrf_transfer --help")
-
def has_perseustest(self):
"""
To use a Microtelecom Perseus HF receiver, compile and
@@ -273,7 +253,7 @@ class FeatureDetector(object):
"""
The SoapySDR module for sdrplay devices is required for interfacing with SDRPlay devices (RSP1*, RSP2*, RSPDuo)
- You can get it [here](https://github.com/pothosware/SoapySDRPlay/wiki).
+ You can get it [here](https://github.com/SDRplay/SoapySDRPlay).
"""
return self._has_soapy_driver("sdrplay")
@@ -342,6 +322,14 @@ class FeatureDetector(object):
"""
return self._has_soapy_driver("radioberry")
+ def has_soapy_hackrf(self):
+ """
+ The SoapyHackRF allows HackRF to be used with SoapySDR.
+
+ You can get it [here](https://github.com/pothosware/SoapyHackRF/wiki).
+ """
+ return self._has_soapy_driver("hackrf")
+
def has_dsd(self):
"""
The digital voice modes NXDN and D-Star can be decoded by the dsd project. Please note that you need the version
@@ -396,3 +384,11 @@ class FeatureDetector(object):
on the Alsa library. It is available as a package for most Linux distributions.
"""
return self.command_is_runnable("arecord --help")
+
+ def has_rockprog(self):
+ """
+ The "rockprog" executable is required to send commands to your FiFiSDR. It needs to be installed separately.
+
+ You can find instructions and downloads [here](https://o28.sischa.net/fifisdr/trac/wiki/De%3Arockprog).
+ """
+ return self.command_is_runnable("rockprog")
diff --git a/owrx/http.py b/owrx/http.py
index 39e2bdf..812bec0 100644
--- a/owrx/http.py
+++ b/owrx/http.py
@@ -89,18 +89,16 @@ class Router(object):
def __init__(self):
self.routes = [
StaticRoute("/", IndexController),
- StaticRoute("/status", StatusController),
- StaticRoute("/status.json", StatusController, options={"action": "jsonAction"}),
+ StaticRoute("/status.json", StatusController),
RegexRoute("/static/(.+)", OwrxAssetsController),
RegexRoute("/compiled/(.+)", CompiledAssetsController),
RegexRoute("/aprs-symbols/(.+)", AprsSymbolsController),
StaticRoute("/ws/", WebSocketController),
RegexRoute("(/favicon.ico)", OwrxAssetsController),
- # backwards compatibility for the sdr.hu portal
- RegexRoute("(/gfx/openwebrx-avatar.png)", OwrxAssetsController),
StaticRoute("/map", MapController),
StaticRoute("/features", FeatureController),
StaticRoute("/api/features", ApiController),
+ StaticRoute("/api/receiverdetails", ApiController, options={"action": "receiverDetails"}),
StaticRoute("/metrics", MetricsController),
StaticRoute("/settings", SettingsController),
StaticRoute("/generalsettings", GeneralSettingsController),
diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py
index 853cca3..f3560fa 100644
--- a/owrx/property/__init__.py
+++ b/owrx/property/__init__.py
@@ -40,6 +40,10 @@ class PropertyManager(ABC):
def __dict__(self):
pass
+ @abstractmethod
+ def __delitem__(self, key):
+ pass
+
@abstractmethod
def keys(self):
pass
@@ -98,6 +102,9 @@ class PropertyLayer(PropertyManager):
def __dict__(self):
return {k: v for k, v in self.properties.items()}
+ def __delitem__(self, key):
+ return self.properties.__delitem__(key)
+
def keys(self):
return self.properties.keys()
@@ -132,6 +139,11 @@ class PropertyFilter(PropertyManager):
def __dict__(self):
return {k: v for k, v in self.pm.__dict__().items() if k in self.props}
+ def __delitem__(self, key):
+ if key not in self.props:
+ raise KeyError(key)
+ return self.pm.__delitem__(key)
+
def keys(self):
return [k for k in self.pm.keys() if k in self.props]
@@ -226,5 +238,9 @@ class PropertyStack(PropertyManager):
def __dict__(self):
return {k: self.__getitem__(k) for k in self.keys()}
+ def __delitem__(self, key):
+ for layer in self.layers:
+ layer["props"].__delitem__(key)
+
def keys(self):
return set([key for l in self.layers for key in l["props"].keys()])
diff --git a/owrx/sdrhu.py b/owrx/sdrhu.py
deleted file mode 100644
index 193399f..0000000
--- a/owrx/sdrhu.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import threading
-import time
-from owrx.config import Config
-from urllib import request, parse
-
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-class SdrHuUpdater(threading.Thread):
- def __init__(self):
- self.doRun = True
- super().__init__(daemon=True)
-
- def update(self):
- pm = Config.get().filter("server_hostname", "web_port", "sdrhu_key")
- data = parse.urlencode({
- "url": "http://{server_hostname}:{web_port}".format(**pm.__dict__()),
- "apikey": pm["sdrhu_key"]
- }).encode()
-
- res = request.urlopen("https://sdr.hu/update", data=data)
- if res.getcode() < 200 or res.getcode() >= 300:
- logger.warning('sdr.hu update failed with error code %i', res.getcode())
- return 2
-
- returned = res.read().decode("utf-8")
- if "UPDATE:" not in returned:
- logger.warning("Update failed, your receiver cannot be listed on sdr.hu!")
- return 2
-
- value = returned.split("UPDATE:")[1].split("\n", 1)[0]
- if value.startswith("SUCCESS"):
- logger.info("Update succeeded!")
- else:
- logger.warning("Update failed, your receiver cannot be listed on sdr.hu! Reason: %s", value)
- return 20
-
- def run(self):
- while self.doRun:
- retrytime_mins = self.update()
- time.sleep(60 * retrytime_mins)
diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py
index d4c97df..b4070a1 100644
--- a/owrx/service/__init__.py
+++ b/owrx/service/__init__.py
@@ -265,6 +265,7 @@ class ServiceHandler(object):
d.set_secondary_demodulator(mode)
d.set_audio_compression("none")
d.set_samp_rate(source.getProps()["samp_rate"])
+ d.set_temporary_directory(Config.get()['temporary_directory'])
d.set_service()
d.start()
return d
diff --git a/owrx/source/hackrf.py b/owrx/source/hackrf.py
index f59a448..a103218 100644
--- a/owrx/source/hackrf.py
+++ b/owrx/source/hackrf.py
@@ -1,23 +1,11 @@
-from .direct import DirectSource
-from owrx.command import Option
-import time
+from .soapy import SoapyConnectorSource
-class HackrfSource(DirectSource):
- def getCommandMapper(self):
- return super().getCommandMapper().setBase("hackrf_transfer").setMappings(
- {
- "samp_rate": Option("-s"),
- "tuner_freq": Option("-f"),
- "rf_gain": Option("-g"),
- "lna_gain": Option("-l"),
- "rf_amp": Option("-a"),
- "ppm": Option("-C"),
- }
- ).setStatic("-r-")
+class HackrfSource(SoapyConnectorSource):
+ def getSoapySettingsMappings(self):
+ mappings = super().getSoapySettingsMappings()
+ mappings.update({"bias_tee": "bias_tx"})
+ return mappings
- def getFormatConversion(self):
- return ["csdr convert_s8_f"]
-
- def sleepOnRestart(self):
- time.sleep(1)
+ def getDriver(self):
+ return "hackrf"
\ No newline at end of file
diff --git a/owrx/source/resampler.py b/owrx/source/resampler.py
index 6afe50c..1f6a4e1 100644
--- a/owrx/source/resampler.py
+++ b/owrx/source/resampler.py
@@ -1,10 +1,4 @@
from .direct import DirectSource
-from . import SdrSource
-import subprocess
-import threading
-import os
-import socket
-import time
import logging
@@ -29,7 +23,7 @@ class Resampler(DirectSource):
def getCommand(self):
return [
"nc -v 127.0.0.1 {nc_port}".format(nc_port=self.sdr.getPort()),
- "csdr shift_addition_cc {shift}".format(shift=self.shift),
+ "csdr shift_addfast_cc {shift}".format(shift=self.shift),
"csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING".format(
decimation=self.decimation, ddc_transition_bw=self.transition_bw
),
diff --git a/sdrhu.py b/sdrhu.py
deleted file mode 100755
index 9678a9b..0000000
--- a/sdrhu.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/python3
-"""
-
- This file is part of OpenWebRX,
- an open-source SDR receiver software with a web UI.
- Copyright (c) 2013-2015 by Andras Retzler
- Copyright (c) 2019-2020 by Jakob Ketterl
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-"""
-
-from owrx.sdrhu import SdrHuUpdater
-from owrx.config import Config
-
-import logging
-logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
-logger = logging.getLogger(__name__)
-
-if __name__ == "__main__":
- pm = Config.get()
-
- if "sdrhu_public_listing" not in pm or not pm["sdrhu_public_listing"]:
- logger.error('Public listing on sdr.hu is not activated. Please check "sdrhu_public_listing" in your config.')
- exit(1)
- if "sdrhu_key" not in pm or pm["sdrhu_key"] is None or pm["sdrhu_key"] == "":
- logger.error('Missing "sdrhu_key" in your config. Aborting')
- exit(1)
- SdrHuUpdater().update()