Merge branch 'develop' into pycsdr

This commit is contained in:
Jakob Ketterl 2021-07-15 18:09:39 +02:00
commit 5e7a0a38aa
273 changed files with 16169 additions and 3734 deletions

View File

@ -1,14 +1,39 @@
**unreleased**
**1.1.x - unreleased**
- Reworked most graphical elements as SVGs for faster loadtimes and crispier display on hi-dpi displays
- Updated pipelines to match changes in digiham
- Changed D-Star and NXDN integrations to use new decoders from digiham
- Added D-Star and NXDN metadata display
**1.0.0**
- Introduced `squelch_auto_margin` config option that allows configuring the auto squelch level
- Removed `port` configuration option; `rtltcp_compat` takes the port number with the new connectors
- Added support for new WSJT-X modes FST4 and FST4W (only available with WSJT-X 2.3)
- Added support for new WSJT-X modes FST4, FST4W (only available with WSJT-X 2.3) and Q65 (only avilable with
WSJT-X 2.4)
- Added support for demodulating M17 digital voice signals using m17-cxx-demod
- New reporting infrastructure, allowing WSPR and FST4W spots to be sent to wsprnet.org
- Add some basic filtering capabilities to the map
- New arguments to the `openwebrx` command-line to facilitate the administration of users (try `openwebrx admin`)
- Default bandwidth changes:
- "WFM" changed to 150kHz
- "Packet" (APRS) changed to 12.5kHz
- Configuration rework:
- New: fully web-based configuration interface
- System configuration parameters have been moved to a new, separate `openwebrx.conf` file
- Remaining parameters are now editable in the web configuration
- Existing `config_webrx.py` files will still be read, but changes made in the web configuration will be written to
a new storage system
- Added upload of avatar and panorama image via web configuration
- New devices supported:
- HPSDR devices (Hermes Lite 2)
- HPSDR devices (Hermes Lite 2) thanks to @jancona
- BBRF103 / RX666 / RX888 devices supported by libsddc
- Devices using the EB200 protocol
- R&S devices using the EB200 or Ammos protocols
**0.20.3**
- Fix a compatibility issue with python versions <= 3.6
**0.20.2**
- Fix a security problem that allowed arbitrary commands to be executed on the receiver
([See github issue #215](https://github.com/jketterl/openwebrx/issues/215))
**0.20.1**
- Remove broken OSM map fallback

View File

@ -13,8 +13,7 @@ It has the following features:
- it works in Google Chrome, Chromium and Mozilla Firefox
- supports a wide range of [SDR hardware](https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices)
- Multiple SDR devices can be used simultaneously
- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag)
- [dsd](https://github.com/f4exb/dsdcc) based demodulators (D-Star, NXDN)
- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag, D-Star, NXDN)
- [wsjt-x](https://physics.princeton.edu/pulsar/k1jt/wsjtx.html) based demodulators (FT8, FT4, WSPR, JT65, JT9, FST4,
FST4W)
- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets

View File

@ -6,7 +6,8 @@
"frequencies": {
"fst4": 136000,
"fst4w": 136000
}
},
"tags": ["hamradio"]
},
{
"name": "630m",
@ -15,7 +16,8 @@
"frequencies": {
"fst4": 474200,
"fst4w": 474200
}
},
"tags": ["hamradio"]
},
{
"name": "160m",
@ -30,7 +32,8 @@
"js8": 1842000,
"fst4": 1839000,
"fst4w": 1836800
}
},
"tags": ["hamradio"]
},
{
"name": "80m",
@ -44,7 +47,8 @@
"jt9": 3572000,
"ft4": [3568000, 3575000],
"js8": 3578000
}
},
"tags": ["hamradio"]
},
{
"name": "60m",
@ -53,7 +57,8 @@
"frequencies": {
"ft8": 5357000,
"wspr": 5364700
}
},
"tags": ["hamradio"]
},
{
"name": "40m",
@ -67,7 +72,8 @@
"jt9": 7078000,
"ft4": 7047500,
"js8": 7078000
}
},
"tags": ["hamradio"]
},
{
"name": "30m",
@ -81,7 +87,8 @@
"jt9": 10140000,
"ft4": 10140000,
"js8": 10130000
}
},
"tags": ["hamradio"]
},
{
"name": "20m",
@ -95,7 +102,8 @@
"jt9": 14078000,
"ft4": 14080000,
"js8": 14078000
}
},
"tags": ["hamradio"]
},
{
"name": "17m",
@ -109,7 +117,8 @@
"jt9": 18104000,
"ft4": 18104000,
"js8": 18104000
}
},
"tags": ["hamradio"]
},
{
"name": "15m",
@ -123,7 +132,8 @@
"jt9": 21078000,
"ft4": 21140000,
"js8": 21078000
}
},
"tags": ["hamradio"]
},
{
"name": "12m",
@ -137,7 +147,8 @@
"jt9": 24919000,
"ft4": 24919000,
"js8": 24922000
}
},
"tags": ["hamradio"]
},
{
"name": "10m",
@ -151,7 +162,8 @@
"jt9": 28078000,
"ft4": 28180000,
"js8": 28078000
}
},
"tags": ["hamradio"]
},
{
"name": "6m",
@ -164,8 +176,10 @@
"jt65": 50310000,
"jt9": 50312000,
"ft4": 50318000,
"js8": 50318000
}
"js8": 50318000,
"q65": [50211000, 50275000]
},
"tags": ["hamradio"]
},
{
"name": "4m",
@ -173,7 +187,8 @@
"upper_bound": 70200000,
"frequencies": {
"wspr": 70091000
}
},
"tags": ["hamradio"]
},
{
"name": "2m",
@ -184,110 +199,169 @@
"ft8": 144174000,
"ft4": 144170000,
"jt65": 144120000,
"packet": 144800000
}
"packet": 144800000,
"q65": 144116000
},
"tags": ["hamradio"]
},
{
"name": "70cm",
"lower_bound": 430000000,
"upper_bound": 440000000,
"frequencies": {
"pocsag": 439987500
}
"pocsag": 439987500,
"q65": 432065000
},
"tags": ["hamradio"]
},
{
"name": "23cm",
"lower_bound": 1240000000,
"upper_bound": 1300000000
"upper_bound": 1300000000,
"frequencies": {
"q65": 1296065000
},
"tags": ["hamradio"]
},
{
"name": "13cm",
"lower_bound": 2320000000,
"upper_bound": 2450000000
"upper_bound": 2450000000,
"frequencies": {
"q65": [2301065000, 2304065000, 2320065000]
},
"tags": ["hamradio"]
},
{
"name": "9cm",
"lower_bound": 3400000000,
"upper_bound": 3475000000
"upper_bound": 3475000000,
"frequencies": {
"q65": 3400065000
},
"tags": ["hamradio"]
},
{
"name": "6cm",
"lower_bound": 5650000000,
"upper_bound": 5850000000
"upper_bound": 5850000000,
"frequencies": {
"q65": 5760200000
},
"tags": ["hamradio"]
},
{
"name": "3cm",
"lower_bound": 10000000000,
"upper_bound": 10500000000
"upper_bound": 10500000000,
"frequencies": {
"q65": 10368200000
},
"tags": ["hamradio"]
},
{
"name": "120m Broadcast",
"lower_bound": 2300000,
"upper_bound": 2495000
"upper_bound": 2495000,
"tags": ["broadcast"]
},
{
"name": "90m Broadcast",
"lower_bound": 3200000,
"upper_bound": 3400000
"upper_bound": 3400000,
"tags": ["broadcast"]
},
{
"name": "75m Broadcast",
"lower_bound": 3900000,
"upper_bound": 4000000
"upper_bound": 4000000,
"tags": ["broadcast"]
},
{
"name": "60m Broadcast",
"lower_bound": 4750000,
"upper_bound": 4995000
"upper_bound": 4995000,
"tags": ["broadcast"]
},
{
"name": "49m Broadcast",
"lower_bound": 5900000,
"upper_bound": 6200000
"upper_bound": 6200000,
"tags": ["broadcast"]
},
{
"name": "41m Broadcast",
"lower_bound": 7200000,
"upper_bound": 7450000
"upper_bound": 7450000,
"tags": ["broadcast"]
},
{
"name": "31m Broadcast",
"lower_bound": 9400000,
"upper_bound": 9900000
"upper_bound": 9900000,
"tags": ["broadcast"]
},
{
"name": "25m Broadcast",
"lower_bound": 11600000,
"upper_bound": 12100000
"upper_bound": 12100000,
"tags": ["broadcast"]
},
{
"name": "22m Broadcast",
"lower_bound": 13570000,
"upper_bound": 13870000
"upper_bound": 13870000,
"tags": ["broadcast"]
},
{
"name": "19m Broadcast",
"lower_bound": 15100000,
"upper_bound": 15830000
"upper_bound": 15830000,
"tags": ["broadcast"]
},
{
"name": "16m Broadcast",
"lower_bound": 17480000,
"upper_bound": 17900000
"upper_bound": 17900000,
"tags": ["broadcast"]
},
{
"name": "15m Broadcast",
"lower_bound": 18900000,
"upper_bound": 19020000
"upper_bound": 19020000,
"tags": ["broadcast"]
},
{
"name": "13m Broadcast",
"lower_bound": 21450000,
"upper_bound": 21850000
"upper_bound": 21850000,
"tags": ["broadcast"]
},
{
"name": "11m Broadcast",
"lower_bound": 25670000,
"upper_bound": 26100000
"upper_bound": 26100000,
"tags": ["broadcast"]
},
{
"name": "FM Broadcast",
"lower_bound": 87500000,
"upper_bound": 108000000,
"tags": ["broadcast"]
},
{
"name": "11m CB",
"lower_bound": 26965000,
"upper_bound": 27405000,
"frequencies": {
"js8": 27245000
},
"tags": ["public"]
},
{
"name": "PMR446",
"lower_bound": 446000000,
"upper_bound": 446200000,
"tags": ["public"]
}
]

View File

@ -1,217 +0,0 @@
[
{
"name": "DB0ZU",
"frequency": 145725000,
"modulation": "nfm"
},
{
"name": "DB0ZM",
"frequency": 145750000,
"modulation": "nfm"
},
{
"name": "DM0ULR",
"frequency": 145787500,
"modulation": "nfm"
},
{
"name": "DB0EL",
"frequency": 439275000,
"modulation": "nfm"
},
{
"name": "DB0NJ",
"frequency": 438775000,
"modulation": "nfm"
},
{
"name": "DB0NJ",
"frequency": 439437500,
"modulation": "dmr"
},
{
"name": "DB0UFO",
"frequency": 438312500,
"modulation": "dmr"
},
{
"name": "DB0PV",
"frequency": 438525000,
"modulation": "ysf"
},
{
"name": "DB0BZA",
"frequency": 438412500,
"modulation": "ysf"
},
{
"name": "DB0OSH",
"frequency": 438250000,
"modulation": "ysf"
},
{
"name": "DB0ULR",
"frequency": 439325000,
"modulation": "nfm"
},
{
"name": "DB0ZU",
"frequency": 438850000,
"modulation": "nfm"
},
{
"name": "DB0ISW",
"frequency": 438650000,
"modulation": "nfm"
},
{
"name": "Radio DARC",
"frequency": 6070000,
"modulation": "am"
},
{
"name": "DB0TVM",
"frequency": 439575000,
"modulation": "dstar"
},
{
"name": "DB0TVM",
"frequency": 439800000,
"modulation": "dmr"
},
{
"name": "DB0TR",
"frequency": 438700000,
"modulation": "nfm"
},
{
"name": "DB0PME",
"frequency": 439825000,
"modulation": "dmr"
},
{
"name": "DB0HKN",
"frequency": 438300000,
"modulation": "dmr"
},
{
"name": "OE2XHM",
"frequency": 438825000,
"modulation": "nfm"
},
{
"name": "DM0WW",
"frequency": 438962500,
"modulation": "dmr"
},
{
"name": "OE7XXR",
"frequency": 438200000,
"modulation": "dstar"
},
{
"name": "OE2XZR",
"frequency": 439000000,
"modulation": "dstar"
},
{
"name": "DB0OAL",
"frequency": 439912500,
"modulation": "dmr"
},
{
"name": "DB0AAT",
"frequency": 439550000,
"modulation": "dmr"
},
{
"name": "DB0FSG",
"frequency": 439937500,
"modulation": "dmr"
},
{
"name": "DB0ULR",
"frequency": 145575000,
"modulation": "nfm"
},
{
"name": "DB0RDH",
"frequency": 145737500,
"modulation": "dstar"
},
{
"name": "DM0GAP",
"frequency": 145612500,
"modulation": "nfm"
},
{
"name": "DB0XF",
"frequency": 145600000,
"modulation": "nfm"
},
{
"name": "DB0TOL",
"frequency": 145712500,
"modulation": "nfm"
},
{
"name": "DB0TTB",
"frequency": 439587500,
"modulation": "dmr"
},
{
"name": "DB0TRS",
"frequency": 439125000,
"modulation": "nfm"
},
{
"name": "DB0OAL",
"frequency": 438937500,
"modulation": "nfm"
},
{
"name": "DM0ULR",
"frequency": 439337500,
"modulation": "nxdn"
},
{
"name": "DB0MIR",
"frequency": 439300000,
"modulation": "nfm"
},
{
"name": "DB0PM",
"frequency": 439075000,
"modulation": "nfm"
},
{
"name": "DB0CP",
"frequency": 439025000,
"modulation": "nfm"
},
{
"name": "OE7XGR",
"frequency": 438925000,
"modulation": "dmr"
},
{
"name": "DB0TOL",
"frequency": 438725000,
"modulation": "nfm"
},
{
"name": "DB0OAL",
"frequency": 438325000,
"modulation": "dstar"
},
{
"name": "DB0ROL",
"frequency": 439237500,
"modulation": "nfm"
},
{
"name": "DB0ABX",
"frequency": 439137500,
"modulation": "nfm"
}
]

View File

