merge recent openwebrx changes into our work
This commit is contained in:
parent
ac6e001fd6
commit
bf4c70dfef
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
tags
|
||||||
|
35
README.md
35
README.md
@ -1,21 +1,26 @@
|
|||||||
OpenWebRX
|
OpenWebRX
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
[:floppy_disk: Setup guide for Ubuntu](http://blog.sdr.hu/2015/06/30/quick-setup-openwebrx.html) | [:blue_book: Knowledge base on the Wiki](https://github.com/simonyiszk/openwebrx/wiki/) | [:earth_americas: Receivers on SDR.hu](http://sdr.hu/)
|
||||||
|
|
||||||
OpenWebRX is a multi-user SDR receiver software with a web interface.
|
OpenWebRX is a multi-user SDR receiver software with a web interface.
|
||||||
|
|
||||||
![OpenWebRX](/screenshot.png?raw=true)
|
![OpenWebRX](http://blog.sdr.hu/images/openwebrx/screenshot.png)
|
||||||
|
|
||||||
It has the following features:
|
It has the following features:
|
||||||
|
|
||||||
- <a href="https://github.com/simonyiszk/csdr">libcsdr</a> based demodulators (AM/FM/SSB),
|
- <a href="https://github.com/simonyiszk/csdr">csdr</a> based demodulators (AM/FM/SSB/CW/BPSK31),
|
||||||
- filter passband can be set from GUI,
|
- filter passband can be set from GUI,
|
||||||
- waterfall display can be shifted back in time,
|
- waterfall display can be shifted back in time,
|
||||||
- it extensively uses HTML5 features like WebSocket, Web Audio API, and <canvas>.
|
- it extensively uses HTML5 features like WebSocket, Web Audio API, and <canvas>,
|
||||||
- it works in Google Chrome, Chromium (above version 37) and Mozilla Firefox (above version 28),
|
- it works in Google Chrome, Chromium (above version 37) and Mozilla Firefox (above version 28),
|
||||||
- currently supports RTL-SDR and HackRF; other SDR hardware may be easily added.
|
- currently supports RTL-SDR, HackRF, SDRplay, AirSpy and many other devices, see the <a href="https://github.com/simonyiszk/openwebrx/wiki/">OpenWebRX Wiki</a>,
|
||||||
|
- it has a 3D waterfall display:
|
||||||
|
|
||||||
|
![OpenWebRX 3D waterfall](http://blog.sdr.hu/images/openwebrx/screenshot-3d.gif)
|
||||||
|
|
||||||
**News (2015-08-18)**
|
**News (2015-08-18)**
|
||||||
- My BSc. thesis written on OpenWebRX is <a href="http://openwebrx.org/bsc-thesis.pdf">available here.</a>
|
- My BSc. thesis written on OpenWebRX is <a href="https://sdr.hu/static/bsc-thesis.pdf">available here.</a>
|
||||||
- Several bugs were fixed to improve reliability and stability.
|
- Several bugs were fixed to improve reliability and stability.
|
||||||
- OpenWebRX now supports compression of audio and waterfall stream, so the required network uplink bandwidth has been decreased from 2 Mbit/s to about 200 kbit/s per client! (Measured with the default settings. It is also dependent on `fft_size`.)
|
- OpenWebRX now supports compression of audio and waterfall stream, so the required network uplink bandwidth has been decreased from 2 Mbit/s to about 200 kbit/s per client! (Measured with the default settings. It is also dependent on `fft_size`.)
|
||||||
- OpenWebRX now uses <a href="https://github.com/simonyiszk/csdr#sdrjs">sdr.js</a> (*libcsdr* compiled to JavaScript) for some client-side DSP tasks.
|
- OpenWebRX now uses <a href="https://github.com/simonyiszk/csdr#sdrjs">sdr.js</a> (*libcsdr* compiled to JavaScript) for some client-side DSP tasks.
|
||||||
@ -28,13 +33,20 @@ It has the following features:
|
|||||||
- OpenWebRX now supports URLs like: `http://localhost:8073/#freq=145555000,mod=usb`
|
- OpenWebRX now supports URLs like: `http://localhost:8073/#freq=145555000,mod=usb`
|
||||||
- UI improvements were made, thanks to John Seamons and Gnoxter.
|
- UI improvements were made, thanks to John Seamons and Gnoxter.
|
||||||
|
|
||||||
> When upgrading OpenWebRX, please make sure that you also upgrade *csdr*, and install the new dependency, *ncat*!
|
**News (2017-04-04)**
|
||||||
|
- *ncat* has been replaced with a custom implementation called *nmux* due to a bug that caused regular crashes on some machines. The *nmux* tool is part of the *csdr* package.
|
||||||
|
- Most consumer SDR devices are supported via <a href="https://github.com/rxseger/rx_tools">rx_tools</a>, see the <a href="https://github.com/simonyiszk/openwebrx/wiki/Using-rx_tools-with-OpenWebRX">OpenWebRX Wiki</a> on that.
|
||||||
|
|
||||||
|
**News (2017-07-12)**
|
||||||
|
- OpenWebRX now has a BPSK31 demodulator and a 3D waterfall display.
|
||||||
|
|
||||||
|
> When upgrading OpenWebRX, please make sure that you also upgrade *csdr*!
|
||||||
|
|
||||||
## OpenWebRX servers on SDR.hu
|
## OpenWebRX servers on SDR.hu
|
||||||
|
|
||||||
[SDR.hu](http://sdr.hu) is a site which lists the active, public OpenWebRX servers. Your receiver [can also be part of it](http://sdr.hu/openwebrx), if you want.
|
[SDR.hu](http://sdr.hu) is a site which lists the active, public OpenWebRX servers. Your receiver [can also be part of it](http://sdr.hu/openwebrx), if you want.
|
||||||
|
|
||||||
![sdr.hu](/screenshot-sdrhu.png?raw=true)
|
![sdr.hu](http://blog.sdr.hu/images/openwebrx/screenshot-sdrhu.png)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@ -44,11 +56,6 @@ First you will need to install the dependencies:
|
|||||||
|
|
||||||
- <a href="https://github.com/simonyiszk/csdr">libcsdr</a>
|
- <a href="https://github.com/simonyiszk/csdr">libcsdr</a>
|
||||||
- <a href="http://sdr.osmocom.org/trac/wiki/rtl-sdr">rtl-sdr</a>
|
- <a href="http://sdr.osmocom.org/trac/wiki/rtl-sdr">rtl-sdr</a>
|
||||||
- ncat (On Debian/Ubuntu, it is in the *nmap* package).
|
|
||||||
|
|
||||||
> By the way, *nmap* is a tool commonly used for auditing network security, and it is not used by OpenWebRX in any way. We need to install it, because the *ncat* command is packaged with it.
|
|
||||||
>
|
|
||||||
> *ncat* is a better *netcat* alternative, which is used by OpenWebRX for internally distributing the I/Q data stream. It also solves the problem of having different versions of *netcat* on different Linux distributions, which are not compatible by their command-line arguments.
|
|
||||||
|
|
||||||
After cloning this repository and connecting an RTL-SDR dongle to your computer, you can run the server:
|
After cloning this repository and connecting an RTL-SDR dongle to your computer, you can run the server:
|
||||||
|
|
||||||
@ -62,7 +69,7 @@ Please note that the server is also listening on the following ports (on localho
|
|||||||
|
|
||||||
Now the next step is to customize the parameters of your server in `config_webrx.py`.
|
Now the next step is to customize the parameters of your server in `config_webrx.py`.
|
||||||
|
|
||||||
Actually, if you do something cool with OpenWebRX (or just have a problem), please drop me a mail:
|
Actually, if you do something cool with OpenWebRX, please drop me a mail:
|
||||||
*Andras Retzler, HA7ILM <randras@sdr.hu>*
|
*Andras Retzler, HA7ILM <randras@sdr.hu>*
|
||||||
|
|
||||||
## Usage tips
|
## Usage tips
|
||||||
@ -85,4 +92,4 @@ If you want to run OpenWebRX on a remote server instead of *localhost*, do not f
|
|||||||
|
|
||||||
OpenWebRX is available under Affero GPL v3 license (<a href="https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)">summary</a>).
|
OpenWebRX is available under Affero GPL v3 license (<a href="https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)">summary</a>).
|
||||||
|
|
||||||
OpenWebRX is also available under a commercial license on request. Please contact me at the address *<randras@sdr.hu>* for other licensing options.
|
OpenWebRX is also available under a commercial license on request. Please contact me at the address *<randras@sdr.hu>* for licensing options.
|
||||||
|
@ -67,34 +67,49 @@ sdrhu_key = ""
|
|||||||
sdrhu_public_listing = False
|
sdrhu_public_listing = False
|
||||||
|
|
||||||
# ==== DSP/RX settings ====
|
# ==== DSP/RX settings ====
|
||||||
dsp_plugin="csdr"
|
|
||||||
fft_fps=9
|
fft_fps=9
|
||||||
fft_size=4096
|
fft_size=4096 #Should be power of 2
|
||||||
samp_rate = 250000
|
fft_voverlap_factor=0.3 #If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the diagram.
|
||||||
|
|
||||||
center_freq = 145525000
|
# samp_rate = 250000
|
||||||
|
samp_rate = 2400000
|
||||||
|
center_freq = 144250000
|
||||||
rf_gain = 5 #in dB. For an RTL-SDR, rf_gain=0 will set the tuner to auto gain mode, else it will be in manual gain mode.
|
rf_gain = 5 #in dB. For an RTL-SDR, rf_gain=0 will set the tuner to auto gain mode, else it will be in manual gain mode.
|
||||||
ppm = 0
|
ppm = 0
|
||||||
|
|
||||||
audio_compression="adpcm" #valid values: "adpcm", "none"
|
audio_compression="adpcm" #valid values: "adpcm", "none"
|
||||||
fft_compression="adpcm" #valid values: "adpcm", "none"
|
fft_compression="adpcm" #valid values: "adpcm", "none"
|
||||||
|
|
||||||
|
digimodes_enable=True #Decoding digimodes come with higher CPU usage.
|
||||||
|
digimodes_fft_size=1024
|
||||||
|
|
||||||
start_rtl_thread=True
|
start_rtl_thread=True
|
||||||
|
|
||||||
|
"""
|
||||||
|
Note: if you experience audio underruns while CPU usage is 100%, you can:
|
||||||
|
- decrease `samp_rate`,
|
||||||
|
- set `fft_voverlap_factor` to 0,
|
||||||
|
- decrease `fft_fps` and `fft_size`,
|
||||||
|
- limit the number of users by decreasing `max_clients`.
|
||||||
|
"""
|
||||||
|
|
||||||
# ==== I/Q sources ====
|
# ==== I/Q sources ====
|
||||||
# (Uncomment the appropriate by removing # characters at the beginning of the corresponding lines.)
|
# (Uncomment the appropriate by removing # characters at the beginning of the corresponding lines.)
|
||||||
|
|
||||||
# There are guides for setting may different SDR hardware including AirSpy, AFEDRI-SDR, RTL-SDR in direct sampling mode, etc. in the Wiki:
|
#################################################################################################
|
||||||
# https://github.com/simonyiszk/openwebrx/wiki
|
# Is my SDR hardware supported? #
|
||||||
|
# Check here: https://github.com/simonyiszk/openwebrx/wiki#guides-for-receiver-hardware-support #
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
# You can use other SDR hardware as well, by giving your own command that outputs the I/Q samples... Some examples of configuration are available here (default is RTL-SDR):
|
# You can use other SDR hardware as well, by giving your own command that outputs the I/Q samples... Some examples of configuration are available here (default is RTL-SDR):
|
||||||
|
|
||||||
# >> RTL-SDR via rtl_sdr
|
# >> RTL-SDR via rtl_sdr
|
||||||
|
|
||||||
start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
|
start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
|
||||||
format_conversion="csdr convert_u8_f"
|
format_conversion="csdr convert_u8_f"
|
||||||
|
|
||||||
#start_rtl_command="hackrf_transfer -s {samp_rate} -f {center_freq} -g {rf_gain} -l16 -a0 -q -r-".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
|
#lna_gain=8
|
||||||
|
#rf_amp=1
|
||||||
|
#start_rtl_command="hackrf_transfer -s {samp_rate} -f {center_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm, rf_amp=rf_amp, lna_gain=lna_gain)
|
||||||
#format_conversion="csdr convert_s8_f"
|
#format_conversion="csdr convert_s8_f"
|
||||||
"""
|
"""
|
||||||
To use a HackRF, compile the HackRF host tools from its "stdout" branch:
|
To use a HackRF, compile the HackRF host tools from its "stdout" branch:
|
||||||
@ -126,11 +141,20 @@ To use a HackRF, compile the HackRF host tools from its "stdout" branch:
|
|||||||
#start_rtl_command="(while true; do cat my_iq_file.raw; done) | csdr flowcontrol {sr} 20 ".format(sr=samp_rate*2*1.05)
|
#start_rtl_command="(while true; do cat my_iq_file.raw; done) | csdr flowcontrol {sr} 20 ".format(sr=samp_rate*2*1.05)
|
||||||
#format_conversion="csdr convert_u8_f"
|
#format_conversion="csdr convert_u8_f"
|
||||||
|
|
||||||
|
#>> The rx_sdr command works with a variety of SDR harware: RTL-SDR, HackRF, SDRplay, UHD, Airspy, Red Pitaya, audio devices, etc.
|
||||||
|
# It will auto-detect your SDR hardware if the following tools are installed:
|
||||||
|
# * the vendor provided driver and library,
|
||||||
|
# * the vendor-specific SoapySDR wrapper library,
|
||||||
|
# * and SoapySDR itself.
|
||||||
|
# Check out this article on the OpenWebRX Wiki: https://github.com/simonyiszk/openwebrx/wiki/Using-rx_tools-with-OpenWebRX/
|
||||||
|
#start_rtl_command="rx_sdr -F CF32 -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
|
||||||
|
#format_conversion=""
|
||||||
|
|
||||||
# >> gr-osmosdr signal source using GNU Radio (follow this guide: https://github.com/simonyiszk/openwebrx/wiki/Using-GrOsmoSDR-as-signal-source)
|
# >> gr-osmosdr signal source using GNU Radio (follow this guide: https://github.com/simonyiszk/openwebrx/wiki/Using-GrOsmoSDR-as-signal-source)
|
||||||
#start_rtl_command="cat /tmp/osmocom_fifo"
|
#start_rtl_command="cat /tmp/osmocom_fifo"
|
||||||
#format_conversion=""
|
#format_conversion=""
|
||||||
|
|
||||||
# ==== Misc options ====
|
# ==== Misc settings ====
|
||||||
|
|
||||||
shown_center_freq = center_freq #you can change this if you use an upconverter
|
shown_center_freq = center_freq #you can change this if you use an upconverter
|
||||||
|
|
||||||
@ -146,16 +170,43 @@ iq_server_port = 4951 #TCP port for ncat to listen on. It will send I/Q data ove
|
|||||||
|
|
||||||
#access_log = "~/openwebrx_access.log"
|
#access_log = "~/openwebrx_access.log"
|
||||||
|
|
||||||
waterfall_colors = "[0x000000ff,0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff, 0xfff775ff, 0xff8a8aff, 0xb20000ff]"
|
# ==== Color themes ====
|
||||||
waterfall_min_level = -115 #in dB
|
|
||||||
waterfall_max_level = 0
|
|
||||||
#A guide is available to help you set these values: https://github.com/simonyiszk/openwebrx/wiki/Calibrating-waterfall-display-levels
|
#A guide is available to help you set these values: https://github.com/simonyiszk/openwebrx/wiki/Calibrating-waterfall-display-levels
|
||||||
|
|
||||||
|
### default theme by teejez:
|
||||||
|
waterfall_colors = "[0x000000ff,0x0000ffff,0x00ffffff,0x00ff00ff,0xffff00ff,0xff0000ff,0xff00ffff,0xffffffff]"
|
||||||
|
waterfall_min_level = -88 #in dB
|
||||||
|
waterfall_max_level = -20
|
||||||
|
waterfall_auto_level_margin = (5, 40)
|
||||||
|
### old theme by HA7ILM:
|
||||||
|
#waterfall_colors = "[0x000000ff,0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff, 0xfff775ff, 0xff8a8aff, 0xb20000ff]"
|
||||||
|
#waterfall_min_level = -115 #in dB
|
||||||
|
#waterfall_max_level = 0
|
||||||
|
#waterfall_auto_level_margin = (20, 30)
|
||||||
|
##For the old colors, you might also want to set [fft_voverlap_factor] to 0.
|
||||||
|
|
||||||
|
#Note: When the auto waterfall level button is clicked, the following happens:
|
||||||
|
# [waterfall_min_level] = [current_min_power_level] - [waterfall_auto_level_margin[0]]
|
||||||
|
# [waterfall_max_level] = [current_max_power_level] + [waterfall_auto_level_margin[1]]
|
||||||
|
#
|
||||||
|
# ___|____________________________________|____________________________________|____________________________________|___> signal power
|
||||||
|
# \_waterfall_auto_level_margin[0]_/ |__ current_min_power_level | \_waterfall_auto_level_margin[1]_/
|
||||||
|
# current_max_power_level __|
|
||||||
|
|
||||||
|
# 3D view settings
|
||||||
|
mathbox_waterfall_frequency_resolution = 128 #bins
|
||||||
|
mathbox_waterfall_history_length = 10 #seconds
|
||||||
|
mathbox_waterfall_colors = "[0x000000ff,0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff, 0xfff775ff, 0xff8a8aff, 0xb20000ff]"
|
||||||
|
|
||||||
|
# === Experimental settings ===
|
||||||
#Warning! The settings below are very experimental.
|
#Warning! The settings below are very experimental.
|
||||||
csdr_dynamic_bufsize = False # This allows you to change the buffering mode of csdr.
|
csdr_dynamic_bufsize = False # This allows you to change the buffering mode of csdr.
|
||||||
csdr_print_bufsizes = False # This prints the buffer sizes used for csdr processes.
|
csdr_print_bufsizes = False # This prints the buffer sizes used for csdr processes.
|
||||||
csdr_through = False # Setting this True will print out how much data is going into the DSP chains.
|
csdr_through = False # Setting this True will print out how much data is going into the DSP chains.
|
||||||
|
|
||||||
|
nmux_memory = 50 #in megabytes. This sets the approximate size of the circular buffer used by nmux.
|
||||||
|
|
||||||
#Look up external IP address automatically from icanhazip.com, and use it as [server_hostname]
|
#Look up external IP address automatically from icanhazip.com, and use it as [server_hostname]
|
||||||
"""
|
"""
|
||||||
print "[openwebrx-config] Detecting external IP address..."
|
print "[openwebrx-config] Detecting external IP address..."
|
||||||
|
453
csdr.py
Executable file
453
csdr.py
Executable file
@ -0,0 +1,453 @@
|
|||||||
|
"""
|
||||||
|
OpenWebRX csdr plugin: do the signal processing with csdr
|
||||||
|
|
||||||
|
This file is part of OpenWebRX,
|
||||||
|
an open-source SDR receiver software with a web UI.
|
||||||
|
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import code
|
||||||
|
import signal
|
||||||
|
import fcntl
|
||||||
|
|
||||||
|
class dsp:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.samp_rate = 250000
|
||||||
|
self.output_rate = 11025 #this is default, and cannot be set at the moment
|
||||||
|
self.fft_size = 1024
|
||||||
|
self.fft_fps = 5
|
||||||
|
self.offset_freq = 0
|
||||||
|
self.low_cut = -4000
|
||||||
|
self.high_cut = 4000
|
||||||
|
self.bpf_transition_bw = 320 #Hz, and this is a constant
|
||||||
|
self.ddc_transition_bw_rate = 0.15 # of the IF sample rate
|
||||||
|
self.running = False
|
||||||
|
self.secondary_processes_running = False
|
||||||
|
self.audio_compression = "none"
|
||||||
|
self.fft_compression = "none"
|
||||||
|
self.demodulator = "nfm"
|
||||||
|
self.name = "csdr"
|
||||||
|
self.format_conversion = "csdr convert_u8_f"
|
||||||
|
self.base_bufsize = 512
|
||||||
|
self.nc_port = 4951
|
||||||
|
self.csdr_dynamic_bufsize = False
|
||||||
|
self.csdr_print_bufsizes = False
|
||||||
|
self.csdr_through = False
|
||||||
|
self.squelch_level = 0
|
||||||
|
self.fft_averages = 50
|
||||||
|
self.iqtee = False
|
||||||
|
self.iqtee2 = False
|
||||||
|
self.secondary_demodulator = None
|
||||||
|
self.secondary_fft_size = 1024
|
||||||
|
self.secondary_process_fft = None
|
||||||
|
self.secondary_process_demod = None
|
||||||
|
self.pipe_names=["bpf_pipe", "shift_pipe", "squelch_pipe", "smeter_pipe", "iqtee_pipe", "iqtee2_pipe"]
|
||||||
|
self.secondary_pipe_names=["secondary_shift_pipe"]
|
||||||
|
self.secondary_offset_freq = 1000
|
||||||
|
|
||||||
|
def chain(self,which):
|
||||||
|
if which in [ "dmr", "dstar", "nxdn", "ysf" ]:
|
||||||
|
self.set_output_rate(48000)
|
||||||
|
else:
|
||||||
|
self.set_output_rate(11025)
|
||||||
|
any_chain_base="nc -v 127.0.0.1 {nc_port} | "
|
||||||
|
if self.csdr_dynamic_bufsize: any_chain_base+="csdr setbuf {start_bufsize} | "
|
||||||
|
if self.csdr_through: any_chain_base+="csdr through | "
|
||||||
|
any_chain_base+=self.format_conversion+(" | " if self.format_conversion!="" else "") ##"csdr flowcontrol {flowcontrol} auto 1.5 10 | "
|
||||||
|
if which == "fft":
|
||||||
|
fft_chain_base = any_chain_base+"csdr fft_cc {fft_size} {fft_block_size} | " + \
|
||||||
|
("csdr logpower_cf -70 | " if self.fft_averages == 0 else "csdr logaveragepower_cf -70 {fft_size} {fft_averages} | ") + \
|
||||||
|
"csdr fft_exchange_sides_ff {fft_size}"
|
||||||
|
if self.fft_compression=="adpcm":
|
||||||
|
return fft_chain_base+" | csdr compress_fft_adpcm_f_u8 {fft_size}"
|
||||||
|
else:
|
||||||
|
return fft_chain_base
|
||||||
|
chain_begin=any_chain_base+"csdr shift_addition_cc --fifo {shift_pipe} | csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING | csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING | csdr squelch_and_smeter_cc --fifo {squelch_pipe} --outfifo {smeter_pipe} 5 1 | "
|
||||||
|
if self.secondary_demodulator:
|
||||||
|
chain_begin+="csdr tee {iqtee_pipe} | "
|
||||||
|
chain_begin+="csdr tee {iqtee2_pipe} | "
|
||||||
|
chain_end = ""
|
||||||
|
if self.audio_compression=="adpcm":
|
||||||
|
chain_end = " | csdr encode_ima_adpcm_i16_u8"
|
||||||
|
if which == "nfm": return chain_begin + "csdr fmdemod_quadri_cf | csdr limit_ff | csdr old_fractional_decimator_ff {last_decimation} | csdr deemphasis_nfm_ff 11025 | csdr convert_f_s16"+chain_end
|
||||||
|
if which in [ "dstar", "nxdn" ]:
|
||||||
|
c = chain_begin
|
||||||
|
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
||||||
|
if which == "dstar":
|
||||||
|
c += " | dsd -fd"
|
||||||
|
elif which == "nxdn":
|
||||||
|
c += " | dsd -fi"
|
||||||
|
c += " -i - -o - -u 2 -g 10"
|
||||||
|
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --input-buffer 160 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 220"
|
||||||
|
c += chain_end
|
||||||
|
return c
|
||||||
|
elif which == "dmr":
|
||||||
|
c = chain_begin
|
||||||
|
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
||||||
|
c += " | rrc_filter | gfsk_demodulator | dmr_decoder --fifo {meta_pipe} | mbe_synthesizer"
|
||||||
|
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 256"
|
||||||
|
c += chain_end
|
||||||
|
return c
|
||||||
|
elif which == "ysf":
|
||||||
|
c = chain_begin
|
||||||
|
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
||||||
|
c += " | rrc_filter | gfsk_demodulator | ysf_decoder --fifo {meta_pipe} | mbe_synthesizer -y"
|
||||||
|
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 256"
|
||||||
|
c += chain_end
|
||||||
|
return c
|
||||||
|
elif which == "am": return chain_begin + "csdr amdemod_cf | csdr fastdcblock_ff | csdr old_fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_s16"+chain_end
|
||||||
|
elif which == "ssb": return chain_begin + "csdr realpart_cf | csdr old_fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_s16"+chain_end
|
||||||
|
|
||||||
|
def secondary_chain(self, which):
|
||||||
|
secondary_chain_base="cat {input_pipe} | "
|
||||||
|
if which == "fft":
|
||||||
|
return secondary_chain_base+"csdr realpart_cf | csdr fft_fc {secondary_fft_input_size} {secondary_fft_block_size} | csdr logpower_cf -70 " + (" | csdr compress_fft_adpcm_f_u8 {secondary_fft_size}" if self.fft_compression=="adpcm" else "")
|
||||||
|
elif which == "bpsk31":
|
||||||
|
return secondary_chain_base + "csdr shift_addition_cc --fifo {secondary_shift_pipe} | " + \
|
||||||
|
"csdr bandpass_fir_fft_cc $(csdr '=-(31.25)/{if_samp_rate}') $(csdr '=(31.25)/{if_samp_rate}') $(csdr '=31.25/{if_samp_rate}') | " + \
|
||||||
|
"csdr simple_agc_cc 0.001 0.5 | " + \
|
||||||
|
"csdr timing_recovery_cc GARDNER {secondary_samples_per_bits} 0.5 2 --add_q | " + \
|
||||||
|
"CSDR_FIXED_BUFSIZE=1 csdr dbpsk_decoder_c_u8 | " + \
|
||||||
|
"CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8"
|
||||||
|
|
||||||
|
def set_secondary_demodulator(self, what):
|
||||||
|
self.secondary_demodulator = what
|
||||||
|
|
||||||
|
def secondary_fft_block_size(self):
|
||||||
|
return (self.samp_rate/self.decimation)/(self.fft_fps*2) #*2 is there because we do FFT on real signal here
|
||||||
|
|
||||||
|
def secondary_decimation(self):
|
||||||
|
return 1 #currently unused
|
||||||
|
|
||||||
|
def secondary_bpf_cutoff(self):
|
||||||
|
if self.secondary_demodulator == "bpsk31":
|
||||||
|
return (31.25/2) / self.if_samp_rate()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def secondary_bpf_transition_bw(self):
|
||||||
|
if self.secondary_demodulator == "bpsk31":
|
||||||
|
return (31.25/2) / self.if_samp_rate()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def secondary_samples_per_bits(self):
|
||||||
|
if self.secondary_demodulator == "bpsk31":
|
||||||
|
return int(round(self.if_samp_rate()/31.25))&~3
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def secondary_bw(self):
|
||||||
|
if self.secondary_demodulator == "bpsk31":
|
||||||
|
return 31.25
|
||||||
|
|
||||||
|
def start_secondary_demodulator(self):
|
||||||
|
if(not self.secondary_demodulator): return
|
||||||
|
print "[openwebrx] starting secondary demodulator from IF input sampled at %d"%self.if_samp_rate()
|
||||||
|
secondary_command_fft=self.secondary_chain("fft")
|
||||||
|
secondary_command_demod=self.secondary_chain(self.secondary_demodulator)
|
||||||
|
self.try_create_pipes(self.secondary_pipe_names, secondary_command_demod + secondary_command_fft)
|
||||||
|
|
||||||
|
secondary_command_fft=secondary_command_fft.format( \
|
||||||
|
input_pipe=self.iqtee_pipe, \
|
||||||
|
secondary_fft_input_size=self.secondary_fft_size, \
|
||||||
|
secondary_fft_size=self.secondary_fft_size, \
|
||||||
|
secondary_fft_block_size=self.secondary_fft_block_size(), \
|
||||||
|
)
|
||||||
|
secondary_command_demod=secondary_command_demod.format( \
|
||||||
|
input_pipe=self.iqtee2_pipe, \
|
||||||
|
secondary_shift_pipe=self.secondary_shift_pipe, \
|
||||||
|
secondary_decimation=self.secondary_decimation(), \
|
||||||
|
secondary_samples_per_bits=self.secondary_samples_per_bits(), \
|
||||||
|
secondary_bpf_cutoff=self.secondary_bpf_cutoff(), \
|
||||||
|
secondary_bpf_transition_bw=self.secondary_bpf_transition_bw(), \
|
||||||
|
if_samp_rate=self.if_samp_rate()
|
||||||
|
)
|
||||||
|
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] secondary command (fft) =", secondary_command_fft
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] secondary command (demod) =", secondary_command_demod
|
||||||
|
#code.interact(local=locals())
|
||||||
|
my_env=os.environ.copy()
|
||||||
|
#if self.csdr_dynamic_bufsize: my_env["CSDR_DYNAMIC_BUFSIZE_ON"]="1";
|
||||||
|
if self.csdr_print_bufsizes: my_env["CSDR_PRINT_BUFSIZES"]="1";
|
||||||
|
self.secondary_process_fft = subprocess.Popen(secondary_command_fft, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env)
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] Popen on secondary command (fft)"
|
||||||
|
self.secondary_process_demod = subprocess.Popen(secondary_command_demod, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env) #TODO digimodes
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] Popen on secondary command (demod)" #TODO digimodes
|
||||||
|
self.secondary_processes_running = True
|
||||||
|
|
||||||
|
#open control pipes for csdr and send initialization data
|
||||||
|
# print "==========> 1"
|
||||||
|
if self.secondary_shift_pipe != None: #TODO digimodes
|
||||||
|
# print "==========> 2", self.secondary_shift_pipe
|
||||||
|
self.secondary_shift_pipe_file=open(self.secondary_shift_pipe,"w") #TODO digimodes
|
||||||
|
# print "==========> 3"
|
||||||
|
self.set_secondary_offset_freq(self.secondary_offset_freq) #TODO digimodes
|
||||||
|
# print "==========> 4"
|
||||||
|
|
||||||
|
self.set_pipe_nonblocking(self.secondary_process_demod.stdout)
|
||||||
|
self.set_pipe_nonblocking(self.secondary_process_fft.stdout)
|
||||||
|
|
||||||
|
def set_secondary_offset_freq(self, value):
|
||||||
|
self.secondary_offset_freq=value
|
||||||
|
if self.secondary_processes_running:
|
||||||
|
self.secondary_shift_pipe_file.write("%g\n"%(-float(self.secondary_offset_freq)/self.if_samp_rate()))
|
||||||
|
self.secondary_shift_pipe_file.flush()
|
||||||
|
|
||||||
|
def stop_secondary_demodulator(self):
|
||||||
|
if self.secondary_processes_running == False: return
|
||||||
|
self.try_delete_pipes(self.secondary_pipe_names)
|
||||||
|
if self.secondary_process_fft: os.killpg(os.getpgid(self.secondary_process_fft.pid), signal.SIGTERM)
|
||||||
|
if self.secondary_process_demod: os.killpg(os.getpgid(self.secondary_process_demod.pid), signal.SIGTERM)
|
||||||
|
self.secondary_processes_running = False
|
||||||
|
|
||||||
|
def read_secondary_demod(self, size):
|
||||||
|
return self.secondary_process_demod.stdout.read(size)
|
||||||
|
|
||||||
|
def read_secondary_fft(self, size):
|
||||||
|
return self.secondary_process_fft.stdout.read(size)
|
||||||
|
|
||||||
|
def get_secondary_demodulator(self):
|
||||||
|
return self.secondary_demodulator
|
||||||
|
|
||||||
|
def set_secondary_fft_size(self,secondary_fft_size):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.secondary_fft_size=secondary_fft_size
|
||||||
|
|
||||||
|
def set_audio_compression(self,what):
|
||||||
|
self.audio_compression = what
|
||||||
|
|
||||||
|
def set_fft_compression(self,what):
|
||||||
|
self.fft_compression = what
|
||||||
|
|
||||||
|
def get_fft_bytes_to_read(self):
|
||||||
|
if self.fft_compression=="none": return self.fft_size*4
|
||||||
|
if self.fft_compression=="adpcm": return (self.fft_size/2)+(10/2)
|
||||||
|
|
||||||
|
def get_secondary_fft_bytes_to_read(self):
|
||||||
|
if self.fft_compression=="none": return self.secondary_fft_size*4
|
||||||
|
if self.fft_compression=="adpcm": return (self.secondary_fft_size/2)+(10/2)
|
||||||
|
|
||||||
|
def set_samp_rate(self,samp_rate):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.samp_rate=samp_rate
|
||||||
|
self.decimation=1
|
||||||
|
while self.samp_rate/(self.decimation+1)>self.output_rate:
|
||||||
|
self.decimation+=1
|
||||||
|
self.last_decimation=float(self.if_samp_rate())/self.output_rate
|
||||||
|
|
||||||
|
def if_samp_rate(self):
|
||||||
|
return self.samp_rate/self.decimation
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_output_rate(self):
|
||||||
|
return self.output_rate
|
||||||
|
|
||||||
|
def set_output_rate(self,output_rate):
|
||||||
|
self.output_rate=output_rate
|
||||||
|
self.set_samp_rate(self.samp_rate) #as it depends on output_rate
|
||||||
|
|
||||||
|
def set_demodulator(self,demodulator):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.demodulator=demodulator
|
||||||
|
|
||||||
|
def get_demodulator(self):
|
||||||
|
return self.demodulator
|
||||||
|
|
||||||
|
def set_fft_size(self,fft_size):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.fft_size=fft_size
|
||||||
|
|
||||||
|
def set_fft_fps(self,fft_fps):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.fft_fps=fft_fps
|
||||||
|
|
||||||
|
def set_fft_averages(self,fft_averages):
|
||||||
|
#to change this, restart is required
|
||||||
|
self.fft_averages=fft_averages
|
||||||
|
|
||||||
|
def fft_block_size(self):
|
||||||
|
if self.fft_averages == 0: return self.samp_rate/self.fft_fps
|
||||||
|
else: return self.samp_rate/self.fft_fps/self.fft_averages
|
||||||
|
|
||||||
|
def set_format_conversion(self,format_conversion):
|
||||||
|
self.format_conversion=format_conversion
|
||||||
|
|
||||||
|
def set_offset_freq(self,offset_freq):
|
||||||
|
self.offset_freq=offset_freq
|
||||||
|
if self.running:
|
||||||
|
self.shift_pipe_file.write("%g\n"%(-float(self.offset_freq)/self.samp_rate))
|
||||||
|
self.shift_pipe_file.flush()
|
||||||
|
|
||||||
|
def set_bpf(self,low_cut,high_cut):
|
||||||
|
self.low_cut=low_cut
|
||||||
|
self.high_cut=high_cut
|
||||||
|
if self.running:
|
||||||
|
self.bpf_pipe_file.write( "%g %g\n"%(float(self.low_cut)/self.if_samp_rate(), float(self.high_cut)/self.if_samp_rate()) )
|
||||||
|
self.bpf_pipe_file.flush()
|
||||||
|
|
||||||
|
def get_bpf(self):
|
||||||
|
return [self.low_cut, self.high_cut]
|
||||||
|
|
||||||
|
def set_squelch_level(self, squelch_level):
|
||||||
|
self.squelch_level=squelch_level
|
||||||
|
if self.running:
|
||||||
|
self.squelch_pipe_file.write( "%g\n"%(float(self.squelch_level)) )
|
||||||
|
self.squelch_pipe_file.flush()
|
||||||
|
|
||||||
|
def get_smeter_level(self):
|
||||||
|
if self.running:
|
||||||
|
line=self.smeter_pipe_file.readline()
|
||||||
|
return float(line[:-1])
|
||||||
|
|
||||||
|
def mkfifo(self,path):
|
||||||
|
try:
|
||||||
|
os.unlink(path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
os.mkfifo(path)
|
||||||
|
|
||||||
|
def ddc_transition_bw(self):
|
||||||
|
return self.ddc_transition_bw_rate*(self.if_samp_rate()/float(self.samp_rate))
|
||||||
|
|
||||||
|
def try_create_pipes(self, pipe_names, command_base):
|
||||||
|
# print "try_create_pipes"
|
||||||
|
for pipe_name in pipe_names:
|
||||||
|
# print "\t"+pipe_name
|
||||||
|
if "{"+pipe_name+"}" in command_base:
|
||||||
|
setattr(self, pipe_name, self.pipe_base_path+pipe_name)
|
||||||
|
self.mkfifo(getattr(self, pipe_name))
|
||||||
|
else:
|
||||||
|
setattr(self, pipe_name, None)
|
||||||
|
|
||||||
|
def try_delete_pipes(self, pipe_names):
|
||||||
|
for pipe_name in pipe_names:
|
||||||
|
pipe_path = getattr(self,pipe_name,None)
|
||||||
|
if pipe_path:
|
||||||
|
try: os.unlink(pipe_path)
|
||||||
|
except Exception as e: print "[openwebrx-dsp-plugin:csdr] try_delete_pipes() ::", e
|
||||||
|
|
||||||
|
def set_pipe_nonblocking(self, pipe):
|
||||||
|
flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
|
||||||
|
fcntl.fcntl(pipe, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
command_base=self.chain(self.demodulator)
|
||||||
|
|
||||||
|
#create control pipes for csdr
|
||||||
|
self.pipe_base_path="/tmp/openwebrx_pipe_{myid}_".format(myid=id(self))
|
||||||
|
# self.bpf_pipe = self.shift_pipe = self.squelch_pipe = self.smeter_pipe = None
|
||||||
|
|
||||||
|
self.try_create_pipes(self.pipe_names, command_base)
|
||||||
|
|
||||||
|
# if "{bpf_pipe}" in command_base:
|
||||||
|
# self.bpf_pipe=pipe_base_path+"bpf"
|
||||||
|
# self.mkfifo(self.bpf_pipe)
|
||||||
|
# if "{shift_pipe}" in command_base:
|
||||||
|
# self.shift_pipe=pipe_base_path+"shift"
|
||||||
|
# self.mkfifo(self.shift_pipe)
|
||||||
|
# if "{squelch_pipe}" in command_base:
|
||||||
|
# self.squelch_pipe=pipe_base_path+"squelch"
|
||||||
|
# self.mkfifo(self.squelch_pipe)
|
||||||
|
# if "{smeter_pipe}" in command_base:
|
||||||
|
# self.smeter_pipe=pipe_base_path+"smeter"
|
||||||
|
# self.mkfifo(self.smeter_pipe)
|
||||||
|
# if "{iqtee_pipe}" in command_base:
|
||||||
|
# self.iqtee_pipe=pipe_base_path+"iqtee"
|
||||||
|
# self.mkfifo(self.iqtee_pipe)
|
||||||
|
# if "{iqtee2_pipe}" in command_base:
|
||||||
|
# self.iqtee2_pipe=pipe_base_path+"iqtee2"
|
||||||
|
# self.mkfifo(self.iqtee2_pipe)
|
||||||
|
|
||||||
|
#run the command
|
||||||
|
command=command_base.format( bpf_pipe=self.bpf_pipe, shift_pipe=self.shift_pipe, decimation=self.decimation, \
|
||||||
|
last_decimation=self.last_decimation, fft_size=self.fft_size, fft_block_size=self.fft_block_size(), fft_averages=self.fft_averages, \
|
||||||
|
bpf_transition_bw=float(self.bpf_transition_bw)/self.if_samp_rate(), ddc_transition_bw=self.ddc_transition_bw(), \
|
||||||
|
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, iqtee_pipe=self.iqtee_pipe, iqtee2_pipe=self.iqtee2_pipe )
|
||||||
|
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] Command =",command
|
||||||
|
#code.interact(local=locals())
|
||||||
|
my_env=os.environ.copy()
|
||||||
|
if self.csdr_dynamic_bufsize: my_env["CSDR_DYNAMIC_BUFSIZE_ON"]="1";
|
||||||
|
if self.csdr_print_bufsizes: my_env["CSDR_PRINT_BUFSIZES"]="1";
|
||||||
|
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env)
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
#open control pipes for csdr and send initialization data
|
||||||
|
if self.bpf_pipe != None:
|
||||||
|
self.bpf_pipe_file=open(self.bpf_pipe,"w")
|
||||||
|
self.set_bpf(self.low_cut,self.high_cut)
|
||||||
|
if self.shift_pipe != None:
|
||||||
|
self.shift_pipe_file=open(self.shift_pipe,"w")
|
||||||
|
self.set_offset_freq(self.offset_freq)
|
||||||
|
if self.squelch_pipe != None:
|
||||||
|
self.squelch_pipe_file=open(self.squelch_pipe,"w")
|
||||||
|
self.set_squelch_level(self.squelch_level)
|
||||||
|
if self.smeter_pipe != None:
|
||||||
|
self.smeter_pipe_file=open(self.smeter_pipe,"r")
|
||||||
|
self.set_pipe_nonblocking(self.smeter_pipe_file)
|
||||||
|
|
||||||
|
self.start_secondary_demodulator()
|
||||||
|
|
||||||
|
def read(self,size):
|
||||||
|
return self.process.stdout.read(size)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
|
||||||
|
self.stop_secondary_demodulator()
|
||||||
|
#if(self.process.poll()!=None):return # returns None while subprocess is running
|
||||||
|
#while(self.process.poll()==None):
|
||||||
|
# #self.process.kill()
|
||||||
|
# print "killproc",os.getpgid(self.process.pid),self.process.pid
|
||||||
|
# os.killpg(self.process.pid, signal.SIGTERM)
|
||||||
|
#
|
||||||
|
# time.sleep(0.1)
|
||||||
|
|
||||||
|
self.try_delete_pipes(self.pipe_names)
|
||||||
|
|
||||||
|
# if self.bpf_pipe:
|
||||||
|
# try: os.unlink(self.bpf_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
||||||
|
# if self.shift_pipe:
|
||||||
|
# try: os.unlink(self.shift_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.shift_pipe
|
||||||
|
# if self.squelch_pipe:
|
||||||
|
# try: os.unlink(self.squelch_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.squelch_pipe
|
||||||
|
# if self.smeter_pipe:
|
||||||
|
# try: os.unlink(self.smeter_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.smeter_pipe
|
||||||
|
# if self.iqtee_pipe:
|
||||||
|
# try: os.unlink(self.iqtee_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.iqtee_pipe
|
||||||
|
# if self.iqtee2_pipe:
|
||||||
|
# try: os.unlink(self.iqtee2_pipe)
|
||||||
|
# except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.iqtee2_pipe
|
||||||
|
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def restart(self):
|
||||||
|
self.stop()
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.stop()
|
||||||
|
del(self.process)
|
BIN
htdocs/gfx/openwebrx-3d-spectrum.png
Normal file
BIN
htdocs/gfx/openwebrx-3d-spectrum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
@ -33,9 +33,18 @@
|
|||||||
var waterfall_colors=%[WATERFALL_COLORS];
|
var waterfall_colors=%[WATERFALL_COLORS];
|
||||||
var waterfall_min_level_default=%[WATERFALL_MIN_LEVEL];
|
var waterfall_min_level_default=%[WATERFALL_MIN_LEVEL];
|
||||||
var waterfall_max_level_default=%[WATERFALL_MAX_LEVEL];
|
var waterfall_max_level_default=%[WATERFALL_MAX_LEVEL];
|
||||||
|
var waterfall_auto_level_margin=%[WATERFALL_AUTO_LEVEL_MARGIN];
|
||||||
|
var server_enable_digimodes=%[DIGIMODES_ENABLE];
|
||||||
|
var mathbox_waterfall_frequency_resolution=%[MATHBOX_WATERFALL_FRES];
|
||||||
|
var mathbox_waterfall_history_length=%[MATHBOX_WATERFALL_THIST];
|
||||||
|
var mathbox_waterfall_colors=%[MATHBOX_WATERFALL_COLORS];
|
||||||
</script>
|
</script>
|
||||||
<script src="sdr.js"></script>
|
<script src="sdr.js"></script>
|
||||||
|
<script src="mathbox-bundle.min.js"></script>
|
||||||
<script src="openwebrx.js"></script>
|
<script src="openwebrx.js"></script>
|
||||||
|
<script src="jquery-3.2.1.min.js"></script>
|
||||||
|
<script src="jquery.nanoscroller.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="nanoscroller.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="openwebrx.css" />
|
<link rel="stylesheet" type="text/css" href="openwebrx.css" />
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
</head>
|
</head>
|
||||||
@ -49,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="webrx-top-bar-background" class="webrx-top-bar-parts"></div>
|
<div id="webrx-top-bar-background" class="webrx-top-bar-parts"></div>
|
||||||
<div id="webrx-top-bar" class="webrx-top-bar-parts">
|
<div id="webrx-top-bar" class="webrx-top-bar-parts">
|
||||||
<a href="http://openwebrx.org/" target="_blank"><img src="gfx/openwebrx-top-logo.png" id="webrx-top-logo" /></a>
|
<a href="https://sdr.hu/openwebrx" target="_blank"><img src="gfx/openwebrx-top-logo.png" id="webrx-top-logo" /></a>
|
||||||
<a href="http://ha5kfu.sch.bme.hu/" target="_blank"><img src="gfx/openwebrx-ha5kfu-top-logo.png" id="webrx-ha5kfu-top-logo" /></a>
|
<a href="http://ha5kfu.sch.bme.hu/" target="_blank"><img src="gfx/openwebrx-ha5kfu-top-logo.png" id="webrx-ha5kfu-top-logo" /></a>
|
||||||
<img id="webrx-rx-avatar-background" src="gfx/openwebrx-avatar-background.png" onclick="toggle_rx_photo();"/>
|
<img id="webrx-rx-avatar-background" src="gfx/openwebrx-avatar-background.png" onclick="toggle_rx_photo();"/>
|
||||||
<img id="webrx-rx-avatar" src="gfx/openwebrx-avatar.png" onclick="toggle_rx_photo();"/>
|
<img id="webrx-rx-avatar" src="gfx/openwebrx-avatar.png" onclick="toggle_rx_photo();"/>
|
||||||
@ -72,26 +81,41 @@
|
|||||||
<div id="openwebrx-scale-container">
|
<div id="openwebrx-scale-container">
|
||||||
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
|
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="openwebrx-mathbox-container"> </div>
|
||||||
<div id="webrx-canvas-container">
|
<div id="webrx-canvas-container">
|
||||||
|
|
||||||
<div id="openwebrx-phantom-canvas"></div>
|
<div id="openwebrx-phantom-canvas"></div>
|
||||||
<!-- add canvas here by javascript -->
|
<!-- add canvas here by javascript -->
|
||||||
</div>
|
</div>
|
||||||
<div id="openwebrx-panels-container">
|
<div id="openwebrx-panels-container">
|
||||||
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="259,260">
|
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="259,115">
|
||||||
<div id="webrx-actual-freq">---.--- MHz</div>
|
<div id="webrx-actual-freq">---.--- MHz</div>
|
||||||
<div id="webrx-mouse-freq">---.--- MHz</div>
|
<div id="webrx-mouse-freq">---.--- MHz</div>
|
||||||
<!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>-->
|
|
||||||
<div class="openwebrx-panel-line">
|
<div class="openwebrx-panel-line">
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('nfm');">FM</div>
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-nfm"
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('am');">AM</div>
|
onclick="demodulator_analog_replace('nfm');">FM</div>
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('lsb');">LSB</div>
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-am"
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('usb');">USB</div>
|
onclick="demodulator_analog_replace('am');">AM</div>
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('cw');">CW</div>
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-lsb"
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('dmr');">DMR</div>
|
onclick="demodulator_analog_replace('lsb');">LSB</div>
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('dstar');">DStar</div>
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-usb"
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('nxdn');">NXDN</div>
|
onclick="demodulator_analog_replace('usb');">USB</div>
|
||||||
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('ysf');">YSF</div>
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-cw"
|
||||||
|
onclick="demodulator_analog_replace('cw');">CW</div>
|
||||||
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dmr"
|
||||||
|
onclick="demodulator_analog_replace('dmr');">DMR</div>
|
||||||
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dstar"
|
||||||
|
onclick="demodulator_analog_replace('dstar');">DStar</div>
|
||||||
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-nxdn"
|
||||||
|
onclick="demodulator_analog_replace('nxdn');">NXDN</div>
|
||||||
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-ysf"
|
||||||
|
onclick="demodulator_analog_replace('ysf');">YSF</div>
|
||||||
|
</div>
|
||||||
|
<div class="openwebrx-panel-line">
|
||||||
|
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dig" onclick="demodulator_digital_replace_last();">DIG</div>
|
||||||
|
<select id="openwebrx-secondary-demod-listbox" onchange="secondary_demod_listbox_changed();">
|
||||||
|
<option value="none"></option>
|
||||||
|
<option value="bpsk31">BPSK31</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-panel-line">
|
<div class="openwebrx-panel-line">
|
||||||
<div title="Mute on/off" id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute();"><img src="gfx/openwebrx-speaker.png" class="openwebrx-sliderbtn-img" id="openwebrx-mute-img"></div>
|
<div title="Mute on/off" id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute();"><img src="gfx/openwebrx-speaker.png" class="openwebrx-sliderbtn-img" id="openwebrx-mute-img"></div>
|
||||||
@ -110,6 +134,7 @@
|
|||||||
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutOneStep();" title="Zoom out one step"> <img src="gfx/openwebrx-zoom-out.png" /></div>
|
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutOneStep();" title="Zoom out one step"> <img src="gfx/openwebrx-zoom-out.png" /></div>
|
||||||
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInTotal();" title="Zoom in totally"><img src="gfx/openwebrx-zoom-in-total.png" /></div>
|
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInTotal();" title="Zoom in totally"><img src="gfx/openwebrx-zoom-in-total.png" /></div>
|
||||||
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutTotal();" title="Zoom out totally"><img src="gfx/openwebrx-zoom-out-total.png" /></div>
|
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutTotal();" title="Zoom out totally"><img src="gfx/openwebrx-zoom-out-total.png" /></div>
|
||||||
|
<div class="openwebrx-button openwebrx-square-button" onclick="mathbox_toggle();" title="Toggle 3D view"><img src="gfx/openwebrx-3d-spectrum.png" /></div>
|
||||||
<div id="openwebrx-smeter-db">0 dB</div>
|
<div id="openwebrx-smeter-db">0 dB</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-panel-line">
|
<div class="openwebrx-panel-line">
|
||||||
@ -118,13 +143,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" data-panel-pos="left" data-panel-order="2" data-panel-size="619,142">
|
<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" data-panel-pos="left" data-panel-order="1" data-panel-size="619,137">
|
||||||
<div class="openwebrx-panel-inner" id="openwebrx-log-scroll">
|
<div class="openwebrx-panel-inner nano" id="openwebrx-log-scroll">
|
||||||
|
<div class="nano-content">
|
||||||
<div id="openwebrx-client-log-title">OpenWebRX client log</strong><span id="openwebrx-problems"></span></div>
|
<div id="openwebrx-client-log-title">OpenWebRX client log</strong><span id="openwebrx-problems"></span></div>
|
||||||
<span id="openwebrx-client-1">Author: </span><a href="http://blog.sdr.hu/about" target="_blank">András Retzler, HA7ILM</a><br />You can support OpenWebRX development via <a href="http://blog.sdr.hu/support" target="_blank">PayPal!</a><br/>
|
<span id="openwebrx-client-1">Author: </span><a href="http://blog.sdr.hu/about" target="_blank">András Retzler, HA7ILM</a><br />You can support OpenWebRX development via <a href="http://blog.sdr.hu/support" target="_blank">PayPal!</a><br/>
|
||||||
<div id="openwebrx-debugdiv"></div>
|
<div id="openwebrx-debugdiv"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="openwebrx-panel" id="openwebrx-panel-status" data-panel-name="status" data-panel-pos="left" data-panel-order="0" data-panel-size="615,50" data-panel-transparent="true">
|
<div class="openwebrx-panel" id="openwebrx-panel-status" data-panel-name="status" data-panel-pos="left" data-panel-order="0" data-panel-size="615,50" data-panel-transparent="true">
|
||||||
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-buffer"> <span class="openwebrx-progressbar-text">Audio buffer [0 ms]</span><div class="openwebrx-progressbar-bar"></div></div>
|
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-buffer"> <span class="openwebrx-progressbar-text">Audio buffer [0 ms]</span><div class="openwebrx-progressbar-bar"></div></div>
|
||||||
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-output"> <span class="openwebrx-progressbar-text">Audio output [0 sps]</span><div class="openwebrx-progressbar-bar"></div></div>
|
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-output"> <span class="openwebrx-progressbar-text">Audio output [0 sps]</span><div class="openwebrx-progressbar-bar"></div></div>
|
||||||
@ -133,12 +160,23 @@
|
|||||||
<div class="openwebrx-progressbar" id="openwebrx-bar-server-cpu"> <span class="openwebrx-progressbar-text">Server CPU [0%]</span><div class="openwebrx-progressbar-bar"></div></div>
|
<div class="openwebrx-progressbar" id="openwebrx-bar-server-cpu"> <span class="openwebrx-progressbar-text">Server CPU [0%]</span><div class="openwebrx-progressbar-bar"></div></div>
|
||||||
<div class="openwebrx-progressbar" id="openwebrx-bar-clients"> <span class="openwebrx-progressbar-text">Clients [1]</span><div class="openwebrx-progressbar-bar"></div></div>
|
<div class="openwebrx-progressbar" id="openwebrx-bar-clients"> <span class="openwebrx-progressbar-text">Clients [1]</span><div class="openwebrx-progressbar-bar"></div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="openwebrx-panel" data-panel-name="metadata" data-panel-pos="left" data-panel-order="1" data-panel-size="615,36">
|
|
||||||
</div>
|
|
||||||
<div class="openwebrx-panel" data-panel-name="client-under-devel" data-panel-pos="none" data-panel-order="0" data-panel-size="245,55" style="background-color: Red;">
|
<div class="openwebrx-panel" data-panel-name="client-under-devel" data-panel-pos="none" data-panel-order="0" data-panel-size="245,55" style="background-color: Red;">
|
||||||
<span style="font-size: 15pt; font-weight: bold;">Under construction</span>
|
<span style="font-size: 15pt; font-weight: bold;">Under construction</span>
|
||||||
<br />We're working on the code right now, so the application might fail.
|
<br />We're working on the code right now, so the application might fail.
|
||||||
</div>
|
</div>
|
||||||
|
<div class="openwebrx-panel" id="openwebrx-panel-digimodes" data-panel-name="digimodes" data-panel-pos="left" data-panel-order="2" data-panel-size="619,210">
|
||||||
|
<div id="openwebrx-digimode-canvas-container">
|
||||||
|
<div id="openwebrx-digimode-select-channel"></div>
|
||||||
|
</div>
|
||||||
|
<div id="openwebrx-digimode-content-container">
|
||||||
|
<div class="gradient"></div>
|
||||||
|
<div id="openwebrx-digimode-content">
|
||||||
|
<span id="openwebrx-cursor-blink"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="openwebrx-panel" data-panel-name="metadata" data-panel-pos="left" data-panel-order="1" data-panel-size="615,36">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
4
htdocs/jquery-3.2.1.min.js
vendored
Normal file
4
htdocs/jquery-3.2.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1000
htdocs/jquery.nanoscroller.js
Normal file
1000
htdocs/jquery.nanoscroller.js
Normal file
File diff suppressed because it is too large
Load Diff
33
htdocs/mathbox-bundle.min.js
vendored
Normal file
33
htdocs/mathbox-bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
461
htdocs/mathbox.css
Normal file
461
htdocs/mathbox.css
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
.shadergraph-graph {
|
||||||
|
font: 12px sans-serif;
|
||||||
|
line-height: 25px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.shadergraph-graph:after {
|
||||||
|
content: ' ';
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.shadergraph-graph svg {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.shadergraph-clear {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.shadergraph-graph svg {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.shadergraph-column {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.shadergraph-node .shadergraph-graph {
|
||||||
|
float: left;
|
||||||
|
clear: both;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
.shadergraph-node .shadergraph-graph .shadergraph-node {
|
||||||
|
margin: 5px 15px 15px;
|
||||||
|
}
|
||||||
|
.shadergraph-node {
|
||||||
|
margin: 5px 15px 25px;
|
||||||
|
background: rgba(0, 0, 0, .1);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, .2),
|
||||||
|
0 1px 10px rgba(0, 0, 0, .2);
|
||||||
|
min-height: 35px;
|
||||||
|
float: left;
|
||||||
|
clear: left;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.shadergraph-type {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.shadergraph-header {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
height: 25px;
|
||||||
|
background: rgba(0, 0, 0, .3);
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
|
||||||
|
color: #fff;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.shadergraph-outlet div {
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-in .shadergraph-name {
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-out .shadergraph-name {
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-name {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
.shadergraph-point {
|
||||||
|
margin: 6px;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
border-radius: 7.5px;
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-in {
|
||||||
|
float: left;
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-in div {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-out {
|
||||||
|
float: right;
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
.shadergraph-outlet-out div {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-node-callback {
|
||||||
|
background: rgba(205, 209, 221, .5);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 10, 40, .2),
|
||||||
|
0 1px 10px rgba(0, 10, 40, .2);
|
||||||
|
}
|
||||||
|
.shadergraph-node-callback > .shadergraph-header {
|
||||||
|
background: rgba(0, 20, 80, .3);
|
||||||
|
}
|
||||||
|
.shadergraph-graph .shadergraph-graph .shadergraph-node-callback {
|
||||||
|
background: rgba(0, 20, 80, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-node-call {
|
||||||
|
background: rgba(209, 221, 205, .5);
|
||||||
|
box-shadow: 0 1px 2px rgba(10, 40, 0, .2),
|
||||||
|
0 1px 10px rgba(10, 40, 0, .2);
|
||||||
|
}
|
||||||
|
.shadergraph-node-call > .shadergraph-header {
|
||||||
|
background: rgba(20, 80, 0, .3);
|
||||||
|
}
|
||||||
|
.shadergraph-graph .shadergraph-graph .shadergraph-node-call {
|
||||||
|
background: rgba(20, 80, 0, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-node-isolate {
|
||||||
|
background: rgba(221, 205, 209, .5);
|
||||||
|
box-shadow: 0 1px 2px rgba(40, 0, 10, .2),
|
||||||
|
0 1px 10px rgba(40, 0, 10, .2);
|
||||||
|
}
|
||||||
|
.shadergraph-node-isolate > .shadergraph-header {
|
||||||
|
background: rgba(80, 0, 20, .3);
|
||||||
|
}
|
||||||
|
.shadergraph-graph .shadergraph-graph .shadergraph-node-isolate {
|
||||||
|
background: rgba(80, 0, 20, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-node.shadergraph-has-code {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.shadergraph-node.shadergraph-has-code::before {
|
||||||
|
position: absolute;
|
||||||
|
content: ' ';
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: none;
|
||||||
|
border: 2px solid rgba(0, 0, 0, .25);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.shadergraph-node.shadergraph-has-code:hover::before {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.shadergraph-code {
|
||||||
|
z-index: 10000;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
white-space: pre;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, .2),
|
||||||
|
0 1px 10px rgba(0, 0, 0, .2);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadergraph-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #CCC;
|
||||||
|
}
|
||||||
|
.shadergraph-overlay .shadergraph-view {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.shadergraph-overlay .shadergraph-inside {
|
||||||
|
width: 4000px;
|
||||||
|
min-height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.shadergraph-overlay .shadergraph-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(255,255,255,.3);
|
||||||
|
color: rgba(0, 0, 0, .3);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.shadergraph-overlay .shadergraph-close:hover {
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
color: rgba(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
.shadergraph-overlay .shadergraph-graph {
|
||||||
|
padding-top: 10px;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
.shadergraph-overlay span {
|
||||||
|
display: block;
|
||||||
|
padding: 5px 15px;
|
||||||
|
margin: 0;
|
||||||
|
background: rgba(0, 0, 0, .1);
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
.mathbox-loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-loader.mathbox-exit {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition:
|
||||||
|
opacity .15s ease-in-out;
|
||||||
|
transition:
|
||||||
|
opacity .15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-progress {
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 80px;
|
||||||
|
margin: 0 auto 20px;
|
||||||
|
box-shadow:
|
||||||
|
1px 1px 1px rgba(255, 255, 255, .2),
|
||||||
|
1px -1px 1px rgba(255, 255, 255, .2),
|
||||||
|
-1px 1px 1px rgba(255, 255, 255, .2),
|
||||||
|
-1px -1px 1px rgba(255, 255, 255, .2);
|
||||||
|
background: #ccc;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-progress > div {
|
||||||
|
display: block;
|
||||||
|
width: 0px;
|
||||||
|
height: 10px;
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo {
|
||||||
|
position: relative;
|
||||||
|
width: 140px;
|
||||||
|
height: 100px;
|
||||||
|
margin: 0 auto 10px;
|
||||||
|
-webkit-perspective: 200px;
|
||||||
|
perspective: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
-webkit-transform-style: preserve-3d;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > :nth-child(1) {
|
||||||
|
-webkit-transform: rotateZ(22deg) rotateX(24deg) rotateY(30deg);
|
||||||
|
transform: rotateZ(22deg) rotateX(24deg) rotateY(30deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > :nth-child(2) {
|
||||||
|
-webkit-transform: rotateZ(11deg) rotateX(12deg) rotateY(15deg) scale3d(.6, .6, .6);
|
||||||
|
transform: rotateZ(11deg) rotateX(12deg) rotateY(15deg) scale3d(.6, .6, .6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > div > div {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -100px;
|
||||||
|
margin-top: -100px;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > div > :nth-child(1) {
|
||||||
|
-webkit-transform: scale(0.5, 0.5);
|
||||||
|
transform: rotateX(30deg) scale(0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > div > :nth-child(2) {
|
||||||
|
-webkit-transform: rotateX(90deg) scale(0.42, 0.42);
|
||||||
|
transform: rotateX(90deg) scale(0.42, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > div > :nth-child(3) {
|
||||||
|
-webkit-transform: rotateY(90deg) scale(0.35, 0.35);
|
||||||
|
transform: rotateY(90deg) scale(0.35, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-logo > :nth-child(1) > :nth-child(1) {
|
||||||
|
border: 16px solid #808080;
|
||||||
|
}
|
||||||
|
.mathbox-logo > :nth-child(1) > :nth-child(2) {
|
||||||
|
border: 19px solid #A0A0A0;
|
||||||
|
}
|
||||||
|
.mathbox-logo > :nth-child(1) > :nth-child(3) {
|
||||||
|
border: 23px solid #C0C0C0;
|
||||||
|
}
|
||||||
|
.mathbox-logo > :nth-child(2) > :nth-child(1) {
|
||||||
|
border: 27px solid #808080;
|
||||||
|
}
|
||||||
|
.mathbox-logo > :nth-child(2) > :nth-child(2) {
|
||||||
|
border: 32px solid #A0A0A0;
|
||||||
|
}
|
||||||
|
.mathbox-logo > :nth-child(2) > :nth-child(3) {
|
||||||
|
border: 38px solid #C0C0C0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mathbox-splash-blue .mathbox-progress {
|
||||||
|
background: #def;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-progress > div {
|
||||||
|
background: #1979e7;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(1) {
|
||||||
|
border-color: #1979e7;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(2) {
|
||||||
|
border-color: #33b0ff;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(1) > :nth-child(3) {
|
||||||
|
border-color: #75eaff;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(1) {
|
||||||
|
border-color: #18487F;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(2) {
|
||||||
|
border-color: #33b0ff;
|
||||||
|
}
|
||||||
|
.mathbox-splash-blue .mathbox-logo > :nth-child(2) > :nth-child(3) {
|
||||||
|
border-color: #75eaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.mathbox-overlays {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.mathbox-overlays > div {
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
.mathbox-overlay > div {
|
||||||
|
position: absolute;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
.mathbox-label {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
.mathbox-outline-1 {
|
||||||
|
text-shadow:
|
||||||
|
-1px -1px 0px rgb(255, 255, 255),
|
||||||
|
1px 1px 0px rgb(255, 255, 255),
|
||||||
|
-1px 1px 0px rgb(255, 255, 255),
|
||||||
|
1px -1px 0px rgb(255, 255, 255),
|
||||||
|
1px 0px 1px rgb(255, 255, 255),
|
||||||
|
-1px 0px 1px rgb(255, 255, 255),
|
||||||
|
0px -1px 1px rgb(255, 255, 255),
|
||||||
|
0px 1px 1px rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
.mathbox-outline-2 {
|
||||||
|
text-shadow:
|
||||||
|
0px -2px 0px rgb(255, 255, 255),
|
||||||
|
0px 2px 0px rgb(255, 255, 255),
|
||||||
|
-2px 0px 0px rgb(255, 255, 255),
|
||||||
|
2px 0px 0px rgb(255, 255, 255),
|
||||||
|
-1px -2px 0px rgb(255, 255, 255),
|
||||||
|
-2px -1px 0px rgb(255, 255, 255),
|
||||||
|
-1px 2px 0px rgb(255, 255, 255),
|
||||||
|
-2px 1px 0px rgb(255, 255, 255),
|
||||||
|
1px 2px 0px rgb(255, 255, 255),
|
||||||
|
2px 1px 0px rgb(255, 255, 255),
|
||||||
|
1px -2px 0px rgb(255, 255, 255),
|
||||||
|
2px -1px 0px rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
.mathbox-outline-3 {
|
||||||
|
text-shadow:
|
||||||
|
3px 0px 0px rgb(255, 255, 255),
|
||||||
|
-3px 0px 0px rgb(255, 255, 255),
|
||||||
|
0px 3px 0px rgb(255, 255, 255),
|
||||||
|
0px -3px 0px rgb(255, 255, 255),
|
||||||
|
|
||||||
|
-2px -2px 0px rgb(255, 255, 255),
|
||||||
|
-2px 2px 0px rgb(255, 255, 255),
|
||||||
|
2px 2px 0px rgb(255, 255, 255),
|
||||||
|
2px -2px 0px rgb(255, 255, 255),
|
||||||
|
|
||||||
|
-1px -2px 1px rgb(255, 255, 255),
|
||||||
|
-2px -1px 1px rgb(255, 255, 255),
|
||||||
|
-1px 2px 1px rgb(255, 255, 255),
|
||||||
|
-2px 1px 1px rgb(255, 255, 255),
|
||||||
|
1px 2px 1px rgb(255, 255, 255),
|
||||||
|
2px 1px 1px rgb(255, 255, 255),
|
||||||
|
1px -2px 1px rgb(255, 255, 255),
|
||||||
|
2px -1px 1px rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
.mathbox-outline-4 {
|
||||||
|
text-shadow:
|
||||||
|
4px 0px 0px rgb(255, 255, 255),
|
||||||
|
-4px 0px 0px rgb(255, 255, 255),
|
||||||
|
0px 4px 0px rgb(255, 255, 255),
|
||||||
|
0px -4px 0px rgb(255, 255, 255),
|
||||||
|
|
||||||
|
-3px -2px 0px rgb(255, 255, 255),
|
||||||
|
-3px 2px 0px rgb(255, 255, 255),
|
||||||
|
3px 2px 0px rgb(255, 255, 255),
|
||||||
|
3px -2px 0px rgb(255, 255, 255),
|
||||||
|
|
||||||
|
-2px -3px 0px rgb(255, 255, 255),
|
||||||
|
-2px 3px 0px rgb(255, 255, 255),
|
||||||
|
2px 3px 0px rgb(255, 255, 255),
|
||||||
|
2px -3px 0px rgb(255, 255, 255),
|
||||||
|
|
||||||
|
-1px -2px 1px rgb(255, 255, 255),
|
||||||
|
-2px -1px 1px rgb(255, 255, 255),
|
||||||
|
-1px 2px 1px rgb(255, 255, 255),
|
||||||
|
-2px 1px 1px rgb(255, 255, 255),
|
||||||
|
1px 2px 1px rgb(255, 255, 255),
|
||||||
|
2px 1px 1px rgb(255, 255, 255),
|
||||||
|
1px -2px 1px rgb(255, 255, 255),
|
||||||
|
2px -1px 1px rgb(255, 255, 255);
|
||||||
|
|
||||||
|
}
|
||||||
|
.mathbox-outline-fill, .mathbox-outline-fill * {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
55
htdocs/nanoscroller.css
Normal file
55
htdocs/nanoscroller.css
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/** initial setup **/
|
||||||
|
.nano {
|
||||||
|
position : relative;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
overflow : hidden;
|
||||||
|
}
|
||||||
|
.nano > .nano-content {
|
||||||
|
position : absolute;
|
||||||
|
overflow : scroll;
|
||||||
|
overflow-x : hidden;
|
||||||
|
top : 0;
|
||||||
|
right : 0;
|
||||||
|
bottom : 0;
|
||||||
|
left : 0;
|
||||||
|
}
|
||||||
|
.nano > .nano-content:focus {
|
||||||
|
outline: thin dotted;
|
||||||
|
}
|
||||||
|
.nano > .nano-content::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.has-scrollbar > .nano-content::-webkit-scrollbar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.nano > .nano-pane {
|
||||||
|
background : rgba(0,0,0,.25);
|
||||||
|
position : absolute;
|
||||||
|
width : 8px;
|
||||||
|
right : 0;
|
||||||
|
top : 0;
|
||||||
|
bottom : 0;
|
||||||
|
visibility : hidden\9; /* Target only IE7 and IE8 with this hack */
|
||||||
|
opacity : .01;
|
||||||
|
-webkit-transition : .2s;
|
||||||
|
-moz-transition : .2s;
|
||||||
|
-o-transition : .2s;
|
||||||
|
transition : .2s;
|
||||||
|
-moz-border-radius : 3px;
|
||||||
|
-webkit-border-radius : 3px;
|
||||||
|
border-radius : 3px;
|
||||||
|
}
|
||||||
|
.nano > .nano-pane > .nano-slider {
|
||||||
|
background: #444;
|
||||||
|
background: rgba(0,0,0,.5);
|
||||||
|
position : relative;
|
||||||
|
margin : 0 0px;
|
||||||
|
-moz-border-radius : 4px;
|
||||||
|
-webkit-border-radius : 4px;
|
||||||
|
border-radius : 4px;
|
||||||
|
}
|
||||||
|
.nano:hover > .nano-pane, .nano-pane.active, .nano-pane.flashed {
|
||||||
|
visibility : visible\9; /* Target only IE7 and IE8 with this hack */
|
||||||
|
opacity : 0.99;
|
||||||
|
}
|
@ -28,6 +28,11 @@ html, body
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select
|
||||||
|
{
|
||||||
|
font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
input
|
input
|
||||||
{
|
{
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
@ -394,6 +399,13 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
border-style: none;
|
border-style: none;
|
||||||
image-rendering: crisp-edges;
|
image-rendering: crisp-edges;
|
||||||
image-rendering: -webkit-optimize-contrast;
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
/*transition: left 200ms, width 200ms;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-mathbox-container
|
||||||
|
{
|
||||||
|
overflow: none;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#openwebrx-phantom-canvas
|
#openwebrx-phantom-canvas
|
||||||
@ -410,11 +422,15 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
height: 396px;
|
height: 396px;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/*#webrx-debugdiv
|
#openwebrx-log-scroll
|
||||||
{
|
{
|
||||||
font-size: 10pt;
|
/*overflow-y:auto;*/
|
||||||
/*overflow-y:scroll;*/
|
height: 125px;
|
||||||
/*}*/
|
width: 619px
|
||||||
|
}
|
||||||
|
|
||||||
|
.nano .nano-pane { background: #444; }
|
||||||
|
.nano .nano-slider { background: #eee !important; }
|
||||||
|
|
||||||
#webrx-main-container
|
#webrx-main-container
|
||||||
{
|
{
|
||||||
@ -499,7 +515,7 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
|
|
||||||
.openwebrx-panel
|
.openwebrx-panel
|
||||||
{
|
{
|
||||||
transform: perspective( 600px );
|
transform: perspective( 600px ) rotateX( 90deg );
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
background-color: #575757;
|
background-color: #575757;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -526,7 +542,7 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
.openwebrx-button
|
.openwebrx-button
|
||||||
{
|
{
|
||||||
background-color: #373737;
|
background-color: #373737;
|
||||||
padding: 5px;
|
padding: 4.2px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
-moz-border-radius: 5px;
|
-moz-border-radius: 5px;
|
||||||
color: White;
|
color: White;
|
||||||
@ -544,7 +560,7 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openwebrx-button:hover
|
.openwebrx-button:hover, .openwebrx-demodulator-button.highlighted
|
||||||
{
|
{
|
||||||
/*background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #3F3F3F), color-stop(1, #777777) );
|
/*background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #3F3F3F), color-stop(1, #777777) );
|
||||||
background:-moz-linear-gradient( center top, #373737 5%, #4F4F4F 100% );*/
|
background:-moz-linear-gradient( center top, #373737 5%, #4F4F4F 100% );*/
|
||||||
@ -560,7 +576,7 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
|
|
||||||
.openwebrx-demodulator-button
|
.openwebrx-demodulator-button
|
||||||
{
|
{
|
||||||
width: 50px;
|
width: 38px;
|
||||||
height: 19px;
|
height: 19px;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -568,7 +584,7 @@ input[type=range]:focus::-ms-fill-upper
|
|||||||
|
|
||||||
.openwebrx-square-button img
|
.openwebrx-square-button img
|
||||||
{
|
{
|
||||||
height: 30px;
|
height: 27px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openwebrx-round-button
|
.openwebrx-round-button
|
||||||
@ -732,7 +748,7 @@ img.openwebrx-mirror-img
|
|||||||
{
|
{
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
width:91px;
|
width: 95px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openwebrx-sliderbtn-img
|
.openwebrx-sliderbtn-img
|
||||||
@ -776,7 +792,7 @@ img.openwebrx-mirror-img
|
|||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-top: 29px;
|
margin-top: 24px;
|
||||||
font-family: 'expletus-sans-medium';
|
font-family: 'expletus-sans-medium';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,3 +822,152 @@ img.openwebrx-mirror-img
|
|||||||
{
|
{
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-canvas-container
|
||||||
|
{
|
||||||
|
/*margin: -10px -10px 10px -10px;*/
|
||||||
|
margin: -10px -10px 0px -10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
height: 150px;
|
||||||
|
background-color: #333;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-canvas-container canvas
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: width 500ms, left 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-secondary-demod-listbox
|
||||||
|
{
|
||||||
|
width: 201px;
|
||||||
|
height: 27px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #373737;
|
||||||
|
color: White;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 13pt;
|
||||||
|
margin-right: 1px;
|
||||||
|
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #373737), color-stop(1, #4F4F4F) );
|
||||||
|
background:-moz-linear-gradient( center top, #373737 0%, #4F4F4F 100% );
|
||||||
|
border-color: transparent;
|
||||||
|
border-width: 0px;
|
||||||
|
-moz-appearance: none;
|
||||||
|
padding-left:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-secondary-demod-listbox option
|
||||||
|
{
|
||||||
|
border-width: 0px;
|
||||||
|
background-color: #373737;
|
||||||
|
color: White;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-cursor-blink
|
||||||
|
{
|
||||||
|
animation: cursor-blink 1s infinite;
|
||||||
|
/*animation: cursor-3d 2s infinite;*/
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-direction: alternate;
|
||||||
|
height: 1em;
|
||||||
|
width: 8px;
|
||||||
|
background-color: White;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
/*perspective: 60px;*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cursor-blink
|
||||||
|
{
|
||||||
|
0%{ opacity: 0; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
100%{ opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cursor-3d
|
||||||
|
{
|
||||||
|
0%{ transform: rotateX(0deg) rotateX(Ydeg); }
|
||||||
|
50% { transform: rotateX(180deg) rotateY(360deg); opacity: 0.1; }
|
||||||
|
100%{ transform: rotateX(360deg) rotateY(720deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-content
|
||||||
|
{
|
||||||
|
word-wrap: break-word;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-content-container
|
||||||
|
{
|
||||||
|
overflow-y: hidden;
|
||||||
|
display: block;
|
||||||
|
height: 50px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-content-container .gradient
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
background: linear-gradient(to top, rgba(87,87,87,0) 0%,rgba(87,87,87,1) 100%);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#openwebrx-digimode-content .part
|
||||||
|
{
|
||||||
|
perspective: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-content .part
|
||||||
|
{
|
||||||
|
animation: new-digimode-data-3d 100ms;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
display: inline-block;
|
||||||
|
perspective-origin: 50% 50%;
|
||||||
|
transform-origin: 0% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-content .part .subpart
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes new-digimode-data
|
||||||
|
{
|
||||||
|
0%{ opacity: 0; }
|
||||||
|
100%{ opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes new-digimode-data-3d
|
||||||
|
{
|
||||||
|
0%{ transform: rotateX(0deg) rotateY(-90deg) translateX(-5px) scale(1.3); }
|
||||||
|
100%{ transform: rotateX(0deg) rotateY(0deg) translateX(0) scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#openwebrx-digimode-select-channel
|
||||||
|
{
|
||||||
|
transition: all 500ms;
|
||||||
|
background-color: Yellow;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 0px;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
opacity: 0.7;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0px;
|
||||||
|
border-color: Red;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ var audio_compression="none";
|
|||||||
var waterfall_setup_done=0;
|
var waterfall_setup_done=0;
|
||||||
var waterfall_queue = [];
|
var waterfall_queue = [];
|
||||||
var waterfall_timer;
|
var waterfall_timer;
|
||||||
|
var secondary_fft_size;
|
||||||
|
|
||||||
/*function fade(something,from,to,time_ms,fps)
|
/*function fade(something,from,to,time_ms,fps)
|
||||||
{
|
{
|
||||||
@ -72,7 +73,8 @@ var rx_photo_state=1;
|
|||||||
|
|
||||||
function e(what) { return document.getElementById(what); }
|
function e(what) { return document.getElementById(what); }
|
||||||
|
|
||||||
ios = /iPad|iPod|iPhone/.test(navigator.userAgent);
|
ios = /iPad|iPod|iPhone|Chrome/.test(navigator.userAgent);
|
||||||
|
is_chrome = /Chrome/.test(navigator.userAgent);
|
||||||
//alert("ios="+ios.toString()+" "+navigator.userAgent);
|
//alert("ios="+ios.toString()+" "+navigator.userAgent);
|
||||||
|
|
||||||
function init_rx_photo()
|
function init_rx_photo()
|
||||||
@ -183,8 +185,8 @@ function waterfallColorsDefault()
|
|||||||
|
|
||||||
function waterfallColorsAuto()
|
function waterfallColorsAuto()
|
||||||
{
|
{
|
||||||
e("openwebrx-waterfall-color-min").value=(waterfall_measure_minmax_min-20).toString();
|
e("openwebrx-waterfall-color-min").value=(waterfall_measure_minmax_min-waterfall_auto_level_margin[0]).toString();
|
||||||
e("openwebrx-waterfall-color-max").value=(waterfall_measure_minmax_max+30).toString();
|
e("openwebrx-waterfall-color-max").value=(waterfall_measure_minmax_max+waterfall_auto_level_margin[1]).toString();
|
||||||
updateWaterfallColors(0);
|
updateWaterfallColors(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,6 +561,7 @@ function demodulator_default_analog(offset_frequency,subtype)
|
|||||||
|
|
||||||
this.envelope.drag_end=function(x)
|
this.envelope.drag_end=function(x)
|
||||||
{ //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here.
|
{ //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here.
|
||||||
|
demodulator_buttons_update();
|
||||||
to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset
|
to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset
|
||||||
this.dragged_range=demodulator.draggable_ranges.none;
|
this.dragged_range=demodulator.draggable_ranges.none;
|
||||||
return to_return;
|
return to_return;
|
||||||
@ -575,6 +578,7 @@ function mkenvelopes(visible_range) //called from mkscale
|
|||||||
{
|
{
|
||||||
demodulators[i].envelope.draw(visible_range);
|
demodulators[i].envelope.draw(visible_range);
|
||||||
}
|
}
|
||||||
|
if(demodulators.length) secondary_demod_waterfall_set_zoom(demodulators[0].low_cut, demodulators[0].high_cut);
|
||||||
}
|
}
|
||||||
|
|
||||||
function demodulator_remove(which)
|
function demodulator_remove(which)
|
||||||
@ -589,8 +593,17 @@ function demodulator_add(what)
|
|||||||
mkenvelopes(get_visible_freq_range());
|
mkenvelopes(get_visible_freq_range());
|
||||||
}
|
}
|
||||||
|
|
||||||
function demodulator_analog_replace(subtype)
|
last_analog_demodulator_subtype = 'nfm';
|
||||||
|
last_digital_demodulator_subtype = 'bpsk31';
|
||||||
|
|
||||||
|
function demodulator_analog_replace(subtype, for_digital)
|
||||||
{ //this function should only exist until the multi-demodulator capability is added
|
{ //this function should only exist until the multi-demodulator capability is added
|
||||||
|
if(!(typeof for_digital !== "undefined" && for_digital && secondary_demod))
|
||||||
|
{
|
||||||
|
secondary_demod_close_window();
|
||||||
|
secondary_demod_listbox_update();
|
||||||
|
}
|
||||||
|
last_analog_demodulator_subtype = subtype;
|
||||||
var temp_offset=0;
|
var temp_offset=0;
|
||||||
if(demodulators.length)
|
if(demodulators.length)
|
||||||
{
|
{
|
||||||
@ -598,6 +611,7 @@ function demodulator_analog_replace(subtype)
|
|||||||
demodulator_remove(0);
|
demodulator_remove(0);
|
||||||
}
|
}
|
||||||
demodulator_add(new demodulator_default_analog(temp_offset,subtype));
|
demodulator_add(new demodulator_default_analog(temp_offset,subtype));
|
||||||
|
demodulator_buttons_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function demodulator_set_offset_frequency(which,to_what)
|
function demodulator_set_offset_frequency(which,to_what)
|
||||||
@ -1028,7 +1042,7 @@ function canvas_mousewheel(evt)
|
|||||||
|
|
||||||
|
|
||||||
zoom_max_level_hps=33; //Hz/pixel
|
zoom_max_level_hps=33; //Hz/pixel
|
||||||
zoom_levels_count=5;
|
zoom_levels_count=14;
|
||||||
|
|
||||||
function get_zoom_coeff_from_hps(hps)
|
function get_zoom_coeff_from_hps(hps)
|
||||||
{
|
{
|
||||||
@ -1050,8 +1064,10 @@ function mkzoomlevels()
|
|||||||
zoom_levels=[1];
|
zoom_levels=[1];
|
||||||
maxc=get_zoom_coeff_from_hps(zoom_max_level_hps);
|
maxc=get_zoom_coeff_from_hps(zoom_max_level_hps);
|
||||||
if(maxc<1) return;
|
if(maxc<1) return;
|
||||||
|
// logarithmic interpolation
|
||||||
|
zoom_ratio = Math.pow(maxc, 1/zoom_levels_count);
|
||||||
for(i=1;i<zoom_levels_count;i++)
|
for(i=1;i<zoom_levels_count;i++)
|
||||||
zoom_levels.push(1+(maxc-1)*(i/(zoom_levels_count-1)));
|
zoom_levels.push(Math.pow(zoom_ratio, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoom_step(out, where, onscreen)
|
function zoom_step(out, where, onscreen)
|
||||||
@ -1063,7 +1079,7 @@ function zoom_step(out, where, onscreen)
|
|||||||
zoom_center_rel=canvas_get_freq_offset(where);
|
zoom_center_rel=canvas_get_freq_offset(where);
|
||||||
//console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString());
|
//console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString());
|
||||||
zoom_center_where=onscreen;
|
zoom_center_where=onscreen;
|
||||||
console.log(zoom_center_where, zoom_center_rel, where);
|
//console.log(zoom_center_where, zoom_center_rel, where);
|
||||||
resize_canvases(true);
|
resize_canvases(true);
|
||||||
mkscale();
|
mkscale();
|
||||||
}
|
}
|
||||||
@ -1095,9 +1111,17 @@ function zoom_calc()
|
|||||||
function resize_waterfall_container(check_init)
|
function resize_waterfall_container(check_init)
|
||||||
{
|
{
|
||||||
if(check_init&&!waterfall_setup_done) return;
|
if(check_init&&!waterfall_setup_done) return;
|
||||||
canvas_container.style.height=(window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px";
|
var numHeight;
|
||||||
|
mathbox_container.style.height=canvas_container.style.height=(numHeight=window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px";
|
||||||
|
if(mathbox)
|
||||||
|
{
|
||||||
|
//mathbox.three.camera.aspect = document.body.offsetWidth / numHeight;
|
||||||
|
//mathbox.three.camera.updateProjectionMatrix();
|
||||||
|
mathbox.three.renderer.setSize(document.body.offsetWidth, numHeight);
|
||||||
|
console.log(document.body.offsetWidth, numHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
audio_server_output_rate=11025;
|
audio_server_output_rate=11025;
|
||||||
audio_client_resampling_factor=4;
|
audio_client_resampling_factor=4;
|
||||||
@ -1134,14 +1158,15 @@ function on_ws_recv(evt)
|
|||||||
if(!(evt.data instanceof ArrayBuffer)) { divlog("on_ws_recv(): Not ArrayBuffer received...",1); return; }
|
if(!(evt.data instanceof ArrayBuffer)) { divlog("on_ws_recv(): Not ArrayBuffer received...",1); return; }
|
||||||
//
|
//
|
||||||
debug_ws_data_received+=evt.data.byteLength/1000;
|
debug_ws_data_received+=evt.data.byteLength/1000;
|
||||||
firstChars=getFirstChars(evt.data,3);
|
first4Chars=getFirstChars(evt.data,4);
|
||||||
if(firstChars=="CLI")
|
first3Chars=first4Chars.slice(0,3);
|
||||||
|
if(first3Chars=="CLI")
|
||||||
{
|
{
|
||||||
var stringData=arrayBufferToString(evt.data);
|
var stringData=arrayBufferToString(evt.data);
|
||||||
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData);
|
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Server acknowledged WebSocket connection.");
|
||||||
|
|
||||||
}
|
}
|
||||||
if(firstChars=="AUD")
|
if(first3Chars=="AUD")
|
||||||
{
|
{
|
||||||
var audio_data;
|
var audio_data;
|
||||||
if(audio_compression=="adpcm") audio_data=new Uint8Array(evt.data,4)
|
if(audio_compression=="adpcm") audio_data=new Uint8Array(evt.data,4)
|
||||||
@ -1149,11 +1174,12 @@ function on_ws_recv(evt)
|
|||||||
audio_prepare(audio_data);
|
audio_prepare(audio_data);
|
||||||
audio_buffer_current_size_debug+=audio_data.length;
|
audio_buffer_current_size_debug+=audio_data.length;
|
||||||
audio_buffer_all_size_debug+=audio_data.length;
|
audio_buffer_all_size_debug+=audio_data.length;
|
||||||
if(!ios && (audio_initialized==0 && audio_prepared_buffers.length>audio_buffering_fill_to)) audio_init()
|
if(!(ios||is_chrome) && (audio_initialized==0 && audio_prepared_buffers.length>audio_buffering_fill_to)) audio_init()
|
||||||
}
|
}
|
||||||
else if(firstChars=="FFT")
|
else if(first3Chars=="FFT")
|
||||||
{
|
{
|
||||||
//alert("Yupee! Doing FFT");
|
//alert("Yupee! Doing FFT");
|
||||||
|
//if(first4Chars=="FFTS") console.log("FFTS");
|
||||||
if(fft_compression=="none") waterfall_add_queue(new Float32Array(evt.data,4));
|
if(fft_compression=="none") waterfall_add_queue(new Float32Array(evt.data,4));
|
||||||
else if(fft_compression="adpcm")
|
else if(fft_compression="adpcm")
|
||||||
{
|
{
|
||||||
@ -1162,9 +1188,17 @@ function on_ws_recv(evt)
|
|||||||
var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4));
|
var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4));
|
||||||
var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N);
|
var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N);
|
||||||
for(var i=0;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100;
|
for(var i=0;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100;
|
||||||
waterfall_add_queue(waterfall_f32);
|
if(first4Chars=="FFTS") secondary_demod_waterfall_add_queue(waterfall_f32); //TODO digimodes
|
||||||
|
else waterfall_add_queue(waterfall_f32);
|
||||||
}
|
}
|
||||||
} else if(firstChars=="MSG")
|
}
|
||||||
|
else if(first3Chars=="DAT")
|
||||||
|
{
|
||||||
|
//secondary_demod_push_binary_data(new Uint8Array(evt.data,4));
|
||||||
|
secondary_demod_push_data(arrayBufferToString(evt.data).substring(4));
|
||||||
|
//console.log("DAT");
|
||||||
|
}
|
||||||
|
else if(first3Chars=="MSG")
|
||||||
{
|
{
|
||||||
/*try
|
/*try
|
||||||
{*/
|
{*/
|
||||||
@ -1187,6 +1221,18 @@ function on_ws_recv(evt)
|
|||||||
break;
|
break;
|
||||||
case "fft_size":
|
case "fft_size":
|
||||||
fft_size=parseInt(param[1]);
|
fft_size=parseInt(param[1]);
|
||||||
|
break;
|
||||||
|
case "secondary_fft_size":
|
||||||
|
secondary_fft_size=parseInt(param[1]);
|
||||||
|
break;
|
||||||
|
case "secondary_setup":
|
||||||
|
secondary_demod_init_canvases();
|
||||||
|
break;
|
||||||
|
case "if_samp_rate":
|
||||||
|
if_samp_rate=parseInt(param[1]);
|
||||||
|
break;
|
||||||
|
case "secondary_bw":
|
||||||
|
secondary_bw=parseFloat(param[1]);
|
||||||
break;
|
break;
|
||||||
case "fft_fps":
|
case "fft_fps":
|
||||||
fft_fps=parseInt(param[1]);
|
fft_fps=parseInt(param[1]);
|
||||||
@ -1336,8 +1382,10 @@ function divlog(what, is_error)
|
|||||||
if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present
|
if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present
|
||||||
}
|
}
|
||||||
e("openwebrx-debugdiv").innerHTML+=what+"<br />";
|
e("openwebrx-debugdiv").innerHTML+=what+"<br />";
|
||||||
var wls=e("openwebrx-log-scroll");
|
//var wls=e("openwebrx-log-scroll");
|
||||||
wls.scrollTop=wls.scrollHeight; //scroll to bottom
|
//wls.scrollTop=wls.scrollHeight; //scroll to bottom
|
||||||
|
$(".nano").nanoScroller();
|
||||||
|
$(".nano").nanoScroller({ scroll: 'bottom' });
|
||||||
}
|
}
|
||||||
|
|
||||||
var audio_context;
|
var audio_context;
|
||||||
@ -1585,16 +1633,24 @@ function webrx_set_param(what, value)
|
|||||||
ws.send("SET "+what+"="+value.toString());
|
ws.send("SET "+what+"="+value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var starting_mute = false;
|
||||||
|
|
||||||
function parsehash()
|
function parsehash()
|
||||||
{
|
{
|
||||||
if(h=window.location.hash)
|
if(h=window.location.hash)
|
||||||
{
|
{
|
||||||
h.substring(1).split(",").forEach(function(x){
|
h.substring(1).split(",").forEach(function(x){
|
||||||
harr=x.split("=");
|
harr=x.split("=");
|
||||||
console.log(harr);
|
//console.log(harr);
|
||||||
if(harr[0]=="mod") starting_mod = harr[1];
|
if(harr[0]=="mute") toggleMute();
|
||||||
if(harr[0]=="sql") { e("openwebrx-panel-squelch").value=harr[1]; updateSquelch(); }
|
else if(harr[0]=="mod") starting_mod = harr[1];
|
||||||
if(harr[0]=="freq") {
|
else if(harr[0]=="sql")
|
||||||
|
{
|
||||||
|
e("openwebrx-panel-squelch").value=harr[1];
|
||||||
|
updateSquelch();
|
||||||
|
}
|
||||||
|
else if(harr[0]=="freq")
|
||||||
|
{
|
||||||
console.log(parseInt(harr[1]));
|
console.log(parseInt(harr[1]));
|
||||||
console.log(center_freq);
|
console.log(center_freq);
|
||||||
starting_offset_frequency = parseInt(harr[1])-center_freq;
|
starting_offset_frequency = parseInt(harr[1])-center_freq;
|
||||||
@ -1638,6 +1694,9 @@ function audio_preinit()
|
|||||||
|
|
||||||
function audio_init()
|
function audio_init()
|
||||||
{
|
{
|
||||||
|
if(is_chrome) audio_context.resume()
|
||||||
|
if(starting_mute) toggleMute();
|
||||||
|
|
||||||
if(audio_client_resampling_factor==0) return; //if failed to find a valid resampling factor...
|
if(audio_client_resampling_factor==0) return; //if failed to find a valid resampling factor...
|
||||||
|
|
||||||
audio_debug_time_start=(new Date()).getTime();
|
audio_debug_time_start=(new Date()).getTime();
|
||||||
@ -1682,6 +1741,7 @@ function audio_init()
|
|||||||
//window.setTimeout(function(){toggle_panel("openwebrx-panel-log");e("openwebrx-panel-log").style.opacity="1";},1200)
|
//window.setTimeout(function(){toggle_panel("openwebrx-panel-log");e("openwebrx-panel-log").style.opacity="1";},1200)
|
||||||
}
|
}
|
||||||
},2000);
|
},2000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_ws_closed()
|
function on_ws_closed()
|
||||||
@ -1722,17 +1782,18 @@ function open_websocket()
|
|||||||
ws.onerror = on_ws_error;
|
ws.onerror = on_ws_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
function waterfall_mkcolor(db_value)
|
function waterfall_mkcolor(db_value, waterfall_colors_arg)
|
||||||
{
|
{
|
||||||
|
if(typeof waterfall_colors_arg === 'undefined') waterfall_colors_arg = waterfall_colors;
|
||||||
if(db_value<waterfall_min_level) db_value=waterfall_min_level;
|
if(db_value<waterfall_min_level) db_value=waterfall_min_level;
|
||||||
if(db_value>waterfall_max_level) db_value=waterfall_max_level;
|
if(db_value>waterfall_max_level) db_value=waterfall_max_level;
|
||||||
full_scale=waterfall_max_level-waterfall_min_level;
|
full_scale=waterfall_max_level-waterfall_min_level;
|
||||||
relative_value=db_value-waterfall_min_level;
|
relative_value=db_value-waterfall_min_level;
|
||||||
value_percent=relative_value/full_scale;
|
value_percent=relative_value/full_scale;
|
||||||
percent_for_one_color=1/(waterfall_colors.length-1);
|
percent_for_one_color=1/(waterfall_colors_arg.length-1);
|
||||||
index=Math.floor(value_percent/percent_for_one_color);
|
index=Math.floor(value_percent/percent_for_one_color);
|
||||||
remain=(value_percent-percent_for_one_color*index)/percent_for_one_color;
|
remain=(value_percent-percent_for_one_color*index)/percent_for_one_color;
|
||||||
return color_between(waterfall_colors[index+1],waterfall_colors[index],remain);
|
return color_between(waterfall_colors_arg[index+1],waterfall_colors_arg[index],remain);
|
||||||
}
|
}
|
||||||
|
|
||||||
function color_between(first, second, percent)
|
function color_between(first, second, percent)
|
||||||
@ -1755,7 +1816,7 @@ var canvas_phantom;
|
|||||||
|
|
||||||
function add_canvas()
|
function add_canvas()
|
||||||
{
|
{
|
||||||
new_canvas = document.createElement("canvas");
|
var new_canvas = document.createElement("canvas");
|
||||||
new_canvas.width=fft_size;
|
new_canvas.width=fft_size;
|
||||||
new_canvas.height=canvas_default_height;
|
new_canvas.height=canvas_default_height;
|
||||||
canvas_actual_line=canvas_default_height-1;
|
canvas_actual_line=canvas_default_height-1;
|
||||||
@ -1780,9 +1841,11 @@ function add_canvas()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function init_canvas_container()
|
function init_canvas_container()
|
||||||
{
|
{
|
||||||
canvas_container=e("webrx-canvas-container");
|
canvas_container=e("webrx-canvas-container");
|
||||||
|
mathbox_container=e("openwebrx-mathbox-container");
|
||||||
canvas_container.addEventListener("mouseout",canvas_container_mouseout, false);
|
canvas_container.addEventListener("mouseout",canvas_container_mouseout, false);
|
||||||
//window.addEventListener("mouseout",window_mouseout,false);
|
//window.addEventListener("mouseout",window_mouseout,false);
|
||||||
//document.body.addEventListener("mouseup",body_mouseup,false);
|
//document.body.addEventListener("mouseup",body_mouseup,false);
|
||||||
@ -1839,7 +1902,7 @@ function resize_canvases(zoom)
|
|||||||
function waterfall_init()
|
function waterfall_init()
|
||||||
{
|
{
|
||||||
init_canvas_container();
|
init_canvas_container();
|
||||||
waterfall_timer = window.setInterval(waterfall_dequeue,900/fft_fps);
|
waterfall_timer = window.setInterval(()=>{waterfall_dequeue(); secondary_demod_waterfall_dequeue();},900/fft_fps);
|
||||||
resize_waterfall_container(false); /* then */ resize_canvases();
|
resize_waterfall_container(false); /* then */ resize_canvases();
|
||||||
scale_setup();
|
scale_setup();
|
||||||
mkzoomlevels();
|
mkzoomlevels();
|
||||||
@ -1848,6 +1911,42 @@ function waterfall_init()
|
|||||||
|
|
||||||
var waterfall_dont_scale=0;
|
var waterfall_dont_scale=0;
|
||||||
|
|
||||||
|
var mathbox_shift = function()
|
||||||
|
{
|
||||||
|
if(mathbox_data_current_depth < mathbox_data_max_depth) mathbox_data_current_depth++;
|
||||||
|
if(mathbox_data_index+1>=mathbox_data_max_depth) mathbox_data_index = 0;
|
||||||
|
else mathbox_data_index++;
|
||||||
|
mathbox_data_global_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mathbox_clear_data = function()
|
||||||
|
{
|
||||||
|
mathbox_data_index = 50;
|
||||||
|
mathbox_data_current_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var mathbox_get_data_line = function(x) //x counts from 0 to mathbox_data_current_depth
|
||||||
|
//{
|
||||||
|
// return (mathbox_data_max_depth + mathbox_data_index - mathbox_data_current_depth + x - 1) % mathbox_data_max_depth;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//var mathbox_data_index_valid = function(x) //x counts from 0 to mathbox_data_current_depth
|
||||||
|
//{
|
||||||
|
// return x<mathbox_data_current_depth;
|
||||||
|
//}
|
||||||
|
|
||||||
|
var mathbox_get_data_line = function(x)
|
||||||
|
{
|
||||||
|
return (mathbox_data_max_depth + mathbox_data_index + x - 1) % mathbox_data_max_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mathbox_data_index_valid = function(x)
|
||||||
|
{
|
||||||
|
return x>mathbox_data_max_depth-mathbox_data_current_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function waterfall_add(data)
|
function waterfall_add(data)
|
||||||
{
|
{
|
||||||
if(!waterfall_setup_done) return;
|
if(!waterfall_setup_done) return;
|
||||||
@ -1924,6 +2023,14 @@ function waterfall_add(data)
|
|||||||
waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
|
waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
if(mathbox_mode==MATHBOX_MODES.WATERFALL)
|
||||||
|
{
|
||||||
|
//Handle mathbox
|
||||||
|
for(var i=0;i<fft_size;i++) mathbox_data[i+mathbox_data_index*fft_size]=data[i];
|
||||||
|
mathbox_shift();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
//Add line to waterfall image
|
//Add line to waterfall image
|
||||||
oneline_image = canvas_context.createImageData(w,1);
|
oneline_image = canvas_context.createImageData(w,1);
|
||||||
for(x=0;x<w;x++)
|
for(x=0;x<w;x++)
|
||||||
@ -1933,13 +2040,13 @@ function waterfall_add(data)
|
|||||||
oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
|
oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Draw image
|
//Draw image
|
||||||
canvas_context.putImageData(oneline_image, 0, canvas_actual_line--);
|
canvas_context.putImageData(oneline_image, 0, canvas_actual_line--);
|
||||||
shift_canvases();
|
shift_canvases();
|
||||||
if(canvas_actual_line<0) add_canvas();
|
if(canvas_actual_line<0) add_canvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//divlog("Drawn FFT");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1969,6 +2076,180 @@ function check_top_bar_congestion()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MATHBOX_MODES =
|
||||||
|
{
|
||||||
|
UNINITIALIZED: 0,
|
||||||
|
NONE: 1,
|
||||||
|
WATERFALL: 2,
|
||||||
|
CONSTELLATION: 3
|
||||||
|
};
|
||||||
|
var mathbox_mode = MATHBOX_MODES.UNINITIALIZED;
|
||||||
|
var mathbox;
|
||||||
|
var mathbox_element;
|
||||||
|
|
||||||
|
function mathbox_init()
|
||||||
|
{
|
||||||
|
//mathbox_waterfall_history_length is defined in the config
|
||||||
|
mathbox_data_max_depth = fft_fps * mathbox_waterfall_history_length; //how many lines can the buffer store
|
||||||
|
mathbox_data_current_depth = 0; //how many lines are in the buffer currently
|
||||||
|
mathbox_data_index = 0; //the index of the last empty line / the line to be overwritten
|
||||||
|
mathbox_data = new Float32Array(fft_size * mathbox_data_max_depth);
|
||||||
|
mathbox_data_global_index = 0;
|
||||||
|
mathbox_correction_for_z = 0;
|
||||||
|
|
||||||
|
mathbox = mathBox({
|
||||||
|
plugins: ['core', 'controls', 'cursor', 'stats'],
|
||||||
|
controls: { klass: THREE.OrbitControls },
|
||||||
|
});
|
||||||
|
three = mathbox.three;
|
||||||
|
if(typeof three == "undefined") divlog("3D waterfall cannot be initialized because WebGL is not supported in your browser.", true);
|
||||||
|
|
||||||
|
three.renderer.setClearColor(new THREE.Color(0x808080), 1.0);
|
||||||
|
mathbox_container.appendChild((mathbox_element=three.renderer.domElement));
|
||||||
|
view = mathbox
|
||||||
|
.set({
|
||||||
|
scale: 1080,
|
||||||
|
focus: 3,
|
||||||
|
})
|
||||||
|
.camera({
|
||||||
|
proxy: true,
|
||||||
|
position: [-2, 1, 3],
|
||||||
|
})
|
||||||
|
.cartesian({
|
||||||
|
range: [[-1, 1], [0, 1], [0, 1]],
|
||||||
|
scale: [2, 2/3, 1],
|
||||||
|
});
|
||||||
|
|
||||||
|
view.axis({
|
||||||
|
axis: 1,
|
||||||
|
width: 3,
|
||||||
|
color: "#fff",
|
||||||
|
});
|
||||||
|
view.axis({
|
||||||
|
axis: 2,
|
||||||
|
width: 3,
|
||||||
|
color: "#fff",
|
||||||
|
//offset: [0, 0, 0],
|
||||||
|
});
|
||||||
|
view.axis({
|
||||||
|
axis: 3,
|
||||||
|
width: 3,
|
||||||
|
color: "#fff",
|
||||||
|
});
|
||||||
|
|
||||||
|
view.grid({
|
||||||
|
width: 2,
|
||||||
|
opacity: 0.5,
|
||||||
|
axes: [1, 3],
|
||||||
|
zOrder: 1,
|
||||||
|
color: "#fff",
|
||||||
|
});
|
||||||
|
|
||||||
|
//var remap = function (v) { return Math.sqrt(.5 + .5 * v); };
|
||||||
|
|
||||||
|
|
||||||
|
var remap = function(x,z,t)
|
||||||
|
{
|
||||||
|
var currentTimePos = mathbox_data_global_index/(fft_fps*1.0);
|
||||||
|
var realZAdd = (-(t-currentTimePos)/mathbox_waterfall_history_length);
|
||||||
|
var zAdd = realZAdd - mathbox_correction_for_z;
|
||||||
|
if(zAdd<-0.2 || zAdd>0.2) { mathbox_correction_for_z = realZAdd; }
|
||||||
|
|
||||||
|
var xIndex = Math.trunc(((x+1)/2.0)*fft_size); //x: frequency
|
||||||
|
var zIndex = Math.trunc(z*(mathbox_data_max_depth-1)); //z: time
|
||||||
|
var realZIndex = mathbox_get_data_line(zIndex);
|
||||||
|
if(!mathbox_data_index_valid(zIndex)) return {y: undefined, dBValue: undefined, zAdd: 0 };
|
||||||
|
//if(realZIndex>=(mathbox_data_max_depth-1)) console.log("realZIndexundef", realZIndex, zIndex);
|
||||||
|
var index = Math.trunc(xIndex + realZIndex * fft_size);
|
||||||
|
/*if(mathbox_data[index]==undefined) console.log("Undef", index, mathbox_data.length, zIndex,
|
||||||
|
realZIndex, mathbox_data_max_depth,
|
||||||
|
mathbox_data_current_depth, mathbox_data_index);*/
|
||||||
|
var dBValue = mathbox_data[index];
|
||||||
|
//y=1;
|
||||||
|
if(dBValue>waterfall_max_level) y = 1;
|
||||||
|
else if(dBValue<waterfall_min_level) y = 0;
|
||||||
|
else y = (dBValue-waterfall_min_level)/(waterfall_max_level-waterfall_min_level);
|
||||||
|
mathbox_dbg = { dbv: dBValue, indexval: index, mbd: mathbox_data.length, yval: y };
|
||||||
|
if(!y) y=0;
|
||||||
|
return {y: y, dBValue: dBValue, zAdd: zAdd};
|
||||||
|
}
|
||||||
|
|
||||||
|
var points = view.area({
|
||||||
|
expr: function (emit, x, z, i, j, t) {
|
||||||
|
var y;
|
||||||
|
remapResult=remap(x,z,t);
|
||||||
|
if((y=remapResult.y)==undefined) return;
|
||||||
|
emit(x, y, z+remapResult.zAdd);
|
||||||
|
},
|
||||||
|
width: mathbox_waterfall_frequency_resolution,
|
||||||
|
height: mathbox_data_max_depth - 1,
|
||||||
|
channels: 3,
|
||||||
|
axes: [1, 3],
|
||||||
|
});
|
||||||
|
|
||||||
|
var colors = view.area({
|
||||||
|
expr: function (emit, x, z, i, j, t) {
|
||||||
|
var dBValue;
|
||||||
|
if((dBValue=remap(x,z,t).dBValue)==undefined) return;
|
||||||
|
var color=waterfall_mkcolor(dBValue, mathbox_waterfall_colors);
|
||||||
|
var b = (color&0xff)/255.0;
|
||||||
|
var g = ((color&0xff00)>>8)/255.0;
|
||||||
|
var r = ((color&0xff0000)>>16)/255.0;
|
||||||
|
emit(r, g, b, 1.0);
|
||||||
|
},
|
||||||
|
width: mathbox_waterfall_frequency_resolution,
|
||||||
|
height: mathbox_data_max_depth - 1,
|
||||||
|
channels: 4,
|
||||||
|
axes: [1, 3],
|
||||||
|
});
|
||||||
|
|
||||||
|
view.surface({
|
||||||
|
shaded: true,
|
||||||
|
points: '<<',
|
||||||
|
colors: '<',
|
||||||
|
color: 0xFFFFFF,
|
||||||
|
});
|
||||||
|
|
||||||
|
view.surface({
|
||||||
|
fill: false,
|
||||||
|
lineX: false,
|
||||||
|
lineY: false,
|
||||||
|
points: '<<',
|
||||||
|
colors: '<',
|
||||||
|
color: 0xFFFFFF,
|
||||||
|
width: 2,
|
||||||
|
blending: 'add',
|
||||||
|
opacity: .25,
|
||||||
|
zBias: 5,
|
||||||
|
});
|
||||||
|
mathbox_mode = MATHBOX_MODES.NONE;
|
||||||
|
|
||||||
|
//mathbox_element.style.width="100%";
|
||||||
|
//mathbox_element.style.height="100%";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function mathbox_toggle()
|
||||||
|
{
|
||||||
|
|
||||||
|
if(mathbox_mode == MATHBOX_MODES.UNINITIALIZED) mathbox_init();
|
||||||
|
mathbox_mode = (mathbox_mode == MATHBOX_MODES.NONE) ? MATHBOX_MODES.WATERFALL : MATHBOX_MODES.NONE;
|
||||||
|
mathbox_container.style.display = (mathbox_mode == MATHBOX_MODES.WATERFALL) ? "block" : "none";
|
||||||
|
mathbox_clear_data();
|
||||||
|
waterfall_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function waterfall_clear()
|
||||||
|
{
|
||||||
|
while(canvases.length) //delete all canvases
|
||||||
|
{
|
||||||
|
var x=canvases.shift();
|
||||||
|
x.parentNode.removeChild(x);
|
||||||
|
delete x;
|
||||||
|
}
|
||||||
|
add_canvas();
|
||||||
|
}
|
||||||
|
|
||||||
function openwebrx_resize()
|
function openwebrx_resize()
|
||||||
{
|
{
|
||||||
resize_canvases();
|
resize_canvases();
|
||||||
@ -1979,10 +2260,11 @@ function openwebrx_resize()
|
|||||||
|
|
||||||
function openwebrx_init()
|
function openwebrx_init()
|
||||||
{
|
{
|
||||||
if(ios) e("openwebrx-big-grey").style.display="table-cell";
|
if(ios||is_chrome) e("openwebrx-big-grey").style.display="table-cell";
|
||||||
(opb=e("openwebrx-play-button-text")).style.marginTop=(window.innerHeight/2-opb.clientHeight/2).toString()+"px";
|
(opb=e("openwebrx-play-button-text")).style.marginTop=(window.innerHeight/2-opb.clientHeight/2).toString()+"px";
|
||||||
init_rx_photo();
|
init_rx_photo();
|
||||||
open_websocket();
|
open_websocket();
|
||||||
|
secondary_demod_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);
|
||||||
@ -2076,9 +2358,14 @@ function pop_bottommost_panel(from)
|
|||||||
return to_return;
|
return to_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle_panel(what)
|
function toggle_panel(what, on)
|
||||||
{
|
{
|
||||||
var item=e(what);
|
var item=e(what);
|
||||||
|
if(typeof on !== "undefined")
|
||||||
|
{
|
||||||
|
if(item.openwebrxHidden && !on) return;
|
||||||
|
if(!item.openwebrxHidden && on) return;
|
||||||
|
}
|
||||||
if(item.openwebrxDisableClick) return;
|
if(item.openwebrxDisableClick) return;
|
||||||
item.style.transitionDuration="599ms";
|
item.style.transitionDuration="599ms";
|
||||||
item.style.transitionDelay="0ms";
|
item.style.transitionDelay="0ms";
|
||||||
@ -2167,6 +2454,7 @@ function place_panels(function_apply)
|
|||||||
p.style.visibility="visible";
|
p.style.visibility="visible";
|
||||||
y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin;
|
y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin;
|
||||||
if(function_apply) function_apply(p);
|
if(function_apply) function_apply(p);
|
||||||
|
//console.log(p.id, y, p.openwebrxPanelTransparent);
|
||||||
}
|
}
|
||||||
y=hoffset;
|
y=hoffset;
|
||||||
while(right_col.length>0)
|
while(right_col.length>0)
|
||||||
@ -2199,3 +2487,331 @@ function progressbar_set(obj,val,text,over)
|
|||||||
if(innerText==null) return;
|
if(innerText==null) return;
|
||||||
innerText.innerHTML=text;
|
innerText.innerHTML=text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function demodulator_buttons_update()
|
||||||
|
{
|
||||||
|
$(".openwebrx-demodulator-button").removeClass("highlighted");
|
||||||
|
if(secondary_demod) $("#openwebrx-button-dig").addClass("highlighted");
|
||||||
|
else switch(demodulators[0].subtype)
|
||||||
|
{
|
||||||
|
case "nfm":
|
||||||
|
$("#openwebrx-button-nfm").addClass("highlighted");
|
||||||
|
break;
|
||||||
|
case "am":
|
||||||
|
$("#openwebrx-button-am").addClass("highlighted");
|
||||||
|
break;
|
||||||
|
case "lsb":
|
||||||
|
case "usb":
|
||||||
|
case "cw":
|
||||||
|
if(demodulators[0].high_cut-demodulators[0].low_cut<300)
|
||||||
|
$("#openwebrx-button-cw").addClass("highlighted");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(demodulators[0].high_cut<0)
|
||||||
|
$("#openwebrx-button-lsb").addClass("highlighted");
|
||||||
|
else if(demodulators[0].low_cut>0)
|
||||||
|
$("#openwebrx-button-usb").addClass("highlighted");
|
||||||
|
else $("#openwebrx-button-lsb, #openwebrx-button-usb").addClass("highlighted");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function demodulator_analog_replace_last() { demodulator_analog_replace(last_analog_demodulator_subtype); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
_____ _ _ _
|
||||||
|
| __ \(_) (_) | |
|
||||||
|
| | | |_ __ _ _ _ __ ___ ___ __| | ___ ___
|
||||||
|
| | | | |/ _` | | '_ ` _ \ / _ \ / _` |/ _ \/ __|
|
||||||
|
| |__| | | (_| | | | | | | | (_) | (_| | __/\__ \
|
||||||
|
|_____/|_|\__, |_|_| |_| |_|\___/ \__,_|\___||___/
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
*/
|
||||||
|
|
||||||
|
secondary_demod = false;
|
||||||
|
secondary_demod_offset_freq = 0;
|
||||||
|
secondary_demod_waterfall_queue = [];
|
||||||
|
|
||||||
|
function demodulator_digital_replace_last()
|
||||||
|
{
|
||||||
|
demodulator_digital_replace(last_digital_demodulator_subtype);
|
||||||
|
secondary_demod_listbox_update();
|
||||||
|
}
|
||||||
|
function demodulator_digital_replace(subtype)
|
||||||
|
{
|
||||||
|
switch(subtype)
|
||||||
|
{
|
||||||
|
case "bpsk31":
|
||||||
|
case "rtty":
|
||||||
|
secondary_demod_start(subtype);
|
||||||
|
demodulator_analog_replace('usb', true);
|
||||||
|
demodulator_buttons_update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
toggle_panel("openwebrx-panel-digimodes", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_create_canvas()
|
||||||
|
{
|
||||||
|
var new_canvas = document.createElement("canvas");
|
||||||
|
new_canvas.width=secondary_fft_size;
|
||||||
|
new_canvas.height=$(secondary_demod_canvas_container).height();
|
||||||
|
new_canvas.style.width=$(secondary_demod_canvas_container).width()+"px";
|
||||||
|
new_canvas.style.height=$(secondary_demod_canvas_container).height()+"px";
|
||||||
|
console.log(new_canvas.width, new_canvas.height, new_canvas.style.width, new_canvas.style.height);
|
||||||
|
secondary_demod_current_canvas_actual_line=new_canvas.height-1;
|
||||||
|
$(secondary_demod_canvas_container).children().last().before(new_canvas);
|
||||||
|
return new_canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_remove_canvases()
|
||||||
|
{
|
||||||
|
$(secondary_demod_canvas_container).children("canvas").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_init_canvases()
|
||||||
|
{
|
||||||
|
secondary_demod_remove_canvases();
|
||||||
|
secondary_demod_canvases=[];
|
||||||
|
secondary_demod_canvases.push(secondary_demod_create_canvas());
|
||||||
|
secondary_demod_canvases.push(secondary_demod_create_canvas());
|
||||||
|
secondary_demod_canvases[0].openwebrx_top=-$(secondary_demod_canvas_container).height();
|
||||||
|
secondary_demod_canvases[1].openwebrx_top=0;
|
||||||
|
secondary_demod_canvases_update_top();
|
||||||
|
secondary_demod_current_canvas_context = secondary_demod_canvases[0].getContext("2d");
|
||||||
|
secondary_demod_current_canvas_actual_line=$(secondary_demod_canvas_container).height()-1;
|
||||||
|
secondary_demod_current_canvas_index=0;
|
||||||
|
secondary_demod_canvases_initialized=true;
|
||||||
|
//secondary_demod_update_channel_freq_from_event();
|
||||||
|
mkscale(); //so that the secondary waterfall zoom level will be initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_canvases_update_top()
|
||||||
|
{
|
||||||
|
for(var i=0;i<2;i++) secondary_demod_canvases[i].style.top=secondary_demod_canvases[i].openwebrx_top+"px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_swap_canvases()
|
||||||
|
{
|
||||||
|
console.log("swap");
|
||||||
|
secondary_demod_canvases[0+!secondary_demod_current_canvas_index].openwebrx_top-=$(secondary_demod_canvas_container).height()*2;
|
||||||
|
secondary_demod_current_canvas_index=0+!secondary_demod_current_canvas_index;
|
||||||
|
secondary_demod_current_canvas_context = secondary_demod_canvases[secondary_demod_current_canvas_index].getContext("2d");
|
||||||
|
secondary_demod_current_canvas_actual_line=$(secondary_demod_canvas_container).height()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_init()
|
||||||
|
{
|
||||||
|
$("#openwebrx-panel-digimodes")[0].openwebrxHidden = true;
|
||||||
|
secondary_demod_canvas_container = $("#openwebrx-digimode-canvas-container")[0];
|
||||||
|
$(secondary_demod_canvas_container)
|
||||||
|
.mousemove(secondary_demod_canvas_container_mousemove)
|
||||||
|
.mouseup(secondary_demod_canvas_container_mouseup)
|
||||||
|
.mousedown(secondary_demod_canvas_container_mousedown)
|
||||||
|
.mouseenter(secondary_demod_canvas_container_mousein)
|
||||||
|
.mouseleave(secondary_demod_canvas_container_mouseout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_start(subtype)
|
||||||
|
{
|
||||||
|
secondary_demod_canvases_initialized = false;
|
||||||
|
ws.send("SET secondary_mod="+subtype);
|
||||||
|
secondary_demod = subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_set()
|
||||||
|
{
|
||||||
|
ws.send("SET secondary_offset_freq="+secondary_demod_offset_freq.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_stop()
|
||||||
|
{
|
||||||
|
ws.send("SET secondary_mod=off");
|
||||||
|
secondary_demod = false;
|
||||||
|
secondary_demod_waterfall_queue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_waterfall_add_queue(x)
|
||||||
|
{
|
||||||
|
secondary_demod_waterfall_queue.push(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_push_binary_data(x)
|
||||||
|
{
|
||||||
|
secondary_demod_push_data(Array.from(x).map( y => (y)?"1":"0" ).join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_push_data(x)
|
||||||
|
{
|
||||||
|
x=Array.from(x).map((y)=>{
|
||||||
|
var c=y.charCodeAt(0);
|
||||||
|
if(y=="\r") return " ";
|
||||||
|
if(y=="\n") return " ";
|
||||||
|
//if(y=="\n") return "<br />";
|
||||||
|
if(c<32||c>126) return "";
|
||||||
|
if(y=="&") return "&";
|
||||||
|
if(y=="<") return "<";
|
||||||
|
if(y==">") return ">";
|
||||||
|
if(y==" ") return " ";
|
||||||
|
return y;
|
||||||
|
}).join("");
|
||||||
|
$("#openwebrx-cursor-blink").before("<span class=\"part\"><span class=\"subpart\">"+x+"</span></span>");
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_data_clear()
|
||||||
|
{
|
||||||
|
$("#openwebrx-cursor-blink").prevAll().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_close_window()
|
||||||
|
{
|
||||||
|
secondary_demod_stop();
|
||||||
|
toggle_panel("openwebrx-panel-digimodes", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_demod_fft_offset_db=30; //need to calculate that later
|
||||||
|
|
||||||
|
function secondary_demod_waterfall_add(data)
|
||||||
|
{
|
||||||
|
if(!secondary_demod) return;
|
||||||
|
var w=secondary_fft_size;
|
||||||
|
|
||||||
|
//Add line to waterfall image
|
||||||
|
var oneline_image = secondary_demod_current_canvas_context.createImageData(w,1);
|
||||||
|
for(x=0;x<w;x++)
|
||||||
|
{
|
||||||
|
var color=waterfall_mkcolor(data[x]+secondary_demod_fft_offset_db);
|
||||||
|
for(i=0;i<4;i++) oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Draw image
|
||||||
|
secondary_demod_current_canvas_context.putImageData(oneline_image, 0, secondary_demod_current_canvas_actual_line--);
|
||||||
|
secondary_demod_canvases.map((x)=>{x.openwebrx_top += 1;});
|
||||||
|
secondary_demod_canvases_update_top();
|
||||||
|
if(secondary_demod_current_canvas_actual_line<0) secondary_demod_swap_canvases();
|
||||||
|
}
|
||||||
|
|
||||||
|
var secondary_demod_canvases_initialized = false;
|
||||||
|
|
||||||
|
function secondary_demod_waterfall_dequeue()
|
||||||
|
{
|
||||||
|
if(!secondary_demod || !secondary_demod_canvases_initialized) return;
|
||||||
|
if(secondary_demod_waterfall_queue.length) secondary_demod_waterfall_add(secondary_demod_waterfall_queue.shift());
|
||||||
|
if(secondary_demod_waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of fft overflow
|
||||||
|
{
|
||||||
|
console.log("secondary waterfall overflow, queue length:", secondary_demod_waterfall_queue.length);
|
||||||
|
while(secondary_demod_waterfall_queue.length) secondary_demod_waterfall_add(secondary_demod_waterfall_queue.shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_demod_listbox_updating = false;
|
||||||
|
function secondary_demod_listbox_changed()
|
||||||
|
{
|
||||||
|
if(secondary_demod_listbox_updating) return;
|
||||||
|
switch ($("#openwebrx-secondary-demod-listbox")[0].value)
|
||||||
|
{
|
||||||
|
case "none":
|
||||||
|
demodulator_analog_replace_last();
|
||||||
|
break;
|
||||||
|
case "bpsk31":
|
||||||
|
demodulator_digital_replace('bpsk31');
|
||||||
|
break;
|
||||||
|
case "rtty":
|
||||||
|
demodulator_digital_replace('rtty');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_listbox_update()
|
||||||
|
{
|
||||||
|
secondary_demod_listbox_updating = true;
|
||||||
|
$("#openwebrx-secondary-demod-listbox").val((secondary_demod)?secondary_demod:"none");
|
||||||
|
console.log("update");
|
||||||
|
secondary_demod_listbox_updating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_demod_channel_freq=1000;
|
||||||
|
function secondary_demod_update_marker()
|
||||||
|
{
|
||||||
|
var width = Math.max( (secondary_bw / (if_samp_rate/2)) * secondary_demod_canvas_width, 5);
|
||||||
|
var center_at = (secondary_demod_channel_freq / (if_samp_rate/2)) * secondary_demod_canvas_width + secondary_demod_canvas_left;
|
||||||
|
var left = center_at-width/2;
|
||||||
|
//console.log("sdum", width, left);
|
||||||
|
$("#openwebrx-digimode-select-channel").width(width).css("left",left+"px")
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_demod_waiting_for_set = false;
|
||||||
|
function secondary_demod_update_channel_freq_from_event(evt)
|
||||||
|
{
|
||||||
|
if(typeof evt !== "undefined")
|
||||||
|
{
|
||||||
|
var relativeX=(evt.offsetX)?evt.offsetX:evt.layerX;
|
||||||
|
secondary_demod_channel_freq=secondary_demod_low_cut +
|
||||||
|
(relativeX/$(secondary_demod_canvas_container).width()) * (secondary_demod_high_cut-secondary_demod_low_cut);
|
||||||
|
}
|
||||||
|
//console.log("toset:", secondary_demod_channel_freq);
|
||||||
|
if(!secondary_demod_waiting_for_set)
|
||||||
|
{
|
||||||
|
secondary_demod_waiting_for_set = true;
|
||||||
|
window.setTimeout(()=>{
|
||||||
|
ws.send("SET secondary_offset_freq="+Math.floor(secondary_demod_channel_freq));
|
||||||
|
//console.log("doneset:", secondary_demod_channel_freq);
|
||||||
|
secondary_demod_waiting_for_set = false;
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
secondary_demod_update_marker();
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_demod_mousedown=false;
|
||||||
|
function secondary_demod_canvas_container_mousein()
|
||||||
|
{
|
||||||
|
$("#openwebrx-digimode-select-channel").css("opacity","0.7"); //.css("border-width", "1px");
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_canvas_container_mouseout()
|
||||||
|
{
|
||||||
|
$("#openwebrx-digimode-select-channel").css("opacity","0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_canvas_container_mousemove(evt)
|
||||||
|
{
|
||||||
|
if(secondary_demod_mousedown) secondary_demod_update_channel_freq_from_event(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_canvas_container_mousedown(evt)
|
||||||
|
{
|
||||||
|
if(evt.which==1) secondary_demod_mousedown=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondary_demod_canvas_container_mouseup(evt)
|
||||||
|
{
|
||||||
|
if(evt.which==1) secondary_demod_mousedown=false;
|
||||||
|
secondary_demod_update_channel_freq_from_event(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function secondary_demod_waterfall_set_zoom(low_cut, high_cut)
|
||||||
|
{
|
||||||
|
if(!secondary_demod || !secondary_demod_canvases_initialized) return;
|
||||||
|
if(low_cut<0 && high_cut<0)
|
||||||
|
{
|
||||||
|
var hctmp = high_cut;
|
||||||
|
var lctmp = low_cut;
|
||||||
|
low_cut = -hctmp;
|
||||||
|
low_cut = -lctmp;
|
||||||
|
}
|
||||||
|
else if(low_cut<0 && high_cut>0)
|
||||||
|
{
|
||||||
|
high_cut=Math.max(Math.abs(high_cut), Math.abs(low_cut));
|
||||||
|
low_cut=0;
|
||||||
|
}
|
||||||
|
secondary_demod_low_cut = low_cut;
|
||||||
|
secondary_demod_high_cut = high_cut;
|
||||||
|
var shown_bw = high_cut-low_cut;
|
||||||
|
secondary_demod_canvas_width = $(secondary_demod_canvas_container).width() * (if_samp_rate/2)/shown_bw;
|
||||||
|
secondary_demod_canvas_left = -secondary_demod_canvas_width*(low_cut/(if_samp_rate/2));
|
||||||
|
//console.log("setzoom", secondary_demod_canvas_width, secondary_demod_canvas_left, low_cut, high_cut);
|
||||||
|
secondary_demod_canvases.map((x)=>{$(x).css("left",secondary_demod_canvas_left+"px").css("width",secondary_demod_canvas_width+"px");});
|
||||||
|
secondary_demod_update_channel_freq_from_event();
|
||||||
|
}
|
||||||
|
115
openwebrx.py
115
openwebrx.py
@ -20,13 +20,13 @@ print "" # python2.7 is required to run OpenWebRX instead of python3. Please run
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
sw_version="v0.14+"
|
sw_version="v0.17"
|
||||||
|
#0.15 (added nmux)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import code
|
import code
|
||||||
import importlib
|
import importlib
|
||||||
import plugins
|
import csdr
|
||||||
import plugins.dsp
|
|
||||||
import thread
|
import thread
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
@ -62,6 +62,7 @@ try: import __pypy__
|
|||||||
except: pass
|
except: pass
|
||||||
pypy="__pypy__" in globals()
|
pypy="__pypy__" in globals()
|
||||||
|
|
||||||
|
"""
|
||||||
def import_all_plugins(directory):
|
def import_all_plugins(directory):
|
||||||
for subdir in os.listdir(directory):
|
for subdir in os.listdir(directory):
|
||||||
if os.path.isdir(directory+subdir) and not subdir[0]=="_":
|
if os.path.isdir(directory+subdir) and not subdir[0]=="_":
|
||||||
@ -70,6 +71,7 @@ def import_all_plugins(directory):
|
|||||||
importname=(directory+subdir+"/plugin").replace("/",".")
|
importname=(directory+subdir+"/plugin").replace("/",".")
|
||||||
print "[openwebrx-import] Found plugin:",importname
|
print "[openwebrx-import] Found plugin:",importname
|
||||||
importlib.import_module(importname)
|
importlib.import_module(importname)
|
||||||
|
"""
|
||||||
|
|
||||||
class MultiThreadHTTPServer(ThreadingMixIn, HTTPServer):
|
class MultiThreadHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
pass
|
pass
|
||||||
@ -90,7 +92,8 @@ def handle_signal(sig, frame):
|
|||||||
print
|
print
|
||||||
for key in client._fields:
|
for key in client._fields:
|
||||||
print "\t%s = %s"%(key,str(getattr(client,key)))
|
print "\t%s = %s"%(key,str(getattr(client,key)))
|
||||||
|
elif sig == signal.SIGUSR2:
|
||||||
|
code.interact(local=globals())
|
||||||
else:
|
else:
|
||||||
print "[openwebrx] Ctrl+C: aborting."
|
print "[openwebrx] Ctrl+C: aborting."
|
||||||
cleanup_clients(True)
|
cleanup_clients(True)
|
||||||
@ -126,12 +129,10 @@ def main():
|
|||||||
#Set signal handler
|
#Set signal handler
|
||||||
signal.signal(signal.SIGINT, handle_signal) #http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
|
signal.signal(signal.SIGINT, handle_signal) #http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
|
||||||
signal.signal(signal.SIGUSR1, handle_signal)
|
signal.signal(signal.SIGUSR1, handle_signal)
|
||||||
|
signal.signal(signal.SIGUSR2, handle_signal)
|
||||||
#Load plugins
|
|
||||||
import_all_plugins("plugins/dsp/")
|
|
||||||
|
|
||||||
#Pypy
|
#Pypy
|
||||||
if pypy: print "pypy detected (and now something completely different: a c code is expected to run at a speed of 3*10^8 m/s?)"
|
if pypy: print "pypy detected (and now something completely different: c code is expected to run at a speed of 3*10^8 m/s?)"
|
||||||
|
|
||||||
#Change process name to "openwebrx" (to be seen in ps)
|
#Change process name to "openwebrx" (to be seen in ps)
|
||||||
try:
|
try:
|
||||||
@ -144,11 +145,21 @@ def main():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
#Start rtl thread
|
#Start rtl thread
|
||||||
if os.system("ncat --version > /dev/null") == 32512: #check for ncat
|
if os.system("csdr 2> /dev/null") == 32512: #check for csdr
|
||||||
print "[openwebrx-main] Error: ncat not detected, please install it! The ncat tool is a netcat alternative, used for distributing the I/Q data stream. It is usually available in the nmap package (sudo apt-get install nmap). For more explanation, look into the README.md"
|
print "[openwebrx-main] You need to install \"csdr\" to run OpenWebRX!\n"
|
||||||
|
return
|
||||||
|
if os.system("nmux --help 2> /dev/null") == 32512: #check for nmux
|
||||||
|
print "[openwebrx-main] You need to install an up-to-date version of \"csdr\" that contains the \"nmux\" tool to run OpenWebRX! Please upgrade \"csdr\"!\n"
|
||||||
return
|
return
|
||||||
if cfg.start_rtl_thread:
|
if cfg.start_rtl_thread:
|
||||||
cfg.start_rtl_command += "| ncat -4l %d -k --send-only --allow 127.0.0.1" % cfg.iq_server_port
|
nmux_bufcnt = nmux_bufsize = 0
|
||||||
|
while nmux_bufsize < cfg.samp_rate/4: nmux_bufsize += 4096
|
||||||
|
while nmux_bufsize * nmux_bufcnt < cfg.nmux_memory * 1e6: nmux_bufcnt += 1
|
||||||
|
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
||||||
|
print "[openwebrx-main] Error: nmux_bufsize or nmux_bufcnt is zero. These depend on nmux_memory and samp_rate options in config_webrx.py"
|
||||||
|
return
|
||||||
|
print "[openwebrx-main] nmux_bufsize = %d, nmux_bufcnt = %d" % (nmux_bufsize, nmux_bufcnt)
|
||||||
|
cfg.start_rtl_command += "| nmux --bufsize %d --bufcnt %d --port %d --address 127.0.0.1" % (nmux_bufsize, nmux_bufcnt, cfg.iq_server_port)
|
||||||
rtl_thread=threading.Thread(target = lambda:subprocess.Popen(cfg.start_rtl_command, shell=True), args=())
|
rtl_thread=threading.Thread(target = lambda:subprocess.Popen(cfg.start_rtl_command, shell=True), args=())
|
||||||
rtl_thread.start()
|
rtl_thread.start()
|
||||||
print "[openwebrx-main] Started rtl_thread: "+cfg.start_rtl_command
|
print "[openwebrx-main] Started rtl_thread: "+cfg.start_rtl_command
|
||||||
@ -281,12 +292,13 @@ def apply_csdr_cfg_to_dsp(dsp):
|
|||||||
|
|
||||||
def spectrum_thread_function():
|
def spectrum_thread_function():
|
||||||
global clients, spectrum_dsp, spectrum_thread_watchdog_last_tick
|
global clients, spectrum_dsp, spectrum_thread_watchdog_last_tick
|
||||||
spectrum_dsp=dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
spectrum_dsp=dsp=csdr.dsp()
|
||||||
dsp.nc_port=cfg.iq_server_port
|
dsp.nc_port=cfg.iq_server_port
|
||||||
dsp.set_demodulator("fft")
|
dsp.set_demodulator("fft")
|
||||||
dsp.set_samp_rate(cfg.samp_rate)
|
dsp.set_samp_rate(cfg.samp_rate)
|
||||||
dsp.set_fft_size(cfg.fft_size)
|
dsp.set_fft_size(cfg.fft_size)
|
||||||
dsp.set_fft_fps(cfg.fft_fps)
|
dsp.set_fft_fps(cfg.fft_fps)
|
||||||
|
dsp.set_fft_averages(int(round(1.0 * cfg.samp_rate / cfg.fft_size / cfg.fft_fps / (1.0 - cfg.fft_voverlap_factor))) if cfg.fft_voverlap_factor>0 else 0)
|
||||||
dsp.set_fft_compression(cfg.fft_compression)
|
dsp.set_fft_compression(cfg.fft_compression)
|
||||||
dsp.set_format_conversion(cfg.format_conversion)
|
dsp.set_format_conversion(cfg.format_conversion)
|
||||||
apply_csdr_cfg_to_dsp(dsp)
|
apply_csdr_cfg_to_dsp(dsp)
|
||||||
@ -415,6 +427,7 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
# there's even another cool tip at http://stackoverflow.com/questions/4419650/how-to-implement-timeout-in-basehttpserver-basehttprequesthandler-python
|
# there's even another cool tip at http://stackoverflow.com/questions/4419650/how-to-implement-timeout-in-basehttpserver-basehttprequesthandler-python
|
||||||
#if self.path[:5]=="/lock": cma("do_GET /lock/") # to test mutex_watchdog_thread. Do not uncomment in production environment!
|
#if self.path[:5]=="/lock": cma("do_GET /lock/") # to test mutex_watchdog_thread. Do not uncomment in production environment!
|
||||||
if self.path[:4]=="/ws/":
|
if self.path[:4]=="/ws/":
|
||||||
|
print "[openwebrx-ws] Client requested WebSocket connection"
|
||||||
if receiver_failed: self.send_error(500,"Internal server error")
|
if receiver_failed: self.send_error(500,"Internal server error")
|
||||||
try:
|
try:
|
||||||
# ========= WebSocket handshake =========
|
# ========= WebSocket handshake =========
|
||||||
@ -447,21 +460,22 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
rxws.send(self, "MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} audio_compression={4} fft_compression={5} max_clients={6} setup".format(str(cfg.shown_center_freq),str(cfg.samp_rate),cfg.fft_size,cfg.fft_fps,cfg.audio_compression,cfg.fft_compression,cfg.max_clients))
|
rxws.send(self, "MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} audio_compression={4} fft_compression={5} max_clients={6} setup".format(str(cfg.shown_center_freq),str(cfg.samp_rate),cfg.fft_size,cfg.fft_fps,cfg.audio_compression,cfg.fft_compression,cfg.max_clients))
|
||||||
|
|
||||||
# ========= Initialize DSP =========
|
# ========= Initialize DSP =========
|
||||||
dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
dsp=csdr.dsp()
|
||||||
dsp_initialized=False
|
dsp_initialized=False
|
||||||
dsp.set_audio_compression(cfg.audio_compression)
|
dsp.set_audio_compression(cfg.audio_compression)
|
||||||
|
dsp.set_fft_compression(cfg.fft_compression) #used by secondary chains
|
||||||
dsp.set_format_conversion(cfg.format_conversion)
|
dsp.set_format_conversion(cfg.format_conversion)
|
||||||
dsp.set_offset_freq(0)
|
dsp.set_offset_freq(0)
|
||||||
dsp.set_bpf(-4000,4000)
|
dsp.set_bpf(-4000,4000)
|
||||||
|
dsp.set_secondary_fft_size(cfg.digimodes_fft_size)
|
||||||
dsp.nc_port=cfg.iq_server_port
|
dsp.nc_port=cfg.iq_server_port
|
||||||
apply_csdr_cfg_to_dsp(dsp)
|
apply_csdr_cfg_to_dsp(dsp)
|
||||||
myclient.dsp=dsp
|
myclient.dsp=dsp
|
||||||
|
do_secondary_demod=False
|
||||||
access_log("Started streaming to client: "+self.client_address[0]+"#"+myclient.id+" (users now: "+str(len(clients))+")")
|
access_log("Started streaming to client: "+self.client_address[0]+"#"+myclient.id+" (users now: "+str(len(clients))+")")
|
||||||
|
|
||||||
myclient.loopstat=0
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
myclient.loopstat=0
|
||||||
if myclient.closed[0]:
|
if myclient.closed[0]:
|
||||||
print "[openwebrx-httpd:ws] client closed by other thread"
|
print "[openwebrx-httpd:ws] client closed by other thread"
|
||||||
break
|
break
|
||||||
@ -517,12 +531,34 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
rxws.send(self,myclient.bcastmsg)
|
rxws.send(self,myclient.bcastmsg)
|
||||||
myclient.bcastmsg=""
|
myclient.bcastmsg=""
|
||||||
|
|
||||||
|
# ========= send secondary =========
|
||||||
|
if do_secondary_demod:
|
||||||
|
myclient.loopstat=41
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
secondary_spectrum_data=dsp.read_secondary_fft(dsp.get_secondary_fft_bytes_to_read())
|
||||||
|
if len(secondary_spectrum_data) == 0: break
|
||||||
|
# print "len(secondary_spectrum_data)", len(secondary_spectrum_data) #TODO digimodes
|
||||||
|
rxws.send(self, secondary_spectrum_data, "FFTS")
|
||||||
|
except: break
|
||||||
|
myclient.loopstat=42
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
myclient.loopstat=422
|
||||||
|
secondary_demod_data=dsp.read_secondary_demod(1)
|
||||||
|
myclient.loopstat=423
|
||||||
|
if len(secondary_demod_data) == 0: break
|
||||||
|
# print "len(secondary_demod_data)", len(secondary_demod_data), secondary_demod_data #TODO digimodes
|
||||||
|
rxws.send(self, secondary_demod_data, "DAT ")
|
||||||
|
except: break
|
||||||
|
|
||||||
# ========= process commands =========
|
# ========= process commands =========
|
||||||
while True:
|
while True:
|
||||||
myclient.loopstat=50
|
myclient.loopstat=50
|
||||||
rdata=rxws.recv(self, False)
|
rdata=rxws.recv(self, False)
|
||||||
if not rdata: break
|
myclient.loopstat=51
|
||||||
#try:
|
#try:
|
||||||
|
if not rdata: break
|
||||||
elif rdata[:3]=="SET":
|
elif rdata[:3]=="SET":
|
||||||
print "[openwebrx-httpd:ws,%d] command: %s"%(client_i,rdata)
|
print "[openwebrx-httpd:ws,%d] command: %s"%(client_i,rdata)
|
||||||
pairs=rdata[4:].split(" ")
|
pairs=rdata[4:].split(" ")
|
||||||
@ -531,13 +567,13 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
filter_limit=dsp.get_output_rate()/2
|
filter_limit=dsp.get_output_rate()/2
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
param_name, param_value = pair.split("=")
|
param_name, param_value = pair.split("=")
|
||||||
if param_name == "low_cut" and -filter_limit <= float(param_value) <= filter_limit:
|
if param_name == "low_cut" and -filter_limit <= int(param_value) <= filter_limit:
|
||||||
bpf_set=True
|
bpf_set=True
|
||||||
new_bpf[0]=int(param_value)
|
new_bpf[0]=int(param_value)
|
||||||
elif param_name == "high_cut" and -filter_limit <= float(param_value) <= filter_limit:
|
elif param_name == "high_cut" and -filter_limit <= int(param_value) <= filter_limit:
|
||||||
bpf_set=True
|
bpf_set=True
|
||||||
new_bpf[1]=int(param_value)
|
new_bpf[1]=int(param_value)
|
||||||
elif param_name == "offset_freq" and -cfg.samp_rate/2 <= float(param_value) <= cfg.samp_rate/2:
|
elif param_name == "offset_freq" and -cfg.samp_rate/2 <= int(param_value) <= cfg.samp_rate/2:
|
||||||
myclient.loopstat=510
|
myclient.loopstat=510
|
||||||
dsp.set_offset_freq(int(param_value))
|
dsp.set_offset_freq(int(param_value))
|
||||||
elif param_name == "squelch_level" and float(param_value) >= 0:
|
elif param_name == "squelch_level" and float(param_value) >= 0:
|
||||||
@ -560,6 +596,19 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
myclient.loopstat=550
|
myclient.loopstat=550
|
||||||
dsp.start()
|
dsp.start()
|
||||||
dsp_initialized=True
|
dsp_initialized=True
|
||||||
|
elif param_name=="secondary_mod" and cfg.digimodes_enable:
|
||||||
|
if (dsp.get_secondary_demodulator() != param_value):
|
||||||
|
if dsp_initialized: dsp.stop()
|
||||||
|
if param_value == "off":
|
||||||
|
dsp.set_secondary_demodulator(None)
|
||||||
|
do_secondary_demod = False
|
||||||
|
else:
|
||||||
|
dsp.set_secondary_demodulator(param_value)
|
||||||
|
do_secondary_demod = True
|
||||||
|
rxws.send(self, "MSG secondary_fft_size={0} if_samp_rate={1} secondary_bw={2} secondary_setup".format(cfg.digimodes_fft_size, dsp.if_samp_rate(), dsp.secondary_bw()))
|
||||||
|
if dsp_initialized: dsp.start()
|
||||||
|
elif param_name=="secondary_offset_freq" and 0 <= int(param_value) <= dsp.if_samp_rate()/2 and cfg.digimodes_enable:
|
||||||
|
dsp.set_secondary_offset_freq(int(param_value))
|
||||||
else:
|
else:
|
||||||
print "[openwebrx-httpd:ws] invalid parameter"
|
print "[openwebrx-httpd:ws] invalid parameter"
|
||||||
if bpf_set:
|
if bpf_set:
|
||||||
@ -567,16 +616,20 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
dsp.set_bpf(*new_bpf)
|
dsp.set_bpf(*new_bpf)
|
||||||
#code.interact(local=locals())
|
#code.interact(local=locals())
|
||||||
except:
|
except:
|
||||||
|
myclient.loopstat=990
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
if exc_value[0]==32: #"broken pipe", client disconnected
|
print "[openwebrx-httpd:ws] exception: ",exc_type,exc_value
|
||||||
pass
|
traceback.print_tb(exc_traceback) #TODO digimodes
|
||||||
elif exc_value[0]==11: #"resource unavailable" on recv, client disconnected
|
#if exc_value[0]==32: #"broken pipe", client disconnected
|
||||||
pass
|
# pass
|
||||||
else:
|
#elif exc_value[0]==11: #"resource unavailable" on recv, client disconnected
|
||||||
print "[openwebrx-httpd] error in /ws/ handler: ",exc_type,exc_value
|
# pass
|
||||||
traceback.print_tb(exc_traceback)
|
#else:
|
||||||
|
# print "[openwebrx-httpd] error in /ws/ handler: ",exc_type,exc_value
|
||||||
|
# traceback.print_tb(exc_traceback)
|
||||||
|
|
||||||
#stop dsp for the disconnected client
|
#stop dsp for the disconnected client
|
||||||
|
myclient.loopstat=991
|
||||||
try:
|
try:
|
||||||
dsp.stop()
|
dsp.stop()
|
||||||
del dsp
|
del dsp
|
||||||
@ -584,6 +637,7 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
print "[openwebrx-httpd] error in dsp.stop()"
|
print "[openwebrx-httpd] error in dsp.stop()"
|
||||||
|
|
||||||
#delete disconnected client
|
#delete disconnected client
|
||||||
|
myclient.loopstat=992
|
||||||
try:
|
try:
|
||||||
cma("do_GET /ws/ delete disconnected")
|
cma("do_GET /ws/ delete disconnected")
|
||||||
id_to_close=get_client_by_id(myclient.id,False)
|
id_to_close=get_client_by_id(myclient.id,False)
|
||||||
@ -646,7 +700,12 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
("%[START_MOD]",cfg.start_mod),
|
("%[START_MOD]",cfg.start_mod),
|
||||||
("%[WATERFALL_COLORS]",cfg.waterfall_colors),
|
("%[WATERFALL_COLORS]",cfg.waterfall_colors),
|
||||||
("%[WATERFALL_MIN_LEVEL]",str(cfg.waterfall_min_level)),
|
("%[WATERFALL_MIN_LEVEL]",str(cfg.waterfall_min_level)),
|
||||||
("%[WATERFALL_MAX_LEVEL]",str(cfg.waterfall_max_level))
|
("%[WATERFALL_MAX_LEVEL]",str(cfg.waterfall_max_level)),
|
||||||
|
("%[WATERFALL_AUTO_LEVEL_MARGIN]","[%d,%d]"%cfg.waterfall_auto_level_margin),
|
||||||
|
("%[DIGIMODES_ENABLE]",("true" if cfg.digimodes_enable else "false")),
|
||||||
|
("%[MATHBOX_WATERFALL_FRES]",str(cfg.mathbox_waterfall_frequency_resolution)),
|
||||||
|
("%[MATHBOX_WATERFALL_THIST]",str(cfg.mathbox_waterfall_history_length)),
|
||||||
|
("%[MATHBOX_WATERFALL_COLORS]",cfg.mathbox_waterfall_colors)
|
||||||
)
|
)
|
||||||
for rule in replace_dictionary:
|
for rule in replace_dictionary:
|
||||||
while data.find(rule[0])!=-1:
|
while data.find(rule[0])!=-1:
|
||||||
|
@ -1,294 +0,0 @@
|
|||||||
"""
|
|
||||||
OpenWebRX csdr plugin: do the signal processing with csdr
|
|
||||||
|
|
||||||
This file is part of OpenWebRX,
|
|
||||||
an open-source SDR receiver software with a web UI.
|
|
||||||
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
|
|
||||||
|
|
||||||
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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import code
|
|
||||||
import signal
|
|
||||||
import fcntl
|
|
||||||
import select
|
|
||||||
|
|
||||||
class dsp_plugin:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.samp_rate = 250000
|
|
||||||
self.output_rate = 11025 #this is default, and cannot be set at the moment
|
|
||||||
self.fft_size = 1024
|
|
||||||
self.fft_fps = 5
|
|
||||||
self.offset_freq = 0
|
|
||||||
self.low_cut = -4000
|
|
||||||
self.high_cut = 4000
|
|
||||||
self.bpf_transition_bw = 320 #Hz, and this is a constant
|
|
||||||
self.ddc_transition_bw_rate = 0.15 # of the IF sample rate
|
|
||||||
self.running = False
|
|
||||||
self.audio_compression = "none"
|
|
||||||
self.fft_compression = "none"
|
|
||||||
self.demodulator = "nfm"
|
|
||||||
self.name = "csdr"
|
|
||||||
self.format_conversion = "csdr convert_u8_f"
|
|
||||||
self.base_bufsize = 512
|
|
||||||
self.nc_port = 4951
|
|
||||||
self.csdr_dynamic_bufsize = False
|
|
||||||
self.csdr_print_bufsizes = False
|
|
||||||
self.csdr_through = False
|
|
||||||
self.squelch_level = 0
|
|
||||||
|
|
||||||
def chain(self,which):
|
|
||||||
if which in [ "dmr", "dstar", "nxdn", "ysf" ]:
|
|
||||||
self.set_output_rate(48000)
|
|
||||||
else:
|
|
||||||
self.set_output_rate(11025)
|
|
||||||
any_chain_base="ncat -v 127.0.0.1 {nc_port} | "
|
|
||||||
if self.csdr_dynamic_bufsize: any_chain_base+="csdr setbuf {start_bufsize} | "
|
|
||||||
if self.csdr_through: any_chain_base+="csdr through | "
|
|
||||||
any_chain_base+=self.format_conversion+(" | " if self.format_conversion!="" else "") ##"csdr flowcontrol {flowcontrol} auto 1.5 10 | "
|
|
||||||
if which == "fft":
|
|
||||||
fft_chain_base = any_chain_base+"csdr fft_cc {fft_size} {fft_block_size} | csdr logpower_cf -70 | csdr fft_exchange_sides_ff {fft_size}"
|
|
||||||
if self.fft_compression=="adpcm":
|
|
||||||
return fft_chain_base+" | csdr compress_fft_adpcm_f_u8 {fft_size}"
|
|
||||||
else:
|
|
||||||
return fft_chain_base
|
|
||||||
chain_begin=any_chain_base+"csdr shift_addition_cc --fifo {shift_pipe} | csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING | csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING | csdr squelch_and_smeter_cc --fifo {squelch_pipe} --outfifo {smeter_pipe} 5 1 | "
|
|
||||||
chain_end = ""
|
|
||||||
if self.audio_compression=="adpcm":
|
|
||||||
chain_end = " | csdr encode_ima_adpcm_i16_u8"
|
|
||||||
if which == "nfm": return chain_begin + "csdr fmdemod_quadri_cf | csdr limit_ff | csdr fractional_decimator_ff {last_decimation} | csdr deemphasis_nfm_ff 11025 | csdr convert_f_s16"+chain_end
|
|
||||||
if which in [ "dstar", "nxdn" ]:
|
|
||||||
c = chain_begin
|
|
||||||
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
|
||||||
if which == "dstar":
|
|
||||||
c += " | dsd -fd"
|
|
||||||
elif which == "nxdn":
|
|
||||||
c += " | dsd -fi"
|
|
||||||
c += " -i - -o - -u 2 -g 10"
|
|
||||||
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --input-buffer 160 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 220"
|
|
||||||
c += chain_end
|
|
||||||
return c
|
|
||||||
elif which == "dmr":
|
|
||||||
c = chain_begin
|
|
||||||
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
|
||||||
c += " | rrc_filter | gfsk_demodulator | dmr_decoder --fifo {meta_pipe} | mbe_synthesizer"
|
|
||||||
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 256"
|
|
||||||
c += chain_end
|
|
||||||
return c
|
|
||||||
elif which == "ysf":
|
|
||||||
c = chain_begin
|
|
||||||
c += "csdr fmdemod_quadri_cf | csdr fastdcblock_ff | csdr convert_f_s16"
|
|
||||||
c += " | rrc_filter | gfsk_demodulator | ysf_decoder --fifo {meta_pipe} | mbe_synthesizer -y"
|
|
||||||
c += " | sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r 11025 -e signed-integer -b 16 -c 1 - | csdr setbuf 256"
|
|
||||||
c += chain_end
|
|
||||||
return c
|
|
||||||
elif which == "am": return chain_begin + "csdr amdemod_cf | csdr fastdcblock_ff | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_s16"+chain_end
|
|
||||||
elif which == "ssb": return chain_begin + "csdr realpart_cf | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_s16"+chain_end
|
|
||||||
|
|
||||||
def set_audio_compression(self,what):
|
|
||||||
self.audio_compression = what
|
|
||||||
|
|
||||||
def set_fft_compression(self,what):
|
|
||||||
self.fft_compression = what
|
|
||||||
|
|
||||||
def get_fft_bytes_to_read(self):
|
|
||||||
if self.fft_compression=="none": return self.fft_size*4
|
|
||||||
if self.fft_compression=="adpcm": return (self.fft_size/2)+(10/2)
|
|
||||||
|
|
||||||
def set_samp_rate(self,samp_rate):
|
|
||||||
#to change this, restart is required
|
|
||||||
self.samp_rate=samp_rate
|
|
||||||
self.decimation=1
|
|
||||||
while self.samp_rate/(self.decimation+1)>=self.output_rate:
|
|
||||||
self.decimation+=1
|
|
||||||
self.last_decimation=float(self.if_samp_rate())/self.output_rate
|
|
||||||
|
|
||||||
def if_samp_rate(self):
|
|
||||||
return self.samp_rate/self.decimation
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_output_rate(self):
|
|
||||||
return self.output_rate
|
|
||||||
|
|
||||||
def set_output_rate(self,output_rate):
|
|
||||||
self.output_rate=output_rate
|
|
||||||
self.set_samp_rate(self.samp_rate) #as it depends on output_rate
|
|
||||||
|
|
||||||
def set_demodulator(self,demodulator):
|
|
||||||
#to change this, restart is required
|
|
||||||
self.demodulator=demodulator
|
|
||||||
|
|
||||||
def get_demodulator(self):
|
|
||||||
return self.demodulator
|
|
||||||
|
|
||||||
def set_fft_size(self,fft_size):
|
|
||||||
#to change this, restart is required
|
|
||||||
self.fft_size=fft_size
|
|
||||||
|
|
||||||
def set_fft_fps(self,fft_fps):
|
|
||||||
#to change this, restart is required
|
|
||||||
self.fft_fps=fft_fps
|
|
||||||
|
|
||||||
def fft_block_size(self):
|
|
||||||
return self.samp_rate/self.fft_fps
|
|
||||||
|
|
||||||
def set_format_conversion(self,format_conversion):
|
|
||||||
self.format_conversion=format_conversion
|
|
||||||
|
|
||||||
def set_offset_freq(self,offset_freq):
|
|
||||||
self.offset_freq=offset_freq
|
|
||||||
if self.running:
|
|
||||||
self.shift_pipe_file.write("%g\n"%(-float(self.offset_freq)/self.samp_rate))
|
|
||||||
self.shift_pipe_file.flush()
|
|
||||||
|
|
||||||
def set_bpf(self,low_cut,high_cut):
|
|
||||||
self.low_cut=low_cut
|
|
||||||
self.high_cut=high_cut
|
|
||||||
if self.running:
|
|
||||||
self.bpf_pipe_file.write( "%g %g\n"%(float(self.low_cut)/self.if_samp_rate(), float(self.high_cut)/self.if_samp_rate()) )
|
|
||||||
self.bpf_pipe_file.flush()
|
|
||||||
|
|
||||||
def get_bpf(self):
|
|
||||||
return [self.low_cut, self.high_cut]
|
|
||||||
|
|
||||||
def set_squelch_level(self, squelch_level):
|
|
||||||
self.squelch_level=squelch_level
|
|
||||||
if self.running:
|
|
||||||
self.squelch_pipe_file.write( "%g\n"%(float(self.squelch_level)) )
|
|
||||||
self.squelch_pipe_file.flush()
|
|
||||||
|
|
||||||
def get_smeter_level(self):
|
|
||||||
if self.running:
|
|
||||||
line=self.smeter_pipe_file.readline()
|
|
||||||
return float(line[:-1])
|
|
||||||
|
|
||||||
def get_metadata(self):
|
|
||||||
if self.running and self.meta_pipe:
|
|
||||||
return self.meta_pipe_file.readline()
|
|
||||||
|
|
||||||
def mkfifo(self,path):
|
|
||||||
try:
|
|
||||||
os.unlink(path)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.mkfifo(path)
|
|
||||||
|
|
||||||
def ddc_transition_bw(self):
|
|
||||||
return self.ddc_transition_bw_rate*(self.if_samp_rate()/float(self.samp_rate))
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
command_base=self.chain(self.demodulator)
|
|
||||||
|
|
||||||
#create control pipes for csdr
|
|
||||||
pipe_base_path="/tmp/openwebrx_pipe_{myid}_".format(myid=id(self))
|
|
||||||
self.bpf_pipe = self.shift_pipe = self.squelch_pipe = self.smeter_pipe = self.meta_pipe = None
|
|
||||||
if "{bpf_pipe}" in command_base:
|
|
||||||
self.bpf_pipe=pipe_base_path+"bpf"
|
|
||||||
self.mkfifo(self.bpf_pipe)
|
|
||||||
if "{shift_pipe}" in command_base:
|
|
||||||
self.shift_pipe=pipe_base_path+"shift"
|
|
||||||
self.mkfifo(self.shift_pipe)
|
|
||||||
if "{squelch_pipe}" in command_base:
|
|
||||||
self.squelch_pipe=pipe_base_path+"squelch"
|
|
||||||
self.mkfifo(self.squelch_pipe)
|
|
||||||
if "{smeter_pipe}" in command_base:
|
|
||||||
self.smeter_pipe=pipe_base_path+"smeter"
|
|
||||||
self.mkfifo(self.smeter_pipe)
|
|
||||||
if "{meta_pipe}" in command_base:
|
|
||||||
self.meta_pipe=pipe_base_path+"meta"
|
|
||||||
self.mkfifo(self.meta_pipe)
|
|
||||||
else:
|
|
||||||
self.meta_pipe=None
|
|
||||||
|
|
||||||
#run the command
|
|
||||||
command=command_base.format( bpf_pipe=self.bpf_pipe, shift_pipe=self.shift_pipe, decimation=self.decimation, \
|
|
||||||
last_decimation=self.last_decimation, fft_size=self.fft_size, fft_block_size=self.fft_block_size(), \
|
|
||||||
bpf_transition_bw=float(self.bpf_transition_bw)/self.if_samp_rate(), ddc_transition_bw=self.ddc_transition_bw(), \
|
|
||||||
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 )
|
|
||||||
|
|
||||||
print "[openwebrx-dsp-plugin:csdr] Command =",command
|
|
||||||
#code.interact(local=locals())
|
|
||||||
my_env=os.environ.copy()
|
|
||||||
if self.csdr_dynamic_bufsize: my_env["CSDR_DYNAMIC_BUFSIZE_ON"]="1";
|
|
||||||
if self.csdr_print_bufsizes: my_env["CSDR_PRINT_BUFSIZES"]="1";
|
|
||||||
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env)
|
|
||||||
self.running = True
|
|
||||||
|
|
||||||
#open control pipes for csdr and send initialization data
|
|
||||||
if self.bpf_pipe != None:
|
|
||||||
self.bpf_pipe_file=open(self.bpf_pipe,"w")
|
|
||||||
self.set_bpf(self.low_cut,self.high_cut)
|
|
||||||
if self.shift_pipe != None:
|
|
||||||
self.shift_pipe_file=open(self.shift_pipe,"w")
|
|
||||||
self.set_offset_freq(self.offset_freq)
|
|
||||||
if self.squelch_pipe != None:
|
|
||||||
self.squelch_pipe_file=open(self.squelch_pipe,"w")
|
|
||||||
self.set_squelch_level(self.squelch_level)
|
|
||||||
if self.smeter_pipe != None:
|
|
||||||
self.smeter_pipe_file=open(self.smeter_pipe,"r")
|
|
||||||
fcntl.fcntl(self.smeter_pipe_file, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
||||||
if self.meta_pipe != None:
|
|
||||||
self.meta_pipe_file=open(self.meta_pipe,"r")
|
|
||||||
fcntl.fcntl(self.meta_pipe_file, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
||||||
|
|
||||||
def read(self,size):
|
|
||||||
return self.process.stdout.read(size)
|
|
||||||
|
|
||||||
def read_async(self, size):
|
|
||||||
if (select.select([self.process.stdout], [], [], 0)[0] != []):
|
|
||||||
return self.process.stdout.read(size)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
|
|
||||||
#if(self.process.poll()!=None):return # returns None while subprocess is running
|
|
||||||
#while(self.process.poll()==None):
|
|
||||||
# #self.process.kill()
|
|
||||||
# print "killproc",os.getpgid(self.process.pid),self.process.pid
|
|
||||||
# os.killpg(self.process.pid, signal.SIGTERM)
|
|
||||||
#
|
|
||||||
# time.sleep(0.1)
|
|
||||||
if self.bpf_pipe:
|
|
||||||
try: os.unlink(self.bpf_pipe)
|
|
||||||
except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
|
||||||
if self.shift_pipe:
|
|
||||||
try: os.unlink(self.shift_pipe)
|
|
||||||
except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.shift_pipe
|
|
||||||
if self.squelch_pipe:
|
|
||||||
try: os.unlink(self.squelch_pipe)
|
|
||||||
except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.squelch_pipe
|
|
||||||
if self.smeter_pipe:
|
|
||||||
try: os.unlink(self.smeter_pipe)
|
|
||||||
except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.smeter_pipe
|
|
||||||
if self.meta_pipe:
|
|
||||||
try: os.unlink(self.meta_pipe)
|
|
||||||
except: print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.meta_pipe
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
def restart(self):
|
|
||||||
self.stop()
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.stop()
|
|
||||||
del(self.process)
|
|
Binary file not shown.
Before Width: | Height: | Size: 511 KiB |
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB |
5
sdrhu.py
5
sdrhu.py
@ -26,8 +26,8 @@ def run(continuously=True):
|
|||||||
if not cfg.sdrhu_key: return
|
if not cfg.sdrhu_key: return
|
||||||
firsttime="(Your receiver is soon getting listed on sdr.hu!)"
|
firsttime="(Your receiver is soon getting listed on sdr.hu!)"
|
||||||
while True:
|
while True:
|
||||||
cmd = "wget --timeout=15 -qO- http://sdr.hu/update --post-data \"url=http://"+cfg.server_hostname+":"+str(cfg.web_port)+"&apikey="+cfg.sdrhu_key+"\" 2>&1"
|
cmd = "wget --timeout=15 -4qO- https://sdr.hu/update --post-data \"url=http://"+cfg.server_hostname+":"+str(cfg.web_port)+"&apikey="+cfg.sdrhu_key+"\" 2>&1"
|
||||||
#print "[openwebrx-sdrhu]", cmd
|
print "[openwebrx-sdrhu]", cmd
|
||||||
returned=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()
|
returned=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()
|
||||||
returned=returned[0]
|
returned=returned[0]
|
||||||
#print returned
|
#print returned
|
||||||
@ -48,4 +48,3 @@ def run(continuously=True):
|
|||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
run(False)
|
run(False)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user