Merge branch 'jketterl:develop' into tuning_step
This commit is contained in:
commit
caf77f686a
@ -1,5 +1,9 @@
|
|||||||
**unreleased**
|
**unreleased**
|
||||||
|
|
||||||
|
**1.2.1**
|
||||||
|
- FifiSDR support fixed (pipeline formats now line up correctly)
|
||||||
|
- Added "Device" input for FifiSDR devices for sound card selection
|
||||||
|
|
||||||
**1.2.0**
|
**1.2.0**
|
||||||
- Major rewrite of all demodulation components to make use of the new csdr/pycsdr and digiham/pydigiham demodulator
|
- Major rewrite of all demodulation components to make use of the new csdr/pycsdr and digiham/pydigiham demodulator
|
||||||
modules
|
modules
|
||||||
|
9
debian/changelog
vendored
9
debian/changelog
vendored
@ -1,6 +1,13 @@
|
|||||||
openwebrx (1.3.0) UNRELEASED; urgency=low
|
openwebrx (1.3.0) UNRELEASED; urgency=low
|
||||||
|
|
||||||
-- Jakob Ketterl <jakob.ketterl@gmx.de> Thu, 16 Jun 2022 21:47:00 +0000
|
-- Jakob Ketterl <jakob.ketterl@gmx.de> Fri, 30 Sep 2022 16:47:00 +0000
|
||||||
|
|
||||||
|
openwebrx (1.2.1) bullseye jammy; urgency=low
|
||||||
|
|
||||||
|
* FifiSDR support fixed (pipeline formats now line up correctly)
|
||||||
|
* Added "Device" input for FifiSDR devices for sound card selection
|
||||||
|
|
||||||
|
-- Jakob Ketterl <jakob.ketterl@gmx.de> Tue, 20 Sep 2022 16:01:00 +0000
|
||||||
|
|
||||||
openwebrx (1.2.0) bullseye jammy; urgency=low
|
openwebrx (1.2.0) bullseye jammy; urgency=low
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ apt-get update
|
|||||||
apt-get -y install --no-install-recommends $BUILD_PACKAGES
|
apt-get -y install --no-install-recommends $BUILD_PACKAGES
|
||||||
|
|
||||||
git clone https://github.com/jketterl/owrx_connector.git
|
git clone https://github.com/jketterl/owrx_connector.git
|
||||||
cmakebuild owrx_connector 0.6.0
|
cmakebuild owrx_connector 0.6.1
|
||||||
|
|
||||||
apt-get -y purge --autoremove $BUILD_PACKAGES
|
apt-get -y purge --autoremove $BUILD_PACKAGES
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
@ -25,7 +25,7 @@ apt-get update
|
|||||||
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
|
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
|
||||||
|
|
||||||
git clone https://github.com/jketterl/runds_connector.git
|
git clone https://github.com/jketterl/runds_connector.git
|
||||||
cmakebuild runds_connector 0.2.1
|
cmakebuild runds_connector 0.2.2
|
||||||
|
|
||||||
apt-get -y purge --autoremove $BUILD_PACKAGES
|
apt-get -y purge --autoremove $BUILD_PACKAGES
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
@ -31,11 +31,11 @@ popd
|
|||||||
rm -rf js8py
|
rm -rf js8py
|
||||||
|
|
||||||
git clone https://github.com/jketterl/csdr.git
|
git clone https://github.com/jketterl/csdr.git
|
||||||
cmakebuild csdr 0.18.0
|
cmakebuild csdr 0.18.1
|
||||||
|
|
||||||
git clone https://github.com/jketterl/pycsdr.git
|
git clone https://github.com/jketterl/pycsdr.git
|
||||||
cd pycsdr
|
cd pycsdr
|
||||||
git checkout 0.18.0
|
git checkout 0.18.1
|
||||||
./setup.py install install_headers
|
./setup.py install install_headers
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf pycsdr
|
rm -rf pycsdr
|
||||||
@ -46,11 +46,11 @@ cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver
|
|||||||
cmakebuild codecserver 0.2.0
|
cmakebuild codecserver 0.2.0
|
||||||
|
|
||||||
git clone https://github.com/jketterl/digiham.git
|
git clone https://github.com/jketterl/digiham.git
|
||||||
cmakebuild digiham 0.6.0
|
cmakebuild digiham 0.6.1
|
||||||
|
|
||||||
git clone https://github.com/jketterl/pydigiham.git
|
git clone https://github.com/jketterl/pydigiham.git
|
||||||
cd pydigiham
|
cd pydigiham
|
||||||
git checkout 0.6.0
|
git checkout 0.6.1
|
||||||
./setup.py install
|
./setup.py install
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf pydigiham
|
rm -rf pydigiham
|
||||||
|
@ -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);
|
||||||
@ -338,7 +342,33 @@ $(function(){
|
|||||||
delete infowindow.locator;
|
delete infowindow.locator;
|
||||||
delete infowindow.callsign;
|
delete infowindow.callsign;
|
||||||
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) {
|
||||||
@ -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,16 +406,20 @@ $(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
|
||||||
);
|
);
|
||||||
infowindow.open(map, marker);
|
infowindow.open(map, marker);
|
||||||
}
|
};
|
||||||
|
|
||||||
var showReceiverInfoWindow = function(marker) {
|
var showReceiverInfoWindow = function(marker) {
|
||||||
var infowindow = getInfoWindow()
|
var infowindow = getInfoWindow()
|
||||||
@ -392,7 +428,7 @@ $(function(){
|
|||||||
'<div>Receiver location</div>'
|
'<div>Receiver location</div>'
|
||||||
);
|
);
|
||||||
infowindow.open(map, marker);
|
infowindow.open(map, marker);
|
||||||
}
|
};
|
||||||
|
|
||||||
var getScale = function(lastseen) {
|
var getScale = function(lastseen) {
|
||||||
var age = new Date().getTime() - lastseen;
|
var age = new Date().getTime() - lastseen;
|
||||||
|
@ -802,6 +802,7 @@ 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)
|
||||||
@ -969,9 +970,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)
|
||||||
|
@ -159,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,
|
||||||
|
@ -457,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.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -249,10 +249,13 @@ class SdrSource(ABC):
|
|||||||
def getPort(self):
|
def getPort(self):
|
||||||
return self.port
|
return self.port
|
||||||
|
|
||||||
|
def _getTcpSourceFormat(self):
|
||||||
|
return Format.COMPLEX_FLOAT
|
||||||
|
|
||||||
def _getTcpSource(self):
|
def _getTcpSource(self):
|
||||||
with self.modificationLock:
|
with self.modificationLock:
|
||||||
if self.tcpSource is None:
|
if self.tcpSource is None:
|
||||||
self.tcpSource = TcpSource(self.port, Format.COMPLEX_FLOAT)
|
self.tcpSource = TcpSource(self.port, self._getTcpSourceFormat())
|
||||||
return self.tcpSource
|
return self.tcpSource
|
||||||
|
|
||||||
def getBuffer(self):
|
def getBuffer(self):
|
||||||
|
@ -11,6 +11,10 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class DirectSource(SdrSource, metaclass=ABCMeta):
|
class DirectSource(SdrSource, metaclass=ABCMeta):
|
||||||
|
def __init__(self, id, props):
|
||||||
|
self._conversion = None
|
||||||
|
super().__init__(id, props)
|
||||||
|
|
||||||
def onPropertyChange(self, changes):
|
def onPropertyChange(self, changes):
|
||||||
logger.debug("restarting sdr source due to property changes: {0}".format(changes))
|
logger.debug("restarting sdr source due to property changes: {0}".format(changes))
|
||||||
self.stop()
|
self.stop()
|
||||||
@ -48,6 +52,10 @@ class DirectSource(SdrSource, metaclass=ABCMeta):
|
|||||||
def getFormatConversion(self) -> Optional[Chain]:
|
def getFormatConversion(self) -> Optional[Chain]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _getTcpSourceFormat(self):
|
||||||
|
conversion = self.getFormatConversion()
|
||||||
|
return Format.COMPLEX_FLOAT if conversion is None else conversion.getInputFormat()
|
||||||
|
|
||||||
# override this in subclasses, if necessary
|
# override this in subclasses, if necessary
|
||||||
def sleepOnRestart(self):
|
def sleepOnRestart(self):
|
||||||
pass
|
pass
|
||||||
@ -57,12 +65,12 @@ class DirectSource(SdrSource, metaclass=ABCMeta):
|
|||||||
source = self._getTcpSource()
|
source = self._getTcpSource()
|
||||||
buffer = Buffer(source.getOutputFormat())
|
buffer = Buffer(source.getOutputFormat())
|
||||||
source.setWriter(buffer)
|
source.setWriter(buffer)
|
||||||
conversion = self.getFormatConversion()
|
self._conversion = self.getFormatConversion()
|
||||||
if conversion is not None:
|
if self._conversion is not None:
|
||||||
conversion.setReader(buffer.getReader())
|
self._conversion.setReader(buffer.getReader())
|
||||||
# this one must be COMPLEX_FLOAT
|
# this one must be COMPLEX_FLOAT
|
||||||
buffer = Buffer(Format.COMPLEX_FLOAT)
|
buffer = Buffer(Format.COMPLEX_FLOAT)
|
||||||
conversion.setWriter(buffer)
|
self._conversion.setWriter(buffer)
|
||||||
self.buffer = buffer
|
self.buffer = buffer
|
||||||
return self.buffer
|
return self.buffer
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ from subprocess import Popen
|
|||||||
from csdr.chain import Chain
|
from csdr.chain import Chain
|
||||||
from pycsdr.modules import Convert, Gain
|
from pycsdr.modules import Convert, Gain
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
|
from typing import List
|
||||||
|
from owrx.form.input import Input, TextInput
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -49,3 +51,15 @@ class FifiSdrDeviceDescription(DirectSourceDeviceDescription):
|
|||||||
def supportsPpm(self):
|
def supportsPpm(self):
|
||||||
# not currently mapped, and it's unclear how this should be sent to the device
|
# not currently mapped, and it's unclear how this should be sent to the device
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def getInputs(self) -> List[Input]:
|
||||||
|
return super().getInputs() + [
|
||||||
|
TextInput(
|
||||||
|
"device",
|
||||||
|
"Device identifier",
|
||||||
|
infotext="Alsa audio device identifier",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def getDeviceOptionalKeys(self):
|
||||||
|
return super().getDeviceOptionalKeys() + ["device"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user