@ -32,32 +32,45 @@ config_webrx: configuration options for OpenWebRX
and use them for running your web service with OpenWebRX.)
"""
"""
DEPRECATION notice
As of OpenWebRX 0.21, the configuration system has been completely overhauled.
The configuration of OpenWebRX should now be done in the new web-based
configuration interface exclusively.
Existing configurations can still be used, but their values will be migrated
to the new storage infrastructure as soon as the web configuration is used to
edit them.
The new configuration storage is not intended to be edited manually.
"""
# configuration version. please only modify if you're able to perform the associated migration steps.
version = 3
version = 7
# NOTE: you can find additional information about configuring OpenWebRX in the Wiki:
# https://github.com/jketterl/openwebrx/wiki/Configuration-guide
# ==== Server settings ====
web_port = 8073
max_clients = 20
#max_clients = 20
# ==== Web GUI configuration ====
receiver_name = "[Callsign]"
receiver_location = "Budapest, Hungary"
receiver_asl = 200
receiver_admin = "example@example.com"
receiver_gps = {"lat": 47.000000, "lon": 19.000000}
photo_title = "Panorama of Budapest from Schönherz Zoltán Dormitory"
#receiver_name = "[Callsign]"
#receiver_location = "Budapest, Hungary"
#receiver_asl = 200
#receiver_admin = "example@example.com"
#receiver_gps = {"lat": 47.000000, "lon": 19.000000}
#photo_title = "Panorama of Budapest from Schönherz Zoltán Dormitory"
# photo_desc allows you to put pretty much any HTML you like into the receiver description.
# The lines below should give you some examples of what's possible.
photo_desc = """
You can add your own background photo and receiver information.<br />
Receiver is operated by: <a href="mailto:openwebrx@localhost" target="_blank">Receiver Operator</a><br/>
Device: Receiver Device<br />
Antenna: Receiver Antenna<br />
Website: <a href="http://localhost" target="_blank">http://localhost</a>
"""
#photo_desc = """
#You can add your own background photo and receiver information.<br />
#Receiver is operated by: <a href="mailto:openwebrx@localhost" target="_blank">Receiver Operator</a><br/>
#Device: Receiver Device<br />
#Antenna: Receiver Antenna<br />
#Website: <a href="http://localhost" target="_blank">http://localhost</a>
#"""
# ==== Public receiver listings ====
# You can publish your receiver on online receiver directories, like https://www.receiverbook.de
@ -65,7 +78,7 @@ Website: <a href="http://localhost" target="_blank">http://localhost</a>
# Please note that you not share your receiver keys publicly since anyone that obtains your receiver key can take over
# your public listing.
# Your receiver keys should be placed into this array:
receiver_keys = []
#receiver_keys = []
# If you list your receiver on multiple sites, you can place all your keys into the array above, or you can append
# keys to the arraylike this:
# receiver_keys += ["my-receiver-key"]
@ -73,30 +86,29 @@ receiver_keys = []
# If you're not sure, simply copy & paste the code you received from your listing site below this line:
# ==== DSP/RX settings ====
fft_fps = 9
fft_size = 4096 # Should be power of 2
fft_voverlap_factor = (
0.3 # If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the diagram.
)
#fft_fps = 9
#fft_size = 4096 # Should be power of 2
#fft_voverlap_factor = (
# 0.3 # If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the diagram.
#)
audio_compression = "adpcm" # valid values: "adpcm", "none"
fft_compression = "adpcm" # valid values: "adpcm", "none"
#audio_compression = "adpcm" # valid values: "adpcm", "none"
#fft_compression = "adpcm" # valid values: "adpcm", "none"
# Tau setting for WFM (broadcast FM) deemphasis\
# Quote from wikipedia https://en.wikipedia.org/wiki/FM_broadcasting#Pre-emphasis_and_de-emphasis
# "In most of the world a 50 µs time constant is used. In the Americas and South Korea, 75 µs is used"
# Enable one of the following lines, depending on your location:
# wfm_deemphasis_tau = 75e-6 # for US and South Korea
wfm_deemphasis_tau = 50e-6 # for the rest of the world
#wfm_deemphasis_tau = 50e-6 # for the rest of the world
digimodes_enable = True # Decoding digimodes come with higher CPU usage.
digimodes_fft_size = 2048
#digimodes_fft_size = 2048
# determines the quality, and thus the cpu usage, for the ambe codec used by digital voice modes
# if you're running on a Raspi (up to 3B+) you'll want to leave this on 1
digital_voice_unvoiced_quality = 1
#digital_voice_unvoiced_quality = 1
# enables lookup of DMR ids using the radioid api
digital_voice_dmr_id_lookup = True
#digital_voice_dmr_id_lookup = True
"""
Note: if you experience audio underruns while CPU usage is 100%, you can:
@ -116,232 +128,230 @@ Note: if you experience audio underruns while CPU usage is 100%, you can:
# Currently supported types of sdr receivers:
# "rtl_sdr", "rtl_sdr_soapy", "sdrplay", "hackrf", "airspy", "airspyhf", "fifi_sdr",
# "perseussdr", "lime_sdr", "pluto_sdr", "soapy_remote", "hpsdr", "red_pitaya", "uhd",
# "radioberry", "fcdpp", "rtl_tcp", "sddc", "eb200"
# "perseussdr", "lime_sdr", "pluto_sdr", "soapy_remote", "hpsdr", "uhd",
# "radioberry", "fcdpp", "rtl_tcp", "sddc", "runds"
# For more details on specific types, please checkout the wiki:
# https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices
sdrs = {
"rtlsdr": {
"name": "RTL-SDR USB Stick",
"type": "rtl_sdr",
"ppm": 0,
# you can change this if you use an upconverter. formula is:
# center_freq + lfo_offset = actual frequency on the sdr
# "lfo_offset": 0,
"profiles": {
"70cm": {
"name": "70cm Relais",
"center_freq": 438800000,
"rf_gain": 29,
"samp_rate": 2400000,
"start_freq": 439275000,
"start_mod": "nfm",
},
"2m": {
"name": "2m komplett",
"center_freq": 145000000,
"rf_gain": 29,
"samp_rate": 2048000,
"start_freq": 145725000,
"start_mod": "nfm",
},
},
},
"airspy": {
"name": "Airspy HF+",
"type": "airspyhf",
"ppm": 0,
"rf_gain": "auto",
"profiles": {
"20m": {
"name": "20m",
"center_freq": 14150000,
"samp_rate": 384000,
"start_freq": 14070000,
"start_mod": "usb",
},
"30m": {
"name": "30m",
"center_freq": 10125000,
"samp_rate": 192000,
"start_freq": 10142000,
"start_mod": "usb",
},
"40m": {
"name": "40m",
"center_freq": 7100000,
"samp_rate": 256000,
"start_freq": 7070000,
"start_mod": "lsb",
},
"80m": {
"name": "80m",
"center_freq": 3650000,
"samp_rate": 384000,
"start_freq": 3570000,
"start_mod": "lsb",
},
"49m": {
"name": "49m Broadcast",
"center_freq": 6050000,
"samp_rate": 384000,
"start_freq": 6070000,
"start_mod": "am",
},
},
},
"sdrplay": {
"name": "SDRPlay RSP2",
"type": "sdrplay",
"ppm": 0,
"antenna": "Antenna A",
"profiles": {
"20m": {
"name": "20m",
"center_freq": 14150000,
"rf_gain": 0,
"samp_rate": 500000,
"start_freq": 14070000,
"start_mod": "usb",
},
"30m": {
"name": "30m",
"center_freq": 10125000,
"rf_gain": 0,
"samp_rate": 250000,
"start_freq": 10142000,
"start_mod": "usb",
},
"40m": {
"name": "40m",
"center_freq": 7100000,
"rf_gain": 0,
"samp_rate": 500000,
"start_freq": 7070000,
"start_mod": "lsb",
},
"80m": {
"name": "80m",
"center_freq": 3650000,
"rf_gain": 0,
"samp_rate": 500000,
"start_freq": 3570000,
"start_mod": "lsb",
},
"49m": {
"name": "49m Broadcast",
"center_freq": 6000000,
"rf_gain": 0,
"samp_rate": 500000,
"start_freq": 6070000,
"start_mod": "am",
},
},
},
}
#sdrs = {
# "rtlsdr": {
# "name": "RTL-SDR USB Stick",
# "type": "rtl_sdr",
# "ppm": 0,
# # you can change this if you use an upconverter. formula is:
# # center_freq + lfo_offset = actual frequency on the sdr
# # "lfo_offset": 0,
# "profiles": {
# "70cm": {
# "name": "70cm Relais",
# "center_freq": 438800000,
# "rf_gain": 29,
# "samp_rate": 2400000,
# "start_freq": 439275000,
# "start_mod": "nfm",
# },
# "2m": {
# "name": "2m komplett",
# "center_freq": 145000000,
# "rf_gain": 29,
# "samp_rate": 2048000,
# "start_freq": 145725000,
# "start_mod": "nfm",
# },
# },
# },
# "airspy": {
# "name": "Airspy HF+",
# "type": "airspyhf",
# "ppm": 0,
# "rf_gain": "auto",
# "profiles": {
# "20m": {
# "name": "20m",
# "center_freq": 14150000,
# "samp_rate": 384000,
# "start_freq": 14070000,
# "start_mod": "usb",
# },
# "30m": {
# "name": "30m",
# "center_freq": 10125000,
# "samp_rate": 192000,
# "start_freq": 10142000,
# "start_mod": "usb",
# },
# "40m": {
# "name": "40m",
# "center_freq": 7100000,
# "samp_rate": 256000,
# "start_freq": 7070000,
# "start_mod": "lsb",
# },
# "80m": {
# "name": "80m",
# "center_freq": 3650000,
# "samp_rate": 384000,
# "start_freq": 3570000,
# "start_mod": "lsb",
# },
# "49m": {
# "name": "49m Broadcast",
# "center_freq": 6050000,
# "samp_rate": 384000,
# "start_freq": 6070000,
# "start_mod": "am",
# },
# },
# },
# "sdrplay": {
# "name": "SDRPlay RSP2",
# "type": "sdrplay",
# "ppm": 0,
# "antenna": "Antenna A",
# "profiles": {
# "20m": {
# "name": "20m",
# "center_freq": 14150000,
# "rf_gain": 0,
# "samp_rate": 500000,
# "start_freq": 14070000,
# "start_mod": "usb",
# },
# "30m": {
# "name": "30m",
# "center_freq": 10125000,
# "rf_gain": 0,
# "samp_rate": 250000,
# "start_freq": 10142000,
# "start_mod": "usb",
# },
# "40m": {
# "name": "40m",
# "center_freq": 7100000,
# "rf_gain": 0,
# "samp_rate": 500000,
# "start_freq": 7070000,
# "start_mod": "lsb",
# },
# "80m": {
# "name": "80m",
# "center_freq": 3650000,
# "rf_gain": 0,
# "samp_rate": 500000,
# "start_freq": 3570000,
# "start_mod": "lsb",
# },
# "49m": {
# "name": "49m Broadcast",
# "center_freq": 6000000,
# "rf_gain": 0,
# "samp_rate": 500000,
# "start_freq": 6070000,
# "start_mod": "am",
# },
# },
# },
#}
# ==== Color themes ====
### google turbo colormap (see: https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html)
waterfall_colors = [0x30123b, 0x311542, 0x33184a, 0x341b51, 0x351e58, 0x36215f, 0x372466, 0x38266c, 0x392973, 0x3a2c79, 0x3b2f80, 0x3c3286, 0x3d358b, 0x3e3891, 0x3e3a97, 0x3f3d9c, 0x4040a2, 0x4043a7, 0x4146ac, 0x4248b1, 0x424bb6, 0x434eba, 0x4351bf, 0x4453c3, 0x4456c7, 0x4559cb, 0x455bcf, 0x455ed3, 0x4561d7, 0x4663da, 0x4666dd, 0x4669e1, 0x466be4, 0x466ee7, 0x4671e9, 0x4673ec, 0x4676ee, 0x4678f1, 0x467bf3, 0x467df5, 0x4680f7, 0x4682f9, 0x4685fa, 0x4587fc, 0x458afd, 0x448cfe, 0x448ffe, 0x4391ff, 0x4294ff, 0x4196ff, 0x3f99ff, 0x3e9bff, 0x3d9efe, 0x3ba1fd, 0x3aa3fd, 0x38a6fb, 0x36a8fa, 0x35abf9, 0x33adf7, 0x31b0f6, 0x2fb2f4, 0x2db5f2, 0x2cb7f0, 0x2ab9ee, 0x28bcec, 0x26beea, 0x25c0e7, 0x23c3e5, 0x21c5e2, 0x20c7e0, 0x1fc9dd, 0x1dccdb, 0x1cced8, 0x1bd0d5, 0x1ad2d3, 0x19d4d0, 0x18d6cd, 0x18d8cb, 0x18dac8, 0x17dbc5, 0x17ddc3, 0x17dfc0, 0x18e0be, 0x18e2bb, 0x19e3b9, 0x1ae5b7, 0x1be6b4, 0x1de8b2, 0x1ee9af, 0x20eaad, 0x22ecaa, 0x24eda7, 0x27eea4, 0x29efa1, 0x2cf09e, 0x2ff19b, 0x32f298, 0x35f394, 0x38f491, 0x3cf58e, 0x3ff68b, 0x43f787, 0x46f884, 0x4af980, 0x4efa7d, 0x51fa79, 0x55fb76, 0x59fc73, 0x5dfc6f, 0x61fd6c, 0x65fd69, 0x69fe65, 0x6dfe62, 0x71fe5f, 0x75ff5c, 0x79ff59, 0x7dff56, 0x80ff53, 0x84ff50, 0x88ff4e, 0x8bff4b, 0x8fff49, 0x92ff46, 0x96ff44, 0x99ff42, 0x9cfe40, 0x9ffe3e, 0xa2fd3d, 0xa4fd3b, 0xa7fc3a, 0xaafc39, 0xacfb38, 0xaffa37, 0xb1f936, 0xb4f835, 0xb7f835, 0xb9f634, 0xbcf534, 0xbff434, 0xc1f334, 0xc4f233, 0xc6f033, 0xc9ef34, 0xcbee34, 0xceec34, 0xd0eb34, 0xd2e934, 0xd5e835, 0xd7e635, 0xd9e435, 0xdbe236, 0xdde136, 0xe0df37, 0xe2dd37, 0xe4db38, 0xe6d938, 0xe7d738, 0xe9d539, 0xebd339, 0xedd139, 0xeecf3a, 0xf0cd3a, 0xf1cb3a, 0xf3c93a, 0xf4c73a, 0xf5c53a, 0xf7c33a, 0xf8c13a, 0xf9bf39, 0xfabd39, 0xfaba38, 0xfbb838, 0xfcb637, 0xfcb436, 0xfdb135, 0xfdaf35, 0xfeac34, 0xfea933, 0xfea732, 0xfea431, 0xffa12f, 0xff9e2e, 0xff9c2d, 0xff992c, 0xfe962b, 0xfe932a, 0xfe9028, 0xfe8d27, 0xfd8a26, 0xfd8724, 0xfc8423, 0xfc8122, 0xfb7e20, 0xfb7b1f, 0xfa781e, 0xf9751c, 0xf8721b, 0xf86f1a, 0xf76c19, 0xf66917, 0xf56616, 0xf46315, 0xf36014, 0xf25d13, 0xf05b11, 0xef5810, 0xee550f, 0xed530e, 0xeb500e, 0xea4e0d, 0xe94b0c, 0xe7490b, 0xe6470a, 0xe4450a, 0xe34209, 0xe14009, 0xdf3e08, 0xde3c07, 0xdc3a07, 0xda3806, 0xd83606, 0xd63405, 0xd43205, 0xd23105, 0xd02f04, 0xce2d04, 0xcc2b03, 0xca2903, 0xc82803, 0xc62602, 0xc32402, 0xc12302, 0xbf2102, 0xbc1f01, 0xba1e01, 0xb71c01, 0xb41b01, 0xb21901, 0xaf1801, 0xac1601, 0xaa1501, 0xa71401, 0xa41201, 0xa11101, 0x9e1001, 0x9b0f01, 0x980d01, 0x950c01, 0x920b01, 0x8e0a01, 0x8b0901, 0x880801, 0x850701, 0x810602, 0x7e0502, 0x7a0402]
#waterfall_scheme = "GoogleTurboWaterfall"
### original theme by teejez:
#waterfall_colors = [0x000000, 0x0000FF, 0x00FFFF, 0x00FF00, 0xFFFF00, 0xFF0000, 0xFF00FF, 0xFFFFFF]
#waterfall_scheme = "TeejeezWaterfall"
### old theme by HA7ILM:
#waterfall_colors = [0x000000, 0x2e6893, 0x69a5d0, 0x214b69, 0x9dc4e0, 0xfff775, 0xff8a8a, 0xb20000]
# waterfall_min_level = -115 #in dB
# waterfall_max_level = 0
# waterfall_auto_level_margin = {"min": 20, "max": 30}
#waterfall_scheme = "Ha7ilmWaterfall"
##For the old colors, you might also want to set [fft_voverlap_factor] to 0.
waterfall_min_level = -88 # in dB
waterfall_max_level = -20
waterfall_auto_level_margin = {"min": 3, "max": 10, "min_range": 50}
### custom waterfall schemes can be configured like this:
#waterfall_scheme = "CustomWaterfall"
#waterfall_colors = [0x0000FF, 0x00FF00, 0xFF0000]
### Waterfall calibration
#waterfall_levels = {"min": -88, "max": -20} # in dB
#waterfall_auto_levels = {"min": 3, "max": 10}
#waterfall_auto_min_range = 50
# Note: When the auto waterfall level button is clicked, the following happens:
# [waterfall_min_level] = [current_min_power_level] - [waterfall_auto_level_margin["min"]]
# [waterfall_max_level] = [current_max_power_level] + [waterfall_auto_level_margin["max"]]
# [waterfall_levels.min] = [current_min_power_level] - [waterfall_auto_levels["min"]]
# [waterfall_levels.max] = [current_max_power_level] + [waterfall_auto_levels["max"]]
#
# ___|________________________________________|____________________________________|________________________________________|___> signal power
# \_waterfall_auto_level_margin["min"]_/ |__ current_min_power_level | \_waterfall_auto_level_margin["max"]_/
# current_max_power_level __|
# ___|__________________________________|____________________________________|__________________________________|___> signal power
# \_waterfall_auto_levels["min"]_/ |__ current_min_power_level | \_waterfall_auto_levels["max"]_/
# current_max_power_level __|
# This setting allows you to modify the precision of the frequency displays in OpenWebRX.
# Set this to the number of digits you would like to see:
frequency_display_precision = 4
# Set this to exponent of 10 to select the most precise digit in Hz you'd like to see
# examples:
# a value of 2 selects 10^2 = 100Hz tuning precision (default):
#tuning_precision = 2
# a value of 1 selects 10^1 = 10Hz tuning precision:
#tuning_precision = 1
# This setting tells the auto-squelch the offset to add to the current signal level to use as the new squelch level.
# Lowering this setting will give you a more sensitive squelch, but it may also cause unwanted squelch openings when
# using the auto squelch.
squelch_auto_margin = 10 # in dB
#squelch_auto_margin = 10 # in dB
# === Experimental settings ===
# Warning! The settings below are very experimental.
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_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.
google_maps_api_key = ""
#google_maps_api_key = ""
# how long should positions be visible on the map?
# they will start fading out after half of that
# in seconds; default: 2 hours
map_position_retention_time = 2 * 60 * 60
#map_position_retention_time = 2 * 60 * 60
# decoder queue configuration
# due to the nature of some operating modes (ft8, ft8, jt9, jt65, wspr and js8), the data is recorded for a given amount
# of time (6 seconds up to 2 minutes) and decoded at the end. this can lead to very high peak loads.
# to mitigate this, the recordings will be queued and processed in sequence.
# the number of workers will limit the total amount of work (one worker will losely occupy one cpu / thread)
decoding_queue_workers = 2
#decoding_queue_workers = 2
# the maximum queue length will cause decodes to be dumped if the workers cannot keep up
# if you are running background services, make sure this number is high enough to accept the task influx during peaks
# i.e. this should be higher than the number of decoding services running at the same time
decoding_queue_length = 10
#decoding_queue_length = 10
# wsjt decoding depth will allow more results, but will also consume more cpu
wsjt_decoding_depth = 3
#wsjt_decoding_depth = 3
# can also be set for each mode separately
# jt65 seems to be somewhat prone to erroneous decodes, this setting handles that to some extent
wsjt_decoding_depths = {"jt65": 1}
#wsjt_decoding_depths = {"jt65": 1}
# FST4 can be transmitted in different intervals. This setting determines which intervals will be decoded.
# available values (in seconds): 15, 30, 60, 120, 300, 900, 1800
fst4_enabled_intervals = [15, 30]
#fst4_enabled_intervals = [15, 30]
# FST4W can be transmitted in different intervals. This setting determines which intervals will be decoded.
# available values (in seconds): 120, 300, 900, 1800
fst4w_enabled_intervals = [120, 300]
#fst4w_enabled_intervals = [120, 300]
# Q65 allows many combinations of intervals and submodes. This setting determines which combinations will be decoded.
# Please use the mode letter followed by the decode interval in seconds to specify the combinations. For example:
#q65_enabled_combinations = ["A30", "E120", "C60"]
# JS8 comes in different speeds: normal, slow, fast, turbo. This setting controls which ones are enabled.
js8_enabled_profiles = ["normal", "slow"]
#js8_enabled_profiles = ["normal", "slow"]
# JS8 decoding depth; higher value will get more results, but will also consume more cpu
js8_decoding_depth = 3
temporary_directory = "/tmp"
#js8_decoding_depth = 3
# Enable background service for decoding digital data. You can find more information at:
# https://github.com/jketterl/openwebrx/wiki/Background-decoding
services_enabled = False
services_decoders = ["ft8", "ft4", "wspr", "packet"]
#services_enabled = False
#services_decoders = ["ft8", "ft4", "wspr", "packet"]
# === aprs igate settings ===
# If you want to share your APRS decodes with the aprs network, configure these settings accordingly.
# Make sure that you have set services_enabled to true and customize services_decoders to your needs.
aprs_callsign = "N0CALL"
aprs_igate_enabled = False
aprs_igate_server = "euro.aprs2.net"
aprs_igate_password = ""
#aprs_callsign = "N0CALL"
#aprs_igate_enabled = False
#aprs_igate_server = "euro.aprs2.net"
#aprs_igate_password = ""
# beacon uses the receiver_gps setting, so if you enable this, make sure the location is correct there
aprs_igate_beacon = False
# path to the aprs symbols repository (get it here: https://github.com/hessu/aprs-symbols)
aprs_symbols_path = "/usr/share/aprs-symbols/png"
#aprs_igate_beacon = False
# Uncomment the following to customize gateway beacon details reported to the aprs network
# Plese see Dire Wolf's documentation on PBEACON configuration for complete details:
@ -367,18 +377,13 @@ aprs_symbols_path = "/usr/share/aprs-symbols/png"
# === PSK Reporter settings ===
# enable this if you want to upload all ft8, ft4 etc spots to pskreporter.info
# this also uses the receiver_gps setting from above, so make sure it contains a correct locator
pskreporter_enabled = False
pskreporter_callsign = "N0CALL"
#pskreporter_enabled = False
#pskreporter_callsign = "N0CALL"
# optional antenna information, uncomment to enable
#pskreporter_antenna_information = "Dipole"
# === WSPRNet reporting settings
# enable this if you want to upload WSPR spots to wsprnet.ort
# in addition to these settings also make sure that receiver_gps contains your correct location
wsprnet_enabled = False
wsprnet_callsign = "N0CALL"
# === Web admin settings ===
# this feature is experimental at the moment. it should not be enabled on shared receivers since it allows remote
# changes to the receiver settings. enable for testing in controlled environment only.
# webadmin_enabled = False
#wsprnet_enabled = False
#wsprnet_callsign = "N0CALL"

View File

@ -28,10 +28,10 @@ import threading
import math
from functools import partial
from owrx.kiss import KissClient, DirewolfConfig
from owrx.wsjt import Ft8Profile, WsprProfile, Jt9Profile, Jt65Profile, Ft4Profile, Fst4Profile, Fst4wProfile
from owrx.js8 import Js8Profiles
from owrx.audio import AudioChopper
from csdr.output import Output
from owrx.kiss import KissClient, DirewolfConfig, DirewolfConfigSubscriber
from owrx.audio.chopper import AudioChopper
from csdr.pipe import Pipe
@ -40,40 +40,8 @@ import logging
logger = logging.getLogger(__name__)
class output(object):
def send_output(self, t, read_fn):
if not self.supports_type(t):
# TODO rewrite the output mechanism in a way that avoids producing unnecessary data
logger.warning("dumping output of type %s since it is not supported.", t)
threading.Thread(target=self.pump(read_fn, lambda x: None), name="csdr_pump_thread").start()
return
self.receive_output(t, read_fn)
def receive_output(self, t, read_fn):
pass
def pump(self, read, write):
def copy():
run = True
while run:
data = None
try:
data = read()
except ValueError:
pass
if data is None or (isinstance(data, bytes) and len(data) == 0):
run = False
else:
write(data)
return copy
def supports_type(self, t):
return True
class dsp(object):
def __init__(self, output):
class Dsp(DirewolfConfigSubscriber):
def __init__(self, output: Output):
self.pycsdr_enabled = True
self.pycsdr_chain = None
self.buffer = None
@ -98,9 +66,6 @@ class dsp(object):
self.decimation = None
self.last_decimation = None
self.nc_port = None
self.csdr_dynamic_bufsize = False
self.csdr_print_bufsizes = False
self.csdr_through = False
self.squelch_level = -150
self.fft_averages = 50
self.wfm_deemphasis_tau = 50e-6
@ -124,6 +89,7 @@ class dsp(object):
self.secondary_pipe_names = {"secondary_shift_pipe": Pipe.WRITE}
self.secondary_offset_freq = 1000
self.unvoiced_quality = 1
self.codecserver = None
self.modification_lock = threading.Lock()
self.output = output
@ -133,7 +99,7 @@ class dsp(object):
self.is_service = False
self.direwolf_config = None
self.direwolf_port = None
self.direwolf_config_path = None
self.process = None
def setBuffer(self, buffer):
@ -150,10 +116,6 @@ class dsp(object):
def chain(self, which):
chain = ["nc -v 127.0.0.1 {nc_port}"]
if self.csdr_dynamic_bufsize:
chain += ["csdr setbuf {start_bufsize}"]
if self.csdr_through:
chain += ["csdr through"]
chain += ["csdr shift_addfast_cc --fifo {shift_pipe}"]
if self.decimation > 1:
chain += ["csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING"]
@ -199,43 +161,43 @@ class dsp(object):
elif self.isDigitalVoice(which):
chain += ["csdr fmdemod_quadri_cf"]
chain += last_decimation_block
# dsd modes
if which in ["dstar", "nxdn"]:
chain += ["dc_block", "csdr limit_ff", "csdr convert_f_s16"]
if which == "dstar":
chain += ["dsd -fd -i - -o - -u {unvoiced_quality} -g -1 "]
elif which == "nxdn":
chain += ["dsd -fi -i - -o - -u {unvoiced_quality} -g -1 "]
chain += [
"digitalvoice_filter",
"CSDR_FIXED_BUFSIZE=32 csdr agc_s16 --max 30 --initial 3",
"sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
]
chain += ["dc_block"]
# m17
elif which == "m17":
if which == "m17":
chain += [
"csdr limit_ff",
"csdr convert_f_s16",
"m17-demod",
"CSDR_FIXED_BUFSIZE=32 csdr agc_s16 --max 30 --initial 3",
"sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
]
# digiham modes
else:
chain += ["dc_block", "rrc_filter", "gfsk_demodulator"]
if which == "dmr":
# digiham modes
if which == "dstar":
chain += [
"dmr_decoder --fifo {meta_pipe} --control-fifo {dmr_control_pipe}",
"mbe_synthesizer -f -u {unvoiced_quality}",
"fsk_demodulator -s 10",
"dstar_decoder --fifo {meta_pipe}",
"mbe_synthesizer -d {codecserver_arg}",
]
elif which == "ysf":
chain += ["ysf_decoder --fifo {meta_pipe}", "mbe_synthesizer -y -f -u {unvoiced_quality}"]
max_gain = 0.005
chain += [
"digitalvoice_filter -f",
"CSDR_FIXED_BUFSIZE=32 csdr agc_ff --max 0.005 --initial 0.0005",
"sox -t raw -r 8000 -e floating-point -b 32 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
]
elif which == "nxdn":
chain += [
"rrc_filter --narrow",
"gfsk_demodulator --samples 20",
"nxdn_decoder --fifo {meta_pipe}",
"mbe_synthesizer {codecserver_arg}",
]
else:
chain += ["rrc_filter", "gfsk_demodulator"]
if which == "dmr":
chain += [
"dmr_decoder --fifo {meta_pipe} --control-fifo {dmr_control_pipe}",
"mbe_synthesizer {codecserver_arg}",
]
elif which == "ysf":
chain += ["ysf_decoder --fifo {meta_pipe}", "mbe_synthesizer -y {codecserver_arg}"]
chain += ["digitalvoice_filter"]
chain += [
"CSDR_FIXED_BUFSIZE=32 csdr agc_s16 --max 30 --initial 3",
"sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
]
elif which == "am":
chain += ["csdr amdemod_cf", "csdr fastdcblock_ff"]
chain += last_decimation_block
@ -377,14 +339,10 @@ class dsp(object):
if_samp_rate=self.if_samp_rate(),
last_decimation=self.last_decimation,
audio_rate=self.get_audio_rate(),
direwolf_config=self.direwolf_config,
direwolf_config=self.direwolf_config_path,
)
logger.debug("secondary command (demod) = %s", secondary_command_demod)
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"
if self.output.supports_type("secondary_fft"):
secondary_command_fft = " | ".join(self.secondary_chain("fft"))
secondary_command_fft = secondary_command_fft.format(
@ -397,7 +355,7 @@ class dsp(object):
logger.debug("secondary command (fft) = %s", secondary_command_fft)
self.secondary_process_fft = subprocess.Popen(
secondary_command_fft, stdout=subprocess.PIPE, shell=True, start_new_session=True, env=my_env
secondary_command_fft, stdout=subprocess.PIPE, shell=True, start_new_session=True
)
self.output.send_output(
"secondary_fft",
@ -409,38 +367,18 @@ class dsp(object):
# it would block if not read. by piping it to devnull, we avoid a potential pitfall here.
secondary_output = subprocess.DEVNULL if self.isPacket() else subprocess.PIPE
self.secondary_process_demod = subprocess.Popen(
secondary_command_demod, stdout=secondary_output, shell=True, start_new_session=True, env=my_env
secondary_command_demod, stdout=secondary_output, shell=True, start_new_session=True
)
self.secondary_processes_running = True
if self.isWsjtMode():
smd = self.get_secondary_demodulator()
chopper_profiles = None
if smd == "ft8":
chopper_profiles = [Ft8Profile()]
elif smd == "wspr":
chopper_profiles = [WsprProfile()]
elif smd == "jt65":
chopper_profiles = [Jt65Profile()]
elif smd == "jt9":
chopper_profiles = [Jt9Profile()]
elif smd == "ft4":
chopper_profiles = [Ft4Profile()]
elif smd == "fst4":
chopper_profiles = Fst4Profile.getEnabledProfiles()
elif smd == "fst4w":
chopper_profiles = Fst4wProfile.getEnabledProfiles()
if chopper_profiles is not None and len(chopper_profiles):
chopper = AudioChopper(self, self.secondary_process_demod.stdout, *chopper_profiles)
chopper.start()
self.output.send_output("wsjt_demod", chopper.read)
elif self.isJs8():
chopper = AudioChopper(self, self.secondary_process_demod.stdout, *Js8Profiles.getEnabledProfiles())
chopper.start()
self.output.send_output("js8_demod", chopper.read)
if self.isWsjtMode() or self.isJs8():
chopper = AudioChopper(self, self.get_secondary_demodulator())
chopper.send_output("audio", self.secondary_process_demod.stdout.read)
output_type = "js8_demod" if self.isJs8() else "wsjt_demod"
self.output.send_output(output_type, chopper.read)
elif self.isPacket():
# we best get the ax25 packets from the kiss socket
kiss = KissClient(self.direwolf_port)
kiss = KissClient(self.direwolf_config.getPort())
self.output.send_output("packet_demod", kiss.read)
elif self.isPocsag():
self.output.send_output("pocsag_demod", self.secondary_process_demod.stdout.readline)
@ -487,11 +425,16 @@ class dsp(object):
return self.secondary_demodulator
def set_secondary_fft_size(self, secondary_fft_size):
# to change this, restart is required
if self.secondary_fft_size == secondary_fft_size:
return
self.secondary_fft_size = secondary_fft_size
self.restart()
def set_audio_compression(self, what):
if self.audio_compression == what:
return
self.audio_compression = what
self.restart()
def get_audio_bytes_to_read(self):
# desired latency: 5ms
@ -506,6 +449,7 @@ class dsp(object):
if self.fft_compression == what:
return
self.fft_compression = what
self.restart()
def get_secondary_fft_bytes_to_read(self):
if self.fft_compression == "none":
@ -528,6 +472,8 @@ class dsp(object):
(self.decimation, self.last_decimation) = self.get_decimation(self.samp_rate, self.get_audio_rate())
def get_decimation(self, input_rate, output_rate):
if output_rate <= 0:
raise ValueError("invalid output rate: {rate}".format(rate=output_rate))
decimation = 1
target_rate = output_rate
# wideband fm has a much higher frequency deviation (75kHz).
@ -571,7 +517,7 @@ class dsp(object):
def isWsjtMode(self, demodulator=None):
if demodulator is None:
demodulator = self.get_secondary_demodulator()
return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w"]
return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]
def isJs8(self, demodulator=None):
if demodulator is None:
@ -665,7 +611,7 @@ class dsp(object):
# no squelch required on digital voice modes
actual_squelch = (
-150
if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isFreeDV()
if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isFreeDV() or self.isDrm()
else self.squelch_level
)
if self.running:
@ -678,6 +624,15 @@ class dsp(object):
def get_unvoiced_quality(self):
return self.unvoiced_quality
def set_codecserver(self, s):
if self.codecserver == s:
return
self.codecserver = s
self.restart()
def get_codecserver_arg(self):
return "-s {}".format(self.codecserver) if self.codecserver else ""
def set_dmr_filter(self, filter):
if self.has_pipe("dmr_control_pipe"):
self.pipes["dmr_control_pipe"].write("{0}\n".format(filter))
@ -718,27 +673,34 @@ class dsp(object):
def try_create_configs(self, command):
if "{direwolf_config}" in command:
self.direwolf_config = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
self.direwolf_config_path = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
tmp_dir=self.temporary_directory, myid=id(self)
)
self.direwolf_port = KissClient.getFreePort()
file = open(self.direwolf_config, "w")
file.write(DirewolfConfig().getConfig(self.direwolf_port, self.is_service))
self.direwolf_config = DirewolfConfig()
self.direwolf_config.wire(self)
file = open(self.direwolf_config_path, "w")
file.write(self.direwolf_config.getConfig(self.is_service))
file.close()
else:
self.direwolf_config = None
self.direwolf_port = None
self.direwolf_config_path = None
def try_delete_configs(self):
if self.direwolf_config:
if self.direwolf_config is not None:
self.direwolf_config.unwire(self)
self.direwolf_config = None
if self.direwolf_config_path is not None:
try:
os.unlink(self.direwolf_config)
os.unlink(self.direwolf_config_path)
except FileNotFoundError:
# result suits our expectations. fine :)
pass
except Exception:
logger.exception("try_delete_configs()")
self.direwolf_config = None
self.direwolf_config_path = None
def onConfigChanged(self):
self.restart()
def start(self):
with self.modification_lock:
@ -781,19 +743,15 @@ class dsp(object):
output_rate=self.get_output_rate(),
smeter_report_every=int(self.if_samp_rate() / 6000),
unvoiced_quality=self.get_unvoiced_quality(),
codecserver_arg=self.get_codecserver_arg(),
audio_rate=self.get_audio_rate(),
wfm_deemphasis_tau=self.wfm_deemphasis_tau,
)
logger.debug("Command = %s", command)
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"
out = subprocess.PIPE if self.output.supports_type("audio") else subprocess.DEVNULL
self.process = subprocess.Popen(command, stdout=out, shell=True, start_new_session=True, env=my_env)
self.process = subprocess.Popen(command, stdout=out, shell=True, start_new_session=True)
def watch_thread():
rc = self.process.wait()
@ -837,10 +795,6 @@ class dsp(object):
self.output.send_output("meta", read_meta)
if self.csdr_dynamic_bufsize:
self.process.stdout.read(8) # dummy read to skip bufsize & preamble
logger.debug("Note: CSDR_DYNAMIC_BUFSIZE_ON = 1")
def stop(self):
with self.modification_lock:
self.running = False
@ -859,6 +813,7 @@ class dsp(object):
self.stop_secondary_demodulator()
self.try_delete_pipes(self.pipe_names)
self.try_delete_configs()
def restart(self):
if not self.running:

36
csdr/output.py Normal file
View File

@ -0,0 +1,36 @@
import threading
import logging
logger = logging.getLogger(__name__)
class Output(object):
def send_output(self, t, read_fn):
if not self.supports_type(t):
# TODO rewrite the output mechanism in a way that avoids producing unnecessary data
logger.warning("dumping output of type %s since it is not supported.", t)
threading.Thread(target=self.pump(read_fn, lambda x: None), name="csdr_pump_thread").start()
return
self.receive_output(t, read_fn)
def receive_output(self, t, read_fn):
pass
def pump(self, read, write):
def copy():
run = True
while run:
data = None
try:
data = read()
except ValueError:
pass
if data is None or (isinstance(data, bytes) and len(data) == 0):
run = False
else:
write(data)
return copy
def supports_type(self, t):
return True

53
debian/changelog vendored
View File

@ -1,21 +1,60 @@
openwebrx (0.21.0) UNRELEASED; urgency=low
openwebrx (1.1.0) UNRELEASED; urgency=low
* Reworked most graphical elements as SVGs for faster loadtimes and crispier
display on hi-dpi displays
* Updated pipelines to match changes in digiham
* Changed D-Star and NXDN integrations to use new decoder from digiham
* Added D-Star and NXDN metadata display
-- Jakob Ketterl <jakob.ketterl@gmx.de> Sun, 09 May 2021 14:05:00 +0000
openwebrx (1.0.0) buster hirsute; urgency=low
* Introduced `squelch_auto_margin` config option that allows configuring the
auto squelch level
* Removed `port` configuration option; `rtltcp_compat` takes the port number
with the new connectors
* Added support for new WSJT-X modes FST4 and FST4W (only available with
WSJT-X 2.3)
* Added support for new WSJT-X modes FST4, FST4W (only available with WSJT-X
2.3) and Q65 (only available with WSJT-X 2.4)
* Added support for demodulating M17 digital voice signals using
m17-cxx-demod
* New reporting infrastructure, allowing WSPR and FST4W spots to be sent to
wsprnet.org
* Add some basic filtering capabilities to the map
* New arguments to the `openwebrx` command-line to facilitate the
administration of users (try `openwebrx admin`)
* New command-line tool `openwebrx-admin` that facilitates the
administration of users
* Default bandwidth changes:
- "WFM" changed to 150kHz
- "Packet" (APRS) changed to 12.5kHz
* Configuration rework:
- New: fully web-based configuration interface
- System configuration parameters have been moved to a new, separate
`openwebrx.conf` file
- Remaining parameters are now editable in the web configuration
- Existing `config_webrx.py` files will still be read, but changes made in
the web configuration will be written to a new storage system
- Added upload of avatar and panorama image via web configuration
* New devices supported:
- HPSDR devices (Hermes Lite 2) (`"type": "hpsdr"`)
- BBRF103 / RX666 / RX888 devices supported by libsddc (`"type": "sddc"`)
- Devices using the EB200 protocol (`"type": "eb200"`)
- HPSDR devices (Hermes Lite 2) thanks to @jancona
- BBRF103 / RX666 / RX888 devices supported by libsddc
- R&S devices using the EB200 or Ammos protocols
-- Jakob Ketterl <jakob.ketterl@gmx.de> Sun, 11 Oct 2020 21:12:00 +0000
-- Jakob Ketterl <jakob.ketterl@gmx.de> Thu, 06 May 2021 17:22:00 +0000
openwebrx (0.20.3) buster focal; urgency=low
* Fix a compatibility issue with python versions <= 3.6
-- Jakob Ketterl <jakob.ketterl@gmx.de> Tue, 26 Jan 2021 15:28:00 +0000
openwebrx (0.20.2) buster focal; urgency=high
* Fix a security problem that allowed arbitrary commands to be executed on
the receiver (See github issue #215:
https://github.com/jketterl/openwebrx/issues/215)
-- Jakob Ketterl <jakob.ketterl@gmx.de> Sun, 24 Jan 2021 22:50:00 +0000
openwebrx (0.20.1) buster focal; urgency=low

6
debian/control vendored
View File

@ -10,7 +10,7 @@ Vcs-Git: https://github.com/jketterl/openwebrx.git
Package: openwebrx
Architecture: all
Depends: adduser, python3 (>= 3.5), python3-pkg-resources, csdr (>= 0.17), netcat, owrx-connector (>= 0.4), soapysdr-tools, python3-js8py (>= 0.1), ${python3:Depends}, ${misc:Depends}
Recommends: digiham (>= 0.3), dsd (>= 1.7), sox, direwolf (>= 1.4), wsjtx, eb200-connector, hpsdrconnector, aprs-symbols, m17-demod
Depends: adduser, python3 (>= 3.5), python3-pkg-resources, csdr (>= 0.17), netcat, owrx-connector (>= 0.5), soapysdr-tools, python3-js8py (>= 0.1), ${python3:Depends}, ${misc:Depends}
Recommends: digiham (>= 0.5), sox, direwolf (>= 1.4), wsjtx, runds-connector (>= 0.2), hpsdrconnector, aprs-symbols, m17-demod, js8call
Description: multi-user web sdr
Open source, multi-user SDR receiver with a web interface
Open source, multi-user SDR receiver with a web interface

8
debian/openwebrx.config vendored Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
db_get openwebrx/admin_user_configured
if [ "${1:-}" = "reconfigure" ] || [ "${RET}" != true ]; then
db_input high openwebrx/admin_user_password || true
db_go
fi

1
debian/openwebrx.dirs vendored Normal file
View File

@ -0,0 +1 @@
/etc/openwebrx/openwebrx.conf.d

View File

@ -1,5 +1,3 @@
config_webrx.py etc/openwebrx/
bands.json etc/openwebrx/
bookmarks.json etc/openwebrx/
users.json etc/openwebrx/
openwebrx.conf etc/openwebrx/
systemd/openwebrx.service lib/systemd/system/

59
debian/openwebrx.postinst vendored Executable file
View File

@ -0,0 +1,59 @@
#!/bin/bash
. /usr/share/debconf/confmodule
set -euo pipefail
OWRX_USER="openwebrx"
OWRX_DATADIR="/var/lib/openwebrx"
OWRX_USERS_FILE="${OWRX_DATADIR}/users.json"
OWRX_SETTINGS_FILE="${OWRX_DATADIR}/settings.json"
OWRX_BOOKMARKS_FILE="${OWRX_DATADIR}/bookmarks.json"
case "$1" in
configure|reconfigure)
adduser --system --group --no-create-home --home /nonexistent --quiet "${OWRX_USER}"
usermod -aG plugdev "${OWRX_USER}"
# create OpenWebRX data directory and set the correct permissions
if [ ! -d "${OWRX_DATADIR}" ] && [ ! -L "${OWRX_DATADIR}" ]; then mkdir "${OWRX_DATADIR}"; fi
chown "${OWRX_USER}". ${OWRX_DATADIR}
# create empty config files now to avoid permission problems later
if [ ! -e "${OWRX_USERS_FILE}" ]; then
echo "[]" > "${OWRX_USERS_FILE}"
chown "${OWRX_USER}". "${OWRX_USERS_FILE}"
chmod 0600 "${OWRX_USERS_FILE}"
fi
if [ ! -e "${OWRX_SETTINGS_FILE}" ]; then
echo "{}" > "${OWRX_SETTINGS_FILE}"
chown "${OWRX_USER}". "${OWRX_SETTINGS_FILE}"
fi
if [ ! -e "${OWRX_BOOKMARKS_FILE}" ]; then
touch "${OWRX_BOOKMARKS_FILE}"
chown "${OWRX_USER}". "${OWRX_BOOKMARKS_FILE}"
fi
db_get openwebrx/admin_user_password
if [ ! -z "${RET}" ]; then
if ! openwebrx admin --silent hasuser admin; then
# create initial openwebrx user
OWRX_PASSWORD="${RET}" openwebrx admin --noninteractive adduser admin
else
# change existing user's password
OWRX_PASSWORD="${RET}" openwebrx admin --noninteractive resetpassword admin
fi
fi
# remove password from debconf database
db_unregister openwebrx/admin_user_password
# set a marker that admin is configured to avoid future questions
db_set openwebrx/admin_user_configured true
;;
*)
echo "postinst called with unknown argument '$1'" 1>&2
exit 1
;;
esac
#DEBHELPER#

8
debian/openwebrx.postrm vendored Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh -e
if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then
. /usr/share/debconf/confmodule
db_purge
fi
#DEBHELPER#

23
debian/openwebrx.templates vendored Normal file
View File

@ -0,0 +1,23 @@
Template: openwebrx/admin_user_password
Type: password
Description: OpenWebRX "admin" user password:
The system can create a user for the OpenWebRX web configuration interface for
you. Using this user, you will be able to log into the "settings" area of
OpenWebRX to configure your receiver conveniently through your browser.
.
The name of the created user will be "admin".
.
If you do not wish to create a web admin user right now, you can leave this
empty for now. You can return to this prompt at a later time by running the
command "sudo dpkg-reconfigure openwebrx".
.
You can also use the "openwebrx admin" command to create, delete or manage
existing users. More information is available in by running the command
"openwebrx admin --help".
Template: openwebrx/admin_user_configured
Type: boolean
Default: false
Description: OpenWebRX "admin" user previously configured?
Marker used internally by the config scripts to remember if an admin user has
been created.

7
debian/postinst vendored
View File

@ -1,7 +0,0 @@
#!/bin/bash
set -euxo pipefail
adduser --system --group --no-create-home --home /nonexistent --quiet openwebrx
usermod -aG plugdev openwebrx
#DEBHELPER#

View File

@ -2,10 +2,10 @@
set -euo pipefail
ARCH=$(uname -m)
IMAGES="openwebrx-rtlsdr openwebrx-sdrplay openwebrx-hackrf openwebrx-airspy openwebrx-rtlsdr-soapy openwebrx-plutosdr openwebrx-limesdr openwebrx-soapyremote openwebrx-perseus openwebrx-fcdpp openwebrx-radioberry openwebrx-uhd openwebrx-redpitaya openwebrx-rtltcp openwebrx-eb200 openwebrx-hpsdr openwebrx-full openwebrx"
IMAGES="openwebrx-rtlsdr openwebrx-sdrplay openwebrx-hackrf openwebrx-airspy openwebrx-rtlsdr-soapy openwebrx-plutosdr openwebrx-limesdr openwebrx-soapyremote openwebrx-perseus openwebrx-fcdpp openwebrx-radioberry openwebrx-uhd openwebrx-rtltcp openwebrx-runds openwebrx-hpsdr openwebrx-full openwebrx"
ALL_ARCHS="x86_64 armv7l aarch64"
TAG=${TAG:-"latest"}
ARCHTAG="$TAG-$ARCH"
ARCHTAG="${TAG}-${ARCH}"
usage () {
echo "Usage: ${0} [command]"
@ -36,7 +36,7 @@ build () {
push () {
for image in ${IMAGES}; do
docker push jketterl/$image:$ARCHTAG
docker push jketterl/${image}:${ARCHTAG}
done
}
@ -45,11 +45,11 @@ manifest () {
# there's no docker manifest rm command, and the create --amend does not work, so we have to clean up manually
rm -rf "${HOME}/.docker/manifests/docker.io_jketterl_${image}-${TAG}"
IMAGE_LIST=""
for a in $ALL_ARCHS; do
IMAGE_LIST="$IMAGE_LIST jketterl/$image:$TAG-$a"
for a in ${ALL_ARCHS}; do
IMAGE_LIST="${IMAGE_LIST} jketterl/${image}:${TAG}-${a}"
done
docker manifest create jketterl/$image:$TAG $IMAGE_LIST
docker manifest push --purge jketterl/$image:$TAG
docker manifest create jketterl/${image}:${TAG} ${IMAGE_LIST}
docker manifest push --purge jketterl/${image}:${TAG}
done
}

View File

@ -13,12 +13,16 @@ COPY docker/scripts/install-owrx-tools.sh /
RUN /install-owrx-tools.sh && \
rm /install-owrx-tools.sh
COPY docker/files/services/codecserver /etc/services.d/codecserver
ENTRYPOINT ["/init"]
WORKDIR /opt/openwebrx
VOLUME /etc/openwebrx
VOLUME /var/lib/openwebrx
CMD [ "/opt/openwebrx/docker/scripts/run.sh" ]
ENV S6_CMD_ARG0="/opt/openwebrx/docker/scripts/run.sh"
CMD []
EXPOSE 8073

View File

@ -18,10 +18,9 @@ RUN /install-dependencies-rtlsdr.sh &&\
/install-dependencies-fcdpp.sh &&\
/install-dependencies-radioberry.sh &&\
/install-dependencies-uhd.sh &&\
/install-dependencies-redpitaya.sh &&\
/install-dependencies-hpsdr.sh &&\
/install-connectors.sh &&\
/install-dependencies-eb200.sh &&\
/install-dependencies-runds.sh &&\
rm /install-dependencies-*.sh &&\
rm /install-lib.*.patch && \
rm /install-connectors.sh

View File

@ -1,8 +0,0 @@
ARG ARCHTAG
FROM openwebrx-soapysdr-base:$ARCHTAG
COPY docker/scripts/install-dependencies-redpitaya.sh /
RUN /install-dependencies-redpitaya.sh &&\
rm /install-dependencies-redpitaya.sh
COPY . /opt/openwebrx

View File

@ -2,11 +2,11 @@ ARG ARCHTAG
FROM openwebrx-base:$ARCHTAG
COPY docker/scripts/install-connectors.sh \
docker/scripts/install-dependencies-eb200.sh /
docker/scripts/install-dependencies-runds.sh /
RUN /install-connectors.sh &&\
rm /install-connectors.sh && \
/install-dependencies-eb200.sh && \
rm /install-dependencies-eb200.sh
/install-dependencies-runds.sh && \
rm /install-dependencies-runds.sh
COPY . /opt/openwebrx

View File

@ -1,5 +0,0 @@
ARCH=$(uname -m)
IMAGES="openwebrx-rtlsdr openwebrx-sdrplay openwebrx-hackrf openwebrx-airspy openwebrx-rtlsdr-soapy openwebrx-plutosdr openwebrx-limesdr openwebrx-soapyremote openwebrx-perseus openwebrx-fcdpp openwebrx-radioberry openwebrx-uhd openwebrx-redpitaya openwebrx-rtltcp openwebrx-full openwebrx"
ALL_ARCHS="x86_64 armv7l aarch64"
TAG=${TAG:-"latest"}
ARCHTAG="$TAG-$ARCH"

View File

@ -0,0 +1,2 @@
#!/usr/bin/execlineb -P
/usr/local/bin/codecserver

View File

@ -1,18 +1,17 @@
--- CMakeLists.txt.orig 2020-07-21 20:59:55.982026645 +0200
+++ CMakeLists.txt 2020-07-21 21:01:25.444836112 +0200
@@ -80,24 +80,6 @@
--- CMakeLists.txt.orig 2021-03-30 15:28:36.956587995 +0200
+++ CMakeLists.txt 2021-03-30 15:29:45.719326832 +0200
@@ -106,24 +106,6 @@
include (ExternalProject)
-
-#
#
-# build and install hamlib locally so it can be referenced by the
-# WSJT-X build
-#
-ExternalProject_Add (hamlib
- GIT_REPOSITORY ${hamlib_repo}
- GIT_TAG ${hamlib_TAG}
- URL ${CMAKE_CURRENT_SOURCE_DIR}/src/${__hamlib_upstream}
- GIT_SHALLOW False
- URL ${CMAKE_CURRENT_SOURCE_DIR}/src/${__hamlib_upstream}.tar.gz
- URL_HASH MD5=${hamlib_md5sum}
- #UPDATE_COMMAND ${CMAKE_COMMAND} -E env "[ -f ./bootstrap ] && ./bootstrap"
- PATCH_COMMAND ${PATCH_EXECUTABLE} -p1 -N < ${CMAKE_CURRENT_SOURCE_DIR}/hamlib.patch
@ -22,10 +21,11 @@
- STEP_TARGETS update install
- )
-
#
-#
# custom target to make a hamlib source tarball
#
@@ -136,7 +118,6 @@
add_custom_target (hamlib_sources
@@ -161,7 +143,6 @@
# build and optionally install WSJT-X using the hamlib package built
# above
#
@ -33,11 +33,18 @@
ExternalProject_Add (wsjtx
GIT_REPOSITORY ${wsjtx_repo}
GIT_TAG ${WSJTX_TAG}
@@ -160,7 +141,6 @@
@@ -186,14 +167,8 @@
DEPENDEES build
)
-set_target_properties (hamlib PROPERTIES EXCLUDE_FROM_ALL 1)
set_target_properties (wsjtx PROPERTIES EXCLUDE_FROM_ALL 1)
add_dependencies (wsjtx-configure hamlib-install)
-add_dependencies (wsjtx-configure hamlib-install)
-add_dependencies (wsjtx-build hamlib-install)
-add_dependencies (wsjtx-install hamlib-install)
-add_dependencies (wsjtx-package hamlib-install)
-
# export traditional targets
add_custom_target (build ALL DEPENDS wsjtx-build)
add_custom_target (install DEPENDS wsjtx-install)

View File

@ -1,6 +1,6 @@
diff -ur wsjtx-orig/CMake/Modules/Findhamlib.cmake wsjtx/CMake/Modules/Findhamlib.cmake
--- wsjtx-orig/CMake/Modules/Findhamlib.cmake 2020-07-21 21:10:43.124810140 +0200
+++ wsjtx/CMake/Modules/Findhamlib.cmake 2020-07-21 21:11:03.368019114 +0200
--- wsjtx-orig/CMake/Modules/Findhamlib.cmake 2021-05-31 18:56:20.657682124 +0200
+++ wsjtx/CMake/Modules/Findhamlib.cmake 2021-05-31 18:57:03.963994898 +0200
@@ -85,4 +85,4 @@
# Handle the QUIETLY and REQUIRED arguments and set HAMLIB_FOUND to
# TRUE if all listed variables are TRUE
@ -8,9 +8,93 @@ diff -ur wsjtx-orig/CMake/Modules/Findhamlib.cmake wsjtx/CMake/Modules/Findhamli
-find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES hamlib_LIBRARY_DIRS)
+find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES)
diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
--- wsjtx-orig/CMakeLists.txt 2020-07-21 21:10:43.124810140 +0200
+++ wsjtx/CMakeLists.txt 2020-07-21 22:14:04.454639589 +0200
@@ -871,7 +871,7 @@
--- wsjtx-orig/CMakeLists.txt 2021-05-31 18:56:20.657682124 +0200
+++ wsjtx/CMakeLists.txt 2021-05-31 19:08:02.768474060 +0200
@@ -122,7 +122,7 @@
option (WSJT_QDEBUG_TO_FILE "Redirect Qt debuging messages to a trace file.")
option (WSJT_SOFT_KEYING "Apply a ramp to CW keying envelope to reduce transients." ON)
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation.")
-option (WSJT_GENERATE_DOCS "Generate documentation files." ON)
+option (WSJT_GENERATE_DOCS "Generate documentation files.")
option (WSJT_RIG_NONE_CAN_SPLIT "Allow split operation with \"None\" as rig.")
option (WSJT_TRACE_UDP "Debugging option that turns on UDP message protocol diagnostics.")
option (WSJT_BUILD_UTILS "Build simulators and code demonstrators." ON)
@@ -169,74 +169,7 @@
)
set (wsjt_qt_CXXSRCS
- qt_helpers.cpp
- widgets/MessageBox.cpp
- MetaDataRegistry.cpp
- Network/NetworkServerLookup.cpp
revision_utils.cpp
- L10nLoader.cpp
- WFPalette.cpp
- Radio.cpp
- RadioMetaType.cpp
- NonInheritingProcess.cpp
- models/IARURegions.cpp
- models/Bands.cpp
- models/Modes.cpp
- models/FrequencyList.cpp
- models/StationList.cpp
- widgets/FrequencyLineEdit.cpp
- widgets/FrequencyDeltaLineEdit.cpp
- item_delegates/CandidateKeyFilter.cpp
- item_delegates/ForeignKeyDelegate.cpp
- validators/LiveFrequencyValidator.cpp
- GetUserId.cpp
- Audio/AudioDevice.cpp
- Transceiver/Transceiver.cpp
- Transceiver/TransceiverBase.cpp
- Transceiver/EmulateSplitTransceiver.cpp
- Transceiver/TransceiverFactory.cpp
- Transceiver/PollingTransceiver.cpp
- Transceiver/HamlibTransceiver.cpp
- Transceiver/HRDTransceiver.cpp
- Transceiver/DXLabSuiteCommanderTransceiver.cpp
- Network/NetworkMessage.cpp
- Network/MessageClient.cpp
- widgets/LettersSpinBox.cpp
- widgets/HintedSpinBox.cpp
- widgets/RestrictedSpinBox.cpp
- widgets/HelpTextWindow.cpp
- SampleDownloader.cpp
- SampleDownloader/DirectoryDelegate.cpp
- SampleDownloader/Directory.cpp
- SampleDownloader/FileNode.cpp
- SampleDownloader/RemoteFile.cpp
- DisplayManual.cpp
- MultiSettings.cpp
- validators/MaidenheadLocatorValidator.cpp
- validators/CallsignValidator.cpp
- widgets/SplashScreen.cpp
- EqualizationToolsDialog.cpp
- widgets/DoubleClickablePushButton.cpp
- widgets/DoubleClickableRadioButton.cpp
- Network/LotWUsers.cpp
- models/DecodeHighlightingModel.cpp
- widgets/DecodeHighlightingListView.cpp
- models/FoxLog.cpp
- widgets/AbstractLogWindow.cpp
- widgets/FoxLogWindow.cpp
- widgets/CabrilloLogWindow.cpp
- item_delegates/CallsignDelegate.cpp
- item_delegates/MaidenheadLocatorDelegate.cpp
- item_delegates/FrequencyDelegate.cpp
- item_delegates/FrequencyDeltaDelegate.cpp
- item_delegates/SQLiteDateTimeDelegate.cpp
- models/CabrilloLog.cpp
- logbook/AD1CCty.cpp
- logbook/WorkedBefore.cpp
- logbook/Multiplier.cpp
- Network/NetworkAccessManager.cpp
- widgets/LazyFillComboBox.cpp
- widgets/CheckableItemComboBox.cpp
)
set (wsjt_qtmm_CXXSRCS
@@ -857,7 +790,7 @@
#
# libhamlib setup
#
@ -19,31 +103,47 @@ diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
find_package (hamlib 3 REQUIRED)
find_program (RIGCTL_EXE rigctl)
find_program (RIGCTLD_EXE rigctld)
@@ -1348,53 +1348,10 @@
@@ -895,9 +828,6 @@
if (WSJT_GENERATE_DOCS)
add_subdirectory (doc)
endif (WSJT_GENERATE_DOCS)
-if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
- add_subdirectory (tests)
-endif ()
endif(WSJT_BUILD_UTILS)
#
# Library building setup
@@ -1380,60 +1310,6 @@
target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
endif (${OPENMP_FOUND} OR APPLE)
-# build the main application
-generate_version_info (wsjtx_VERSION_RESOURCES
- NAME wsjtx
- BUNDLE ${PROJECT_BUNDLE_NAME}
- ICON ${WSJTX_ICON_FILE}
- )
-
-add_executable (wsjtx MACOSX_BUNDLE
- ${wsjtx_CXXSRCS}
- ${wsjtx_GENUISRCS}
- wsjtx.rc
- ${WSJTX_ICON_FILE}
- ${wsjtx_RESOURCES_RCC}
- ${wsjtx_VERSION_RESOURCES}
- )
-
if (WSJT_CREATE_WINMAIN)
set_target_properties (wsjtx PROPERTIES WIN32_EXECUTABLE ON)
endif (WSJT_CREATE_WINMAIN)
-if (WSJT_CREATE_WINMAIN)
- set_target_properties (wsjtx PROPERTIES WIN32_EXECUTABLE ON)
-endif (WSJT_CREATE_WINMAIN)
-
-set_target_properties (wsjtx PROPERTIES
- MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Darwin/Info.plist.in"
- MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
- MACOSX_BUNDLE_INFO_STRING "${PROJECT_DESCRIPTION}"
- MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
- MACOSX_BUNDLE_BUNDLE_VERSION ${wsjtx_VERSION}
- MACOSX_BUNDLE_SHORT_VERSION_STRING "v${wsjtx_VERSION}"
- MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${wsjtx_VERSION}"
- MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
- MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}
- MACOSX_BUNDLE_SHORT_VERSION_STRING "v${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
- MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${SCS_VERSION_STR}"
- MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_BUNDLE_NAME}"
- MACOSX_BUNDLE_BUNDLE_EXECUTABLE_NAME "${PROJECT_NAME}"
- MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT}"
- MACOSX_BUNDLE_GUI_IDENTIFIER "org.k1jt.wsjtx"
@ -51,9 +151,9 @@ diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
-
-target_include_directories (wsjtx PRIVATE ${FFTW3_INCLUDE_DIRS})
-if (APPLE)
- target_link_libraries (wsjtx Qt5::SerialPort wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
- target_link_libraries (wsjtx wsjt_fort)
-else ()
- target_link_libraries (wsjtx Qt5::SerialPort wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
- target_link_libraries (wsjtx wsjt_fort_omp)
- if (OpenMP_C_FLAGS)
- set_target_properties (wsjtx PROPERTIES
- COMPILE_FLAGS "${OpenMP_C_FLAGS}"
@ -65,18 +165,42 @@ diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
- )
- if (WIN32)
- set_target_properties (wsjtx PROPERTIES
- LINK_FLAGS -Wl,--stack,16777216
- LINK_FLAGS -Wl,--stack,0x1000000,--heap,0x20000000
- )
- endif ()
-endif ()
-target_link_libraries (wsjtx Qt5::SerialPort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES} ${LIBM_LIBRARIES})
-
# make a library for WSJT-X UDP servers
# add_library (wsjtx_udp SHARED ${UDP_library_CXXSRCS})
add_library (wsjtx_udp-static STATIC ${UDP_library_CXXSRCS})
@@ -1437,24 +1394,9 @@
set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON)
endif (WSJT_CREATE_WINMAIN)
@@ -1473,47 +1349,9 @@
add_executable (wsjtx_app_version AppVersion/AppVersion.cpp ${wsjtx_app_version_VERSION_RESOURCES})
target_link_libraries (wsjtx_app_version wsjt_qt)
-generate_version_info (message_aggregator_VERSION_RESOURCES
- NAME message_aggregator
- BUNDLE ${PROJECT_BUNDLE_NAME}
- ICON ${WSJTX_ICON_FILE}
- FILE_DESCRIPTION "Example WSJT-X UDP Message Protocol application"
- )
-add_resources (message_aggregator_RESOURCES /qss ${message_aggregator_STYLESHEETS})
-configure_file (UDPExamples/message_aggregator.qrc.in message_aggregator.qrc @ONLY)
-qt5_add_resources (message_aggregator_RESOURCES_RCC
- ${CMAKE_CURRENT_BINARY_DIR}/message_aggregator.qrc
- contrib/QDarkStyleSheet/qdarkstyle/style.qrc
- )
-add_executable (message_aggregator
- ${message_aggregator_CXXSRCS}
- ${message_aggregator_RESOURCES_RCC}
- ${message_aggregator_VERSION_RESOURCES}
- )
-target_link_libraries (message_aggregator wsjt_qt Qt5::Widgets wsjtx_udp-static)
-
-if (WSJT_CREATE_WINMAIN)
- set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON)
-endif (WSJT_CREATE_WINMAIN)
-
-if (UNIX)
- if (NOT WSJT_SKIP_MANPAGES)
- add_subdirectory (manpages)
@ -98,21 +222,21 @@ diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
# install (TARGETS wsjtx_udp EXPORT udp
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
@@ -1473,12 +1415,7 @@
@@ -1532,12 +1370,7 @@
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wsjtx
# )
-install (TARGETS udp_daemon message_aggregator
-install (TARGETS udp_daemon message_aggregator wsjtx_app_version
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
- BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
- )
-
-install (TARGETS jt9 wsprd fmtave fcal fmeasure
+install (TARGETS jt9 wsprd
+install (TARGETS wsjtx_app_version jt9 wsprd
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
)
@@ -1491,39 +1428,6 @@
@@ -1549,38 +1382,6 @@
)
endif(WSJT_BUILD_UTILS)
@ -143,13 +267,50 @@ diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
- AUTHORS
- THANKS
- NEWS
- INSTALL
- BUGS
- DESTINATION ${CMAKE_INSTALL_DOCDIR}
- #COMPONENT runtime
- )
-
install (FILES
contrib/Ephemeris/JPLEPH
DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
Only in wsjtx: .idea
cty.dat
cty.dat_copyright.txt
@@ -1589,13 +1390,6 @@
#COMPONENT runtime
)
-install (DIRECTORY
- example_log_configurations
- DESTINATION ${CMAKE_INSTALL_DOCDIR}
- FILES_MATCHING REGEX "^.*[^~]$"
- #COMPONENT runtime
- )
-
#
# Mac installer files
#
@@ -1648,22 +1442,6 @@
)
-if (NOT WIN32 AND NOT APPLE)
- # install a desktop file so wsjtx appears in the application start
- # menu with an icon
- install (
- FILES wsjtx.desktop message_aggregator.desktop
- DESTINATION share/applications
- #COMPONENT runtime
- )
- install (
- FILES icons/Unix/wsjtx_icon.png
- DESTINATION share/pixmaps
- #COMPONENT runtime
- )
-endif (NOT WIN32 AND NOT APPLE)
-
-
#
# bundle fixup only done in non-Debug configurations
#
Only in wsjtx/: CMakeLists.txt.orig
Only in wsjtx/: .idea

View File

@ -24,8 +24,8 @@ apt-get update
apt-get -y install --no-install-recommends $BUILD_PACKAGES
git clone https://github.com/jketterl/owrx_connector.git
# latest develop as of 2020-11-28 (int32 samples; debhelper)
cmakebuild owrx_connector 87a2fcc54e221aad71ec0700737ca7f385c388de
# latest develop as of 2021-05-18 (individual connector versions)
cmakebuild owrx_connector 167324219813f61b2be8164eb4acb1237ba2c304
apt-get -y purge --autoremove $BUILD_PACKAGES
apt-get clean

View File

@ -1,32 +0,0 @@
#!/bin/bash
set -euo pipefail
export MAKEFLAGS="-j4"
function cmakebuild() {
cd $1
if [[ ! -z "${2:-}" ]]; then
git checkout $2
fi
mkdir build
cd build
cmake ..
make
make install
cd ../..
rm -rf $1
}
cd /tmp
STATIC_PACKAGES=""
BUILD_PACKAGES="git cmake make gcc g++"
apt-get update
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
git clone https://github.com/pothosware/SoapyRedPitaya.git
cmakebuild SoapyRedPitaya soapy-redpitaya-0.1.1
SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
apt-get clean
rm -rf /var/lib/apt/lists/*

View File

@ -24,9 +24,9 @@ BUILD_PACKAGES="git cmake make gcc g++ pkg-config"
apt-get update
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
git clone https://github.com/jketterl/eb200_connector.git
# latest from develop as of 2020-12-01
cmakebuild eb200_connector 9c8313770c1072df72d2fdb85307ca206c29c60a
git clone https://github.com/jketterl/runds_connector.git
# latest develop as of 2021-05-18 (individual connector versions)
cmakebuild runds_connector adfa04bee4ee36852feb07e7b845be2c3d3f97c7
apt-get -y purge --autoremove $BUILD_PACKAGES
apt-get clean

View File

@ -48,9 +48,9 @@ cd ..
rm -rf sdrplay
rm $BINARY
git clone https://github.com/SDRplay/SoapySDRPlay.git
# latest from master as of 2020-09-04
cmakebuild SoapySDRPlay 105f8a6b3d449982d7ef860790c201aa066b8fa9
git clone https://github.com/pothosware/SoapySDRPlay3.git
# latest from master as of 2021-06-19 (reliability fixes)
cmakebuild SoapySDRPlay3 a869f25364a1f0d5b16169ff908aa21a2ace475d
SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
apt-get clean

View File

@ -18,8 +18,8 @@ function cmakebuild() {
cd /tmp
STATIC_PACKAGES="sox libfftw3-bin python3 python3-setuptools netcat-openbsd libsndfile1 liblapack3 libusb-1.0-0 libqt5core5a libreadline7 libgfortran4 libgomp1 libasound2 libudev1 ca-certificates libqt5gui5 libqt5sql5 libqt5printsupport5 libpulse0 libfaad2 libopus0 libboost-program-options1.67.0"
BUILD_PACKAGES="wget git libsndfile1-dev libfftw3-dev cmake make gcc g++ liblapack-dev texinfo gfortran libusb-1.0-0-dev qtbase5-dev qtmultimedia5-dev qttools5-dev libqt5serialport5-dev qttools5-dev-tools asciidoctor asciidoc libasound2-dev libudev-dev libhamlib-dev patch xsltproc qt5-default libfaad-dev libopus-dev libgtest-dev libboost-dev libboost-program-options-dev"
STATIC_PACKAGES="sox libfftw3-bin python3 python3-setuptools netcat-openbsd libsndfile1 liblapack3 libusb-1.0-0 libqt5core5a libreadline7 libgfortran4 libgomp1 libasound2 libudev1 ca-certificates libqt5gui5 libqt5sql5 libqt5printsupport5 libpulse0 libfaad2 libopus0 libboost-program-options1.67.0 libboost-log1.67.0"
BUILD_PACKAGES="wget git libsndfile1-dev libfftw3-dev cmake make gcc g++ liblapack-dev texinfo gfortran libusb-1.0-0-dev qtbase5-dev qtmultimedia5-dev qttools5-dev libqt5serialport5-dev qttools5-dev-tools asciidoctor asciidoc libasound2-dev libudev-dev libhamlib-dev patch xsltproc qt5-default libfaad-dev libopus-dev libgtest-dev libboost-dev libboost-program-options-dev libboost-log-dev libboost-regex-dev"
apt-get update
apt-get -y install auto-apt-proxy
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
@ -40,15 +40,6 @@ wget https://github.com/just-containers/s6-overlay/releases/download/v1.21.8.0/s
tar xzf s6-overlay-${PLATFORM}.tar.gz -C /
rm s6-overlay-${PLATFORM}.tar.gz
git clone https://git.code.sf.net/p/itpp/git itpp
cmakebuild itpp bb5c7e95f40e8fdb5c3f3d01a84bcbaf76f3676d
git clone https://github.com/szechyjs/mbelib.git
cmakebuild mbelib 9a04ed5c78176a9965f3d43f7aa1b1f5330e771f
git clone https://github.com/f4exb/dsd.git
cmakebuild dsd f6939f9edbbc6f66261833616391a4e59cb2b3d7
JS8CALL_VERSION=2.2.0
JS8CALL_DIR=js8call
JS8CALL_TGZ=js8call-${JS8CALL_VERSION}.tgz
@ -60,7 +51,7 @@ rm /js8call-hamlib.patch
CMAKE_ARGS="-D CMAKE_CXX_FLAGS=-DJS8_USE_HAMLIB_THREE" cmakebuild ${JS8CALL_DIR}
rm ${JS8CALL_TGZ}
WSJT_DIR=wsjtx-2.2.2
WSJT_DIR=wsjtx-2.4.0
WSJT_TGZ=${WSJT_DIR}.tgz
wget http://physics.princeton.edu/pulsar/k1jt/${WSJT_TGZ}
tar xfz ${WSJT_TGZ}
@ -111,12 +102,14 @@ rm -rf dream
rm dream-2.1.1-svn808.tar.gz
git clone https://github.com/mobilinkd/m17-cxx-demod.git
# latest master as of 2020-12-27 (new sync words)
cmakebuild m17-cxx-demod 2b84657676efb3b07b33de3ab3d0a6218e9d88b5
# latest master as of 2021-04-20
cmakebuild m17-cxx-demod c1d954fd5e5c53d28a2524e99484f832f9dcb826
git clone https://github.com/hessu/aprs-symbols /usr/share/aprs-symbols
pushd /usr/share/aprs-symbols
git checkout 5c2abe2658ee4d2563f3c73b90c6f59124839802
# remove unused files (including git meta information)
rm -rf .git aprs-symbols.ai aprs-sym-export.js
popd
apt-get -y purge --autoremove $BUILD_PACKAGES

View File

@ -18,8 +18,8 @@ function cmakebuild() {
cd /tmp
STATIC_PACKAGES="libfftw3-bin"
BUILD_PACKAGES="git autoconf automake libtool libfftw3-dev pkg-config cmake make gcc g++"
STATIC_PACKAGES="libfftw3-bin libprotobuf17"
BUILD_PACKAGES="git autoconf automake libtool libfftw3-dev pkg-config cmake make gcc g++ libprotobuf-dev protobuf-compiler"
apt-get update
apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
@ -40,8 +40,15 @@ make install
cd ..
rm -rf csdr
git clone https://github.com/jketterl/codecserver.git
mkdir -p /usr/local/etc/codecserver
cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver
#latest develop as of 2021-07-04 (optional checksum fix)
cmakebuild codecserver d73c9a56a773355679bc2d4a10f199b62223d7a0
git clone https://github.com/jketterl/digiham.git
cmakebuild digiham 0.3.0
#latest develop as of 2021-06-15 (DMR LCSS overflow fix; D-Star alternate terminator)
cmakebuild digiham 418145d74b528596a39198a537ab56207d932595
apt-get -y purge --autoremove $BUILD_PACKAGES
apt-get clean

View File

@ -1,19 +1,25 @@
#!/bin/bash
set -euo pipefail
mkdir -p /etc/openwebrx/
mkdir -p /etc/openwebrx/openwebrx.conf.d
mkdir -p /var/lib/openwebrx
mkdir -p /tmp/openwebrx/
if [[ ! -f /etc/openwebrx/config_webrx.py ]] ; then
sed 's/temporary_directory = "\/tmp"/temporary_directory = "\/tmp\/openwebrx"/' < "/opt/openwebrx/config_webrx.py" > "/etc/openwebrx/config_webrx.py"
if [[ ! -f /etc/openwebrx/openwebrx.conf.d/20-temporary-directory.conf ]] ; then
cat << EOF > /etc/openwebrx/openwebrx.conf.d/20-temporary-directory.conf
[core]
temporary_directory = /tmp/openwebrx
EOF
fi
if [[ ! -f /etc/openwebrx/bands.json ]] ; then
cp bands.json /etc/openwebrx/
fi
if [[ ! -f /etc/openwebrx/bookmarks.json ]] ; then
cp bookmarks.json /etc/openwebrx/
if [[ ! -f /etc/openwebrx/openwebrx.conf ]] ; then
cp openwebrx.conf /etc/openwebrx/
fi
if [[ ! -f /etc/openwebrx/users.json ]] ; then
cp users.json /etc/openwebrx/
if [[ ! -z "${OPENWEBRX_ADMIN_USER:-}" ]] && [[ ! -z "${OPENWEBRX_ADMIN_PASSWORD:-}" ]] ; then
if ! python3 openwebrx.py admin --silent hasuser "${OPENWEBRX_ADMIN_USER}" ; then
OWRX_PASSWORD="${OPENWEBRX_ADMIN_PASSWORD}" python3 openwebrx.py admin --noninteractive adduser "${OPENWEBRX_ADMIN_USER}"
fi
fi

BIN
htdocs/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,14 +1,162 @@
@import url("openwebrx-header.css");
@import url("openwebrx-globals.css");
html, body {
height: unset;
}
body {
margin-bottom: 5rem;
}
hr {
background: #444;
}
.buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #222;
z-index: 2;
padding: 10px;
text-align: right;
border-top: 1px solid #444;
}
.row .map-input {
margin: 15px 15px 0;
}
.device {
margin-top: 20px;
.settings-section h3 {
margin-top: 1em;
margin-bottom: 1em;
}
h1 {
margin: 1em 0;
text-align: center;
}
.matrix {
display: grid;
}
.q65-matrix {
grid-template-columns: repeat(5, auto);
}
.imageupload .image-container {
max-width: 100%;
padding: 7px;
}
.imageupload img.webrx-top-photo {
max-height: 350px;
max-width: 100%;
}
.settings-grid > div {
padding: 20px;
}
.settings-grid .btn {
width: 100%;
height: 100px;
padding: 20px;
font-size: 1.2rem;
}
.tab-body {
overflow: auto;
border: 1px solid #444;
border-top: none;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
}
.tab-body .form-group {
padding-right: 15px;
}
.bookmarks table .frequency, .bookmark-list table .frequency {
text-align: right;
}
.bookmarks table input, .bookmarks table select {
width: initial;
text-align: inherit;
display: initial;
}
.bookmark-list table .form-check-input {
margin-left: 0;
}
.actions {
margin: 1rem 0;
}
.actions .btn {
width: 100%;
}
.wsjt-decoding-depths-table {
width: auto;
margin: 0;
}
.wsjt-decoding-depths-table td:first-child {
padding-left: 0;
}
.sdr-device-list .list-group-item,
.sdr-profile-list .list-group-item {
background: initial;
}
.sdr-device-list .sdr-profile-list {
max-height: 20rem;
overflow-y: auto;
}
.removable-group.removable, .add-group {
display: flex;
flex-direction: row;
}
.removable-group.removable .removable-item, .add-group .add-group-select {
flex: 1 0 auto;
margin-right: .25rem;
}
.removable-group.removable .option-remove-button, .add-group .option-add-button {
flex: 0 0 70px;
}
.option-add-button, .option-remove-button {
width: 70px;
}
.scheduler-static-time-inputs {
display: flex;
flex-direction: row;
}
.scheduler-static-time-inputs > * {
flex: 0 0 auto;
width: unset;
}
.scheduler-static-time-inputs > select {
flex: 1 0 auto;
}
.breadcrumb {
margin-top: .5rem;
}
.imageupload.is-invalid ~ .invalid-feedback {
display: block;
}

View File

@ -1,7 +0,0 @@
@import url("openwebrx-header.css");
@import url("openwebrx-globals.css");
h1 {
text-align: center;
margin: 50px 0;
}

View File

@ -1,6 +1,16 @@
@import url("openwebrx-header.css");
@import url("openwebrx-globals.css");
body {
display: flex;
flex-direction: column;
}
.login-container {
flex: 1;
position: relative;
}
.login {
position: absolute;
left: 50%;

View File

@ -6,10 +6,6 @@ body {
flex-direction: column;
}
#webrx-top-container {
flex: none;
}
.openwebrx-map {
flex: 1 1 auto;
}

View File

@ -5,21 +5,3 @@ html, body
height: 100%;
font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
}
.sprite {
background-image: url(../gfx/openwebrx-sprites.png);
display: inline-block;
}
.openwebrx-button.highlighted .sprite {
background-image: linear-gradient(rgba(255,127,0,0.5), rgba(255,127,0,0.5)), url(../gfx/openwebrx-sprites.png);
background-blend-mode: overlay;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-device-pixel-ratio: 2) {
.sprite {
background-image: url(../gfx/openwebrx-sprites-2x.png);
background-size: 198px 77px;
}
}

View File

@ -1,4 +1,4 @@
#webrx-top-container {
.webrx-top-container {
position: relative;
z-index:1000;
background-color: #575757;
@ -12,7 +12,7 @@
overflow: hidden;
}
#openwebrx-description-container {
.openwebrx-description-container {
transition-property: height, opacity;
transition-duration: 1s;
transition-timing-function: ease-out;
@ -23,12 +23,12 @@
overflow: hidden;
}
#openwebrx-description-container.expanded {
.openwebrx-description-container.expanded {
opacity: 1;
height: 283px;
}
.webrx-top-bar-parts {
.webrx-top-bar {
height:67px;
background: rgba(128, 128, 128, 0.15);
@ -46,26 +46,27 @@
flex-direction: row;
}
.webrx-top-bar-parts > * {
.webrx-top-bar > * {
flex: 0;
}
#webrx-top-container, #webrx-top-container * {
.webrx-top-container, .webrx-top-container * {
line-height: initial;
box-sizing: initial;
}
#webrx-top-logo {
.webrx-top-logo {
width: 261px;
padding: 12px;
filter: drop-shadow(0 0 2.5px rgba(0, 0, 0, .9));
/* overwritten by media queries */
display: none;
}
#webrx-rx-avatar {
.webrx-rx-avatar {
background-color: rgba(154, 154, 154, .5);
margin: 7px;
cursor:pointer;
width: 46px;
height: 46px;
padding: 4px;
@ -73,82 +74,66 @@
box-sizing: content-box;
}
#webrx-rx-texts {
.webrx-rx-texts {
/* minimum layout width */
width: 0;
/* will be getting wider with flex */
flex: 1;
overflow: hidden;
margin: auto 0;
}
#webrx-rx-texts div {
.webrx-rx-texts div, .webrx-rx-texts h1 {
margin: 0 10px;
padding: 3px;
white-space:nowrap;
overflow: hidden;
cursor:pointer;
color: #909090;
text-align: left;
}
#webrx-rx-texts div:first-child {
margin-top: 10px;
}
#webrx-rx-title {
.webrx-rx-title {
font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
font-size: 11pt;
font-weight: bold;
}
#webrx-rx-desc {
.webrx-rx-desc {
font-size: 10pt;
}
#openwebrx-rx-details-arrow {
cursor:pointer;
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
}
#openwebrx-rx-details-arrow a {
margin: 0;
padding: 0;
line-height: 0;
display: block;
}
#openwebrx-main-buttons .button {
.openwebrx-main-buttons .button {
display: block;
width: 55px;
cursor:pointer;
}
#openwebrx-main-buttons .button[data-toggle-panel] {
.openwebrx-main-buttons .button[data-toggle-panel] {
/* will be enabled by javascript if the panel is present in the DOM */
display: none;
}
#openwebrx-main-buttons .button img {
.openwebrx-main-buttons .button img,
.openwebrx-main-buttons .button svg {
height: 38px;
filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5));
}
#openwebrx-main-buttons a {
.openwebrx-main-buttons a {
color: inherit;
text-decoration: inherit;
}
#openwebrx-main-buttons .button:hover {
.openwebrx-main-buttons .button:hover {
background-color: rgba(255, 255, 255, 0.3);
}
#openwebrx-main-buttons .button:active {
.openwebrx-main-buttons .button:active {
background-color: rgba(255, 255, 255, 0.55);
}
#openwebrx-main-buttons {
.openwebrx-main-buttons {
padding: 5px 15px;
display: flex;
list-style: none;
@ -160,7 +145,7 @@
font-weight: bold;
}
#webrx-rx-photo-title {
.webrx-rx-photo-title {
margin: 10px 15px;
color: white;
font-size: 16pt;
@ -168,7 +153,7 @@
opacity: 1;
}
#webrx-rx-photo-desc {
.webrx-rx-photo-desc {
margin: 10px 15px;
color: white;
font-size: 10pt;
@ -178,17 +163,21 @@
line-height: 1.5em;
}
#webrx-rx-photo-desc a {
.webrx-rx-photo-desc a {
color: #5ca8ff;
text-shadow: none;
}
.openwebrx-photo-trigger {
cursor: pointer;
}
/*
* Responsive stuff
*/
@media (min-width: 576px) {
#webrx-rx-texts {
.webrx-rx-texts {
display: initial;
}
}
@ -197,7 +186,7 @@
}
@media (min-width: 992px) {
#webrx-top-logo {
.webrx-top-logo {
display: initial;
}
}
@ -206,47 +195,33 @@
}
/*
* Sprites (images)
* RX details arrow up/down switching
*/
.sprite-panel-status {
background-position: 0 0;
width: 44px;
height: 38px;
.openwebrx-rx-details-arrow {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
margin: 0;
padding: 0;
line-height: 0;
display: block;
}
.sprite-panel-log {
background-position: -44px 0;
width: 38px;
height: 38px;
}
.sprite-panel-receiver {
background-position: -82px 0;
width: 40px;
height: 38px;
}
.sprite-panel-map {
background-position: -122px 0;
width: 38px;
height: 38px;
}
.sprite-panel-settings {
background-position: -160px 0;
width: 38px;
height: 38px;
}
.sprite-rx-details-arrow-down {
background-position: 0 -65px;
width: 43px;
.openwebrx-rx-details-arrow svg {
height: 12px;
}
.sprite-rx-details-arrow-up {
background-position: -43px -65px;
width: 43px;
height: 12px;
.openwebrx-rx-details-arrow .up {
display: none;
}
.openwebrx-rx-details-arrow--up .down {
display: none;
}
.openwebrx-rx-details-arrow--up .up {
display: initial;
}

View File

@ -36,15 +36,14 @@ input
vertical-align:middle;
}
input[type=range]
{
input[type=range] {
-webkit-appearance: none;
margin: 0 0;
background: transparent;
background: transparent !important;
--track-background: #B6B6B6;
}
input[type=range]:focus
{
input[type=range]:focus {
outline: none;
}
@ -297,11 +296,13 @@ input[type=range]:disabled {
#webrx-canvas-container canvas
{
position: absolute;
top: 0;
border-style: none;
image-rendering: crisp-edges;
image-rendering: -webkit-optimize-contrast;
width: 100%;
height: 200px;
will-change: transform;
}
#openwebrx-log-scroll
@ -336,12 +337,58 @@ input[type=range]:disabled {
margin: 0;
display: flex;
flex-direction: row;
cursor: pointer;
}
.webrx-actual-freq > * {
flex: 1;
}
.webrx-actual-freq .input-group {
display: flex;
flex-direction: row;
}
.webrx-actual-freq .input-group > * {
flex: 0 0 auto;
}
.webrx-actual-freq .input-group input {
flex: 1 0 auto;
margin-right: 0;
border-right: 1px solid #373737;
-moz-appearance: textfield;
}
.webrx-actual-freq .input-group input::-webkit-outer-spin-button,
.webrx-actual-freq .input-group input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.input-group > :not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > :not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group :first-child {
padding-left: 5px;
}
.input-group :last-child {
padding-right: 5px
}
.webrx-actual-freq .input-group input, .webrx-actual-freq .input-group select {
outline: none;
font-size: 16pt;
}
.webrx-actual-freq input {
font-family: 'roboto-mono';
width: 0;
@ -355,7 +402,10 @@ input[type=range]:disabled {
.webrx-actual-freq, .webrx-actual-freq input {
font-size: 16pt;
font-family: 'roboto-mono';
line-height: 22px;
}
.webrx-actual-freq .digit {
cursor: ns-resize;
}
.webrx-actual-freq .digit:hover {
@ -552,6 +602,7 @@ img.openwebrx-mirror-img
-moz-user-select: none;
-ms-user-select: none;
overflow: hidden;
z-index: 1
}
.openwebrx-progressbar-bar {
@ -564,6 +615,7 @@ img.openwebrx-mirror-img
transition-timing-function: ease-in-out;
transform: translate(-100%) translateZ(0);
will-change: transform, background-color;
z-index: 0;
}
.openwebrx-progressbar--over .openwebrx-progressbar-bar {
@ -573,11 +625,11 @@ img.openwebrx-mirror-img
.openwebrx-progressbar-text
{
position: absolute;
left:50;
left:50%;
top:50%;
transform: translate(-50%, -50%);
white-space: nowrap;
z-index: 1;
z-index: 2;
}
#openwebrx-panel-status
@ -602,6 +654,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-receiver .frequencies-container {
display: flex;
flex-direction: row;
gap: 5px;
}
#openwebrx-panel-receiver .frequencies {
@ -614,16 +667,6 @@ img.openwebrx-mirror-img
text-align: center;
}
#openwebrx-mute-on
{
color: lime;
}
#openwebrx-mute-off
{
color: white;
}
.openwebrx-panel-slider
{
position: relative;
@ -631,13 +674,6 @@ img.openwebrx-mirror-img
width: 95px;
}
.openwebrx-sliderbtn-img
{
width: 14px;
position:relative;
top: 1px;
}
.openwebrx-panel-line
{
padding-top: 5px;
@ -714,19 +750,19 @@ img.openwebrx-mirror-img
}
.openwebrx-overlay {
position: fixed;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
opacity: 0.8;
background-color: #777;
left: 0;
top: 0;
z-index: 1001;
color: white;
font-weight: bold;
font-size: 20pt;
position: absolute;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
opacity: 0.8;
background-color: #777;
left: 0;
top: 0;
z-index: 1001;
color: white;
font-weight: bold;
font-size: 20pt;
}
#openwebrx-autoplay-overlay
@ -735,8 +771,7 @@ img.openwebrx-mirror-img
transition: opacity 0.3s linear;
}
#openwebrx-autoplay-overlay img
{
#openwebrx-autoplay-overlay svg {
width: 150px;
}
@ -768,11 +803,14 @@ img.openwebrx-mirror-img
#openwebrx-digimode-canvas-container canvas
{
position: absolute;
top: 0;
pointer-events: none;
transition: width 500ms, left 500ms;
will-change: transform;
}
.openwebrx-panel select,
.openwebrx-panel input,
.openwebrx-dialog select,
.openwebrx-dialog input {
border-radius: 5px;
@ -781,11 +819,26 @@ img.openwebrx-mirror-img
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% );
background:linear-gradient(#373737, #4F4F4F);
border-color: transparent;
border-width: 0px;
-moz-appearance: none;
}
@supports(-moz-appearance: none) {
.openwebrx-panel select,
.openwebrx-dialog select {
-moz-appearance: none;
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%20%20xmlns%3Av%3D%22https%3A%2F%2Fvecta.io%2Fnano%22%3E%3Cpath%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8s-1.9-9.2-5.5-12.8z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E'),
linear-gradient(#373737, #4F4F4F);
background-repeat: no-repeat, repeat;
background-position: right .3em top 50%, 0 0;
background-size: .65em auto, 100%;
}
.openwebrx-panel .input-group select,
.openwebrx-dialog .input-group select {
padding-right: 1em;
}
}
.openwebrx-panel select option,
@ -912,6 +965,8 @@ img.openwebrx-mirror-img
display: flex;
flex-direction: row;
gap: 10px;
/* compatibility with iOS 14.2 */
flex: 0 0 auto;
}
.openwebrx-meta-slot {
@ -928,26 +983,22 @@ img.openwebrx-mirror-img
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
}
.openwebrx-meta-slot > * {
flex: 0;
flex-basis: 1.2em;
flex: 1 0 0;
line-height: 1.2em;
}
.openwebrx-meta-slot, .openwebrx-meta-slot.muted:before {
.openwebrx-meta-slot, .openwebrx-meta-slot .mute {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.openwebrx-meta-slot.muted:before {
display: block;
content: "";
background-image: url("../gfx/openwebrx-mute.png");
background-position: center;
background-repeat: no-repeat;
.openwebrx-meta-slot .mute {
display: none;
cursor: pointer;
position: absolute;
@ -958,6 +1009,17 @@ img.openwebrx-mirror-img
background-color: rgba(0,0,0,.3);
}
.openwebrx-meta-slot .mute svg {
position: absolute;
top: 50%;
left: 0;
transform: translate(0, -50%);
}
.openwebrx-meta-slot.muted .mute {
display: block;
}
.openwebrx-meta-slot.active {
background-color: #95bbdf;
}
@ -974,18 +1036,29 @@ img.openwebrx-mirror-img
}
.openwebrx-meta-slot .openwebrx-meta-user-image {
flex: 1;
flex: 0 1 100%;
background-position: center;
background-repeat: no-repeat;
line-height: 0;
overflow: hidden;
}
.openwebrx-meta-slot.active.direct .openwebrx-meta-user-image,
#openwebrx-panel-metadata-ysf .openwebrx-meta-slot.active .openwebrx-meta-user-image {
background-image: url("../gfx/openwebrx-directcall.png");
.openwebrx-meta-slot .openwebrx-meta-user-image img {
max-width: 100%;
max-height: 100%;
display: none;
}
.openwebrx-meta-slot.active.group .openwebrx-meta-user-image {
background-image: url("../gfx/openwebrx-groupcall.png");
.openwebrx-meta-slot.active.direct .openwebrx-meta-user-image .directcall,
.openwebrx-meta-slot.active.individual .openwebrx-meta-user-image .directcall,
#openwebrx-panel-metadata-ysf .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall,
#openwebrx-panel-metadata-dstar .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall {
display: initial;
}
.openwebrx-meta-slot.active.group .openwebrx-meta-user-image .groupcall,
.openwebrx-meta-slot.active.conference .openwebrx-meta-user-image .groupcall {
display: initial;
}
.openwebrx-meta-slot.group .openwebrx-dmr-target:not(:empty):before {
@ -998,6 +1071,7 @@ img.openwebrx-mirror-img
.openwebrx-dmr-timeslot-panel * {
cursor: pointer;
user-select: none;
}
.openwebrx-ysf-mode:not(:empty):before {
@ -1012,14 +1086,30 @@ img.openwebrx-mirror-img
content: "Down: ";
}
.openwebrx-maps-pin {
background-image: url("../gfx/google_maps_pin.svg");
background-position: center;
background-repeat: no-repeat;
.openwebrx-dstar-yourcall:not(:empty):before {
content: "UR: ";
}
.openwebrx-dstar-departure:not(:empty):before {
content: "RPT1: ";
}
.openwebrx-dstar-destination:not(:empty):before {
content: "RPT2: ";
}
.openwebrx-meta-slot.individual .openwebrx-nxdn-destination:not(:empty):before {
content: "Direct: ";
}
.openwebrx-meta-slot.conference .openwebrx-nxdn-destination:not(:empty):before {
content: "Conference: ";
}
.openwebrx-maps-pin svg {
width: 15px;
height: 15px;
background-size: contain;
display: inline-block;
vertical-align: middle;
}
.openwebrx-message-panel {
@ -1192,6 +1282,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-select-channel,
@ -1201,7 +1292,8 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="pocsag"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-select-channel
{
display: none;
}
@ -1215,81 +1307,58 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="pocsag"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-canvas-container
{
height: 200px;
margin: -10px;
}
.sprite-zoom-in {
background-position: 0 -38px;
width: 27px;
.openwebrx-zoom-button svg {
height: 27px;
}
.sprite-zoom-out {
background-position: -27px -38px;
width: 27px;
height: 27px;
}
.sprite-zoom-in-total {
background-position: -54px -38px;
width: 24px;
height: 27px;
}
.sprite-zoom-out-total {
background-position: -78px -38px;
width: 25px;
height: 27px;
}
.sprite-edit {
background-position: -131px -51px;
width: 14px;
.openwebrx-slider-button svg {
position:relative;
top: 1px;
height: 14px;
}
.sprite-trashcan {
background-position: -145px -38px;
width: 14px;
.openwebrx-mute-button svg.muted {
display: none;
}
.openwebrx-mute-button.muted svg.muted {
display: initial;
}
.openwebrx-mute-button.muted svg.unmuted {
display: none;
}
.bookmark .bookmark-actions .openwebrx-button svg {
height: 14px;
}
.sprite-speaker {
width: 14px;
height: 15px;
#openwebrx-waterfall-colors-auto .continuous {
display: none;
}
.openwebrx-mute-button .sprite-speaker {
background-position: -103px -38px;
#openwebrx-waterfall-colors-auto.highlighted .continuous {
display: initial;
}
.openwebrx-mute-button.muted .sprite-speaker {
background-position: -117px -38px;
#openwebrx-waterfall-colors-auto.highlighted .auto {
display: none;
}
.sprite-squelch {
background-position: -131px -38px;
width: 14px;
height: 13px;
.openwebrx-waterfall-container {
flex-grow: 1;
display: flex;
flex-direction: column;
position: relative;
}
.sprite-waterfall-auto {
background-position: -103px -53px;
width: 14px;
height: 11px;
}
.sprite-waterfall-default {
background-position: -117px -53px;
width: 14px;
height: 12px;
}
.sprite-bookmark {
background-position: -159px -38px;
width: 21px;
height: 27px;
.openwebrx-waterfall-container > * {
flex: 0 0 auto;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -3,7 +3,6 @@
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico" />
<link rel="stylesheet" href="static/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="static/css/admin.css" />
<link rel="stylesheet" href="static/css/features.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
<script src="static/lib/jquery-3.2.1.min.js"></script>
<script src="static/lib/Header.js"></script>
@ -11,6 +10,7 @@
</HEAD><BODY>
${header}
<div class="container">
${breadcrumb}
<h1>OpenWebRX Feature Report</h1>
<table class="features table">
<tr>
@ -20,5 +20,6 @@
<th>Available</th>
</tr>
</table>
${breadcrumb}
</div>
</BODY></HTML>

View File

@ -13,8 +13,7 @@ $(function(){
});
$table.append(
'<tr>' +
'<td colspan=2>' + name + '</td>' +
'<td>' + converter.makeHtml(details.description) + '</td>' +
'<td colspan=3>' + name + '</td>' +
'<td>' + (details.available ? 'YES' : 'NO') + '</td>' +
'</tr>' +
requirements.join("")

View File

@ -1,20 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>OpenWebRX Settings</title>
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico" />
<link rel="stylesheet" href="static/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="static/css/admin.css" />
<script src="https://unpkg.com/location-picker/dist/location-picker.min.js"></script>
<script src="compiled/settings.js"></script>
<meta charset="utf-8">
</head>
<body>
${header}
<div class="container">
<div class="col-12">
<h1>General settings</h1>
</div>
${sections}
</div>
</body>

BIN
htdocs/gfx/favicon128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
htdocs/gfx/favicon32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
htdocs/gfx/favicon44.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
htdocs/gfx/favicon64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
htdocs/gfx/favicon96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1 @@
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg"><defs><filter id="a" x="-.25" y="-.25" width="1.5" height="1.5" color-interpolation-filters="sRGB"><feFlood flood-color="#000" flood-opacity=".4" result="flood"/><feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/><feGaussianBlur in="composite1" result="blur" stdDeviation="66.6"/><feOffset result="offset"/><feComposite in="SourceGraphic" in2="offset" result="composite2"/></filter></defs><path d="M550.98 541.91c-.99-28.904-4.377-57.939-9.421-86.393-6.111-34.469-13.889-85.002-43.983-107.46-17.404-12.988-39.941-17.249-59.865-25.081-9.697-3.81-18.384-7.594-26.537-11.901-27.518 30.176-63.4 45.962-105.19 45.964-41.774 0-77.652-15.786-105.17-45.964-8.153 4.308-16.84 8.093-26.537 11.901-19.924 7.832-42.461 12.092-59.863 25.081-30.096 22.463-37.873 72.996-43.983 107.46-5.045 28.454-8.433 57.489-9.422 86.393-.766 22.387 10.288 25.525 29.017 32.284 23.453 8.458 47.666 14.737 72.041 19.884 47.077 9.941 95.603 17.582 143.92 17.924 48.318-.343 96.844-7.983 143.92-17.924 24.375-5.145 48.59-11.424 72.041-19.884 18.736-6.757 29.789-9.895 29.023-32.284zM306 325.99c90.56-.01 123.15-90.68 131.68-165.17C448.19 69.06 404.8 0 306 0c-98.78 0-142.19 69.055-131.68 160.82C182.86 235.304 215.434 326 306 325.99z" filter="url(#a)" transform="matrix(.42446 0 0 .42484 70.12 69)" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1 @@
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg"><defs><filter id="a" x="-.25" y="-.25" width="1.5" height="1.5" color-interpolation-filters="sRGB"><feFlood flood-color="#000" flood-opacity=".4" result="flood"/><feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/><feGaussianBlur in="composite1" result="blur" stdDeviation="66.6"/><feOffset result="offset"/><feComposite in="SourceGraphic" in2="offset" result="composite2"/></filter></defs><g fill="#fff"><g transform="matrix(.42446 0 0 .42484 129.12 42)" filter="url(#a)"><path d="M27.982.002c-98.778 0-142.188 69.056-131.678 160.823 8.54 74.484 41.112 165.183 131.678 165.173 90.558-.01 123.148-90.682 131.678-165.173C170.17 69.063 126.781.002 27.982.002zm277.996 0C207.2.002 163.79 69.058 174.3 160.825c8.54 74.484 41.113 165.183 131.678 165.173 90.559-.01 123.148-90.682 131.678-165.173C448.166 69.063 404.777.002 305.978.002zM-77.209 311.093c-8.153 4.308-16.84 8.09-26.537 11.898-19.924 7.833-42.463 12.095-59.863 25.084-30.095 22.463-37.871 72.996-43.98 107.46-5.045 28.454-8.435 57.492-9.424 86.395-.766 22.388 10.288 25.523 29.015 32.284 23.453 8.458 47.665 14.737 72.04 19.884 47.075 9.941 95.6 17.583 143.916 17.925 46.64-.33 93.461-7.487 138.998-16.923 45.538 9.437 92.359 16.593 138.999 16.923 48.317-.343 96.841-7.984 143.917-17.925 24.374-5.145 48.593-11.424 72.043-19.884 18.736-6.757 29.786-9.894 29.02-32.284h.01c-.99-28.903-4.38-57.941-9.424-86.395-6.111-34.47-13.886-85.002-43.98-107.46-17.404-12.989-39.94-17.252-59.863-25.084-9.697-3.81-18.384-7.59-26.537-11.898-27.517 30.177-63.398 45.962-105.186 45.965-41.773 0-77.65-15.787-105.17-45.965-8.153 4.308-16.84 8.09-26.537 11.898-2.394.941-4.828 1.826-7.284 2.685-2.456-.859-4.89-1.744-7.284-2.685-9.697-3.81-18.383-7.59-26.537-11.898-27.517 30.177-63.397 45.962-105.186 45.965-41.773 0-77.65-15.787-105.17-45.965z"/></g><g transform="matrix(.42446 0 0 .42484 70.12 102)" filter="url(#a)"><path d="M550.98 541.91c-.99-28.904-4.377-57.939-9.421-86.393-6.111-34.469-13.889-85.002-43.983-107.46-17.404-12.988-39.941-17.249-59.865-25.081-9.697-3.81-18.384-7.594-26.537-11.901-27.518 30.176-63.4 45.962-105.19 45.964-41.774 0-77.652-15.786-105.17-45.964-8.153 4.308-16.84 8.093-26.537 11.901-19.924 7.832-42.461 12.092-59.863 25.081-30.096 22.463-37.873 72.996-43.983 107.46-5.045 28.454-8.433 57.489-9.422 86.393-.766 22.387 10.288 25.525 29.017 32.284 23.453 8.458 47.666 14.737 72.041 19.884 47.077 9.941 95.603 17.582 143.92 17.924 48.318-.343 96.844-7.983 143.92-17.924 24.375-5.145 48.59-11.424 72.041-19.884 18.736-6.757 29.789-9.895 29.023-32.284zM306 325.99c90.56-.01 123.15-90.68 131.68-165.17C448.19 69.06 404.8 0 306 0c-98.78 0-142.19 69.055-131.68 160.82C182.86 235.304 215.434 326 306 325.99z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

28
htdocs/gfx/svg-defs.svg Normal file
View File

@ -0,0 +1,28 @@
<svg width="80" height="80" xmlns="http://www.w3.org/2000/svg">
<defs>
<g id="top-logo"><g fill="none" stroke="#fff" stroke-linecap="square" stroke-linejoin="round" stroke-width="6"><path d="M517.23 68.215a35.5 33.2 0 0 1-11.936-26.458 35.5 33.2 0 0 1 14.593-25.266M561.62 16.49a35.5 33.2 0 0 1 14.593 25.266 35.5 33.2 0 0 1-11.936 26.458M551.57 29.079a18.4 19 0 0 1 7.564 14.46 18.4 19 0 0 1-6.187 15.142M528.56 58.681a18.4 19 0 0 1-6.187-15.142 18.4 19 0 0 1 7.564-14.46"/></g><g fill="none"><path d="M540.75 40.25v41.5h-322v-.5h322" stroke="#fff" stroke-width="4.5"/><path d="M13 81.5h58zm76 0h128z" stroke="#ccc" stroke-width="5"/></g><g aria-label="OpenWebRX"><path d="M207.85 45.906c0-11.755-5.104-17.787-14.771-17.787-5.877 0-10.363 2.475-13.688 7.579v-6.574h-6.96v42.301h6.96V47.53c0-8.352 4.717-13.379 12.141-13.379 6.264 0 9.357 4.022 9.357 11.987v25.288h6.96zm-53.515.619h-24.747c.696-7.966 5.645-12.528 13.301-12.528 6.805 0 11.368 4.794 11.445 12.528zm6.96 2.01c0-12.683-7.192-20.416-18.483-20.416-12.605 0-20.725 8.662-20.725 22.581 0 13.301 8.352 21.808 21.963 21.808 5.336 0 10.595-1.082 15.621-3.248v-6.573c-4.872 2.63-9.821 3.944-15.235 3.944-9.203 0-14.461-5.104-15.08-14.693h31.939zm-54.907 1.78c0 10.44-4.64 16.394-11.832 16.394-7.192 0-11.832-5.955-11.832-16.395s4.64-16.395 11.832-16.395c7.192 0 11.832 5.955 11.832 16.395zm-23.664 14.77c2.939 5.027 7.347 7.424 13.533 7.424 10.053 0 17.323-8.661 17.323-22.195S106.31 28.12 96.257 28.12c-6.187 0-10.595 2.397-13.533 7.424v-6.419h-6.96v58.387h6.96zm-44.389-51.04c-15.699 0-26.139 11.291-26.139 29.232 0 18.02 10.44 29.232 26.139 29.232s26.061-11.29 26.061-29.232-10.363-29.232-26.061-29.232zm0 6.187c10.827 0 18.019 8.506 18.019 23.045s-7.192 23.045-18.019 23.045-18.096-8.507-18.096-23.045 7.27-23.045 18.096-23.045z" fill="#ccc"/><path d="m480.99 42.658 18.792-27.608h-15.235l-12.296 18.173-12.373-18.173h-15.157l18.792 27.608-19.565 28.768h15.235l13.069-19.256 13.147 19.256h15.157zm-76.405 7.424h4.099c5.026 0 7.269 2.01 9.898 7.347l6.883 13.997h15.467l-7.888-16.008c-2.939-5.955-5.8-9.358-10.13-10.363 7.268-2.243 10.826-7.038 10.826-14.461 0-10.904-6.883-15.544-21.499-15.544h-22.195v56.376h14.54zm6.11-10.053h-6.11V25.568h6.11c5.954 0 8.506 1.933 8.506 7.192s-2.552 7.27-8.507 7.27zm-62.873-4.485V12.653h-6.96v58.773h6.96v-6.342c2.939 5.027 7.347 7.424 13.533 7.424 10.053 0 17.323-8.66 17.323-22.195s-7.269-22.195-17.323-22.195c-6.186 0-10.595 2.398-13.533 7.424zm23.664 14.77c0 10.44-4.64 16.396-11.832 16.396s-11.832-5.955-11.832-16.395 4.64-16.395 11.832-16.395 11.832 5.954 11.832 16.395zm-48.72-3.789H298.02c.696-7.965 5.646-12.528 13.301-12.528 6.806 0 11.368 4.795 11.445 12.528zm6.96 2.011c0-12.683-7.192-20.416-18.483-20.416-12.605 0-20.725 8.661-20.725 22.581 0 13.301 8.352 21.808 21.963 21.808 5.336 0 10.595-1.083 15.621-3.248v-6.573c-4.872 2.63-9.821 3.944-15.235 3.944-9.202 0-14.46-5.104-15.08-14.693h31.94zm-112.75-33.485 14.152 56.376h9.59l11.987-48.952 11.909 48.952h9.589l14.152-56.376h-7.733L268.79 62.688l-11.832-47.637h-8.584l-11.832 47.637-11.832-47.637z" fill="#fff"/></g></g>
<g id="panel-log" stroke="#fff"><g fill="none" stroke-linecap="round" stroke-width="3.5"><path d="M21 57h39M21 48.5h39M21 40h34M21 31.5h39M21 23h39"/></g><g fill="#fff" stroke-linejoin="round" stroke-width="2.1"><path d="M13.5 63.5c0 2.5 2.5 5 5 5h45c2.5 0 5-2.5 5-5v-46c0-2.5-2.5-5-5-5H25l-11.5 16z" fill-opacity=".35" style="mix-blend-mode:normal"/><path d="M25 12.5s-1.323 7.847 4 15c-9.294-1.268-15.5 1-15.5 1"/></g></g>
<g id="panel-status" fill="#fff" stroke="#fff" stroke-linejoin="round" stroke-width="2.1"><g fill-opacity=".35"><path d="M9.683 26.483c-3.13 0-5.666-2.455-5.666-5.483 0-3.029 2.536-5.484 5.666-5.484h48.634c3.13 0 5.666 2.455 5.666 5.484 0 3.028-2.537 5.483-5.666 5.483zM19.113 44.913c-3.13 0-5.666-2.455-5.666-5.483s2.537-5.484 5.666-5.484h48.634c3.13 0 5.666 2.455 5.666 5.484s-2.537 5.483-5.666 5.483zM9.683 63.483c-3.13 0-5.666-2.455-5.666-5.483s2.536-5.484 5.666-5.484h48.634c3.13 0 5.666 2.455 5.666 5.484s-2.537 5.483-5.666 5.483z"/></g><path d="M10.06 26.456c-3.322 0-6.016-2.443-6.016-5.456 0-3.013 2.694-5.456 6.016-5.456h5.932c3.322 0 6.016 2.443 6.016 5.456 0 3.013-2.694 5.456-6.016 5.456zM19.48 44.886c-3.317 0-6.007-2.443-6.007-5.456 0-3.013 2.69-5.456 6.007-5.456h32.516c3.317 0 6.007 2.443 6.007 5.456 0 3.014-2.69 5.456-6.007 5.456zM10.052 63.456c-3.318 0-6.008-2.443-6.008-5.456 0-3.013 2.69-5.456 6.008-5.456h24.944c3.318 0 6.008 2.443 6.008 5.456 0 3.013-2.69 5.456-6.008 5.456z"/></g>
<g id="panel-receiver" stroke="#fff" stroke-width="2.1"><path d="M66 29.05 12 10" fill="none" stroke-linecap="round" stroke-linejoin="round"/><rect x="6.8" y="29.05" width="67.5" height="38.5" rx="5" ry="5" fill="#fff" fill-opacity=".35"/><g fill="#fff" stroke-linecap="round" stroke-linejoin="round" fill-opacity=".35"><rect x="12.3" y="34.05" width="28.5" height="9.5" rx="2" ry="2"/><circle cx="56.55" cy="48.55" r="13.5"/><g transform="translate(0 -.5)"><circle cx="17.05" cy="50.55" r="3"/><circle cx="26.3" cy="50.55" r="3"/><circle cx="35.55" cy="50.55" r="3"/><circle cx="17.05" cy="57.05" r="3"/><circle cx="26.3" cy="57.05" r="3"/><circle cx="35.55" cy="57.05" r="3"/></g></g></g>
<g id="panel-map" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.1"><g stroke-opacity=".989"><path d="m30 11-19 8v50l19-5 19 5 20-5V11l-10.844 4.879a11.93 11.93 0 0 1 2.394 7.172c0 2.111-.55 4.09-1.507 5.814h.004L49 47 38.279 29.256h.013a11.922 11.922 0 0 1-1.742-6.205c0-2.905 1.034-5.568 2.752-7.644z" fill="#fff" fill-opacity=".35"/><g fill="none"><path d="M30 11v53M49 47v22M11 59l19-25 19 18 20-6"/></g></g><circle cx="48.55" cy="23.05" r="6.5" fill="none" stroke-opacity=".989"/><path d="M48.551 11.051c-6.627 0-12 5.373-12 12 0 2.274.643 4.393 1.742 6.205h-.013L49 47l10.048-18.135h-.004a11.935 11.935 0 0 0 1.507-5.814c0-6.628-5.372-12-12-12z" fill="#fff" fill-opacity=".2"/></g>
<g id="panel-settings" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.6"><path d="m39.855 10-1 .021c-1.041.04-2.078.134-3.11.282-.546.078-.853.587-.78 1.135a7.876 7.876 0 0 1-.022 1.841 6.504 6.504 0 0 1-5.428 5.428 7.889 7.889 0 0 1-1.992 0 6.501 6.501 0 0 1-4.156-2.463c-.336-.437-.916-.581-1.358-.25a29.998 29.998 0 0 0-6.017 6.016c-.331.442-.188 1.02.25 1.355a6.505 6.505 0 0 1 2.465 4.158 7.885 7.885 0 0 1 0 1.993 6.504 6.504 0 0 1-5.428 5.427 7.862 7.862 0 0 1-1.992-.002c-.461-.072-.904.256-.983.803a30.012 30.012 0 0 0-.002 8.51c.079.547.588.853 1.135.781a7.876 7.876 0 0 1 1.842.022 6.504 6.504 0 0 1 5.428 5.427 7.889 7.889 0 0 1 0 1.992 6.501 6.501 0 0 1-2.463 4.157c-.438.336-.581.915-.25 1.357a29.998 29.998 0 0 0 6.015 6.018c.442.33 1.02.187 1.356-.25a6.505 6.505 0 0 1 4.158-2.465 7.885 7.885 0 0 1 1.992 0 6.504 6.504 0 0 1 5.428 5.428 7.862 7.862 0 0 1-.002 1.992c-.072.46.256.904.803.982a30.012 30.012 0 0 0 8.51.002c.546-.078.853-.59.78-1.136a7.859 7.859 0 0 1 .022-1.84 6.504 6.504 0 0 1 5.428-5.428 7.889 7.889 0 0 1 1.992 0 6.501 6.501 0 0 1 4.156 2.463c.336.437.916.581 1.358.25a29.998 29.998 0 0 0 6.017-6.016c.331-.442.188-1.02-.25-1.355a6.505 6.505 0 0 1-2.465-4.158 7.885 7.885 0 0 1 0-1.993 6.504 6.504 0 0 1 5.428-5.427 7.862 7.862 0 0 1 1.992.002c.461.072.904-.258.983-.805a29.977 29.977 0 0 0 .002-8.508c-.079-.546-.59-.853-1.137-.781a7.859 7.859 0 0 1-1.84-.022 6.504 6.504 0 0 1-5.427-5.427 7.889 7.889 0 0 1 0-1.992 6.501 6.501 0 0 1 2.462-4.157c.438-.336.582-.915.25-1.357a29.998 29.998 0 0 0-6.015-6.018c-.442-.33-1.02-.187-1.356.25a6.505 6.505 0 0 1-4.158 2.465 7.885 7.885 0 0 1-1.992 0 6.504 6.504 0 0 1-5.428-5.428 7.862 7.862 0 0 1 .002-1.992c.072-.46-.256-.904-.803-.982a30.002 30.002 0 0 0-4.4-.305zM40 26.5A13.5 13.5 0 0 1 53.5 40 13.5 13.5 0 0 1 40 53.5 13.5 13.5 0 0 1 26.5 40 13.5 13.5 0 0 1 40 26.5z" fill="#fff" fill-opacity=".35"/><circle cx="40" cy="40" r="13.5" fill="#fff" fill-opacity=".2"/><circle cx="40" cy="40" r="8" fill="none"/></g>
<g id="zoom-in" stroke="#fff"><circle cx="31.75" cy="32" r="27.5" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="8"/><path d="m57.147 47.242 20.644 20.645a4 4 90 0 1 0 5.657l-3.748 3.748a4 4 180 0 1-5.656 0L47.743 56.647" fill="#fff" stroke-width="2.1"/><path d="M31.75 18.5v27M18.25 32h27" fill="none" stroke-linecap="round" stroke-width="10"/></g>
<g id="zoom-out" stroke="#fff"><circle cx="31.75" cy="32" r="27.5" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="8"/><path d="m57.147 47.242 20.644 20.645a4 4 90 0 1 0 5.657l-3.748 3.748a4 4 180 0 1-5.656 0L47.743 56.647" fill="#fff" stroke-width="2.1"/><path d="M18.25 32h27" fill="none" stroke-linecap="round" stroke-width="10"/></g>
<g id="zoom-out-total" stroke="#fff"><circle cx="44.001" cy="51.307" r="16.326" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.75"/><path d="M59.079 60.356 71.6 72.877a2 2 90 0 1 0 2.829l-2.755 2.754a2 2 180 0 1-2.828 0L53.496 65.94" fill="#fff" stroke-width="1.247"/><path d="m20.893 1.244-16.199 16.9 16.199 16.9V21.698H59.5v13.346l16.199-16.9L59.5 1.244V14.7H20.893z" fill="#fff"/></g>
<g id="zoom-in-total" stroke="#fff"><circle cx="44.701" cy="51.307" r="16.326" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.75"/><path d="M59.779 60.356 72.3 72.877a2 2 90 0 1 0 2.829l-2.755 2.754a2 2-180 0 1-2.828 0L54.196 65.94" fill="#fff" stroke-width="1.247"/><path d="M4.8 1.244v33.801L17.595 21.7H62.4l12.793 13.346v-33.8L62.296 14.698H17.698z" fill="#fff"/></g>
<g id="speaker" stroke="#fff"><path d="M33.5 7.15 16.74 25.201H2.65v31.7h16.041L33.5 72.848z" fill="#fff" stroke-width="2.1"/><g fill="none"><path d="M46.336 16.041A20 30 0 0 1 54.3 40a20 30 0 0 1-7.964 23.959" stroke-width="3"/><path d="M52.716 9.652A30.6 38 0 0 1 64.9 40a30.6 38 0 0 1-12.184 30.348" stroke-width="4.2"/><path d="M59.035 2.065A41.1 47.5 0 0 1 75.4 40a41.1 47.5 0 0 1-16.365 37.935" stroke-width="5.4"/></g></g>
<g id="speaker-muted"><path d="M33.5 7.15 16.74 25.201H2.65v31.7h16.041L33.5 72.848z" fill="#fff" stroke="#fff" stroke-width="2.1"/></g>
<g id="waterfall-auto"><path d="M33.512 72.05s3.393-64.129 8.24-64.093c4.48.034 4.392 28.286 9.98 28.198 4.775-.074 5.279-13.721 6.33-17.88 1.013-4.004 2.915-5.172 5.321-4.894 2.993.346 8.205 5.925 10.588 13.761 4.16 13.677 5.53 44.907 5.53 44.907z" fill="#ccc" stroke="#ccc"/><path d="M29.3 68.8 18.4 54 7.5 68.8zM7.5 11.2 18.4 26l10.9-14.8zM18.4 26v28" fill="#fff" stroke="#fff" stroke-width="7.5"/></g>
<g id="waterfall-default"><path d="M36.356 75.295s3.187-34.502 7.74-34.482c4.207.018 4.125 15.218 9.374 15.171 4.485-.04 4.958-7.382 5.945-9.62.951-2.154 2.738-2.782 4.998-2.632 2.812.186 7.707 3.187 9.945 7.403 3.906 7.358 5.193 24.16 5.193 24.16z" fill="#ccc" stroke="#ccc" stroke-width=".711"/><path d="M18.4 25.424v29.075m-10.9 0L18.4 69.3l10.9-14.801zm10.9-43.875L7.5 25.425h21.801z" fill="#fff" stroke="#fff" stroke-width="7.5"/></g>
<g id="squelch" fill="#fff" stroke-width=".767"><path d="M33.333 24.7c-.058-14.132-5.913-21.9-16.58-21.9-10.145 0-16 7.673-16 20.963 0 10.575 3.305 15.722 11.884 18.343l5.913 1.872c5.797 1.778 7.942 4.399 7.942 9.92 0 5.71-3.246 9.172-8.521 9.172-5.913 0-9.218-4.024-9.508-11.324H0C.521 66.345 6.724 74.3 17.506 74.3c10.899 0 17.333-8.142 17.333-22.087 0-10.762-3.362-16.378-11.188-18.81l-6.609-2.06c-6.203-1.965-8-3.93-8-8.89 0-5.148 2.782-8.423 7.304-8.423 5.507 0 8.58 3.743 8.87 10.669zM75.362 62.508C78.202 56.893 80 47.534 80 38.363c0-10.014-2.203-19.653-5.913-26.11C70.435 5.887 65.681 2.8 59.653 2.8S48.87 5.888 45.218 12.252C41.45 18.71 39.305 28.35 39.305 38.55s2.203 19.84 5.913 26.298c3.652 6.364 8.406 9.452 14.435 9.452 4.405 0 7.594-1.404 10.956-4.68l4.986 7.581L80 69.621zM64.638 46.13l-4.406 7.58 4.464 6.833c-1.391 1.123-3.246 1.778-5.101 1.778C52.638 62.321 48 52.869 48 38.549c0-14.412 4.58-23.771 11.652-23.771s11.652 9.358 11.652 23.864c0 5.615-.638 10.67-1.913 14.787z"/></g>
<g id="trashcan"><path d="M56.667 13.333V6.666A6.67 6.67 0 0 0 50 0H30a6.67 6.67 0 0 0-6.666 6.666v6.667H6.667V20h6.666v53.333A6.67 6.67 0 0 0 20 79.999h40a6.67 6.67 0 0 0 6.667-6.666V20h6.666v-6.667zM30 6.666h20v6.667H30zm30 66.667H20V20h40zM36.667 26.666H30v40h6.667zm13.333 0h-6.667v40H50z" fill="#fff"/></g>
<g id="edit"><path d="m52.5 7.5-45 45v20h20l45-45zm12.93 20-4.697 4.697-12.93-12.93L52.5 14.57zM15.302 51.768l4.06-4.06c1.533 1.125 3.575 2.002 5.81 2.002 2.148 0 4.453-.8 6.595-2.942l10-10c2.52-2.52 4.622-7.728.968-12.43l1.535-1.535 12.93 12.93L28.23 64.698zm22.93-18.535-10 10c-2.062 2.058-4.007 1.595-5.27.875L39.14 27.93c1.465 2.485-.27 4.643-.907 5.303zM12.5 56.035 23.965 67.5H12.5z" fill="#fff"/></g>
<g id="bookmark"><path d="M62.5 0h-45A7.5 7.5 0 0 0 10 7.5V80l30-17.5L70 80V7.5A7.5 7.5 0 0 0 62.5 0zm0 66.942L40 53.817 17.5 66.942V8.437a.938.938 0 0 1 .938-.938h43.124c.518 0 .938.42.938.937z" fill="#fff"/></g>
<g id="rx-details-arrow-down"><path d="M5 0C2.5 0 0 2.5 0 5v7h43V5c0-2.5-2.5-5-5-5zm8 4h17l-8.5 6.5z" fill="#999" fill-opacity=".196"/><path d="M13 4h17l-8.5 6.5z" fill="#848484" fill-opacity=".592"/></g>
<g id="rx-details-arrow-up"><path d="M5 0C2.5 0 0 2.5 0 5v7h43V5c0-2.5-2.5-5-5-5zm16.5 3L30 9.5H13z" fill="#999" fill-opacity=".196"/><path d="M30 9.5H13L21.5 3z" fill="#848484" fill-opacity=".592"/></g>
<g id="maps-pin" transform="translate(-965.78 -331.788) scale(1.1856)"><path d="M817.11 282.97c-1.258 1.343-2.046 3.299-2.016 5.139.064 3.845 1.797 5.3 4.569 10.592.998 2.328 2.04 4.792 3.031 8.873.138.602.272 1.16.334 1.21.062.048.197-.513.334-1.115.991-4.081 2.033-6.543 3.032-8.872 2.77-5.291 4.504-6.747 4.568-10.592.03-1.84-.76-3.797-2.018-5.14-1.437-1.534-3.605-2.67-5.916-2.717-2.311-.047-4.48 1.088-5.918 2.622z" fill="#ff4646" stroke="#d73534"/><circle cx="823.03" cy="288.25" r="3.035" fill="#590000"/></g>
<g id="play-button"><circle cx="350" cy="350" r="330" fill="none" stroke="#fff" stroke-width="36"/><path d="M195 211v278l366-139z" fill="#fff"/></g>
<g id="meta-mute" stroke="#fff" stroke-width="5"><path stroke-linejoin="round" style="paint-order:fill" fill="none" d="m21.989 47.699 17.4 15.051V13.769L22.235 28.606H6v19.093z" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/><path d="m48.652 50.27 20.743-24.299M69.395 50.27 48.652 25.971" stroke-linecap="round" transform="matrix(5.3513 0 0 5.3723 -.73 -1.542)"/></g>
<g id="waterfall-continuous"><g stroke="#fff" stroke-width="8"><path d="M5 40A35 35 0 0 1 26.606 7.664a35 35 0 0 1 38.143 7.587" fill="none"/><path d="m68.284 11.716 2.828 9.9-9.899-2.829z" fill="#fff"/></g><path d="m48.008 48.144 2.816 8.624h9.035L45.075 14h-9.739L20.141 56.768h8.976l2.875-8.624zm-2.405-7.333H34.456l5.573-16.72z" fill="#fff" aria-label="A"/><g stroke="#fff" stroke-width="8"><path d="M75 40a35 35 0 0 1-21.606 32.336 35 35 0 0 1-38.143-7.587" fill="none"/><path d="m11.716 68.284-2.828-9.9 9.899 2.829z" fill="#fff"/></g></g>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,25 +1,25 @@
<div id="webrx-top-container">
<div id="webrx-top-bar" class="webrx-top-bar-parts">
<a href="https://www.openwebrx.de/" target="_blank"><img src="static/gfx/openwebrx-top-logo.png" id="webrx-top-logo" alt="OpenWebRX Logo"/></a>
<img id="webrx-rx-avatar" class="openwebrx-photo-trigger" src="static/gfx/openwebrx-avatar.png" alt="Receiver avatar"/>
<div id="webrx-rx-texts">
<div id="webrx-rx-title" class="openwebrx-photo-trigger"></div>
<div id="webrx-rx-desc" class="openwebrx-photo-trigger"></div>
<div class="webrx-top-container">
<div class="webrx-top-bar">
<a href="https://www.openwebrx.de/" target="_blank"><svg viewBox="0 0 591 100" class="webrx-top-logo"><title>Visit the OpenWebRX homepage</title><use xlink:href="${document_root}static/gfx/svg-defs.svg#top-logo"></use></svg></a>
<img class="webrx-rx-avatar openwebrx-photo-trigger" src="${document_root}static/gfx/openwebrx-avatar.png" alt="Receiver avatar"/>
<div class="webrx-rx-texts openwebrx-photo-trigger">
<h1 class="webrx-rx-title">${receiver_name}</h1>
<div class="webrx-rx-desc">${receiver_location} | Loc: ${locator}, ASL: ${receiver_asl} m</div>
</div>
<section id="openwebrx-main-buttons">
<div class="button" data-toggle-panel="openwebrx-panel-status"><span class="sprite sprite-panel-status"></span><br/>Status</div>
<div class="button" data-toggle-panel="openwebrx-panel-log"><span class="sprite sprite-panel-log"></span><br/>Log</div>
<div class="button" data-toggle-panel="openwebrx-panel-receiver"><span class="sprite sprite-panel-receiver"></span><br/>Receiver</div>
<a class="button" href="map" target="openwebrx-map"><span class="sprite sprite-panel-map"></span><br/>Map</a>
${settingslink}
<section class="openwebrx-main-buttons">
<div class="button" data-toggle-panel="openwebrx-panel-status"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-status"></use></svg><br/>Status</div>
<div class="button" data-toggle-panel="openwebrx-panel-log"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-log"></use></svg><br/>Log</div>
<div class="button" data-toggle-panel="openwebrx-panel-receiver"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-receiver"></use></svg><br/>Receiver</div>
<a class="button" href="${document_root}map" target="openwebrx-map"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-map"></use></svg><br/>Map</a>
<a class="button" href="${document_root}settings" target="openwebrx-settings"><svg viewBox="0 0 80 80"><use xlink:href="${document_root}static/gfx/svg-defs.svg#panel-settings"></use></svg><br/>Settings</a>
</section>
</div>
<div id="openwebrx-description-container">
<div id="webrx-rx-photo-title"></div>
<div id="webrx-rx-photo-desc"></div>
</div>
<div id="openwebrx-rx-details-arrow">
<a id="openwebrx-rx-details-arrow-up" class="openwebrx-photo-trigger" style="display: none;"><span class="sprite sprite-rx-details-arrow-up"></span></a>
<a id="openwebrx-rx-details-arrow-down" class="openwebrx-photo-trigger"><span class="sprite sprite-rx-details-arrow-down"></span></a>
<div class="openwebrx-description-container">
<div class="webrx-rx-photo-title">${photo_title}</div>
<div class="webrx-rx-photo-desc">${photo_desc}</div>
</div>
<a class="openwebrx-rx-details-arrow openwebrx-rx-details-arrow--down openwebrx-photo-trigger">
<svg class="down" viewBox="0 0 43 12"><use xlink:href="static/gfx/svg-defs.svg#rx-details-arrow-down"></use></svg>
<svg class="up" viewBox="0 0 43 12"><use xlink:href="static/gfx/svg-defs.svg#rx-details-arrow-up"></use></svg>
</a>
</div>

View File

@ -23,7 +23,14 @@
<html>
<head>
<title>OpenWebRX | Open Source SDR Web App for Everyone!</title>
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico" />
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico" sizes="16x16 32x32" />
<link rel="icon" type="image/png" sizes="32x32" href="static/gfx/favicon32.png" />
<link rel="icon" type="image/png" sizes="44x44" href="static/gfx/favicon44.png" />
<link rel="icon" type="image/png" sizes="64x64" href="static/gfx/favicon64.png" />
<link rel="icon" type="image/png" sizes="96x96" href="static/gfx/favicon96.png" />
<link rel="icon" type="image/png" sizes="128x128" href="static/gfx/favicon128.png" />
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<meta name="msapplication-TileImage" content="mstile-144x144.png">
<script src="compiled/receiver.js"></script>
<link rel="stylesheet" type="text/css" href="static/lib/nanoscroller.css" />
<link rel="stylesheet" type="text/css" href="static/css/openwebrx.css" />
@ -34,142 +41,191 @@
<body onload="openwebrx_init();">
<div id="webrx-page-container">
${header}
<div id="openwebrx-frequency-container">
<div id="openwebrx-bookmarks-container"></div>
<div id="openwebrx-scale-container">
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
</div>
</div>
<div id="webrx-canvas-background">
<div id="webrx-canvas-container">
<!-- add canvas here by javascript -->
</div>
</div>
<div id="openwebrx-panels-container">
<div id="openwebrx-panels-container-left">
<div class="openwebrx-panel" data-panel-name="client-under-devel" style="width: 245px; background-color: Red;">
<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.
<div class="openwebrx-waterfall-container">
<div id="openwebrx-frequency-container">
<div id="openwebrx-bookmarks-container"></div>
<div id="openwebrx-scale-container">
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
</div>
<div class="openwebrx-panel" id="openwebrx-panel-digimodes" style="display: none; width: 619px;" data-panel-name="digimodes">
<div id="openwebrx-digimode-canvas-container">
<div id="openwebrx-digimode-select-channel"></div>
</div>
<div id="webrx-canvas-background">
<div id="webrx-canvas-container">
<!-- add canvas here by javascript -->
</div>
</div>
<div id="openwebrx-panels-container">
<div id="openwebrx-panels-container-left">
<div class="openwebrx-panel" data-panel-name="client-under-devel" style="width: 245px; background-color: Red;">
<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.
</div>
<div id="openwebrx-digimode-content-container">
<div class="gradient"></div>
<div id="openwebrx-digimode-content">
<span id="openwebrx-cursor-blink"></span>
<div class="openwebrx-panel" id="openwebrx-panel-digimodes" style="display: none; width: 619px;" data-panel-name="digimodes">
<div id="openwebrx-digimode-canvas-container">
<div id="openwebrx-digimode-select-channel"></div>
</div>
</div>
</div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-wsjt-message" style="display: none; width: 619px;" data-panel-name="wsjt-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-js8-message" style="display:none; width: 619px;" data-panel-name="js8-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-message"></div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-ysf" style="display: none;" data-panel-name="metadata-ysf">
<div class="openwebrx-meta-slot">
<div class="openwebrx-ysf-mode"></div>
<div class="openwebrx-meta-user-image"></div>
<div class="openwebrx-ysf-source"><span class="location"></span><span class="callsign"></span></div>
<div class="openwebrx-ysf-up"></div>
<div class="openwebrx-ysf-down"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-dmr" style="display: none;" data-panel-name="metadata-dmr">
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
<div class="openwebrx-dmr-slot">Timeslot 1</div>
<div class="openwebrx-meta-user-image"></div>
<div class="openwebrx-dmr-id"></div>
<div class="openwebrx-dmr-name"></div>
<div class="openwebrx-dmr-target"></div>
</div>
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
<div class="openwebrx-dmr-slot">Timeslot 2</div>
<div class="openwebrx-meta-user-image"></div>
<div class="openwebrx-dmr-id"></div>
<div class="openwebrx-dmr-name"></div>
<div class="openwebrx-dmr-target"></div>
</div>
</div>
<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" style="width: 619px;">
<div class="openwebrx-panel-inner nano" id="openwebrx-log-scroll">
<div class="nano-content">
<div id="openwebrx-client-log-title">OpenWebRX client log</div>
<div>
Author contact: <a href="http://www.justjakob.de/" target="_blank">Jakob Ketterl, DD5JFK</a> |
<a href="https://www.openwebrx.de" target="_blank">OpenWebRX homepage</a>
<div id="openwebrx-digimode-content-container">
<div class="gradient"></div>
<div id="openwebrx-digimode-content">
<span id="openwebrx-cursor-blink"></span>
</div>
<div>Support and information: <a href="https://groups.io/g/openwebrx" target="_blank">Groups.io Mailinglist</a></div>
<div id="openwebrx-debugdiv"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-wsjt-message" style="display: none; width: 619px;" data-panel-name="wsjt-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-js8-message" style="display:none; width: 619px;" data-panel-name="js8-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-message"></div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-ysf" style="display: none;" data-panel-name="metadata-ysf">
<div class="openwebrx-meta-slot">
<div class="openwebrx-ysf-mode"></div>
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
</div>
<div class="openwebrx-ysf-source"><span class="location"></span><span class="callsign"></span></div>
<div class="openwebrx-ysf-up"></div>
<div class="openwebrx-ysf-down"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-dstar" style="display: none;" data-panel-name="metadata-dstar">
<div class="openwebrx-meta-slot">
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
</div>
<div class="openwebrx-dstar-ourcall"><span class="location"></span><span class="callsign"></span></div>
<div class="openwebrx-dstar-message"></div>
<div class="openwebrx-dstar-yourcall"></div>
<div class="openwebrx-dstar-departure"></div>
<div class="openwebrx-dstar-destination"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-nxdn" style="display: none;" data-panel-name="metadata-nxdn">
<div class="openwebrx-meta-slot">
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
</div>
<div class="openwebrx-nxdn-source"></div>
<div class="openwebrx-nxdn-name"></div>
<div class="openwebrx-nxdn-destination"></div>
</div>
</div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-dmr" style="display: none;" data-panel-name="metadata-dmr">
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
<div class="openwebrx-dmr-slot">Timeslot 1</div>
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
</div>
<div class="openwebrx-dmr-id"></div>
<div class="openwebrx-dmr-name"></div>
<div class="openwebrx-dmr-target"></div>
<div class="mute">
<svg viewBox="0 0 400 400"><use xlink:href="static/gfx/svg-defs.svg#meta-mute"></use></svg>
</div>
</div>
<div class="openwebrx-meta-slot openwebrx-dmr-timeslot-panel">
<div class="openwebrx-dmr-slot">Timeslot 2</div>
<div class="openwebrx-meta-user-image">
<img class="directcall" src="static/gfx/openwebrx-directcall.svg">
<img class="groupcall" src="static/gfx/openwebrx-groupcall.svg">
</div>
<div class="openwebrx-dmr-id"></div>
<div class="openwebrx-dmr-name"></div>
<div class="openwebrx-dmr-target"></div>
<div class="mute">
<svg viewBox="0 0 400 400"><use xlink:href="static/gfx/svg-defs.svg#meta-mute"></use></svg>
</div>
</div>
</div>
<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" style="width: 619px;">
<div class="openwebrx-panel-inner nano" id="openwebrx-log-scroll">
<div class="nano-content">
<div id="openwebrx-client-log-title">OpenWebRX client log</div>
<div>
Author contact: <a href="http://www.justjakob.de/" target="_blank">Jakob Ketterl, DD5JFK</a> |
<a href="https://www.openwebrx.de" target="_blank">OpenWebRX homepage</a>
</div>
<div>Support and information: <a href="https://groups.io/g/openwebrx" target="_blank">Groups.io Mailinglist</a></div>
<div id="openwebrx-debugdiv"></div>
</div>
</div>
</div>
<div class="openwebrx-panel" id="openwebrx-panel-status" data-panel-name="status" style="width: 615px;" data-panel-transparent="true">
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-buffer" data-type="audiobuffer"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-output" data-type="audiooutput"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-speed" data-type="audiospeed"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-network-speed" data-type="networkspeed"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-server-cpu" data-type="cpu"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-clients" data-type="clients"></div>
</div>
</div>
<div class="openwebrx-panel" id="openwebrx-panel-status" data-panel-name="status" style="width: 615px;" data-panel-transparent="true">
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-buffer" data-type="audiobuffer"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-output" data-type="audiooutput"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-audio-speed" data-type="audiospeed"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-network-speed" data-type="networkspeed"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-server-cpu" data-type="cpu"></div>
<div class="openwebrx-progressbar" id="openwebrx-bar-clients" data-type="clients"></div>
<div id="openwebrx-panels-container-right">
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" style="width: 259px;">
<div class="openwebrx-panel-line frequencies-container">
<div class="frequencies">
<div class="webrx-actual-freq"></div>
<div class="webrx-mouse-freq"></div>
</div>
<div class="openwebrx-button openwebrx-square-button openwebrx-bookmark-button" style="display:none;" title="Add bookmark...">
<svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#bookmark"></use></svg>
</div>
</div>
<div class="openwebrx-panel-line">
<select id="openwebrx-sdr-profiles-listbox" onchange="sdr_profile_changed();">
</select>
</div>
<div class="openwebrx-modes openwebrx-panel-line"></div>
<div class="openwebrx-panel-line">
<div title="Mute on/off" class="openwebrx-button openwebrx-slider-button openwebrx-mute-button" onclick="toggleMute();">
<svg class="unmuted" viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#speaker"></use></svg>
<svg class="muted" viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#speaker-muted"></use></svg>
</div>
<input title="Volume" id="openwebrx-panel-volume" class="openwebrx-panel-slider" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Auto-adjust waterfall colors (right-click for continuous)" id="openwebrx-waterfall-colors-auto" class="openwebrx-button openwebrx-slider-button">
<svg class="auto" viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#waterfall-auto"></use></svg>
<svg class="continuous" viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#waterfall-continuous"></use></svg>
</div>
<input title="Waterfall minimum level" id="openwebrx-waterfall-color-min" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(0);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div title="Auto-set squelch level" class="openwebrx-squelch-auto openwebrx-button openwebrx-slider-button">
<svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#squelch"></use></svg>
</div>
<input title="Squelch" class="openwebrx-squelch-slider openwebrx-panel-slider" type="range" min="-150" max="0" value="-150" step="1">
<div title="Set waterfall colors to default" id="openwebrx-waterfall-colors-default" class="openwebrx-button openwebrx-slider-button" onclick="waterfallColorsDefault()">
<svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#waterfall-default"></use></svg>
</div>
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomInOneStep();" title="Zoom in one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-in"></use></svg></div>
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomOutOneStep();" title="Zoom out one step"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-out"></use></svg></div>
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomInTotal();" title="Zoom in totally"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-in-total"></use></svg></div>
<div class="openwebrx-button openwebrx-square-button openwebrx-zoom-button" onclick="zoomOutTotal();" title="Zoom out totally"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#zoom-out-total"></use></svg></div>
<div id="openwebrx-smeter-db">0 dB</div>
</div>
<div class="openwebrx-panel-line">
<div id="openwebrx-smeter">
<div class="openwebrx-smeter-bar"></div>
</div>
</div>
</div>
</div>
</div>
<div id="openwebrx-panels-container-right">
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" style="width: 259px;">
<div class="openwebrx-panel-line frequencies-container">
<div class="frequencies">
<div class="webrx-actual-freq"></div>
<div class="webrx-mouse-freq"></div>
</div>
<div class="openwebrx-button openwebrx-square-button openwebrx-bookmark-button" style="display:none;" title="Add bookmark...">
<span class="sprite sprite-bookmark"></span>
</div>
</div>
<div class="openwebrx-panel-line">
<select id="openwebrx-sdr-profiles-listbox" onchange="sdr_profile_changed();">
</select>
</div>
<div class="openwebrx-modes openwebrx-panel-line"></div>
<div class="openwebrx-panel-line">
<div title="Mute on/off" class="openwebrx-button openwebrx-mute-button" onclick="toggleMute();"><span class="sprite sprite-speaker openwebrx-sliderbtn-img"></span></div>
<input title="Volume" id="openwebrx-panel-volume" class="openwebrx-panel-slider" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Auto-adjust waterfall colors (right-click for continuous)" id="openwebrx-waterfall-colors-auto" class="openwebrx-button"><span class="sprite sprite-waterfall-auto openwebrx-sliderbtn-img"></span></div>
<input title="Waterfall minimum level" id="openwebrx-waterfall-color-min" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(0);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div title="Auto-set squelch level" class="openwebrx-squelch-auto openwebrx-button"><span class="sprite sprite-squelch openwebrx-sliderbtn-img"></span></div>
<input title="Squelch" class="openwebrx-squelch-slider openwebrx-panel-slider" type="range" min="-150" max="0" value="-150" step="1">
<div title="Set waterfall colors to default" id="openwebrx-waterfall-colors-default" class="openwebrx-button" onclick="waterfallColorsDefault()"><span class="sprite sprite-waterfall-default openwebrx-sliderbtn-img"></span></div>
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInOneStep();" title="Zoom in one step"><span class="sprite sprite-zoom-in"></span></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutOneStep();" title="Zoom out one step"><span class="sprite sprite-zoom-out"></span></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInTotal();" title="Zoom in totally"><span class="sprite sprite-zoom-in-total"></span></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutTotal();" title="Zoom out totally"><span class="sprite sprite-zoom-out-total"></span></div>
<div id="openwebrx-smeter-db">0 dB</div>
</div>
<div class="openwebrx-panel-line">
<div id="openwebrx-smeter">
<div class="openwebrx-smeter-bar"></div>
</div>
</div>
<div id="openwebrx-error-overlay" class="openwebrx-overlay" style="display:none;">
<div class="overlay-content">
<div>This receiver is currently unavailable due to technical issues.</div>
<div>Error Message:</div>
<div class="errormessage"></div>
</div>
</div>
</div>
</div>
<div id="openwebrx-autoplay-overlay" class="openwebrx-overlay" style="display:none;">
<div class="overlay-content">
<img id="openwebrx-play-button" src="static/gfx/openwebrx-play-button.png" />
<svg viewBox="0 0 700 700"><use xlink:href="static/gfx/svg-defs.svg#play-button"></use></svg>
<div>Start OpenWebRX</div>
</div>
</div>
<div id="openwebrx-error-overlay" class="openwebrx-overlay" style="display:none;">
<div class="overlay-content">
<div>This receiver is currently unavailable due to technical issues.</div>
<div>Error Message:</div>
<div class="errormessage"></div>
</div>
</div>
<div id="openwebrx-dialog-bookmark" class="openwebrx-dialog" style="display:none;">
<form>
<div class="form-field">

View File

@ -6,27 +6,15 @@ function AudioEngine(maxBufferLength, audioReporter) {
this.audioReporter = audioReporter;
this.initStats();
this.resetStats();
var ctx = window.AudioContext || window.webkitAudioContext;
if (!ctx) {
return;
}
this.onStartCallbacks = [];
this.started = false;
// try common working sample rates
if (![48000, 44100].some(function(sr) {
try {
this.audioContext = new ctx({sampleRate: sr});
return true;
} catch (e) {
return false;
}
}, this)) {
// fallback: let the browser decide
// this may cause playback problems down the line
this.audioContext = new ctx();
this.audioContext = this.buildAudioContext();
if (!this.audioContext) {
return;
}
var me = this;
this.audioContext.onstatechange = function() {
if (me.audioContext.state !== 'running') return;
@ -43,6 +31,38 @@ function AudioEngine(maxBufferLength, audioReporter) {
this.maxBufferSize = maxBufferLength * this.getSampleRate();
}
AudioEngine.prototype.buildAudioContext = function() {
var ctxClass = window.AudioContext || window.webkitAudioContext;
if (!ctxClass) {
return;
}
// known good sample rates
var goodRates = [48000, 44100, 96000]
// let the browser chose the sample rate, if it is good, use it
var ctx = new ctxClass({latencyHint: 'playback'});
if (goodRates.indexOf(ctx.sampleRate) >= 0) {
return ctx;
}
// if that didn't work, try if any of the good rates work
if (goodRates.some(function(sr) {
try {
ctx = new ctxClass({sampleRate: sr, latencyHint: 'playback'});
return true;
} catch (e) {
return false;
}
}, this)) {
return ctx;
}
// fallback: let the browser decide
// this may cause playback problems down the line
return new ctxClass({latencyHint: 'playback'});
}
AudioEngine.prototype.resume = function(){
this.audioContext.resume();
}

View File

@ -87,8 +87,8 @@ BookmarkBar.prototype.render = function(){
var $bookmark = $(
'<div class="bookmark" data-source="' + b.source + '"' + (b.editable?' editable="editable"':'') + '>' +
'<div class="bookmark-actions">' +
'<div class="openwebrx-button action" data-action="edit"><span class="sprite sprite-edit"></span></div>' +
'<div class="openwebrx-button action" data-action="delete"><span class="sprite sprite-trashcan"><span></div>' +
'<div class="openwebrx-button action" data-action="edit"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#edit"></use></svg></div>' +
'<div class="openwebrx-button action" data-action="delete"><svg viewBox="0 0 80 80"><use xlink:href="static/gfx/svg-defs.svg#trashcan"></use></svg></div>' +
'</div>' +
'<div class="bookmark-content">' + b.name + '</div>' +
'</div>'
@ -145,21 +145,3 @@ BookmarkBar.prototype.getDemodulatorPanel = function() {
BookmarkBar.prototype.getDemodulator = function() {
return this.getDemodulatorPanel().getDemodulator();
};
BookmarkLocalStorage = function(){
};
BookmarkLocalStorage.prototype.getBookmarks = function(){
return JSON.parse(window.localStorage.getItem("bookmarks")) || [];
};
BookmarkLocalStorage.prototype.setBookmarks = function(bookmarks){
window.localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
};
BookmarkLocalStorage.prototype.deleteBookmark = function(data) {
if (data.id) data = data.id;
var bookmarks = this.getBookmarks();
bookmarks = bookmarks.filter(function(b) { return b.id !== data; });
this.setBookmarks(bookmarks);
};

View File

@ -0,0 +1,17 @@
BookmarkLocalStorage = function(){
};
BookmarkLocalStorage.prototype.getBookmarks = function(){
return JSON.parse(window.localStorage.getItem("bookmarks")) || [];
};
BookmarkLocalStorage.prototype.setBookmarks = function(bookmarks){
window.localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
};
BookmarkLocalStorage.prototype.deleteBookmark = function(data) {
if (data.id) data = data.id;
var bookmarks = this.getBookmarks();
bookmarks = bookmarks.filter(function(b) { return b.id !== data; });
this.setBookmarks(bookmarks);
};

View File

@ -5,14 +5,14 @@ function Filter(demodulator) {
Filter.prototype.getLimits = function() {
var max_bw;
if (this.demodulator.get_secondary_demod() === 'pocsag') {
if (['pocsag', 'packet'].indexOf(this.demodulator.get_secondary_demod()) >= 0) {
max_bw = 12500;
} else if (['dmr', 'dstar', 'nxdn', 'ysf', 'm17'].indexOf(this.demodulator.get_modulation()) >= 0) {
max_bw = 6250;
} else if (this.demodulator.get_modulation() === 'wfm') {
max_bw = 100000;
} else if (this.demodulator.get_modulation() === 'drm') {
max_bw = 100000;
} else if (this.demodulator.get_secondary_demod() === 'packet') {
max_bw = 12500;
max_bw = 50000;
} else {
max_bw = (audioEngine.getOutputRate() / 2) - 1;
}

View File

@ -158,7 +158,7 @@ DemodulatorPanel.prototype.updatePanels = function() {
var modulation = this.getDemodulator().get_secondary_demod();
$('#openwebrx-panel-digimodes').attr('data-mode', modulation);
toggle_panel("openwebrx-panel-digimodes", !!modulation);
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4', 'fst4', 'fst4w'].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4', 'fst4', 'fst4w', "q65"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-js8-message", modulation == "js8");
toggle_panel("openwebrx-panel-packet-message", modulation === "packet");
toggle_panel("openwebrx-panel-pocsag-message", modulation === "pocsag");
@ -213,6 +213,10 @@ DemodulatorPanel.prototype.setInitialParams = function(params) {
$.extend(this.initialParams, params);
};
DemodulatorPanel.prototype.resetInitialParams = function() {
this.initialParams = {};
};
DemodulatorPanel.prototype.onHashChange = function() {
this._apply(this.transformHashParams(this.parseHash()));
};
@ -341,7 +345,7 @@ DemodulatorPanel.prototype.updateSquelch = function() {
};
DemodulatorPanel.prototype.setSquelchMargin = function(margin) {
if (!margin || this.squelchMargin == margin) return;
if (typeof(margin) === 'undefined' || this.squelchMargin == margin) return;
this.squelchMargin = margin;
};
@ -353,9 +357,9 @@ DemodulatorPanel.prototype.setMouseFrequency = function(freq) {
this.mouseFrequencyDisplay.setFrequency(freq);
};
DemodulatorPanel.prototype.setFrequencyPrecision = function(precision) {
this.tuneableFrequencyDisplay.setFrequencyPrecision(precision);
this.mouseFrequencyDisplay.setFrequencyPrecision(precision);
DemodulatorPanel.prototype.setTuningPrecision = function(precision) {
this.tuneableFrequencyDisplay.setTuningPrecision(precision);
this.mouseFrequencyDisplay.setTuningPrecision(precision);
};
$.fn.demodulatorPanel = function(){

View File

@ -1,7 +1,14 @@
function FrequencyDisplay(element) {
this.suffixes = {
'': 0,
'k': 3,
'M': 6,
'G': 9,
'T': 12
};
this.element = $(element);
this.digits = [];
this.precision = 4;
this.precision = 2;
this.setupElements();
this.setFrequency(0);
}
@ -9,15 +16,30 @@ function FrequencyDisplay(element) {
FrequencyDisplay.prototype.setupElements = function() {
this.displayContainer = $('<div>');
this.digitContainer = $('<span>');
this.displayContainer.html([this.digitContainer, $('<span> MHz</span>')]);
this.unitContainer = $('<span> Hz</span>');
this.displayContainer.html([this.digitContainer, this.unitContainer]);
this.element.html(this.displayContainer);
};
FrequencyDisplay.prototype.getSuffix = function() {
var me = this;
return Object.keys(me.suffixes).filter(function(key){
return me.suffixes[key] == me.exponent;
})[0] || "";
};
FrequencyDisplay.prototype.setFrequency = function(freq) {
this.frequency = freq;
var formatted = (freq / 1e6).toLocaleString(
if (this.frequency === 0 || Number.isNaN(this.frequency)) {
this.exponent = 0
} else {
this.exponent = Math.floor(Math.log10(this.frequency) / 3) * 3;
}
var digits = Math.max(0, this.exponent - this.precision);
var formatted = (freq / 10 ** this.exponent).toLocaleString(
undefined,
{maximumFractionDigits: this.precision, minimumFractionDigits: this.precision}
{maximumFractionDigits: digits, minimumFractionDigits: digits}
);
var children = this.digitContainer.children();
for (var i = 0; i < formatted.length; i++) {
@ -36,10 +58,11 @@ FrequencyDisplay.prototype.setFrequency = function(freq) {
while (this.digits.length > formatted.length) {
this.digits.pop().remove();
}
this.unitContainer.text(' ' + this.getSuffix() + 'Hz');
};
FrequencyDisplay.prototype.setFrequencyPrecision = function(precision) {
if (!precision) return;
FrequencyDisplay.prototype.setTuningPrecision = function(precision) {
if (typeof(precision) == 'undefined') return;
this.precision = precision;
this.setFrequency(this.frequency);
};
@ -53,22 +76,28 @@ TuneableFrequencyDisplay.prototype = new FrequencyDisplay();
TuneableFrequencyDisplay.prototype.setupElements = function() {
FrequencyDisplay.prototype.setupElements.call(this);
this.input = $('<input>');
this.input.hide();
this.element.append(this.input);
this.input = $('<input type="number" step="any">');
this.suffixInput = $('<select tabindex="-1">');
this.suffixInput.append($.map(this.suffixes, function(e, p) {
return $('<option value="' + e + '">' + p + 'Hz</option>');
}));
this.inputGroup = $('<div class="input-group">');
this.inputGroup.append([this.input, this.suffixInput]);
this.inputGroup.hide();
this.element.append(this.inputGroup);
};
TuneableFrequencyDisplay.prototype.setupEvents = function() {
var me = this;
me.element.on('wheel', function(e){
me.displayContainer.on('wheel', function(e){
e.preventDefault();
e.stopPropagation();
var index = me.digitContainer.find('.digit').index(e.target);
if (index < 0) return;
var delta = 10 ** (Math.floor(Math.max(6, Math.log10(me.frequency))) - index);
var delta = 10 ** (Math.floor(Math.max(me.exponent, Math.log10(me.frequency))) - index);
if (e.originalEvent.deltaY > 0) delta *= -1;
var newFrequency = me.frequency + delta;
@ -76,26 +105,64 @@ TuneableFrequencyDisplay.prototype.setupEvents = function() {
});
var submit = function(){
var freq = parseInt(me.input.val());
var exponent = parseInt(me.suffixInput.val());
var freq = parseFloat(me.input.val()) * 10 ** exponent;
if (!isNaN(freq)) {
me.element.trigger('frequencychange', freq);
}
me.input.hide();
me.inputGroup.hide();
me.displayContainer.show();
};
me.input.on('blur', submit).on('keyup', function(e){
$inputs = $.merge($(), me.input);
$inputs = $.merge($inputs, me.suffixInput);
$('body').on('click', function(e) {
if (!me.input.is(':visible')) return;
if ($.contains(me.element[0], e.target)) return;
submit();
});
$inputs.on('blur', function(e){
if ($inputs.toArray().indexOf(e.relatedTarget) >= 0) {
return;
}
submit();
});
me.input.on('keydown', function(e){
if (e.keyCode == 13) return submit();
if (e.keyCode == 27) {
me.input.hide();
me.inputGroup.hide();
me.displayContainer.show();
return;
}
var c = String.fromCharCode(e.which);
Object.entries(me.suffixes).forEach(function(e) {
if (e[0].toUpperCase() == c) {
me.suffixInput.val(e[1]);
return submit();
}
})
});
me.input.on('click', function(e){
var currentExponent;
me.suffixInput.on('change', function() {
var newExponent = me.suffixInput.val();
delta = currentExponent - newExponent;
if (delta >= 0) {
me.input.val(parseFloat(me.input.val()) * 10 ** delta);
} else {
// should not be necessary to handle this separately, but floating point precision in javascript
// does not handle this well otherwise
me.input.val(parseFloat(me.input.val()) / 10 ** -delta);
}
currentExponent = newExponent;
me.input.focus();
});
$inputs.on('click', function(e){
e.stopPropagation();
});
me.element.on('click', function(){
me.input.val(me.frequency);
me.input.show();
currentExponent = me.exponent;
me.input.val(me.frequency / 10 ** me.exponent);
me.suffixInput.val(me.exponent);
me.inputGroup.show();
me.displayContainer.hide();
me.input.focus();
});

View File

@ -1,7 +1,7 @@
function Header(el) {
this.el = el;
var $buttons = this.el.find('#openwebrx-main-buttons').find('[data-toggle-panel]').filter(function(){
var $buttons = this.el.find('.openwebrx-main-buttons').find('[data-toggle-panel]').filter(function(){
// ignore buttons when the corresponding panel is not in the DOM
return $('#' + $(this).data('toggle-panel'))[0];
});
@ -11,14 +11,13 @@ function Header(el) {
});
this.init_rx_photo();
this.download_details();
};
Header.prototype.setDetails = function(details) {
this.el.find('#webrx-rx-title').html(details['receiver_name']);
this.el.find('#webrx-rx-desc').html(details['receiver_location'] + ' | Loc: ' + details['locator'] + ', ASL: ' + details['receiver_asl'] + ' m');
this.el.find('#webrx-rx-photo-title').html(details['photo_title']);
this.el.find('#webrx-rx-photo-desc').html(details['photo_desc']);
this.el.find('.webrx-rx-title').html(details['receiver_name']);
this.el.find('.webrx-rx-desc').html(details['receiver_location'] + ' | Loc: ' + details['locator'] + ', ASL: ' + details['receiver_asl'] + ' m');
this.el.find('.webrx-rx-photo-title').html(details['photo_title']);
this.el.find('.webrx-rx-photo-desc').html(details['photo_desc']);
};
Header.prototype.init_rx_photo = function() {
@ -30,21 +29,19 @@ Header.prototype.init_rx_photo = function() {
}
});
$('#webrx-top-container').find('.openwebrx-photo-trigger').click(this.toggle_rx_photo.bind(this));
$('.webrx-top-container').find('.openwebrx-photo-trigger').click(this.toggle_rx_photo.bind(this));
};
Header.prototype.close_rx_photo = function() {
this.rx_photo_state = 0;
this.el.find('#openwebrx-description-container').removeClass('expanded');
this.el.find("#openwebrx-rx-details-arrow-down").show();
this.el.find("#openwebrx-rx-details-arrow-up").hide();
this.el.find('.openwebrx-description-container').removeClass('expanded');
this.el.find(".openwebrx-rx-details-arrow").removeClass('openwebrx-rx-details-arrow--up').addClass('openwebrx-rx-details-arrow--down');
}
Header.prototype.open_rx_photo = function() {
this.rx_photo_state = 1;
this.el.find('#openwebrx-description-container').addClass('expanded');
this.el.find("#openwebrx-rx-details-arrow-down").hide();
this.el.find("#openwebrx-rx-details-arrow-up").show();
this.el.find('.openwebrx-description-container').addClass('expanded');
this.el.find(".openwebrx-rx-details-arrow").removeClass('openwebrx-rx-details-arrow--down').addClass('openwebrx-rx-details-arrow--up');
}
Header.prototype.toggle_rx_photo = function(ev) {
@ -58,13 +55,6 @@ Header.prototype.toggle_rx_photo = function(ev) {
}
};
Header.prototype.download_details = function() {
var self = this;
$.ajax('api/receiverdetails').done(function(data){
self.setDetails(data);
});
};
$.fn.header = function() {
if (!this.data('header')) {
this.data('header', new Header(this));
@ -73,5 +63,5 @@ $.fn.header = function() {
};
$(function(){
$('#webrx-top-container').header();
$('.webrx-top-container').header();
});

View File

@ -78,7 +78,7 @@ WsjtMessagePanel.prototype.pushMessage = function(msg) {
return $('<div/>').text(input).html()
};
if (['FT8', 'JT65', 'JT9', 'FT4', 'FST4'].indexOf(msg['mode']) >= 0) {
if (['FT8', 'JT65', 'JT9', 'FT4', 'FST4', 'Q65'].indexOf(msg['mode']) >= 0) {
matches = linkedmsg.match(/(.*\s[A-Z0-9]+\s)([A-R]{2}[0-9]{2})$/);
if (matches && matches[2] !== 'RR73') {
linkedmsg = html_escape(matches[1]) + '<a href="map?locator=' + matches[2] + '" target="openwebrx-map">' + matches[2] + '</a>';
@ -180,6 +180,7 @@ PacketMessagePanel.prototype.pushMessage = function(msg) {
}
} else if (msg.lat && msg.lon) {
classes.push('openwebrx-maps-pin');
overlay = '<svg viewBox="0 0 20 35"><use xlink:href="static/gfx/svg-defs.svg#maps-pin"></use></svg>';
}
var attrs = [
'class="' + classes.join(' ') + '"',

View File

@ -145,7 +145,7 @@ YsfMetaPanel.prototype.setLocation = function(lat, lon, callsign) {
this.hasLocation = hasLocation; this.callsign = callsign;
var html = '';
if (hasLocation) {
html = '<a class="openwebrx-maps-pin" href="map?callsign=' + encodeURIComponent(callsign) + '" target="_blank"></a>';
html = '<a class="openwebrx-maps-pin" href="map?callsign=' + encodeURIComponent(callsign) + '" target="_blank"><svg viewBox="0 0 20 35"><use xlink:href="static/gfx/svg-defs.svg#maps-pin"></use></svg></a>';
}
this.el.find('.openwebrx-ysf-source .location').html(html);
};
@ -162,9 +162,145 @@ YsfMetaPanel.prototype.setDown = function(down) {
this.el.find('.openwebrx-ysf-down').text(down || '');
}
function DStarMetaPanel(el) {
MetaPanel.call(this, el);
this.modes = ['DSTAR'];
this.clear();
}
DStarMetaPanel.prototype = new MetaPanel();
DStarMetaPanel.prototype.update = function(data) {
if (!this.isSupported(data)) return;
if (data['sync'] && data['sync'] == 'voice') {
this.el.find(".openwebrx-meta-slot").addClass("active");
this.setOurCall(data['ourcall']);
this.setYourCall(data['yourcall']);
this.setDeparture(data['departure']);
this.setDestination(data['destination']);
this.setMessage(data['message']);
this.setLocation(data['lat'], data['lon'], data['ourcall']);
} else {
this.clear();
}
};
DStarMetaPanel.prototype.setOurCall = function(ourcall) {
if (this.ourcall === ourcall) return;
this.ourcall = ourcall;
this.el.find('.openwebrx-dstar-ourcall .callsign').text(ourcall || '');
};
DStarMetaPanel.prototype.setYourCall = function(yourcall) {
if (this.yourcall === yourcall) return;
this.yourcall = yourcall;
this.el.find('.openwebrx-dstar-yourcall').text(yourcall || '');
};
DStarMetaPanel.prototype.setDeparture = function(departure) {
if (this.departure === departure) return;
this.departure = departure;
this.el.find('.openwebrx-dstar-departure').text(departure || '');
};
DStarMetaPanel.prototype.setDestination = function(destination) {
if (this.destination === destination) return;
this.destination = destination;
this.el.find('.openwebrx-dstar-destination').text(destination || '');
};
DStarMetaPanel.prototype.setMessage = function(message) {
if (this.message === message) return;
this.message = message;
this.el.find('.openwebrx-dstar-message').text(message || '');
}
DStarMetaPanel.prototype.clear = function() {
MetaPanel.prototype.clear.call(this);
this.setOurCall();
this.setYourCall();
this.setDeparture();
this.setDestination();
this.setMessage();
this.setLocation();
};
DStarMetaPanel.prototype.setLocation = function(lat, lon, callsign) {
var hasLocation = lat && lon && callsign && callsign != '';
if (hasLocation === this.hasLocation && this.callsign === callsign) return;
this.hasLocation = hasLocation; this.callsign = callsign;
var html = '';
if (hasLocation) {
html = '<a class="openwebrx-maps-pin" href="map?callsign=' + encodeURIComponent(callsign) + '" target="_blank"><svg viewBox="0 0 20 35"><use xlink:href="static/gfx/svg-defs.svg#maps-pin"></use></svg></a>';
}
this.el.find('.openwebrx-dstar-ourcall .location').html(html);
};
function NxdnMetaPanel(el) {
MetaPanel.call(this, el);
this.modes = ['NXDN'];
this.clear();
}
NxdnMetaPanel.prototype = new MetaPanel();
NxdnMetaPanel.prototype.update = function(data) {
if (!this.isSupported(data)) return;
if (data['sync'] && data['sync'] == 'voice') {
this.el.find(".openwebrx-meta-slot").addClass("active");
this.setSource(data['additional'] && data['additional']['callsign'] || data['source']);
this.setName(data['additional'] && data['additional']['fname']);
this.setDestination(data['destination']);
this.setMode(['conference', 'individual'].includes(data['type']) ? data['type'] : undefined);
} else {
this.clear();
}
};
NxdnMetaPanel.prototype.setSource = function(source) {
if (this.source === source) return;
this.source = source;
this.el.find('.openwebrx-nxdn-source').text(source || '');
};
NxdnMetaPanel.prototype.setName = function(name) {
if (this.name === name) return;
this.name = name;
this.el.find('.openwebrx-nxdn-name').text(name || '');
};
NxdnMetaPanel.prototype.setDestination = function(destination) {
if (this.destination === destination) return;
this.destination = destination;
this.el.find('.openwebrx-nxdn-destination').text(destination || '');
};
NxdnMetaPanel.prototype.setMode = function(mode) {
if (this.mode === mode) return;
this.mode = mode;
var modes = ['individual', 'conference'];
var classes = modes.filter(function(c){
return c !== mode;
});
this.el.find('.openwebrx-meta-slot').removeClass(classes.join(' ')).addClass(mode);
};
NxdnMetaPanel.prototype.clear = function() {
MetaPanel.prototype.clear.call(this);
this.setMode();
this.setSource();
this.setName();
this.setDestination();
};
MetaPanel.types = {
dmr: DmrMetaPanel,
ysf: YsfMetaPanel
ysf: YsfMetaPanel,
dstar: DStarMetaPanel,
nxdn: NxdnMetaPanel,
};
$.fn.metaPanel = function() {

6
htdocs/lib/bootstrap.bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
htdocs/lib/location-picker.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/* Taken from https://github.com/cyphercodes/location-picker under GPLv3 license */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.locationPicker=t()}(this,function(){"use strict";return function(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var o=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css","top"===n&&o.firstChild?o.insertBefore(i,o.firstChild):o.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}('.location-picker .centerMarker{position:absolute;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAABWCAYAAACEsWWHAAAGLElEQVR4AdXcA2xsXReH8f3Ztm3btm2br23btm3btm2rd257b835vVnJStL0sj3Tds6TrOTkzN5r/5925qgorQRPwQewDHbHebgXDQxlNXLfeTlmmZzzlNJOpMyXsA86TJ2O7PGl6DnbQr/CncYx1tNj4JzTde+0la5V/6/x+x+Z85Ov6/jWZ0TFduyL12JMjI05E4iev5pxQXwFV0lGOx7Xf/xRulb5n45vflrH1z4xqYo5MTd6RC9JrvGVmRB6C04dLzR/yw10fP2TGbJyRa/oOVHw1Fh7uqS+jAY0e3uaPXvupOM7n48w01LRO9aItSDX/nKrpf6FYRi85AJzfvy1WHxGKtaKNZPhyNIKoadjZ0nfIfvFYrNSsXYiMz2tilg00Bweas7fZO1YYFYrMkQWKVfl7ScadS7z12jcFhVZxsn9ayoHimHI71RbVWRKhiPrZA7pjZn+TFX4zDWW6lSAU/PoFw3auiJjcuqSryjyPJWH9LauyBhZEXxlcdd+VyFOjDGxFhVZk6vCoUwEv5KXSXlFUYuKrJE5+dXCvlt3Ia7TYkKtKjIndxr/XcOXIMzzgrZWFZkje/Kl8WL7QP/xR7Z0wWs/9X5HvutNdnjdq2z6qpdFxXbsi9daulZkT/YZ/zbsQNwTtWSRuz7/YXu9+bU2ePlLFlcxJsa2ZM3InnSEU8EH5J1v3iRWqhs+9QGbv+plS5TKirExp/K6kT0ckg8ULIO4RW/Jd2q81GTkWvGdC4dkmYLdoXvHLSs3zrffVCrmVl4/HJLdC86r/vmKA8X7ImCVqnxACYfkvIL7EE+OKjWNI11FsehRKUM4JPcVNBCPxSo1jcN4RbHoUSlDOCSNgiHEM79KTeMcVVEselTKEA7JUMEA4pqr9mLhkAwUPAqNX36n9m/Fxi+/K3mk4GaY+/df1/7gEQ7JzQUnwPwN16h8XTjbh/twSE4oWBf6jjio9ifocEjWLfgeDN14be0vqcIh+V7BCzFobKw556ffqO1FcGQPh3AJpxLgROjebrPa3rZE9uTEEgT4IwzffENtbzQje/LH8WLPRQPycXatKjInjXAp48GGMHjx+bUTi8zJhmUieAX60ez8359rIxVZ0czsrygLA9uM+6zVoiJrsnVZFHg55sP8jddqe6nImMyP7GVxYA0Yffyxtn4iHNkiY7J6WRJ4Fu6G3n13a1uxyJbcHZnL0oDvQHNwsJmPDNqqIlNkg8haJgOOgKGrL287sciUHFEmC16FTpi/2bptIxVZks7IWKYC/grN+fOa+bBnVisyRBaIbKUKOBsGzjp11sUiQ3J2qQreij7oWn3ZWZOKtZO+yFRaAVaAsSceb3Z874szLhVrxtoQWUqrwFNxeeWfoVX/2dflkaW0ErwPQ2h2Lvf3GZOKtdDMtd9XpgOsByMP3q/j25+bdqlYI9ZK1ivTBZ6JW6D3kH2nXSzWSG6Jtct0gs+iaWSkOfcvv5g2qehtdLSJqM+WmQC7wND110ybWPROdikzBV6Ih2H+5uu1XCp6Jg/HWmUmwa+gOa+rOedHX22ZVPSKngh+VWYDnAP9Jx3TMrHolZxTZgu8B8PxBHbun39eWSp6RC8MR+8ym2AbGLzo3Mpi0SPZpsw2eCm6oPP/f5myVMxNuqJnaQewKgzdMPXDf8xNVi3tAp6Nh2DemstPWirmJNHj2aWdwDIwfNstkxaLOckypd3Ac/AEdP73T0stFWOTJ6JHaUewBgycecpSi8XYZI3SruBlGGwODy/VT0djTIzFYMwt7QwOhZ69d16iWIxJDi3tDr5CPh/5xqcWKRWvxRgEX6mDWP5GOF0r/muRYl0r/ltyV8wpdQCbQ98xhy1SLF5LNi91AV+A0ccfXaRYvJZ8oU5iT0cXNP7wExOlYl/SFWNLncARMH+L9U0Ui33JEaVuYGXoP/lYE8ViX7JyHcW+uahrx9iXfLOOYq+Bse75JorFvuQ1pY6gE8b/kV1sJ52lruCuib+9GtvJXXUWu2ri72fFdnJVncXOhvgXFykmtpOz6yx2MMzfdB0pJraTg+sstiwM33mbxm9/qPG7H4ntZNk6iz0Hd5hA7ntOqTN4OfbDo1mx/fIyzTwJJedUPgRWtocAAAAASUVORK5CYII=") no-repeat;background-size:100%;top:50%;left:50%;z-index:1;margin-left:-14px;margin-top:-43px;height:44px;width:28px;cursor:pointer}'),function(){function e(e,t,n){void 0===t&&(t={}),void 0===n&&(n={});var o={setCurrentPosition:!0};Object.assign(o,t);var i={center:new google.maps.LatLng(o.lat?o.lat:34.4346,o.lng?o.lng:35.8362),zoom:15};Object.assign(i,n),e instanceof HTMLElement?this.element=e:this.element=document.getElementById(e),this.map=new google.maps.Map(this.element,i);var r=document.createElement("div");r.classList.add("centerMarker"),this.element&&(this.element.classList.add("location-picker"),this.element.children[0].appendChild(r)),!o.setCurrentPosition||o.lat||o.lng||this.setCurrentPosition()}return e.prototype.getMarkerPosition=function(){var e=this.map.getCenter();return{lat:e.lat(),lng:e.lng()}},e.prototype.setLocation=function(e,t){this.map.setCenter(new google.maps.LatLng(e,t))},e.prototype.setCurrentPosition=function(){var e=this;navigator.geolocation?navigator.geolocation.getCurrentPosition(function(t){var n={lat:t.coords.latitude,lng:t.coords.longitude};e.map.setCenter(n)},function(){console.log("Could not determine your location...")}):console.log("Your browser does not support Geolocation.")},e}()});

View File

@ -0,0 +1,402 @@
function Editor(table) {
this.table = table;
}
Editor.prototype.getInputHtml = function() {
return '<input>';
}
Editor.prototype.render = function(el) {
this.input = $(this.getInputHtml());
el.append(this.input);
this.setupEvents();
};
Editor.prototype.setupEvents = function() {
var me = this;
this.input.on('blur', function() { me.submit(); }).on('change', function() { me.submit(); }).on('keydown', function(e){
if (e.keyCode == 13) return me.submit();
if (e.keyCode == 27) return me.cancel();
});
};
Editor.prototype.submit = function() {
if (!this.onSubmit) return;
var submit = this.onSubmit;
delete this.onSubmit;
submit();
};
Editor.prototype.cancel = function() {
if (!this.onCancel) return;
var cancel = this.onCancel;
delete this.onCancel;
cancel();
};
Editor.prototype.focus = function() {
this.input.focus();
};
Editor.prototype.disable = function(flag) {
this.input.prop('disabled', flag);
};
Editor.prototype.setValue = function(value) {
this.input.val(value);
};
Editor.prototype.getValue = function() {
return this.input.val();
};
Editor.prototype.getHtml = function() {
return this.getValue();
};
function NameEditor(table) {
Editor.call(this, table);
}
NameEditor.prototype = new Editor();
NameEditor.prototype.getInputHtml = function() {
return '<input class="form-control form-control-sm" type="text">';
}
function FrequencyEditor(table) {
Editor.call(this, table);
}
FrequencyEditor.suffixes = {
'': 0,
'K': 3,
'M': 6,
'G': 9,
'T': 12
};
FrequencyEditor.prototype = new Editor();
FrequencyEditor.prototype.getInputHtml = function() {
return '<div class="input-group input-group-sm exponential-input" name="frequency">' +
'<input class="form-control form-control-sm" type="number" step="1">' +
'<div class="input-group-append">' +
'<select class="input-group-text exponent" tabindex="-1">' +
$.map(FrequencyEditor.suffixes, function(v, k) {
// fix lowercase "kHz"
if (k === "K") k = "k";
return '<option value="' + v + '">' + k + 'Hz</option>';
}).join('') +
'</select>' +
'</div>' +
'</div>';
};
FrequencyEditor.prototype.render = function(el) {
this.input = $(this.getInputHtml());
el.append(this.input);
this.freqInput = el.find('input');
this.expInput = el.find('select');
this.setupEvents();
};
FrequencyEditor.prototype.setupEvents = function() {
var me = this;
var inputs = [this.freqInput, this.expInput].map(function(i) { return i[0]; });
inputs.forEach(function(input) {
$(input).on('blur', function(e){
if (inputs.indexOf(e.relatedTarget) >= 0) {
return;
}
me.submit();
});
});
var me = this;
this.freqInput.on('keydown', function(e){
if (e.keyCode == 13) return me.submit();
if (e.keyCode == 27) return me.cancel();
var c = String.fromCharCode(e.which);
if (c in FrequencyEditor.suffixes) {
me.expInput.val(FrequencyEditor.suffixes[c]);
}
});
}
FrequencyEditor.prototype.getValue = function() {
var frequency = parseFloat(this.freqInput.val());
var exp = parseInt(this.expInput.val());
return Math.floor(frequency * 10 ** exp);
};
FrequencyEditor.prototype.setValue = function(value) {
var value = parseFloat(value);
var exp = 0;
if (!Number.isNaN(value) && value > 0) {
exp = Math.floor(Math.log10(value) / 3) * 3;
}
this.freqInput.val(value / 10 ** exp);
this.expInput.val(exp);
};
FrequencyEditor.prototype.focus = function() {
this.freqInput.focus();
};
var renderFrequency = function(freq) {
var exp = 0;
if (!Number.isNaN(freq)) {
exp = Math.floor(Math.log10(freq) / 3) * 3;
}
var frequency = freq / 10 ** exp;
var suffix = Object.entries(FrequencyEditor.suffixes).find(function(e) {
return e[1] == exp;
});
if (!suffix) {
return freq + ' Hz';
}
// fix lowercase 'kHz'
suffix = suffix[0] == 'K' ? 'k' : suffix[0];
var expString = suffix[0] + 'Hz';
return frequency + ' ' + expString;
}
FrequencyEditor.prototype.getHtml = function() {
return renderFrequency(this.getValue());
};
function ModulationEditor(table) {
Editor.call(this, table);
this.modes = table.data('modes');
}
ModulationEditor.prototype = new Editor();
ModulationEditor.prototype.getInputHtml = function() {
return '<select class="form-control form-control-sm">' +
$.map(this.modes, function(name, modulation) {
return '<option value="' + modulation + '">' + name + '</option>';
}).join('') +
'</select>';
};
ModulationEditor.prototype.getHtml = function() {
var $option = this.input.find('option:selected')
return $option.html();
};
$.fn.bookmarktable = function() {
var editors = {
name: NameEditor,
frequency: FrequencyEditor,
modulation: ModulationEditor
};
$.each(this, function(){
var $table = $(this).find('table');
$table.on('dblclick', 'td', function(e) {
var $cell = $(e.target);
var html = $cell.html();
var $row = $cell.parents('tr');
var name = $cell.data('editor');
var EditorCls = editors[name];
if (!EditorCls) return;
var editor = new EditorCls($table);
editor.render($cell.html(''));
editor.setValue($cell.data('value'));
editor.focus();
editor.onSubmit = function() {
editor.disable(true);
$.ajax(document.location.href + "/" + $row.data('id'), {
data: JSON.stringify(Object.fromEntries([[name, editor.getValue()]])),
contentType: 'application/json',
method: 'POST'
}).done(function(){
$cell.data('value', editor.getValue());
$cell.html(editor.getHtml());
});
};
editor.onCancel = function() {
$cell.html(html);
};
});
var $modal = $('#deleteModal').modal({show:false});
$modal.on('hidden.bs.modal', function() {
var $row = $modal.data('row');
if (!$row) return;
$row.find('.bookmark-delete').prop('disabled', false);
$modal.removeData('row');
});
$modal.on('click', '.confirm', function() {
var $row = $modal.data('row');
if (!$row) return;
$.ajax(document.location.href + "/" + $row.data('id'), {
data: "{}",
contentType: 'application/json',
method: 'DELETE'
}).done(function(){
$row.remove();
$modal.modal('hide');
});
});
$table.on('click', '.bookmark-delete', function(e) {
var $button = $(e.target);
$button.prop('disabled', true);
var $row = $button.parents('tr');
$modal.data('row', $row);
$modal.modal('show');
});
$(this).on('click', '.bookmark-add', function() {
if ($table.find('tr[data-id="new"]').length) return;
$table.find('.emptytext').remove();
var row = $('<tr data-id="new">');
var inputs = Object.fromEntries(
Object.entries(editors).map(function(e) {
return [e[0], new e[1]($table)];
})
);
row.append($.map(inputs, function(editor, name){
var cell = $('<td data-editor="' + name + '" class="' + name + '">');
editor.render(cell);
return cell;
}));
row.append($(
'<td>' +
'<div class="btn-group btn-group-sm">' +
'<button type="button" class="btn btn-primary bookmark-save">Save</button>' +
'<button type="button" class="btn btn-secondary bookmark-cancel">Cancel</button>' +
'</div>' +
'</td>'
));
row.on('click', '.bookmark-cancel', function() {
row.remove();
});
row.on('click', '.bookmark-save', function() {
var data = Object.fromEntries(
$.map(inputs, function(input, name){
input.disable(true);
// double wrapped because jQuery.map() flattens the result
return [[name, input.getValue()]];
})
);
$.ajax(document.location.href, {
data: JSON.stringify([data]),
contentType: 'application/json',
method: 'POST'
}).done(function(data){
if (data.length && data.length === 1 && 'bookmark_id' in data[0]) {
row.attr('data-id', data[0]['bookmark_id']);
var tds = row.find('td');
Object.values(inputs).forEach(function(input, index) {
var td = $(tds[index]);
td.data('value', input.getValue());
td.html(input.getHtml());
});
var $cell = row.find('td').last();
var $group = $cell.find('.btn-group');
if ($group.length) {
$group.remove;
$cell.html('<div class="btn btn-sm btn-danger bookmark-delete">delete</div>');
}
}
});
});
$table.append(row);
row[0].scrollIntoView();
});
var $importModal = $('#importModal').modal({show: false});
$(this).find('.bookmark-import').on('click', function() {
var storage = new BookmarkLocalStorage();
var bookmarks = storage.getBookmarks();
if (bookmarks.length) {
var modes = $table.data('modes');
var $list = $('<table class="table table-sm">');
$list.append(bookmarks.map(function(b) {
var modulation = b.modulation;
if (modulation in modes) {
modulation = modes[modulation];
}
var row = $(
'<tr>' +
'<td><input class="form-check-input select" type="checkbox"></td>' +
'<td>' + b.name + '</td>' +
'<td class="frequency">' + renderFrequency(b.frequency) + '</td>' +
'<td>' + modulation + '</td>' +
'</tr>'
);
row.data('bookmark', b);
return row;
}));
$importModal.find('.bookmark-list').html($list);
} else {
$importModal.find('.bookmark-list').html('No personal bookmarks found in this browser');
}
$importModal.modal('show');
});
$importModal.on('click', '.confirm', function() {
var $list = $importModal.find('.bookmark-list table');
if ($list.length) {
var selected = $list.find('tr').filter(function(){
return $(this).find('.select').is(':checked');
}).map(function(){
return $(this).data('bookmark');
}).toArray();
if (selected.length) {
$.ajax(document.location.href, {
data: JSON.stringify(selected),
contentType: 'application/json',
method: 'POST'
}).done(function(data){
$table.find('.emptytext').remove();
var modes = $table.data('modes');
if (data.length && data.length == selected.length) {
$table.append(data.map(function(obj, index) {
var bookmark = selected[index];
var modulation_name = bookmark.modulation;
if (modulation_name in modes) {
modulation_name = modes[modulation_name];
}
return $(
'<tr data-id="' + obj.bookmark_id + '">' +
'<td data-editor="name" data-value="' + bookmark.name + '">' + bookmark.name + '</td>' +
'<td data-editor="frequency" data-value="' + bookmark.frequency + '" class="frequency">' + renderFrequency(bookmark.frequency) +'</td>' +
'<td data-editor="modulation" data-value="' + bookmark.modulation + '">' + modulation_name + '</td>' +
'<td>' +
'<button type="button" class="btn btn-sm btn-danger bookmark-delete">delete</button>' +
'</td>' +
'</tr>'
)
}));
}
});
}
}
$importModal.modal('hide');
});
});
};

View File

@ -0,0 +1,45 @@
$.fn.exponentialInput = function() {
var prefixes = {
'K': 3,
'M': 6,
'G': 9,
'T': 12
};
this.each(function(){
var $group = $(this);
var currentExponent = 0;
var $input = $group.find('input');
var setExponent = function() {
var newExponent = parseInt($exponent.val());
var delta = currentExponent - newExponent;
if (delta >= 0) {
$input.val(parseFloat($input.val()) * 10 ** delta);
} else {
// should not be necessary to handle this separately, but floating point precision in javascript
// does not handle this well otherwise
$input.val(parseFloat($input.val()) / 10 ** -delta);
}
currentExponent = newExponent;
};
$input.on('keydown', function(e) {
var c = String.fromCharCode(e.which);
if (c in prefixes) {
currentExponent = prefixes[c];
$exponent.val(prefixes[c]);
}
});
var $exponent = $group.find('select.exponent');
$exponent.on('change', setExponent);
// calculate initial exponent
var value = parseFloat($input.val());
if (!Number.isNaN(value)) {
$exponent.val(Math.floor(Math.log10(Math.abs(value)) / 3) * 3);
setExponent();
}
})
};

View File

@ -0,0 +1,17 @@
$.fn.gainInput = function() {
this.each(function() {
var $container = $(this);
var update = function(value){
$container.find('.option').hide();
$container.find('.option.' + value).show();
}
var $select = $container.find('select');
$select.on('change', function(e) {
var value = $(e.target).val();
update(value);
});
update($select.val());
});
}

View File

@ -0,0 +1,87 @@
$.fn.imageUpload = function() {
$.each(this, function(){
var $this = $(this);
var $uploadButton = $this.find('button.upload');
var $restoreButton = $this.find('button.restore');
var $img = $this.find('img');
var originalUrl = $img.prop('src');
var $input = $this.find('input');
var id = $input.prop('id');
var maxSize = $this.data('max-size');
var $error;
var handleError = function(message) {
clearError();
$error = $('<div class="invalid-feedback">' + message + '</div>');
$this.after($error);
$this.addClass('is-invalid');
};
var clearError = function(message) {
if ($error) $error.remove();
$this.removeClass('is-invalid');
};
$uploadButton.click(function(){
var input = document.createElement('input');
input.type = 'file';
input.accept = 'image/jpeg, image/png, image/webp';
input.onchange = function(e) {
$uploadButton.prop('disabled', true);
var $spinner = $('<span class="spinner-border spinner-border-sm mr-1" role="status"></span>');
$uploadButton.prepend($spinner);
var reader = new FileReader()
reader.readAsArrayBuffer(e.target.files[0]);
reader.onprogress = function(e) {
if (e.loaded > maxSize) {
handleError('Maximum file size exceeded');
$uploadButton.prop('disabled', false);
$spinner.remove();
reader.abort();
}
};
reader.onload = function(e) {
if (e.loaded > maxSize) {
handleError('Maximum file size exceeded');
$uploadButton.prop('disabled', false);
$spinner.remove();
return;
}
$.ajax({
url: '../imageupload?id=' + id,
type: 'POST',
data: e.target.result,
processData: false,
contentType: 'application/octet-stream',
}).done(function(data){
$input.val(data.file);
$img.one('load', function() {
$uploadButton.prop('disabled', false);
$spinner.remove();
});
$img.prop('src', '../imageupload?file=' + data.file);
clearError();
}).fail(function(xhr, error){
try {
var res = JSON.parse(xhr.responseText);
handleError(res.error || error);
} catch (e) {
handleError(error);
}
$uploadButton.prop('disabled', false);
$spinner.remove();
});
}
};
input.click();
return false;
});
$restoreButton.click(function(){
$input.val('restore');
$img.prop('src', originalUrl + "&mapped=false");
clearError();
return false;
});
});
}

View File

@ -1,138 +0,0 @@
function Input(name, value, options) {
this.name = name;
this.value = value;
this.options = options;
this.label = options && options.label || name;
};
Input.prototype.getClasses = function() {
return ['form-control', 'form-control-sm'];
}
Input.prototype.bootstrapify = function(input) {
this.getClasses().forEach(input.addClass.bind(input));
return [
'<div class="form-group row">',
'<label class="col-form-label col-form-label-sm col-3" for="' + this.name + '">' + this.label + '</label>',
'<div class="col-9">',
$.map(input, function(el) {
return el.outerHTML;
}).join(''),
'</div>',
'</div>'
].join('');
};
function TextInput() {
Input.apply(this, arguments);
};
TextInput.prototype = new Input();
TextInput.prototype.render = function() {
return this.bootstrapify($('<input type="text" name="' + this.name + '" value="' + this.value + '">'));
}
function NumberInput() {
Input.apply(this, arguments);
};
NumberInput.prototype = new Input();
NumberInput.prototype.render = function() {
return this.bootstrapify($('<input type="number" name="' + this.name + '" value="' + this.value + '">'));
};
function SoapyGainInput() {
Input.apply(this, arguments);
}
SoapyGainInput.prototype = new Input();
SoapyGainInput.prototype.getClasses = function() {
return [];
};
SoapyGainInput.prototype.render = function(){
var markup = $(
'<div class="row form-group">' +
'<div class="col-4">Gain mode</div>' +
'<div class="col-8">' +
'<select class="form-control form-control-sm">' +
'<option value="auto">automatic gain</option>' +
'<option value="single">single gain value</option>' +
'<option value="separate">separate gain values</option>' +
'</select>' +
'</div>' +
'</div>' +
'<div class="row option form-group gain-mode-single">' +
'<div class="col-4">Gain</div>' +
'<div class="col-8">' +
'<input class="form-control form-control-sm" type="number">' +
'</div>' +
'</div>' +
this.options.gains.map(function(g){
return '<div class="row option form-group gain-mode-separate">' +
'<div class="col-4">' + g + '</div>' +
'<div class="col-8">' +
'<input class="form-control form-control-sm" data-gain="' + g + '" type="number">' +
'</div>' +
'</div>';
}).join('')
);
var el = $(this.bootstrapify(markup))
var setMode = function(mode){
el.find('select').val(mode);
el.find('.option').hide();
el.find('.gain-mode-' + mode).show();
};
el.on('change', 'select', function(){
var mode = $(this).val();
setMode(mode);
});
if (typeof(this.value) === 'number') {
setMode('single');
el.find('.gain-mode-single input').val(this.value);
} else if (typeof(this.value) === 'string') {
if (this.value === 'auto') {
setMode('auto');
} else {
setMode('separate');
values = $.extend.apply($, this.value.split(',').map(function(seg){
var split = seg.split('=');
if (split.length < 2) return;
var res = {};
res[split[0]] = parseInt(split[1]);
return res;
}));
el.find('.gain-mode-separate input').each(function(){
var $input = $(this);
var g = $input.data('gain');
$input.val(g in values ? values[g] : 0);
});
}
} else {
setMode('auto');
}
return el;
};
function ProfileInput() {
Input.apply(this, arguments);
};
ProfileInput.prototype = new Input();
ProfileInput.prototype.render = function() {
return $('<div><h3>Profiles</h3></div>');
};
function SchedulerInput() {
Input.apply(this, arguments);
};
SchedulerInput.prototype = new Input();
SchedulerInput.prototype.render = function() {
return $('<div><h3>Scheduler</h3></div>');
};

Some files were not shown because too many files have changed in this diff Show More