Merge branch 'develop' into packet
This commit is contained in:
commit
1f6f755d7f
22
build.sh
Executable file
22
build.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
case $ARCH in
|
||||||
|
x86_64)
|
||||||
|
BASE_IMAGE=alpine
|
||||||
|
;;
|
||||||
|
armv*)
|
||||||
|
BASE_IMAGE=arm32v6/alpine
|
||||||
|
esac
|
||||||
|
|
||||||
|
TAGS=$ARCH
|
||||||
|
|
||||||
|
docker build --build-arg BASE_IMAGE=$BASE_IMAGE -t openwebrx-base:$ARCH -f docker/Dockerfiles/Dockerfile-base .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t jketterl/openwebrx-rtlsdr:$ARCH -f docker/Dockerfiles/Dockerfile-rtlsdr .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t openwebrx-soapysdr-base:$ARCH -f docker/Dockerfiles/Dockerfile-soapysdr .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t jketterl/openwebrx-sdrplay:$ARCH -f docker/Dockerfiles/Dockerfile-sdrplay .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t jketterl/openwebrx-hackrf:$ARCH -f docker/Dockerfiles/Dockerfile-hackrf .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t jketterl/openwebrx-airspy:$ARCH -f docker/Dockerfiles/Dockerfile-airspy .
|
||||||
|
docker build --build-arg ARCH=$ARCH -t jketterl/openwebrx-full:$ARCH -t jketterl/openwebrx:$ARCH -f docker/Dockerfiles/Dockerfile-full .
|
@ -99,7 +99,7 @@ Note: if you experience audio underruns while CPU usage is 100%, you can:
|
|||||||
# Check here: https://github.com/simonyiszk/openwebrx/wiki#guides-for-receiver-hardware-support #
|
# Check here: https://github.com/simonyiszk/openwebrx/wiki#guides-for-receiver-hardware-support #
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
# Currently supported types of sdr receivers: "rtl_sdr", "sdrplay", "hackrf"
|
# Currently supported types of sdr receivers: "rtl_sdr", "sdrplay", "hackrf", "airspy"
|
||||||
|
|
||||||
sdrs = {
|
sdrs = {
|
||||||
"rtlsdr": {
|
"rtlsdr": {
|
||||||
|
30
csdr.py
30
csdr.py
@ -67,7 +67,8 @@ class dsp(object):
|
|||||||
self.secondary_fft_size = 1024
|
self.secondary_fft_size = 1024
|
||||||
self.secondary_process_fft = None
|
self.secondary_process_fft = None
|
||||||
self.secondary_process_demod = None
|
self.secondary_process_demod = None
|
||||||
self.pipe_names=["bpf_pipe", "shift_pipe", "squelch_pipe", "smeter_pipe", "meta_pipe", "iqtee_pipe", "iqtee2_pipe"]
|
self.pipe_names=["bpf_pipe", "shift_pipe", "squelch_pipe", "smeter_pipe", "meta_pipe", "iqtee_pipe",
|
||||||
|
"iqtee2_pipe", "dmr_control_pipe"]
|
||||||
self.secondary_pipe_names=["secondary_shift_pipe"]
|
self.secondary_pipe_names=["secondary_shift_pipe"]
|
||||||
self.secondary_offset_freq = 1000
|
self.secondary_offset_freq = 1000
|
||||||
self.unvoiced_quality = 1
|
self.unvoiced_quality = 1
|
||||||
@ -107,16 +108,19 @@ class dsp(object):
|
|||||||
chain += "dsd -fd"
|
chain += "dsd -fd"
|
||||||
elif which == "nxdn":
|
elif which == "nxdn":
|
||||||
chain += "dsd -fi"
|
chain += "dsd -fi"
|
||||||
chain += " -i - -o - -u {unvoiced_quality} -g 10 | "
|
chain += " -i - -o - -u {unvoiced_quality} -g -1 | CSDR_FIXED_BUFSIZE=32 csdr convert_s16_f | "
|
||||||
chain += "digitalvoice_filter | sox -V -v 0.95 -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - "
|
max_gain = 5
|
||||||
# digiham modes
|
# digiham modes
|
||||||
else:
|
else:
|
||||||
chain += "rrc_filter | csdr convert_f_s16 | gfsk_demodulator | "
|
chain += "rrc_filter | gfsk_demodulator | "
|
||||||
if which == "dmr":
|
if which == "dmr":
|
||||||
chain += "dmr_decoder --fifo {meta_pipe} | mbe_synthesizer -f -u {unvoiced_quality} | "
|
chain += "dmr_decoder --fifo {meta_pipe} --control-fifo {dmr_control_pipe} | mbe_synthesizer -f -u {unvoiced_quality} | "
|
||||||
elif which == "ysf":
|
elif which == "ysf":
|
||||||
chain += "ysf_decoder --fifo {meta_pipe} | mbe_synthesizer -y -f -u {unvoiced_quality} | "
|
chain += "ysf_decoder --fifo {meta_pipe} | mbe_synthesizer -y -f -u {unvoiced_quality} | "
|
||||||
chain += "digitalvoice_filter -f | csdr agc_ff 160000 0.8 1 0.0000001 0.0005 | csdr convert_f_s16 | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - "
|
max_gain = 0.0005
|
||||||
|
chain += "digitalvoice_filter -f | "
|
||||||
|
chain += "CSDR_FIXED_BUFSIZE=32 csdr agc_ff 160000 0.8 1 0.0000001 {max_gain} | ".format(max_gain=max_gain)
|
||||||
|
chain += "sox -t raw -r 8000 -e floating-point -b 32 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - "
|
||||||
elif which == "packet":
|
elif which == "packet":
|
||||||
chain += "csdr fmdemod_quadri_cf | "
|
chain += "csdr fmdemod_quadri_cf | "
|
||||||
chain += last_decimation_block
|
chain += last_decimation_block
|
||||||
@ -370,6 +374,11 @@ class dsp(object):
|
|||||||
def get_unvoiced_quality(self):
|
def get_unvoiced_quality(self):
|
||||||
return self.unvoiced_quality
|
return self.unvoiced_quality
|
||||||
|
|
||||||
|
def set_dmr_filter(self, filter):
|
||||||
|
if self.dmr_control_pipe_file:
|
||||||
|
self.dmr_control_pipe_file.write("{0}\n".format(filter))
|
||||||
|
self.dmr_control_pipe_file.flush()
|
||||||
|
|
||||||
def mkfifo(self,path):
|
def mkfifo(self,path):
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
@ -417,7 +426,8 @@ class dsp(object):
|
|||||||
flowcontrol=int(self.samp_rate*2), start_bufsize=self.base_bufsize*self.decimation, nc_port=self.nc_port,
|
flowcontrol=int(self.samp_rate*2), start_bufsize=self.base_bufsize*self.decimation, nc_port=self.nc_port,
|
||||||
squelch_pipe=self.squelch_pipe, smeter_pipe=self.smeter_pipe, meta_pipe=self.meta_pipe, iqtee_pipe=self.iqtee_pipe, iqtee2_pipe=self.iqtee2_pipe,
|
squelch_pipe=self.squelch_pipe, smeter_pipe=self.smeter_pipe, meta_pipe=self.meta_pipe, iqtee_pipe=self.iqtee_pipe, iqtee2_pipe=self.iqtee2_pipe,
|
||||||
output_rate = self.get_output_rate(), smeter_report_every = int(self.if_samp_rate()/6000),
|
output_rate = self.get_output_rate(), smeter_report_every = int(self.if_samp_rate()/6000),
|
||||||
unvoiced_quality = self.get_unvoiced_quality(), audio_rate = self.get_audio_rate())
|
unvoiced_quality = self.get_unvoiced_quality(), audio_rate = self.get_audio_rate(),
|
||||||
|
dmr_control_pipe = self.dmr_control_pipe)
|
||||||
|
|
||||||
logger.debug("[openwebrx-dsp-plugin:csdr] Command = %s", command)
|
logger.debug("[openwebrx-dsp-plugin:csdr] Command = %s", command)
|
||||||
my_env=os.environ.copy()
|
my_env=os.environ.copy()
|
||||||
@ -437,13 +447,12 @@ class dsp(object):
|
|||||||
self.output.add_output("audio", partial(self.process.stdout.read, int(self.get_fft_bytes_to_read()) if self.demodulator == "fft" else 256))
|
self.output.add_output("audio", partial(self.process.stdout.read, int(self.get_fft_bytes_to_read()) if self.demodulator == "fft" else 256))
|
||||||
|
|
||||||
# open control pipes for csdr
|
# open control pipes for csdr
|
||||||
if self.bpf_pipe != None:
|
if self.bpf_pipe:
|
||||||
self.bpf_pipe_file = open(self.bpf_pipe, "w")
|
self.bpf_pipe_file = open(self.bpf_pipe, "w")
|
||||||
if self.shift_pipe:
|
if self.shift_pipe:
|
||||||
self.shift_pipe_file = open(self.shift_pipe, "w")
|
self.shift_pipe_file = open(self.shift_pipe, "w")
|
||||||
if self.squelch_pipe:
|
if self.squelch_pipe:
|
||||||
self.squelch_pipe_file = open(self.squelch_pipe, "w")
|
self.squelch_pipe_file = open(self.squelch_pipe, "w")
|
||||||
|
|
||||||
self.start_secondary_demodulator()
|
self.start_secondary_demodulator()
|
||||||
|
|
||||||
self.modification_lock.release()
|
self.modification_lock.release()
|
||||||
@ -475,6 +484,9 @@ class dsp(object):
|
|||||||
return raw.rstrip("\n")
|
return raw.rstrip("\n")
|
||||||
self.output.add_output("meta", read_meta)
|
self.output.add_output("meta", read_meta)
|
||||||
|
|
||||||
|
if self.dmr_control_pipe:
|
||||||
|
self.dmr_control_pipe_file = open(self.dmr_control_pipe, "w")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.modification_lock.acquire()
|
self.modification_lock.acquire()
|
||||||
self.running = False
|
self.running = False
|
||||||
|
6
docker/Dockerfiles/Dockerfile-airspy
Normal file
6
docker/Dockerfiles/Dockerfile-airspy
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-airspy.sh /
|
||||||
|
RUN /install-dependencies-airspy.sh
|
||||||
|
|
16
docker/Dockerfiles/Dockerfile-base
Normal file
16
docker/Dockerfiles/Dockerfile-base
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
ARG BASE_IMAGE
|
||||||
|
FROM $BASE_IMAGE
|
||||||
|
|
||||||
|
RUN apk add --no-cache bash
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies.sh /
|
||||||
|
RUN /install-dependencies.sh
|
||||||
|
|
||||||
|
ADD . /openwebrx
|
||||||
|
|
||||||
|
WORKDIR /openwebrx
|
||||||
|
|
||||||
|
VOLUME /config
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/openwebrx/docker/scripts/run.sh" ]
|
||||||
|
EXPOSE 8073
|
11
docker/Dockerfiles/Dockerfile-full
Normal file
11
docker/Dockerfiles/Dockerfile-full
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-*.sh /
|
||||||
|
ADD docker/scripts/install-lib.*.patch /
|
||||||
|
|
||||||
|
RUN /install-dependencies-rtlsdr.sh
|
||||||
|
RUN /install-dependencies-hackrf.sh
|
||||||
|
RUN /install-dependencies-soapysdr.sh
|
||||||
|
RUN /install-dependencies-sdrplay.sh
|
||||||
|
RUN /install-dependencies-airspy.sh
|
6
docker/Dockerfiles/Dockerfile-hackrf
Normal file
6
docker/Dockerfiles/Dockerfile-hackrf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-hackrf.sh /
|
||||||
|
RUN /install-dependencies-hackrf.sh
|
||||||
|
|
6
docker/Dockerfiles/Dockerfile-rtlsdr
Normal file
6
docker/Dockerfiles/Dockerfile-rtlsdr
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-rtlsdr.sh /
|
||||||
|
RUN /install-dependencies-rtlsdr.sh
|
||||||
|
|
7
docker/Dockerfiles/Dockerfile-sdrplay
Normal file
7
docker/Dockerfiles/Dockerfile-sdrplay
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-soapysdr-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-sdrplay.sh /
|
||||||
|
ADD docker/scripts/install-lib.*.patch /
|
||||||
|
RUN /install-dependencies-sdrplay.sh
|
||||||
|
|
6
docker/Dockerfiles/Dockerfile-soapysdr
Normal file
6
docker/Dockerfiles/Dockerfile-soapysdr
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ARG ARCH
|
||||||
|
FROM openwebrx-base:$ARCH
|
||||||
|
|
||||||
|
ADD docker/scripts/install-dependencies-soapysdr.sh /
|
||||||
|
RUN /install-dependencies-soapysdr.sh
|
||||||
|
|
26
docker/scripts/install-dependencies-airspy.sh
Executable file
26
docker/scripts/install-dependencies-airspy.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
STATIC_PACKAGES="libusb"
|
||||||
|
BUILD_PACKAGES="git libusb-dev cmake make gcc musl-dev g++ linux-headers"
|
||||||
|
|
||||||
|
apk add --no-cache $STATIC_PACKAGES
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
git clone https://github.com/airspy/airspyone_host.git
|
||||||
|
cmakebuild airspyone_host
|
||||||
|
|
||||||
|
apk del .build-deps
|
29
docker/scripts/install-dependencies-hackrf.sh
Executable file
29
docker/scripts/install-dependencies-hackrf.sh
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
STATIC_PACKAGES="libusb fftw"
|
||||||
|
BUILD_PACKAGES="git cmake make patch wget sudo udev gcc g++ libusb-dev fftw-dev"
|
||||||
|
|
||||||
|
apk add --no-cache $STATIC_PACKAGES
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
git clone https://github.com/mossmann/hackrf.git
|
||||||
|
cd hackrf
|
||||||
|
cmakebuild host
|
||||||
|
cd ..
|
||||||
|
rm -rf hackrf
|
||||||
|
|
||||||
|
apk del .build-deps
|
26
docker/scripts/install-dependencies-rtlsdr.sh
Executable file
26
docker/scripts/install-dependencies-rtlsdr.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
STATIC_PACKAGES="libusb"
|
||||||
|
BUILD_PACKAGES="git libusb-dev cmake make gcc musl-dev g++ linux-headers"
|
||||||
|
|
||||||
|
apk add --no-cache $STATIC_PACKAGES
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
git clone https://github.com/osmocom/rtl-sdr.git
|
||||||
|
cmakebuild rtl-sdr
|
||||||
|
|
||||||
|
apk del .build-deps
|
47
docker/scripts/install-dependencies-sdrplay.sh
Executable file
47
docker/scripts/install-dependencies-sdrplay.sh
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
STATIC_PACKAGES="libusb"
|
||||||
|
BUILD_PACKAGES="git cmake make patch wget sudo udev gcc g++ libusb-dev"
|
||||||
|
|
||||||
|
apk add --no-cache $STATIC_PACKAGES
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
case $ARCH in
|
||||||
|
x86_64)
|
||||||
|
BINARY=SDRplay_RSP_API-Linux-2.13.1.run
|
||||||
|
;;
|
||||||
|
armv*)
|
||||||
|
BINARY=SDRplay_RSP_API-RPi-2.13.1.run
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
wget http://www.sdrplay.com/software/$BINARY
|
||||||
|
sh $BINARY --noexec --target sdrplay
|
||||||
|
patch --verbose -Np0 < /install-lib.$ARCH.patch
|
||||||
|
|
||||||
|
cd sdrplay
|
||||||
|
./install_lib.sh
|
||||||
|
cd ..
|
||||||
|
rm -rf sdrplay
|
||||||
|
rm $BINARY
|
||||||
|
|
||||||
|
git clone https://github.com/pothosware/SoapySDRPlay.git
|
||||||
|
cmakebuild SoapySDRPlay
|
||||||
|
|
||||||
|
apk del .build-deps
|
27
docker/scripts/install-dependencies-soapysdr.sh
Executable file
27
docker/scripts/install-dependencies-soapysdr.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
BUILD_PACKAGES="git cmake make patch wget sudo udev gcc g++"
|
||||||
|
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
git clone https://github.com/pothosware/SoapySDR
|
||||||
|
cmakebuild SoapySDR
|
||||||
|
|
||||||
|
git clone https://github.com/rxseger/rx_tools
|
||||||
|
cmakebuild rx_tools
|
||||||
|
|
||||||
|
apk del .build-deps
|
78
docker/scripts/install-dependencies.sh
Executable file
78
docker/scripts/install-dependencies.sh
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
function cmakebuild() {
|
||||||
|
cd $1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ../..
|
||||||
|
rm -rf $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
STATIC_PACKAGES="sox fftw python3 netcat-openbsd libsndfile lapack"
|
||||||
|
BUILD_PACKAGES="git libsndfile-dev fftw-dev cmake ca-certificates make gcc musl-dev g++ lapack-dev linux-headers"
|
||||||
|
|
||||||
|
apk add --no-cache $STATIC_PACKAGES
|
||||||
|
apk add --no-cache --virtual .build-deps $BUILD_PACKAGES
|
||||||
|
|
||||||
|
git clone https://git.code.sf.net/p/itpp/git itpp
|
||||||
|
cmakebuild itpp
|
||||||
|
|
||||||
|
git clone https://github.com/simonyiszk/csdr.git
|
||||||
|
cd csdr
|
||||||
|
patch -Np1 <<'EOF'
|
||||||
|
--- a/csdr.c
|
||||||
|
+++ b/csdr.c
|
||||||
|
@@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
+#include <sys/select.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
diff --git a/ddcd_old.h b/ddcd_old.h
|
||||||
|
index af4cfb5..b70092b 100644
|
||||||
|
--- a/ddcd_old.h
|
||||||
|
+++ b/ddcd_old.h
|
||||||
|
@@ -19,6 +19,7 @@
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
+#include <sys/select.h>
|
||||||
|
|
||||||
|
typedef struct client_s
|
||||||
|
{
|
||||||
|
diff --git a/nmux.h b/nmux.h
|
||||||
|
index 038bc51..079e416 100644
|
||||||
|
--- a/nmux.h
|
||||||
|
+++ b/nmux.h
|
||||||
|
@@ -11,6 +11,7 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
+#include <sys/select.h>
|
||||||
|
#include "tsmpool.h"
|
||||||
|
|
||||||
|
#define MSG_START "nmux: "
|
||||||
|
EOF
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
cd ..
|
||||||
|
rm -rf csdr
|
||||||
|
|
||||||
|
git clone https://github.com/szechyjs/mbelib.git
|
||||||
|
cmakebuild mbelib
|
||||||
|
|
||||||
|
git clone https://github.com/jketterl/digiham.git
|
||||||
|
cmakebuild digiham
|
||||||
|
|
||||||
|
git clone https://github.com/f4exb/dsd.git
|
||||||
|
cmakebuild dsd
|
||||||
|
|
||||||
|
apk del .build-deps
|
40
docker/scripts/install-lib.armv7l.patch
Normal file
40
docker/scripts/install-lib.armv7l.patch
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
--- sdrplay/install_lib.sh
|
||||||
|
+++ sdrplay/install_lib_patched.sh
|
||||||
|
@@ -3,19 +3,7 @@
|
||||||
|
|
||||||
|
echo "Installing SDRplay RSP API library 2.13..."
|
||||||
|
|
||||||
|
-more sdrplay_license.txt
|
||||||
|
-
|
||||||
|
-while true; do
|
||||||
|
- echo "Press y and RETURN to accept the license agreement and continue with"
|
||||||
|
- read -p "the installation, or press n and RETURN to exit the installer [y/n] " yn
|
||||||
|
- case $yn in
|
||||||
|
- [Yy]* ) break;;
|
||||||
|
- [Nn]* ) exit;;
|
||||||
|
- * ) echo "Please answer y or n";;
|
||||||
|
- esac
|
||||||
|
-done
|
||||||
|
-
|
||||||
|
-export ARCH=`arch`
|
||||||
|
+export ARCH=`uname -m`
|
||||||
|
export VERS="2.13"
|
||||||
|
|
||||||
|
echo "Architecture: ${ARCH}"
|
||||||
|
@@ -60,16 +48,6 @@
|
||||||
|
echo "ERROR: udev rules directory not found, add udev support and run the"
|
||||||
|
echo "installer again. udev support can be added by running..."
|
||||||
|
echo "sudo apt-get install libudev-dev"
|
||||||
|
- echo " "
|
||||||
|
- exit 1
|
||||||
|
-fi
|
||||||
|
-
|
||||||
|
-if /sbin/ldconfig -p | /bin/fgrep -q libusb-1.0; then
|
||||||
|
- echo "Libusb found, continuing..."
|
||||||
|
-else
|
||||||
|
- echo " "
|
||||||
|
- echo "ERROR: Libusb cannot be found. Please install libusb and then run"
|
||||||
|
- echo "the installer again. Libusb can be installed from http://libusb.info"
|
||||||
|
echo " "
|
||||||
|
exit 1
|
||||||
|
fi
|
40
docker/scripts/install-lib.x86_64.patch
Normal file
40
docker/scripts/install-lib.x86_64.patch
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
--- sdrplay/install_lib.sh 2018-06-21 01:57:02.000000000 +0200
|
||||||
|
+++ sdrplay/install_lib_patched.sh 2019-01-22 17:21:06.445804136 +0100
|
||||||
|
@@ -2,19 +2,7 @@
|
||||||
|
|
||||||
|
echo "Installing SDRplay RSP API library 2.13..."
|
||||||
|
|
||||||
|
-more sdrplay_license.txt
|
||||||
|
-
|
||||||
|
-while true; do
|
||||||
|
- echo "Press y and RETURN to accept the license agreement and continue with"
|
||||||
|
- read -p "the installation, or press n and RETURN to exit the installer [y/n] " yn
|
||||||
|
- case $yn in
|
||||||
|
- [Yy]* ) break;;
|
||||||
|
- [Nn]* ) exit;;
|
||||||
|
- * ) echo "Please answer y or n";;
|
||||||
|
- esac
|
||||||
|
-done
|
||||||
|
-
|
||||||
|
-export ARCH=`arch`
|
||||||
|
+export ARCH=`uname -m`
|
||||||
|
export VERS="2.13"
|
||||||
|
|
||||||
|
echo "Architecture: ${ARCH}"
|
||||||
|
@@ -60,16 +48,6 @@
|
||||||
|
echo " "
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
-
|
||||||
|
-if /sbin/ldconfig -p | /bin/fgrep -q libusb-1.0; then
|
||||||
|
- echo "Libusb found, continuing..."
|
||||||
|
-else
|
||||||
|
- echo " "
|
||||||
|
- echo "ERROR: Libusb cannot be found. Please install libusb and then run"
|
||||||
|
- echo "the installer again. Libusb can be installed from http://libusb.info"
|
||||||
|
- echo " "
|
||||||
|
- exit 1
|
||||||
|
-fi
|
||||||
|
|
||||||
|
#echo "Installing SoapySDRPlay..."
|
||||||
|
|
23
docker/scripts/run.sh
Executable file
23
docker/scripts/run.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ ! -f /config/config_webrx.py ]] ; then
|
||||||
|
cp config_webrx.py /config
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm config_webrx.py
|
||||||
|
ln -s /config/config_webrx.py .
|
||||||
|
|
||||||
|
|
||||||
|
_term() {
|
||||||
|
echo "Caught signal!"
|
||||||
|
kill -TERM "$child" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
trap _term SIGTERM SIGINT
|
||||||
|
|
||||||
|
python3 openwebrx.py $@ &
|
||||||
|
|
||||||
|
child=$!
|
||||||
|
wait "$child"
|
||||||
|
|
77
htdocs/gfx/google_maps_pin.svg
Normal file
77
htdocs/gfx/google_maps_pin.svg
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="5.6444445mm"
|
||||||
|
height="9.847393mm"
|
||||||
|
viewBox="0 0 20 34.892337"
|
||||||
|
id="svg3455"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="Map Pin.svg">
|
||||||
|
<defs
|
||||||
|
id="defs3457" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="12.181359"
|
||||||
|
inkscape:cx="8.4346812"
|
||||||
|
inkscape:cy="14.715224"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1024"
|
||||||
|
inkscape:window-height="705"
|
||||||
|
inkscape:window-x="-4"
|
||||||
|
inkscape:window-y="-4"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3460">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-814.59595,-274.38623)">
|
||||||
|
<g
|
||||||
|
id="g3477"
|
||||||
|
transform="matrix(1.1855854,0,0,1.1855854,-151.17715,-57.3976)">
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="sscccccsscs"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4337-3"
|
||||||
|
d="m 817.11249,282.97118 c -1.25816,1.34277 -2.04623,3.29881 -2.01563,5.13867 0.0639,3.84476 1.79693,5.3002 4.56836,10.59179 0.99832,2.32851 2.04027,4.79237 3.03125,8.87305 0.13772,0.60193 0.27203,1.16104 0.33416,1.20948 0.0621,0.0485 0.19644,-0.51262 0.33416,-1.11455 0.99098,-4.08068 2.03293,-6.54258 3.03125,-8.87109 2.77143,-5.29159 4.50444,-6.74704 4.56836,-10.5918 0.0306,-1.83986 -0.75942,-3.79785 -2.01758,-5.14062 -1.43724,-1.53389 -3.60504,-2.66908 -5.91619,-2.71655 -2.31115,-0.0475 -4.4809,1.08773 -5.91814,2.62162 z"
|
||||||
|
style="display:inline;opacity:1;fill:#ff4646;fill-opacity:1;stroke:#d73534;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
r="3.0355"
|
||||||
|
cy="288.25278"
|
||||||
|
cx="823.03064"
|
||||||
|
id="path3049"
|
||||||
|
style="display:inline;opacity:1;fill:#590000;fill-opacity:1;stroke-width:0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 13 KiB |
BIN
htdocs/gfx/openwebrx-directcall.png
Normal file
BIN
htdocs/gfx/openwebrx-directcall.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
BIN
htdocs/gfx/openwebrx-groupcall.png
Normal file
BIN
htdocs/gfx/openwebrx-groupcall.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
htdocs/gfx/openwebrx-mute.png
Normal file
BIN
htdocs/gfx/openwebrx-mute.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
@ -171,7 +171,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-panel" id="openwebrx-panel-metadata" data-panel-name="metadata" data-panel-pos="left" data-panel-order="1" data-panel-size="615,36">
|
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-ysf" data-panel-name="metadata-ysf" data-panel-pos="left" data-panel-order="2" data-panel-size="145,220">
|
||||||
|
<div class="openwebrx-meta-frame">
|
||||||
|
<div class="openwebrx-meta-slot">
|
||||||
|
<div class="openwebrx-ysf-mode openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-meta-user-image"></div>
|
||||||
|
<div class="openwebrx-ysf-source openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-ysf-up openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-ysf-down openwebrx-meta-autoclear"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-dmr" data-panel-name="metadata-dmr" data-panel-pos="left" data-panel-order="2" data-panel-size="300,220">
|
||||||
|
<div class="openwebrx-meta-frame">
|
||||||
|
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
|
||||||
|
<div class="openwebrx-dmr-slot">Timeslot 1</div>
|
||||||
|
<div class="openwebrx-meta-user-image"></div>
|
||||||
|
<div class="openwebrx-dmr-id openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-dmr-name openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-dmr-target openwebrx-meta-autoclear"></div>
|
||||||
|
</div>
|
||||||
|
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
|
||||||
|
<div class="openwebrx-dmr-slot">Timeslot 2</div>
|
||||||
|
<div class="openwebrx-meta-user-image"></div>
|
||||||
|
<div class="openwebrx-dmr-id openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-dmr-name openwebrx-meta-autoclear"></div>
|
||||||
|
<div class="openwebrx-dmr-target openwebrx-meta-autoclear"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -928,3 +928,88 @@ img.openwebrx-mirror-img
|
|||||||
border-color: Red;
|
border-color: Red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot {
|
||||||
|
width: 145px;
|
||||||
|
height: 196px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
background-color: #676767;
|
||||||
|
padding: 2px 0;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot, .openwebrx-meta-slot.muted:before {
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot.muted:before {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
background-image: url("gfx/openwebrx-mute.png");
|
||||||
|
width:100%;
|
||||||
|
height:133px;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgba(0,0,0,.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot.active {
|
||||||
|
background-color: #95bbdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot.sync .openwebrx-dmr-slot:before {
|
||||||
|
content:"";
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 5px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #ABFF00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot .openwebrx-meta-user-image {
|
||||||
|
width:100%;
|
||||||
|
height:133px;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot.active .openwebrx-meta-user-image {
|
||||||
|
background-image: url("gfx/openwebrx-directcall.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-meta-slot.active .openwebrx-meta-user-image.group {
|
||||||
|
background-image: url("gfx/openwebrx-groupcall.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-dmr-timeslot-panel * {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openwebrx-maps-pin {
|
||||||
|
background-image: url("gfx/google_maps_pin.svg");
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
background-size: contain;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
@ -624,7 +624,8 @@ function demodulator_analog_replace(subtype, for_digital)
|
|||||||
}
|
}
|
||||||
demodulator_add(new demodulator_default_analog(temp_offset,subtype));
|
demodulator_add(new demodulator_default_analog(temp_offset,subtype));
|
||||||
demodulator_buttons_update();
|
demodulator_buttons_update();
|
||||||
clear_metadata();
|
hide_digitalvoice_panels();
|
||||||
|
toggle_panel("openwebrx-panel-metadata-" + subtype, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function demodulator_set_offset_frequency(which,to_what)
|
function demodulator_set_offset_frequency(which,to_what)
|
||||||
@ -1315,56 +1316,78 @@ function on_ws_recv(evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function update_metadata(meta) {
|
function update_metadata(meta) {
|
||||||
var update = function(_, el) {
|
|
||||||
el.innerHTML = "";
|
|
||||||
};
|
|
||||||
if (meta.protocol) switch (meta.protocol) {
|
if (meta.protocol) switch (meta.protocol) {
|
||||||
case 'DMR':
|
case 'DMR':
|
||||||
if (meta.slot) {
|
if (meta.slot) {
|
||||||
var html = 'Timeslot: ' + meta.slot;
|
var el = $("#openwebrx-panel-metadata-dmr .openwebrx-dmr-timeslot-panel").get(meta.slot);
|
||||||
if (meta.type) html += ' Typ: ' + meta.type;
|
var id = "";
|
||||||
if (meta.additional && meta.additional.callsign) {
|
var name = "";
|
||||||
html += ' Source: ' + meta.additional.callsign;
|
var target = "";
|
||||||
if (meta.additional.fname) {
|
var group = false;
|
||||||
html += ' (' + meta.additional.fname + ')';
|
$(el)[meta.sync ? "addClass" : "removeClass"]("sync");
|
||||||
|
if (meta.sync && meta.sync == "voice") {
|
||||||
|
id = (meta.additional && meta.additional.callsign) || meta.source || "";
|
||||||
|
name = (meta.additional && meta.additional.fname) || "";
|
||||||
|
if (meta.type == "group") {
|
||||||
|
target = "Talkgroup: ";
|
||||||
|
group = true;
|
||||||
}
|
}
|
||||||
} else if (meta.source) {
|
if (meta.type == "direct") target = "Direct: ";
|
||||||
html += ' Source: ' + meta.source;
|
target += meta.target || "";
|
||||||
}
|
$(el).addClass("active");
|
||||||
if (meta.target) html += ' Target: ' + meta.target;
|
|
||||||
update = function(_, el) {
|
|
||||||
var slotEl = el.getElementsByClassName('slot-' + meta.slot);
|
|
||||||
if (!slotEl.length) {
|
|
||||||
slotEl = document.createElement('div');
|
|
||||||
slotEl.className = 'slot-' + meta.slot;
|
|
||||||
el.appendChild(slotEl);
|
|
||||||
} else {
|
} else {
|
||||||
slotEl = slotEl[0];
|
$(el).removeClass("active");
|
||||||
}
|
}
|
||||||
slotEl.innerHTML = html;
|
$(el).find(".openwebrx-dmr-id").text(id);
|
||||||
};
|
$(el).find(".openwebrx-dmr-name").text(name);
|
||||||
|
$(el).find(".openwebrx-dmr-target").text(target);
|
||||||
|
$(el).find(".openwebrx-meta-user-image")[group ? "addClass" : "removeClass"]("group");
|
||||||
|
} else {
|
||||||
|
clear_metadata();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'YSF':
|
case 'YSF':
|
||||||
var strings = [];
|
var el = $("#openwebrx-panel-metadata-ysf");
|
||||||
if (meta.mode) strings.push("Mode: " + meta.mode);
|
|
||||||
if (meta.source) strings.push("Source: " + meta.source);
|
var mode = " "
|
||||||
if (meta.target) strings.push("Destination: " + meta.target);
|
var source = "";
|
||||||
if (meta.up) strings.push("Up: " + meta.up);
|
var up = "";
|
||||||
if (meta.down) strings.push("Down: " + meta.down);
|
var down = "";
|
||||||
var html = strings.join(' ');
|
if (meta.mode && meta.mode != "") {
|
||||||
update = function(_, el) {
|
mode = "Mode: " + meta.mode;
|
||||||
el.innerHTML = html;
|
source = meta.source || "";
|
||||||
|
if (meta.lat && meta.lon) {
|
||||||
|
source = "<a class=\"openwebrx-maps-pin\" href=\"https://www.google.com/maps/search/?api=1&query=" + meta.lat + "," + meta.lon + "\" target=\"_blank\"></a>" + source;
|
||||||
}
|
}
|
||||||
|
up = meta.up ? "Up: " + meta.up : "";
|
||||||
|
down = meta.down ? "Down: " + meta.down : "";
|
||||||
|
$(el).find(".openwebrx-meta-slot").addClass("active");
|
||||||
|
} else {
|
||||||
|
$(el).find(".openwebrx-meta-slot").removeClass("active");
|
||||||
|
}
|
||||||
|
$(el).find(".openwebrx-ysf-mode").text(mode);
|
||||||
|
$(el).find(".openwebrx-ysf-source").html(source);
|
||||||
|
$(el).find(".openwebrx-ysf-up").text(up);
|
||||||
|
$(el).find(".openwebrx-ysf-down").text(down);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
clear_metadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.openwebrx-panel[data-panel-name="metadata"]').each(update);
|
}
|
||||||
toggle_panel("openwebrx-panel-metadata", true);
|
|
||||||
|
function hide_digitalvoice_panels() {
|
||||||
|
$(".openwebrx-meta-panel").each(function(_, p){
|
||||||
|
toggle_panel(p.id, false);
|
||||||
|
});
|
||||||
|
clear_metadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear_metadata() {
|
function clear_metadata() {
|
||||||
toggle_panel("openwebrx-panel-metadata", false);
|
$(".openwebrx-meta-panel .openwebrx-meta-autoclear").text("");
|
||||||
|
$(".openwebrx-meta-slot").removeClass("active").removeClass("sync");
|
||||||
|
$(".openwebrx-dmr-timeslot-panel").removeClass("muted");
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_problem(what)
|
function add_problem(what)
|
||||||
@ -1817,7 +1840,12 @@ String.prototype.startswith=function(str){ return this.indexOf(str) == 0; }; //h
|
|||||||
|
|
||||||
function open_websocket()
|
function open_websocket()
|
||||||
{
|
{
|
||||||
ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically -> now default behaviour
|
var protocol = 'ws';
|
||||||
|
if (window.location.toString().startsWith('https://')) {
|
||||||
|
protocol = 'wss';
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_url = protocol + "://" + (window.location.origin.split("://")[1]) + "/ws/"; //guess automatically -> now default behaviour
|
||||||
if (!("WebSocket" in window))
|
if (!("WebSocket" in window))
|
||||||
divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.");
|
divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.");
|
||||||
ws = new WebSocket(ws_url);
|
ws = new WebSocket(ws_url);
|
||||||
@ -2311,7 +2339,7 @@ function openwebrx_init()
|
|||||||
init_rx_photo();
|
init_rx_photo();
|
||||||
open_websocket();
|
open_websocket();
|
||||||
secondary_demod_init();
|
secondary_demod_init();
|
||||||
clear_metadata();
|
digimodes_init();
|
||||||
place_panels(first_show_panel);
|
place_panels(first_show_panel);
|
||||||
window.setTimeout(function(){window.setInterval(debug_audio,1000);},1000);
|
window.setTimeout(function(){window.setInterval(debug_audio,1000);},1000);
|
||||||
window.addEventListener("resize",openwebrx_resize);
|
window.addEventListener("resize",openwebrx_resize);
|
||||||
@ -2322,6 +2350,25 @@ function openwebrx_init()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function digimodes_init() {
|
||||||
|
hide_digitalvoice_panels();
|
||||||
|
|
||||||
|
// initialze DMR timeslot muting
|
||||||
|
$('.openwebrx-dmr-timeslot-panel').click(function(e) {
|
||||||
|
$(e.currentTarget).toggleClass("muted");
|
||||||
|
update_dmr_timeslot_filtering();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_dmr_timeslot_filtering() {
|
||||||
|
var filter = $('.openwebrx-dmr-timeslot-panel').map(function(index, el){
|
||||||
|
return (!$(el).hasClass("muted")) << index;
|
||||||
|
}).toArray().reduce(function(acc, v){
|
||||||
|
return acc | v;
|
||||||
|
}, 0);
|
||||||
|
webrx_set_param("dmr_filter", filter);
|
||||||
|
}
|
||||||
|
|
||||||
function iosPlayButtonClick()
|
function iosPlayButtonClick()
|
||||||
{
|
{
|
||||||
//On iOS, we can only start audio from a click or touch event.
|
//On iOS, we can only start audio from a click or touch event.
|
||||||
@ -2409,6 +2456,7 @@ function pop_bottommost_panel(from)
|
|||||||
function toggle_panel(what, on)
|
function toggle_panel(what, on)
|
||||||
{
|
{
|
||||||
var item=e(what);
|
var item=e(what);
|
||||||
|
if (!item) return;
|
||||||
if(typeof on !== "undefined")
|
if(typeof on !== "undefined")
|
||||||
{
|
{
|
||||||
if(item.openwebrxHidden && !on) return;
|
if(item.openwebrxHidden && !on) return;
|
||||||
@ -2472,7 +2520,7 @@ function place_panels(function_apply)
|
|||||||
for(i=0;i<plist.length;i++)
|
for(i=0;i<plist.length;i++)
|
||||||
{
|
{
|
||||||
c=plist[i];
|
c=plist[i];
|
||||||
if(c.className=="openwebrx-panel")
|
if(c.className.indexOf("openwebrx-panel") >= 0)
|
||||||
{
|
{
|
||||||
if(c.openwebrxHidden)
|
if(c.openwebrxHidden)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,8 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from operator import and_
|
from operator import and_
|
||||||
|
import re
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -16,8 +18,9 @@ class FeatureDetector(object):
|
|||||||
"rtl_sdr": [ "rtl_sdr" ],
|
"rtl_sdr": [ "rtl_sdr" ],
|
||||||
"sdrplay": [ "rx_tools" ],
|
"sdrplay": [ "rx_tools" ],
|
||||||
"hackrf": [ "hackrf_transfer" ],
|
"hackrf": [ "hackrf_transfer" ],
|
||||||
|
"airspy": [ "airspy_rx" ],
|
||||||
"digital_voice_digiham": [ "digiham", "sox" ],
|
"digital_voice_digiham": [ "digiham", "sox" ],
|
||||||
"digital_voice_dsd": [ "dsd", "sox" ],
|
"digital_voice_dsd": [ "dsd", "sox", "digiham" ],
|
||||||
"packet": [ "direwolf" ]
|
"packet": [ "direwolf" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,19 +85,31 @@ class FeatureDetector(object):
|
|||||||
def command_exists(self, command):
|
def command_exists(self, command):
|
||||||
return os.system("which {0}".format(command)) == 0
|
return os.system("which {0}".format(command)) == 0
|
||||||
|
|
||||||
|
"""
|
||||||
|
To use DMR and YSF, the digiham package is required. You can find the package and installation instructions here:
|
||||||
|
https://github.com/jketterl/digiham
|
||||||
|
|
||||||
|
Please note: there is close interaction between digiham and openwebrx, so older versions will probably not work.
|
||||||
|
If you have an older verison of digiham installed, please update it along with openwebrx.
|
||||||
|
As of now, we require version 0.2 of digiham.
|
||||||
|
"""
|
||||||
def has_digiham(self):
|
def has_digiham(self):
|
||||||
# the digiham tools expect to be fed via stdin, they will block until their stdin is closed.
|
required_version = LooseVersion("0.2")
|
||||||
def check_with_stdin(command):
|
|
||||||
|
digiham_version_regex = re.compile('^digiham version (.*)$')
|
||||||
|
def check_digiham_version(command):
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(command, stdin=subprocess.PIPE)
|
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE)
|
||||||
process.communicate("")
|
version = LooseVersion(digiham_version_regex.match(process.stdout.readline().decode()).group(1))
|
||||||
return process.wait() == 0
|
process.wait(1)
|
||||||
|
return version >= required_version
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return False
|
return False
|
||||||
return reduce(and_,
|
return reduce(and_,
|
||||||
map(
|
map(
|
||||||
check_with_stdin,
|
check_digiham_version,
|
||||||
["rrc_filter", "ysf_decoder", "dmr_decoder", "mbe_synthesizer", "gfsk_demodulator"]
|
["rrc_filter", "ysf_decoder", "dmr_decoder", "mbe_synthesizer", "gfsk_demodulator",
|
||||||
|
"digitalvoice_filter"]
|
||||||
),
|
),
|
||||||
True)
|
True)
|
||||||
|
|
||||||
@ -106,3 +121,6 @@ class FeatureDetector(object):
|
|||||||
|
|
||||||
def has_direwolf(self):
|
def has_direwolf(self):
|
||||||
return self.command_is_runnable("direwolf --help")
|
return self.command_is_runnable("direwolf --help")
|
||||||
|
|
||||||
|
def has_airspy_rx(self):
|
||||||
|
return self.command_is_runnable("airspy_rx --help 2> /dev/null")
|
||||||
|
@ -18,7 +18,9 @@ class Router(object):
|
|||||||
{"route": "/status", "controller": StatusController},
|
{"route": "/status", "controller": StatusController},
|
||||||
{"regex": "/static/(.+)", "controller": AssetsController},
|
{"regex": "/static/(.+)", "controller": AssetsController},
|
||||||
{"route": "/ws/", "controller": WebSocketController},
|
{"route": "/ws/", "controller": WebSocketController},
|
||||||
{"regex": "(/favicon.ico)", "controller": AssetsController}
|
{"regex": "(/favicon.ico)", "controller": AssetsController},
|
||||||
|
# backwards compatibility for the sdr.hu portal
|
||||||
|
{"regex": "/(gfx/openwebrx-avatar.png)", "controller": AssetsController}
|
||||||
]
|
]
|
||||||
def find_controller(self, path):
|
def find_controller(self, path):
|
||||||
for m in Router.mappings:
|
for m in Router.mappings:
|
||||||
|
@ -64,11 +64,13 @@ class MetaParser(object):
|
|||||||
enrichers = {
|
enrichers = {
|
||||||
"DMR": DmrMetaEnricher()
|
"DMR": DmrMetaEnricher()
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, handler):
|
def __init__(self, handler):
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
|
|
||||||
def parse(self, meta):
|
def parse(self, meta):
|
||||||
fields = meta.split(";")
|
fields = meta.split(";")
|
||||||
meta = {v[0] : "".join(v[1:]) for v in map(lambda x: x.split(":"), fields)}
|
meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}
|
||||||
|
|
||||||
if "protocol" in meta:
|
if "protocol" in meta:
|
||||||
protocol = meta["protocol"]
|
protocol = meta["protocol"]
|
||||||
|
@ -257,6 +257,16 @@ class SdrplaySource(SdrSource):
|
|||||||
def sleepOnRestart(self):
|
def sleepOnRestart(self):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
class AirspySource(SdrSource):
|
||||||
|
def getCommand(self):
|
||||||
|
frequency = self.props['center_freq'] / 1e6
|
||||||
|
command = "airspy_rx"
|
||||||
|
command += " -f{0}".format(frequency)
|
||||||
|
command += " -r /dev/stdout -a{samp_rate} -g {rf_gain}"
|
||||||
|
return command
|
||||||
|
def getFormatConversion(self):
|
||||||
|
return "csdr convert_s16_f"
|
||||||
|
|
||||||
class SpectrumThread(csdr.output):
|
class SpectrumThread(csdr.output):
|
||||||
def __init__(self, sdrSource):
|
def __init__(self, sdrSource):
|
||||||
self.sdrSource = sdrSource
|
self.sdrSource = sdrSource
|
||||||
@ -339,7 +349,8 @@ class DspManager(csdr.output):
|
|||||||
|
|
||||||
self.localProps = self.sdrSource.getProps().collect(
|
self.localProps = self.sdrSource.getProps().collect(
|
||||||
"audio_compression", "fft_compression", "digimodes_fft_size", "csdr_dynamic_bufsize",
|
"audio_compression", "fft_compression", "digimodes_fft_size", "csdr_dynamic_bufsize",
|
||||||
"csdr_print_bufsizes", "csdr_through", "digimodes_enable", "samp_rate", "digital_voice_unvoiced_quality"
|
"csdr_print_bufsizes", "csdr_through", "digimodes_enable", "samp_rate", "digital_voice_unvoiced_quality",
|
||||||
|
"dmr_filter"
|
||||||
).defaults(PropertyManager.getSharedInstance())
|
).defaults(PropertyManager.getSharedInstance())
|
||||||
|
|
||||||
self.dsp = csdr.dsp(self)
|
self.dsp = csdr.dsp(self)
|
||||||
@ -366,7 +377,8 @@ class DspManager(csdr.output):
|
|||||||
self.localProps.getProperty("low_cut").wire(set_low_cut),
|
self.localProps.getProperty("low_cut").wire(set_low_cut),
|
||||||
self.localProps.getProperty("high_cut").wire(set_high_cut),
|
self.localProps.getProperty("high_cut").wire(set_high_cut),
|
||||||
self.localProps.getProperty("mod").wire(self.dsp.set_demodulator),
|
self.localProps.getProperty("mod").wire(self.dsp.set_demodulator),
|
||||||
self.localProps.getProperty("digital_voice_unvoiced_quality").wire(self.dsp.set_unvoiced_quality)
|
self.localProps.getProperty("digital_voice_unvoiced_quality").wire(self.dsp.set_unvoiced_quality),
|
||||||
|
self.localProps.getProperty("dmr_filter").wire(self.dsp.set_dmr_filter)
|
||||||
]
|
]
|
||||||
|
|
||||||
self.dsp.set_offset_freq(0)
|
self.dsp.set_offset_freq(0)
|
||||||
|
@ -38,12 +38,16 @@ class WebSocketConnection(object):
|
|||||||
# string-type messages are sent as text frames
|
# string-type messages are sent as text frames
|
||||||
if (type(data) == str):
|
if (type(data) == str):
|
||||||
header = self.get_header(len(data), 1)
|
header = self.get_header(len(data), 1)
|
||||||
self.handler.wfile.write(header + data.encode('utf-8'))
|
data_to_send = header + data.encode('utf-8')
|
||||||
self.handler.wfile.flush()
|
|
||||||
# anything else as binary
|
# anything else as binary
|
||||||
else:
|
else:
|
||||||
header = self.get_header(len(data), 2)
|
header = self.get_header(len(data), 2)
|
||||||
self.handler.wfile.write(header + data)
|
data_to_send = header + data
|
||||||
|
written = self.handler.wfile.write(data_to_send)
|
||||||
|
if (written != len(data_to_send)):
|
||||||
|
logger.error("incomplete write! closing socket!")
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
self.handler.wfile.flush()
|
self.handler.wfile.flush()
|
||||||
|
|
||||||
def read_loop(self):
|
def read_loop(self):
|
||||||
@ -78,7 +82,9 @@ class WebSocketConnection(object):
|
|||||||
self.handler.wfile.write(header)
|
self.handler.wfile.write(header)
|
||||||
self.handler.wfile.flush()
|
self.handler.wfile.flush()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.exception("while writing close frame:")
|
logger.exception("ValueError while writing close frame:")
|
||||||
|
except OSError:
|
||||||
|
logger.exception("OSError while writing close frame:")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.handler.finish()
|
self.handler.finish()
|
||||||
|
Loading…
Reference in New Issue
Block a user