From b2efa81b0d6597960207d47779c01adcdc49a51d Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 9 Dec 2020 22:54:06 -0700 Subject: [PATCH 01/37] Formatting additional PBEACON details --- owrx/kiss.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 440cdab..ccd4bba 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -39,6 +39,7 @@ IGLOGIN {callsign} {password} ) if pm["aprs_igate_beacon"]: + #Format beacon lat/lon lat = pm["receiver_gps"]["lat"] lon = pm["receiver_gps"]["lon"] direction_ns = "N" if lat > 0 else "S" @@ -48,10 +49,21 @@ IGLOGIN {callsign} {password} lat = "{0:02d}^{1:05.2f}{2}".format(int(lat), (lat - int(lat)) * 60, direction_ns) lon = "{0:03d}^{1:05.2f}{2}".format(int(lon), (lon - int(lon)) * 60, direction_we) + #Format beacon details + symbol = str(pm["aprs_igate_symbol"]) if checkKey(pm, "aprs_igate_symbol") else "R&" + power = str(pm["aprs_igate_power"]) if checkKey(pm, "aprs_igate_power") else "0" + height = "HEIGHT=" + str(pm["aprs_igate_height"]) if checkKey(pm, "aprs_igate_height") else "" + gain = "GAIN=" + str(pm["aprs_igate_gain"]) if checkKey(pm, "aprs_igate_gain") else "" + adir = "DIR=" + str(pm["aprs_igate_dir"]) if checkKey(pm, "aprs_igate_dir") else "" + freq = "FREQ=" + str(pm["aprs_igate_freq"]) if checkKey(pm, "aprs_igate_freq") else "" + tone = "TONE=" + str(pm["aprs_igate_tone"]) if checkKey(pm, "aprs_igate_tone") else "" + offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if checkKey(pm, "aprs_igate_offset") else "" + comment = str(pm["aprs_igate_comment"]) if checkKey(pm, "aprs_igate_comment") else "OpenWebRX APRS gateway" + config += """ -PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat={lat} long={lon} comment="OpenWebRX APRS gateway" +PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment} """.format( - lat=lat, lon=lon + symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) return config From dc3fd24903057a5d90e4c46ade5ae4ce9fbd7344 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 9 Dec 2020 22:59:16 -0700 Subject: [PATCH 02/37] Correcting key check --- owrx/kiss.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index ccd4bba..ec0531c 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -50,15 +50,15 @@ IGLOGIN {callsign} {password} lon = "{0:03d}^{1:05.2f}{2}".format(int(lon), (lon - int(lon)) * 60, direction_we) #Format beacon details - symbol = str(pm["aprs_igate_symbol"]) if checkKey(pm, "aprs_igate_symbol") else "R&" - power = str(pm["aprs_igate_power"]) if checkKey(pm, "aprs_igate_power") else "0" - height = "HEIGHT=" + str(pm["aprs_igate_height"]) if checkKey(pm, "aprs_igate_height") else "" - gain = "GAIN=" + str(pm["aprs_igate_gain"]) if checkKey(pm, "aprs_igate_gain") else "" - adir = "DIR=" + str(pm["aprs_igate_dir"]) if checkKey(pm, "aprs_igate_dir") else "" - freq = "FREQ=" + str(pm["aprs_igate_freq"]) if checkKey(pm, "aprs_igate_freq") else "" - tone = "TONE=" + str(pm["aprs_igate_tone"]) if checkKey(pm, "aprs_igate_tone") else "" - offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if checkKey(pm, "aprs_igate_offset") else "" - comment = str(pm["aprs_igate_comment"]) if checkKey(pm, "aprs_igate_comment") else "OpenWebRX APRS gateway" + symbol = str(pm["aprs_igate_symbol"]) if "aprs_igate_symbol" in pm else "R&" + power = str(pm["aprs_igate_power"]) if "aprs_igate_power" in pm else "0" + height = "HEIGHT=" + str(pm["aprs_igate_height"]) if "aprs_igate_height" in pm else "" + gain = "GAIN=" + str(pm["aprs_igate_gain"]) if "aprs_igate_gain" in pm else "" + adir = "DIR=" + str(pm["aprs_igate_dir"]) if "aprs_igate_dir" in pm else "" + freq = "FREQ=" + str(pm["aprs_igate_freq"]) if "aprs_igate_freq" in pm else "" + tone = "TONE=" + str(pm["aprs_igate_tone"]) if "aprs_igate_tone" in pm else "" + offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" + comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "OpenWebRX APRS gateway" config += """ PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment} From dc128662da119a0e192dc2325fd3faf2caf6cd96 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 9 Dec 2020 23:05:04 -0700 Subject: [PATCH 03/37] log pbeacon string --- owrx/kiss.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index ec0531c..964b4a4 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,12 +60,16 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "OpenWebRX APRS gateway" - config += """ + pbeacon= """ PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment} """.format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) + logger.info("APRS PBEACON String: " + pbeacon) + + config += pbeacon + return config From 9f45e8880aaf0eb6f134c28fd19ecc6508cc46bc Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 9 Dec 2020 23:09:37 -0700 Subject: [PATCH 04/37] formating pbeacon string --- owrx/kiss.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 964b4a4..b842c68 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,15 +60,14 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "OpenWebRX APRS gateway" - pbeacon= """ -PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment} - """.format( - symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment - ) + pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( + symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) logger.info("APRS PBEACON String: " + pbeacon) - config += pbeacon + config += """ + {pbeacon} + """.format(pbeacon=pbeacon) return config From 5559cded851550989e9635b1a4ca606cf663d007 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 9 Dec 2020 23:17:42 -0700 Subject: [PATCH 05/37] Add quotes around default pbeacon comment --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index b842c68..66d28ce 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -58,7 +58,7 @@ IGLOGIN {callsign} {password} freq = "FREQ=" + str(pm["aprs_igate_freq"]) if "aprs_igate_freq" in pm else "" tone = "TONE=" + str(pm["aprs_igate_tone"]) if "aprs_igate_tone" in pm else "" offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" - comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "OpenWebRX APRS gateway" + comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) From 0fb4ae4fc02594887a9015b341aaf5acbe99a4a2 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 09:59:34 -0700 Subject: [PATCH 06/37] sanitize comment for opening quote --- owrx/kiss.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/owrx/kiss.py b/owrx/kiss.py index 66d28ce..191b183 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,6 +60,9 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" + if(comment[0] != '"'): + comment = "\"" + comment + "\"" + pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) From fdbbbcb64c04fa603fd50243989c1006530da365 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 10:04:42 -0700 Subject: [PATCH 07/37] Sanitize comment closing quote --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 191b183..be3a608 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,7 +60,7 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" - if(comment[0] != '"'): + if(comment[0] != '"' or comment[len(comment)-1] != '"'): comment = "\"" + comment + "\"" pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( From 519b02da79211db708e8eb8fac71f5d99f4a8033 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 10:12:43 -0700 Subject: [PATCH 08/37] improve quotes check --- owrx/kiss.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index be3a608..2ef4624 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,8 +60,10 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" - if(comment[0] != '"' or comment[len(comment)-1] != '"'): + if(len(comment) > 0 and ((comment[0] != '"') or (comment[len(comment)-1] != '"')))): + logger.info(comment) comment = "\"" + comment + "\"" + logger.info(comment) pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) From 11bb04419ba846342693e8046235362fa0e6962b Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 10:13:46 -0700 Subject: [PATCH 09/37] fix parenthesis --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 2ef4624..5ab556d 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,7 +60,7 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" - if(len(comment) > 0 and ((comment[0] != '"') or (comment[len(comment)-1] != '"')))): + if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): logger.info(comment) comment = "\"" + comment + "\"" logger.info(comment) From f83790a5be75374b4fbca4b70a8603ba9a7d7963 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 10:15:26 -0700 Subject: [PATCH 10/37] debug comment length --- owrx/kiss.py | 1 + 1 file changed, 1 insertion(+) diff --git a/owrx/kiss.py b/owrx/kiss.py index 5ab556d..2c8f9ad 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,6 +60,7 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" + logger.info(str(len(comment))) if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): logger.info(comment) comment = "\"" + comment + "\"" From 4c3d037e5890fe73e25fdcd86050a88bfebf47c0 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:07:50 -0700 Subject: [PATCH 11/37] Cleanup debug logging --- owrx/kiss.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 2c8f9ad..5874a14 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -60,11 +60,8 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" - logger.info(str(len(comment))) if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): - logger.info(comment) comment = "\"" + comment + "\"" - logger.info(comment) pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) From 3435052e270f8bc9fdf3407f39499299966e8cc8 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:08:47 -0700 Subject: [PATCH 12/37] sanitize empty comment --- owrx/kiss.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/owrx/kiss.py b/owrx/kiss.py index 5874a14..442371a 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -62,6 +62,8 @@ IGLOGIN {callsign} {password} if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): comment = "\"" + comment + "\"" + else if(len(comment) > 0): + comment = "\"\"" pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) From 1cc88ff3622e97b3c0d215f90d435f1367ef4bfd Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:09:12 -0700 Subject: [PATCH 13/37] if check fix --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 442371a..278cb87 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -62,7 +62,7 @@ IGLOGIN {callsign} {password} if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): comment = "\"" + comment + "\"" - else if(len(comment) > 0): + else if(len(comment) == 0): comment = "\"\"" pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( From b04dcc18d04a1acf2644f9df2fd7844f92022a73 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:10:15 -0700 Subject: [PATCH 14/37] This is Python not C --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 278cb87..478e1f0 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -62,7 +62,7 @@ IGLOGIN {callsign} {password} if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): comment = "\"" + comment + "\"" - else if(len(comment) == 0): + elif(len(comment) == 0): comment = "\"\"" pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( From 86fdbe45e923905e1ec48876ba89cdaa14e754dd Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:23:35 -0700 Subject: [PATCH 15/37] Add examples and comments to default config --- config_webrx.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/config_webrx.py b/config_webrx.py index 427e4dc..ac6d9fa 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -336,6 +336,35 @@ 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" +# 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: +# https://github.com/wb2osz/direwolf/raw/master/doc/User-Guide.pdf + +# Symbol in its two-character form as specified by the APRS spec at http://www.aprs.org/symbols/symbols-new.txt +# Default: Receive only IGate (do not send msgs back to RF) +# aprs_igate_symbol = "R&" +# Custom comment about igate +# Default: OpenWebRX APRS gateway +# aprs_igate_comment = "OpenWebRX APRS gateway" + +# Antenna Power, Height, Gain, and Direction +# Unspecified by default +# Transmitter power (0 by default as only RX is supported) +# aprs_igate_power = "0" +# Antenna height in feet +# aprs_igate_height = "20" +# Antenna gain in dBi +# aprs_igate_gain = "0" +# Antenna direction (N, NE, E, SE, S, SW, W, NW). Omnidirectional by default +# aprs_igate_dir = "NE" + +# Frequency info to contact you by voice +# Unspecified by default +# aprs_igate_freq = "146.955" +# aprs_igate_tone = "74.4" +# aprs_igate_offset = "-0.60" + + # === PSK Reporter setting === # 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 From ae00a14a355ff8aa8e1a49387850d28c7ee8fbfc Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:38:15 -0700 Subject: [PATCH 16/37] Fix comment formatting --- config_webrx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config_webrx.py b/config_webrx.py index ac6d9fa..25420d8 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -343,12 +343,13 @@ aprs_symbols_path = "/usr/share/aprs-symbols/png" # Symbol in its two-character form as specified by the APRS spec at http://www.aprs.org/symbols/symbols-new.txt # Default: Receive only IGate (do not send msgs back to RF) # aprs_igate_symbol = "R&" + # Custom comment about igate # Default: OpenWebRX APRS gateway # aprs_igate_comment = "OpenWebRX APRS gateway" # Antenna Power, Height, Gain, and Direction -# Unspecified by default +# Unspecified by default # Transmitter power (0 by default as only RX is supported) # aprs_igate_power = "0" # Antenna height in feet From dee050f3382688fc561a29b24151e080c7a25081 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Sat, 12 Dec 2020 11:38:50 -0700 Subject: [PATCH 17/37] Fix comment --- config_webrx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_webrx.py b/config_webrx.py index 25420d8..576dfa3 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -348,7 +348,7 @@ aprs_symbols_path = "/usr/share/aprs-symbols/png" # Default: OpenWebRX APRS gateway # aprs_igate_comment = "OpenWebRX APRS gateway" -# Antenna Power, Height, Gain, and Direction +# Antenna Power, Height, Gain # Unspecified by default # Transmitter power (0 by default as only RX is supported) # aprs_igate_power = "0" From 993aa877762cecc5c3f43acdbffb39198f140b1e Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 28 Dec 2020 20:55:02 +0100 Subject: [PATCH 18/37] use css animations for the progressbar (better performance?) --- htdocs/css/openwebrx.css | 15 +++++++++++++-- htdocs/lib/ProgressBar.js | 5 ++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/htdocs/css/openwebrx.css b/htdocs/css/openwebrx.css index 2997348..68ffbb0 100644 --- a/htdocs/css/openwebrx.css +++ b/htdocs/css/openwebrx.css @@ -551,13 +551,23 @@ img.openwebrx-mirror-img -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; + overflow: hidden; } -.openwebrx-progressbar-bar -{ +.openwebrx-progressbar-bar { + background-color: #00aba6; border-radius: 5px; height: 100%; width: 100%; + position: absolute; + left: -100%; + transition-property: left background-color; + transition-duration: 1s; + transition-timing-function: ease-in-out; +} + +.openwebrx-progressbar--over .openwebrx-progressbar-bar { + background-color: #ff6262; } .openwebrx-progressbar-text @@ -566,6 +576,7 @@ img.openwebrx-mirror-img left:0px; top:4px; width: inherit; + z-index: 1; } #openwebrx-panel-status diff --git a/htdocs/lib/ProgressBar.js b/htdocs/lib/ProgressBar.js index 691d2ff..71b1754 100644 --- a/htdocs/lib/ProgressBar.js +++ b/htdocs/lib/ProgressBar.js @@ -3,7 +3,6 @@ ProgressBar = function(el) { this.$innerText = $('' + this.getDefaultText() + ''); this.$innerBar = $('
'); this.$el.empty().append(this.$innerText, this.$innerBar); - this.$innerBar.css('width', '0%'); }; ProgressBar.prototype.getDefaultText = function() { @@ -19,7 +18,7 @@ ProgressBar.prototype.set = function(val, text, over) { ProgressBar.prototype.setValue = function(val) { if (val < 0) val = 0; if (val > 1) val = 1; - this.$innerBar.stop().animate({width: val * 100 + '%'}, 700); + this.$innerBar.css({left: (val - 1) * 100 + '%'}); }; ProgressBar.prototype.setText = function(text) { @@ -27,7 +26,7 @@ ProgressBar.prototype.setText = function(text) { }; ProgressBar.prototype.setOver = function(over) { - this.$innerBar.css('backgroundColor', (over) ? "#ff6262" : "#00aba6"); + this.$el[over ? 'addClass' : 'removeClass']('openwebrx-progressbar--over'); }; AudioBufferProgressBar = function(el) { From eb34c451454f3c28a27839bfa5c4d93cf0a4d747 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 28 Dec 2020 21:16:54 +0100 Subject: [PATCH 19/37] apply transform trick to get GPU optimized animations --- htdocs/css/openwebrx.css | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/css/openwebrx.css b/htdocs/css/openwebrx.css index 68ffbb0..223d1ee 100644 --- a/htdocs/css/openwebrx.css +++ b/htdocs/css/openwebrx.css @@ -564,6 +564,7 @@ img.openwebrx-mirror-img transition-property: left background-color; transition-duration: 1s; transition-timing-function: ease-in-out; + transform: translateZ(0); } .openwebrx-progressbar--over .openwebrx-progressbar-bar { From 2c3146314b883e3bdfbfc3531bd5633a104e0f04 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:14:06 +0100 Subject: [PATCH 20/37] send property changes in bulk to global subscribers --- owrx/property/__init__.py | 49 ++++++++++++++------------- test/property/test_property_filter.py | 4 +-- test/property/test_property_layer.py | 4 +-- test/property/test_property_stack.py | 6 ++-- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index f3560fa..23058d2 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -71,15 +71,22 @@ class PropertyManager(ABC): pass return self - def _fireCallbacks(self, name, value): + def _fireCallbacks(self, changes): + if not changes: + return for c in self.subscribers: try: if c.getName() is None: - c.call(name, value) - elif c.getName() == name: - c.call(value) - except Exception as e: - logger.exception(e) + c.call(changes) + except Exception: + logger.exception("exception while firing changes") + for name in changes: + for c in self.subscribers: + try: + if c.getName() == name: + c.call(changes[name]) + except Exception: + logger.exception("exception while firing changes") class PropertyLayer(PropertyManager): @@ -97,7 +104,7 @@ class PropertyLayer(PropertyManager): if name in self.properties and self.properties[name] == value: return self.properties[name] = value - self._fireCallbacks(name, value) + self._fireCallbacks({name: value}) def __dict__(self): return {k: v for k, v in self.properties.items()} @@ -116,10 +123,9 @@ class PropertyFilter(PropertyManager): self.props = props self.pm.wire(self.receiveEvent) - def receiveEvent(self, name, value): - if name not in self.props: - return - self._fireCallbacks(name, value) + def receiveEvent(self, changes): + changesToForward = {name: value for name, value in changes.items() if name in self.props} + self._fireCallbacks(changesToForward) def __getitem__(self, item): if item not in self.props: @@ -157,7 +163,7 @@ class PropertyStack(PropertyManager): """ highest priority = 0 """ - self._fireChanges(self._addLayer(priority, pm)) + self._fireCallbacks(self._addLayer(priority, pm)) def _addLayer(self, priority: int, pm: PropertyManager): changes = {} @@ -165,8 +171,8 @@ class PropertyStack(PropertyManager): if key not in self or self[key] != pm[key]: changes[key] = pm[key] - def eventClosure(name, value): - self.receiveEvent(pm, name, value) + def eventClosure(changes): + self.receiveEvent(pm, changes) sub = pm.wire(eventClosure) @@ -177,7 +183,7 @@ class PropertyStack(PropertyManager): def removeLayer(self, pm: PropertyManager): for layer in self.layers: if layer["props"] == pm: - self._fireChanges(self._removeLayer(layer)) + self._fireCallbacks(self._removeLayer(layer)) def _removeLayer(self, layer): layer["sub"].cancel() @@ -201,16 +207,11 @@ class PropertyStack(PropertyManager): changes = {**changes, **self._addLayer(priority, pm)} changes = {k: v for k, v in changes.items() if k not in originalState or originalState[k] != v} - self._fireChanges(changes) + self._fireCallbacks(changes) - def _fireChanges(self, changes): - for k, v in changes.items(): - self._fireCallbacks(k, v) - - def receiveEvent(self, layer, name, value): - if layer != self._getTopLayer(name): - return - self._fireCallbacks(name, value) + def receiveEvent(self, layer, changes): + changesToForward = {name: value for name, value in changes.items() if layer == self._getTopLayer(name)} + self._fireCallbacks(changesToForward) def _getTopLayer(self, item): layers = [la["props"] for la in sorted(self.layers, key=lambda l: l["priority"])] diff --git a/test/property/test_property_filter.py b/test/property/test_property_filter.py index 66eff3b..db6dfd3 100644 --- a/test/property/test_property_filter.py +++ b/test/property/test_property_filter.py @@ -11,7 +11,7 @@ class PropertyFilterTest(TestCase): pf = PropertyFilter(pm, "testkey") self.assertEqual(pf["testkey"], "testvalue") - def testMissesPropert(self): + def testMissesProperty(self): pm = PropertyLayer() pm["testkey"] = "testvalue" pf = PropertyFilter(pm, "other_key") @@ -25,7 +25,7 @@ class PropertyFilterTest(TestCase): mock = Mock() pf.wire(mock.method) pm["testkey"] = "testvalue" - mock.method.assert_called_once_with("testkey", "testvalue") + mock.method.assert_called_once_with({"testkey": "testvalue"}) def testForwardsPropertyEvent(self): pm = PropertyLayer() diff --git a/test/property/test_property_layer.py b/test/property/test_property_layer.py index 472c14f..4b77e70 100644 --- a/test/property/test_property_layer.py +++ b/test/property/test_property_layer.py @@ -19,7 +19,7 @@ class PropertyLayerTest(TestCase): mock = Mock() pm.wire(mock.method) pm["testkey"] = "after" - mock.method.assert_called_once_with("testkey", "after") + mock.method.assert_called_once_with({"testkey": "after"}) def testUnsubscribe(self): pm = PropertyLayer() @@ -27,7 +27,7 @@ class PropertyLayerTest(TestCase): mock = Mock() sub = pm.wire(mock.method) pm["testkey"] = "between" - mock.method.assert_called_once_with("testkey", "between") + mock.method.assert_called_once_with({"testkey": "between"}) mock.reset_mock() pm.unwire(sub) diff --git a/test/property/test_property_stack.py b/test/property/test_property_stack.py index 31e3b36..3711868 100644 --- a/test/property/test_property_stack.py +++ b/test/property/test_property_stack.py @@ -49,7 +49,7 @@ class PropertyStackTest(TestCase): mock = Mock() stack.wire(mock.method) layer["testkey"] = "testvalue" - mock.method.assert_called_once_with("testkey", "testvalue") + mock.method.assert_called_once_with({"testkey": "testvalue"}) def testPropertyChangeEventPriority(self): low_layer = PropertyLayer() @@ -64,7 +64,7 @@ class PropertyStackTest(TestCase): low_layer["testkey"] = "modified low value" mock.method.assert_not_called() high_layer["testkey"] = "modified high value" - mock.method.assert_called_once_with("testkey", "modified high value") + mock.method.assert_called_once_with({"testkey": "modified high value"}) def testPropertyEventOnLayerAdd(self): low_layer = PropertyLayer() @@ -162,7 +162,7 @@ class PropertyStackTest(TestCase): mock = Mock() stack.wire(mock.method) stack.removeLayer(layer) - mock.method.assert_called_once_with("testkey", None) + mock.method.assert_called_once_with({"testkey": None}) mock.reset_mock() layer["testkey"] = "after" From abb081394866399750809b971a9f76967401e918 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:15:48 +0100 Subject: [PATCH 21/37] send only necessary config changes --- htdocs/lib/DemodulatorPanel.js | 5 +-- htdocs/openwebrx.js | 62 ++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/htdocs/lib/DemodulatorPanel.js b/htdocs/lib/DemodulatorPanel.js index 22a86f3..2cdca68 100644 --- a/htdocs/lib/DemodulatorPanel.js +++ b/htdocs/lib/DemodulatorPanel.js @@ -4,6 +4,7 @@ function DemodulatorPanel(el) { self.demodulator = null; self.mode = null; self.squelchMargin = 10; + self.initialParams = {}; var displayEl = el.find('.webrx-actual-freq') this.tuneableFrequencyDisplay = displayEl.tuneableFrequencyDisplay(); @@ -180,7 +181,7 @@ DemodulatorPanel.prototype.collectParams = function() { squelch_level: -150, mod: 'nfm' } - return $.extend(new Object(), defaults, this.initialParams || {}, this.transformHashParams(this.parseHash())); + return $.extend(new Object(), defaults, this.initialParams, this.transformHashParams(this.parseHash())); }; DemodulatorPanel.prototype.startDemodulator = function() { @@ -206,7 +207,7 @@ DemodulatorPanel.prototype._apply = function(params) { }; DemodulatorPanel.prototype.setInitialParams = function(params) { - this.initialParams = params; + $.extend(this.initialParams, params); }; DemodulatorPanel.prototype.onHashChange = function() { diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index 4ca7b49..95bdeff 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -700,41 +700,59 @@ function on_ws_recv(evt) { switch (json.type) { case "config": var config = json['value']; - waterfall_colors = buildWaterfallColors(config['waterfall_colors']); - waterfall_min_level_default = config['waterfall_min_level']; - waterfall_max_level_default = config['waterfall_max_level']; - waterfall_auto_level_margin = config['waterfall_auto_level_margin']; + if ('waterfall_colors' in config) + waterfall_colors = buildWaterfallColors(config['waterfall_colors']); + if ('waterfall_min_level' in config) + waterfall_min_level_default = config['waterfall_min_level']; + if ('waterfall_max_level' in config) + waterfall_max_level_default = config['waterfall_max_level']; + if ('waterfall_auto_level_margin' in config) + waterfall_auto_level_margin = config['waterfall_auto_level_margin']; waterfallColorsDefault(); - var initial_demodulator_params = { - mod: config['start_mod'], - offset_frequency: config['start_offset_freq'], - squelch_level: Number.isInteger(config['initial_squelch_level']) ? config['initial_squelch_level'] : -150 - }; + var initial_demodulator_params = {}; + if ('start_mod' in config) + initial_demodulator_params['mod'] = config['start_mod']; + if ('start_offset_freq' in config) + initial_demodulator_params['offset_frequency'] = config['start_offset_freq']; + if ('initial_squelch_level' in config) + initial_demodulator_params['squelch_level'] = Number.isInteger(config['initial_squelch_level']) ? config['initial_squelch_level'] : -150; - bandwidth = config['samp_rate']; - center_freq = config['center_freq']; - fft_size = config['fft_size']; - var audio_compression = config['audio_compression']; - audioEngine.setCompression(audio_compression); - divlog("Audio stream is " + ((audio_compression === "adpcm") ? "compressed" : "uncompressed") + "."); - fft_compression = config['fft_compression']; - divlog("FFT stream is " + ((fft_compression === "adpcm") ? "compressed" : "uncompressed") + "."); - $('#openwebrx-bar-clients').progressbar().setMaxClients(config['max_clients']); + if ('samp_rate' in config) + bandwidth = config['samp_rate']; + if ('center_freq' in config) + center_freq = config['center_freq']; + if ('fft_size' in config) + fft_size = config['fft_size']; + if ('audio_compresion' in config) { + var audio_compression = config['audio_compression']; + audioEngine.setCompression(audio_compression); + divlog("Audio stream is " + ((audio_compression === "adpcm") ? "compressed" : "uncompressed") + "."); + } + if ('fft_compression' in config) { + fft_compression = config['fft_compression']; + divlog("FFT stream is " + ((fft_compression === "adpcm") ? "compressed" : "uncompressed") + "."); + } + if ('max_clients' in config) + $('#openwebrx-bar-clients').progressbar().setMaxClients(config['max_clients']); waterfall_init(); var demodulatorPanel = $('#openwebrx-panel-receiver').demodulatorPanel(); demodulatorPanel.setCenterFrequency(center_freq); demodulatorPanel.setInitialParams(initial_demodulator_params); - demodulatorPanel.setSquelchMargin(config['squelch_auto_margin']); + if ('squelch_auto_margin' in config) + demodulatorPanel.setSquelchMargin(config['squelch_auto_margin']); bookmarks.loadLocalBookmarks(); waterfall_clear(); - currentprofile = config['sdr_id'] + '|' + config['profile_id']; - $('#openwebrx-sdr-profiles-listbox').val(currentprofile); + if ('sdr_id' in config && 'profile_id' in config) { + currentprofile = config['sdr_id'] + '|' + config['profile_id']; + $('#openwebrx-sdr-profiles-listbox').val(currentprofile); + } - $('#openwebrx-panel-receiver').demodulatorPanel().setFrequencyPrecision(config['frequency_display_precision']); + if ('frequency_display_precision' in config) + $('#openwebrx-panel-receiver').demodulatorPanel().setFrequencyPrecision(config['frequency_display_precision']); break; case "secondary_config": From 29703d10b236b4141e3beaa29fea62b662e6ed83 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:17:14 +0100 Subject: [PATCH 22/37] server side: send only changed config keys --- owrx/connection.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index e0893fc..3a26a39 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -248,7 +248,6 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.sdr.addClient(self) def handleSdrAvailable(self): - # send initial config self.getDsp().setProperties(self.connectionProperties) stack = PropertyStack() @@ -256,23 +255,30 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): stack.addLayer(1, Config.get()) configProps = stack.filter(*OpenWebRxReceiverClient.config_keys) - def sendConfig(key, value): - config = configProps.__dict__() - # TODO mathematical properties? hmmmm - config["start_offset_freq"] = configProps["start_freq"] - configProps["center_freq"] - # TODO this is a hack to support multiple sdrs - config["sdr_id"] = self.sdr.getId() + def sendConfig(changes=None): + if changes is None: + config = configProps.__dict__() + else: + config = changes + if changes is None or "start_freq" in changes or "center_freq" in changes: + config["start_offset_freq"] = configProps["start_freq"] - configProps["center_freq"] + if changes is None or "profile_id" in changes: + config["sdr_id"] = self.sdr.getId() self.write_config(config) - cf = configProps["center_freq"] - srh = configProps["samp_rate"] / 2 - frequencyRange = (cf - srh, cf + srh) - self.write_dial_frequendies(Bandplan.getSharedInstance().collectDialFrequencies(frequencyRange)) - bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)] - self.write_bookmarks(bookmarks) + # TODO make a separate subscription for this + if changes is None or "center_freq" in changes or "samp_rate" in changes: + cf = configProps["center_freq"] + srh = configProps["samp_rate"] / 2 + frequencyRange = (cf - srh, cf + srh) + self.write_dial_frequendies(Bandplan.getSharedInstance().collectDialFrequencies(frequencyRange)) + bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)] + self.write_bookmarks(bookmarks) self.configSub = configProps.wire(sendConfig) - sendConfig(None, None) + + # send initial config + sendConfig() self.__sendProfiles() self.sdr.addSpectrumClient(self) From cf0c6e7f9d2724e9fbf92cad276e2f626e254563 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:18:46 +0100 Subject: [PATCH 23/37] adapt to config event api changes --- owrx/dsp.py | 2 +- owrx/fft.py | 4 ++-- owrx/service/__init__.py | 2 +- owrx/service/schedule.py | 2 +- owrx/source/__init__.py | 2 +- owrx/source/connector.py | 18 +++++++++--------- owrx/source/direct.py | 8 ++------ owrx/source/fifi_sdr.py | 7 +++---- owrx/source/resampler.py | 4 ++-- owrx/source/soapy.py | 13 ++++++++----- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/owrx/dsp.py b/owrx/dsp.py index 72e6667..08179cf 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -71,7 +71,7 @@ class DspManager(csdr.output, SdrSourceEventClient): bpf[1] = cut self.dsp.set_bpf(*bpf) - def set_dial_freq(key, value): + def set_dial_freq(changes): if self.props["center_freq"] is None or self.props["offset_freq"] is None: return freq = self.props["center_freq"] + self.props["offset_freq"] diff --git a/owrx/fft.py b/owrx/fft.py index 717c086..c434f7a 100644 --- a/owrx/fft.py +++ b/owrx/fft.py @@ -33,7 +33,7 @@ class SpectrumThread(csdr.output, SdrSourceEventClient): dsp.nc_port = self.sdrSource.getPort() dsp.set_demodulator("fft") - def set_fft_averages(key, value): + def set_fft_averages(changes=None): samp_rate = props["samp_rate"] fft_size = props["fft_size"] fft_fps = props["fft_fps"] @@ -54,7 +54,7 @@ class SpectrumThread(csdr.output, SdrSourceEventClient): props.filter("samp_rate", "fft_size", "fft_fps", "fft_voverlap_factor").wire(set_fft_averages), ] - set_fft_averages(None, None) + set_fft_averages() dsp.csdr_dynamic_bufsize = props["csdr_dynamic_bufsize"] dsp.csdr_print_bufsizes = props["csdr_print_bufsizes"] diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index 5984050..2b52fea 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -111,7 +111,7 @@ class ServiceHandler(SdrSourceEventClient): for service in services: service.stop() - def onFrequencyChange(self, key, value): + def onFrequencyChange(self, changes): self.stopServices() if not self.source.isAvailable(): return diff --git a/owrx/service/schedule.py b/owrx/service/schedule.py index 8cf4261..141962d 100644 --- a/owrx/service/schedule.py +++ b/owrx/service/schedule.py @@ -246,7 +246,7 @@ class ServiceScheduler(SdrSourceEventClient): if state == SdrSource.BUSYSTATE_IDLE: self.scheduleSelection() - def onFrequencyChange(self, name, value): + def onFrequencyChange(self, changes): self.scheduleSelection() def selectProfile(self): diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index cf5e198..eab95ad 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -92,7 +92,7 @@ class SdrSource(ABC): return self.commandMapper @abstractmethod - def onPropertyChange(self, name, value): + def onPropertyChange(self, changes): pass def wireEvents(self): diff --git a/owrx/source/connector.py b/owrx/source/connector.py index 47eec5d..40095b0 100644 --- a/owrx/source/connector.py +++ b/owrx/source/connector.py @@ -29,22 +29,22 @@ class ConnectorSource(SdrSource): } ) - def sendControlMessage(self, prop, value): - logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, value)) - self.controlSocket.sendall("{prop}:{value}\n".format(prop=prop, value=value).encode()) + def sendControlMessage(self, changes): + for prop, value in changes.items(): + logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, value)) + self.controlSocket.sendall("{prop}:{value}\n".format(prop=prop, value=value).encode()) - def onPropertyChange(self, prop, value): + def onPropertyChange(self, changes): if self.monitor is None: return if ( - (prop == "center_freq" or prop == "lfo_offset") + ("center_freq" in changes or "lfo_offset" in changes) and "lfo_offset" in self.sdrProps and self.sdrProps["lfo_offset"] is not None ): - freq = self.sdrProps["center_freq"] + self.sdrProps["lfo_offset"] - self.sendControlMessage("center_freq", freq) - else: - self.sendControlMessage(prop, value) + changes["center_freq"] = self.sdrProps["center_freq"] + self.sdrProps["lfo_offset"] + changes.pop("lfo_offset", None) + self.sendControlMessage(changes) def postStart(self): logger.debug("opening control socket...") diff --git a/owrx/source/direct.py b/owrx/source/direct.py index 904d2ea..cd36b55 100644 --- a/owrx/source/direct.py +++ b/owrx/source/direct.py @@ -7,12 +7,8 @@ logger = logging.getLogger(__name__) class DirectSource(SdrSource, metaclass=ABCMeta): - def onPropertyChange(self, name, value): - logger.debug( - "restarting sdr source due to property change: {0} changed to {1}".format( - name, value - ) - ) + def onPropertyChange(self, changes): + logger.debug("restarting sdr source due to property changes: {0}".format(changes)) self.stop() self.sleepOnRestart() self.start() diff --git a/owrx/source/fifi_sdr.py b/owrx/source/fifi_sdr.py index 69babf4..badf3ac 100644 --- a/owrx/source/fifi_sdr.py +++ b/owrx/source/fifi_sdr.py @@ -30,7 +30,6 @@ class FifiSdrSource(DirectSource): values = self.getCommandValues() self.sendRockProgFrequency(values["tuner_freq"]) - def onPropertyChange(self, name, value): - if name != "center_freq": - return - self.sendRockProgFrequency(value) + def onPropertyChange(self, changes): + if "center_freq" in changes: + self.sendRockProgFrequency(changes["center_freq"]) diff --git a/owrx/source/resampler.py b/owrx/source/resampler.py index 1f6a4e1..a367894 100644 --- a/owrx/source/resampler.py +++ b/owrx/source/resampler.py @@ -6,8 +6,8 @@ logger = logging.getLogger(__name__) class Resampler(DirectSource): - def onPropertyChange(self, name, value): - logger.warning("Resampler is unable to handle property change ({0} changed to {1})".format(name, value)) + def onPropertyChange(self, changes): + logger.warning("Resampler is unable to handle property changes: {0}".format(changes)) def __init__(self, props, sdr): sdrProps = sdr.getProps() diff --git a/owrx/source/soapy.py b/owrx/source/soapy.py index e713ba0..0c03a67 100644 --- a/owrx/source/soapy.py +++ b/owrx/source/soapy.py @@ -80,9 +80,12 @@ class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta): values["soapy_settings"] = settings return values - def onPropertyChange(self, prop, value): + def onPropertyChange(self, changes): mappings = self.getSoapySettingsMappings() - if prop in mappings.keys(): - value = "{0}={1}".format(mappings[prop], self.convertSoapySettingsValue(value)) - prop = "settings" - super().onPropertyChange(prop, value) + settings = {} + for prop, value in changes.items(): + if prop in mappings.keys(): + settings[mappings[prop]] = self.convertSoapySettingsValue(value) + if settings: + changes["settings"] = ",".join("{0}={1}".format(k, v) for k, v in settings.items()) + super().onPropertyChange(changes) From f4b9decd23bf7db158fb29ac005eeb8b12aa18a9 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:45:32 +0100 Subject: [PATCH 24/37] more animation performance optimizations --- htdocs/css/openwebrx.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/css/openwebrx.css b/htdocs/css/openwebrx.css index 223d1ee..05cfcbf 100644 --- a/htdocs/css/openwebrx.css +++ b/htdocs/css/openwebrx.css @@ -561,10 +561,11 @@ img.openwebrx-mirror-img width: 100%; position: absolute; left: -100%; - transition-property: left background-color; + transition-property: left,background-color; transition-duration: 1s; transition-timing-function: ease-in-out; transform: translateZ(0); + will-change: left; } .openwebrx-progressbar--over .openwebrx-progressbar-bar { From 341b94b9ff163757e32c295d6c63619eaaa5d13c Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 17:46:13 +0100 Subject: [PATCH 25/37] prevent KeyError by checking for key existence --- owrx/dsp.py | 69 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/owrx/dsp.py b/owrx/dsp.py index 08179cf..d6bd241 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -28,35 +28,41 @@ class DspManager(csdr.output, SdrSourceEventClient): self.props = PropertyStack() # local demodulator properties not forwarded to the sdr - self.props.addLayer(0, PropertyLayer().filter( - "output_rate", - "hd_output_rate", - "squelch_level", - "secondary_mod", - "low_cut", - "high_cut", - "offset_freq", - "mod", - "secondary_offset_freq", - "dmr_filter", - )) + self.props.addLayer( + 0, + PropertyLayer().filter( + "output_rate", + "hd_output_rate", + "squelch_level", + "secondary_mod", + "low_cut", + "high_cut", + "offset_freq", + "mod", + "secondary_offset_freq", + "dmr_filter", + ), + ) # properties that we inherit from the sdr - self.props.addLayer(1, self.sdrSource.getProps().filter( - "audio_compression", - "fft_compression", - "digimodes_fft_size", - "csdr_dynamic_bufsize", - "csdr_print_bufsizes", - "csdr_through", - "digimodes_enable", - "samp_rate", - "digital_voice_unvoiced_quality", - "temporary_directory", - "center_freq", - "start_mod", - "start_freq", - "wfm_deemphasis_tau", - )) + self.props.addLayer( + 1, + self.sdrSource.getProps().filter( + "audio_compression", + "fft_compression", + "digimodes_fft_size", + "csdr_dynamic_bufsize", + "csdr_print_bufsizes", + "csdr_through", + "digimodes_enable", + "samp_rate", + "digital_voice_unvoiced_quality", + "temporary_directory", + "center_freq", + "start_mod", + "start_freq", + "wfm_deemphasis_tau", + ), + ) self.dsp = csdr.dsp(self) self.dsp.nc_port = self.sdrSource.getPort() @@ -72,7 +78,12 @@ class DspManager(csdr.output, SdrSourceEventClient): self.dsp.set_bpf(*bpf) def set_dial_freq(changes): - if self.props["center_freq"] is None or self.props["offset_freq"] is None: + if ( + "center_freq" not in self.props + or self.props["center_freq"] is None + or "offset_freq" not in self.props + or self.props["offset_freq"] is None + ): return freq = self.props["center_freq"] + self.props["offset_freq"] for parser in self.parsers.values(): From 68fcb8522e380a4e0d7336c6b5ec6af357471451 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 18:05:10 +0100 Subject: [PATCH 26/37] fix typo --- htdocs/openwebrx.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index 95bdeff..05fdef9 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -724,7 +724,7 @@ function on_ws_recv(evt) { center_freq = config['center_freq']; if ('fft_size' in config) fft_size = config['fft_size']; - if ('audio_compresion' in config) { + if ('audio_compression' in config) { var audio_compression = config['audio_compression']; audioEngine.setCompression(audio_compression); divlog("Audio stream is " + ((audio_compression === "adpcm") ? "compressed" : "uncompressed") + "."); From daa499ab93bce4407f60651d7dc8cbd8656d6656 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 30 Dec 2020 10:33:21 -0700 Subject: [PATCH 27/37] PR comments edits --- config_webrx.py | 15 +++------------ owrx/kiss.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/config_webrx.py b/config_webrx.py index 576dfa3..27bcd35 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -348,24 +348,15 @@ aprs_symbols_path = "/usr/share/aprs-symbols/png" # Default: OpenWebRX APRS gateway # aprs_igate_comment = "OpenWebRX APRS gateway" -# Antenna Power, Height, Gain +# Antenna Height and Gain details # Unspecified by default -# Transmitter power (0 by default as only RX is supported) -# aprs_igate_power = "0" -# Antenna height in feet -# aprs_igate_height = "20" +# Antenna height above average terrain (HAAT) in meters +# aprs_igate_height = "5" # Antenna gain in dBi # aprs_igate_gain = "0" # Antenna direction (N, NE, E, SE, S, SW, W, NW). Omnidirectional by default # aprs_igate_dir = "NE" -# Frequency info to contact you by voice -# Unspecified by default -# aprs_igate_freq = "146.955" -# aprs_igate_tone = "74.4" -# aprs_igate_offset = "-0.60" - - # === PSK Reporter setting === # 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 diff --git a/owrx/kiss.py b/owrx/kiss.py index 478e1f0..b204704 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -11,6 +11,7 @@ FESC = 0xDB TFEND = 0xDC TFESC = 0xDD +FEET_PER_METER = 3.28084 class DirewolfConfig(object): def getConfig(self, port, is_service): @@ -52,7 +53,6 @@ IGLOGIN {callsign} {password} #Format beacon details symbol = str(pm["aprs_igate_symbol"]) if "aprs_igate_symbol" in pm else "R&" power = str(pm["aprs_igate_power"]) if "aprs_igate_power" in pm else "0" - height = "HEIGHT=" + str(pm["aprs_igate_height"]) if "aprs_igate_height" in pm else "" gain = "GAIN=" + str(pm["aprs_igate_gain"]) if "aprs_igate_gain" in pm else "" adir = "DIR=" + str(pm["aprs_igate_dir"]) if "aprs_igate_dir" in pm else "" freq = "FREQ=" + str(pm["aprs_igate_freq"]) if "aprs_igate_freq" in pm else "" @@ -60,19 +60,26 @@ IGLOGIN {callsign} {password} offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" + height = "" + if "aprs_igate_height": + try: + height_m = float(pm["aprs_igate_height"]) + height_ft = int(height_m * FEET_PER_METER) + height = "HEIGHT=" + str(height_ft) + except: + logger.error("Cannot parse 'aprs_igate_height': " + str(pm["aprs_igate_height"])) + if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): comment = "\"" + comment + "\"" elif(len(comment) == 0): comment = "\"\"" - pbeacon= "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( + pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) logger.info("APRS PBEACON String: " + pbeacon) - config += """ - {pbeacon} - """.format(pbeacon=pbeacon) + config += "\n" + pbeacon + "\n" return config From b85d80112107aebed31cfd54e12fb51401e65451 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 18:45:13 +0100 Subject: [PATCH 28/37] create separate subscription for bookmarks --- owrx/connection.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index 3a26a39..6c6d458 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -132,7 +132,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.dsp = None self.sdr = None - self.configSub = None + self.configSubs = [] self.connectionProperties = {} try: @@ -231,9 +231,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.stopDsp() - if self.configSub is not None: - self.configSub.cancel() - self.configSub = None + while self.configSubs: + self.configSubs.pop().cancel() if self.sdr is not None: self.sdr.removeClient(self) @@ -260,25 +259,26 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): config = configProps.__dict__() else: config = changes - if changes is None or "start_freq" in changes or "center_freq" in changes: + if changes is None or "start_freq" in changes or "center_freq" in changes: config["start_offset_freq"] = configProps["start_freq"] - configProps["center_freq"] if changes is None or "profile_id" in changes: config["sdr_id"] = self.sdr.getId() self.write_config(config) - # TODO make a separate subscription for this - if changes is None or "center_freq" in changes or "samp_rate" in changes: - cf = configProps["center_freq"] - srh = configProps["samp_rate"] / 2 - frequencyRange = (cf - srh, cf + srh) - self.write_dial_frequendies(Bandplan.getSharedInstance().collectDialFrequencies(frequencyRange)) - bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)] - self.write_bookmarks(bookmarks) + def sendBookmarks(changes=None): + cf = configProps["center_freq"] + srh = configProps["samp_rate"] / 2 + frequencyRange = (cf - srh, cf + srh) + self.write_dial_frequencies(Bandplan.getSharedInstance().collectDialFrequencies(frequencyRange)) + bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)] + self.write_bookmarks(bookmarks) - self.configSub = configProps.wire(sendConfig) + self.configSubs.append(configProps.wire(sendConfig)) + self.configSubs.append(stack.filter("center_freq", "samp_rate").wire(sendBookmarks)) # send initial config sendConfig() + sendBookmarks() self.__sendProfiles() self.sdr.addSpectrumClient(self) @@ -374,7 +374,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): def write_wsjt_message(self, message): self.send({"type": "wsjt_message", "value": message}) - def write_dial_frequendies(self, frequencies): + def write_dial_frequencies(self, frequencies): self.send({"type": "dial_frequencies", "value": frequencies}) def write_bookmarks(self, bookmarks): From 32fe01f128bbdbbb471551352d026068f0189873 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 30 Dec 2020 11:03:59 -0700 Subject: [PATCH 29/37] Round instead of floor height conversion --- owrx/kiss.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index b204704..21eebc2 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -64,10 +64,10 @@ IGLOGIN {callsign} {password} if "aprs_igate_height": try: height_m = float(pm["aprs_igate_height"]) - height_ft = int(height_m * FEET_PER_METER) + height_ft = round(height_m * FEET_PER_METER) height = "HEIGHT=" + str(height_ft) except: - logger.error("Cannot parse 'aprs_igate_height': " + str(pm["aprs_igate_height"])) + logger.error("Cannot parse 'aprs_igate_height', expected float: " + str(pm["aprs_igate_height"])) if((len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment)-1] != '"'))): comment = "\"" + comment + "\"" From 57a6db5df2fb03d0e1c426de7d26c97644d42478 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 30 Dec 2020 12:16:12 -0700 Subject: [PATCH 30/37] Removing inapplicable fields --- owrx/kiss.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index 21eebc2..ce1f13e 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -52,16 +52,13 @@ IGLOGIN {callsign} {password} #Format beacon details symbol = str(pm["aprs_igate_symbol"]) if "aprs_igate_symbol" in pm else "R&" - power = str(pm["aprs_igate_power"]) if "aprs_igate_power" in pm else "0" gain = "GAIN=" + str(pm["aprs_igate_gain"]) if "aprs_igate_gain" in pm else "" adir = "DIR=" + str(pm["aprs_igate_dir"]) if "aprs_igate_dir" in pm else "" - freq = "FREQ=" + str(pm["aprs_igate_freq"]) if "aprs_igate_freq" in pm else "" - tone = "TONE=" + str(pm["aprs_igate_tone"]) if "aprs_igate_tone" in pm else "" - offset = "OFFSET=" + str(pm["aprs_igate_offset"]) if "aprs_igate_offset" in pm else "" comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else "\"OpenWebRX APRS gateway\"" + #Convert height from meters to feet if specified height = "" - if "aprs_igate_height": + if "aprs_igate_height" in pm: try: height_m = float(pm["aprs_igate_height"]) height_ft = round(height_m * FEET_PER_METER) @@ -74,8 +71,8 @@ IGLOGIN {callsign} {password} elif(len(comment) == 0): comment = "\"\"" - pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER={power} {height} {gain} {adir} {freq} {tone} {offset} comment={comment}".format( - symbol=symbol, lat=lat, lon=lon, power=power, height=height, gain=gain, adir=adir, freq=freq, tone=tone, offset=offset, comment=comment ) + pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER=0 {height} {gain} {adir} comment={comment}".format( + symbol=symbol, lat=lat, lon=lon, height=height, gain=gain, adir=adir, comment=comment ) logger.info("APRS PBEACON String: " + pbeacon) From 1730ef27da4157647fb1b64f7b200609908b9ab8 Mon Sep 17 00:00:00 2001 From: Ed Sandor Date: Wed, 30 Dec 2020 12:21:07 -0700 Subject: [PATCH 31/37] Remove POWER from pbeacon string --- owrx/kiss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/kiss.py b/owrx/kiss.py index ce1f13e..bb20489 100644 --- a/owrx/kiss.py +++ b/owrx/kiss.py @@ -71,7 +71,7 @@ IGLOGIN {callsign} {password} elif(len(comment) == 0): comment = "\"\"" - pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} POWER=0 {height} {gain} {adir} comment={comment}".format( + pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} {height} {gain} {adir} comment={comment}".format( symbol=symbol, lat=lat, lon=lon, height=height, gain=gain, adir=adir, comment=comment ) logger.info("APRS PBEACON String: " + pbeacon) From 53553fcce299705ef021827a666df0cbaf43e7a2 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 21:33:02 +0100 Subject: [PATCH 32/37] fix subscription handling --- owrx/connection.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index 6c6d458..c83a1c0 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -295,9 +295,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.stopDsp() CpuUsageThread.getSharedInstance().remove_client(self) ClientRegistry.getSharedInstance().removeClient(self) - if self.configSub is not None: - self.configSub.cancel() - self.configSub = None + while self.configSubs: + self.configSubs.pop().cancel() super().close() def stopDsp(self): From 5a77b6a8e5ba19fb48f3f3c4ec9eefaa51b449fc Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 30 Dec 2020 21:37:25 +0100 Subject: [PATCH 33/37] show bandplan bookmarks only when mode is available --- owrx/bands.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/owrx/bands.py b/owrx/bands.py index fbb7ae6..89bf293 100644 --- a/owrx/bands.py +++ b/owrx/bands.py @@ -1,3 +1,4 @@ +from owrx.modes import Modes import json import logging @@ -12,7 +13,15 @@ class Band(object): self.upper_bound = dict["upper_bound"] self.frequencies = [] if "frequencies" in dict: + availableModes = [mode.modulation for mode in Modes.getAvailableModes()] for (mode, freqs) in dict["frequencies"].items(): + if mode not in availableModes: + logger.info( + "Modulation \"{mode}\" is not available, bandplan bookmark will not be displayed".format( + mode=mode + ) + ) + continue if not isinstance(freqs, list): freqs = [freqs] for f in freqs: @@ -22,8 +31,8 @@ class Band(object): mode=mode, frequency=f, band=self.name ) ) - else: - self.frequencies.append({"mode": mode, "frequency": f}) + continue + self.frequencies.append({"mode": mode, "frequency": f}) def inBand(self, freq): return self.lower_bound <= freq <= self.upper_bound From 90f319ebda7f198c5c4a33fa464cf93cc7887b3e Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Thu, 31 Dec 2020 23:03:36 +0100 Subject: [PATCH 34/37] split config into global and device config * less config properties sent to the client --- htdocs/openwebrx.js | 9 ++++++--- owrx/connection.py | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index 05fdef9..f884263 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -736,7 +736,9 @@ function on_ws_recv(evt) { if ('max_clients' in config) $('#openwebrx-bar-clients').progressbar().setMaxClients(config['max_clients']); - waterfall_init(); + if (typeof(bandwidth) != 'undefined') + waterfall_init(); + var demodulatorPanel = $('#openwebrx-panel-receiver').demodulatorPanel(); demodulatorPanel.setCenterFrequency(center_freq); demodulatorPanel.setInitialParams(initial_demodulator_params); @@ -744,13 +746,14 @@ function on_ws_recv(evt) { demodulatorPanel.setSquelchMargin(config['squelch_auto_margin']); bookmarks.loadLocalBookmarks(); - waterfall_clear(); - if ('sdr_id' in config && 'profile_id' in config) { currentprofile = config['sdr_id'] + '|' + config['profile_id']; $('#openwebrx-sdr-profiles-listbox').val(currentprofile); } + if (typeof(bandwidth) != 'undefined') + waterfall_clear(); + if ('frequency_display_precision' in config) $('#openwebrx-panel-receiver').demodulatorPanel().setFrequencyPrecision(config['frequency_display_precision']); diff --git a/owrx/connection.py b/owrx/connection.py index c83a1c0..73e1f6d 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -12,6 +12,7 @@ from owrx.bookmarks import Bookmarks from owrx.map import Map from owrx.property import PropertyStack from owrx.modes import Modes, DigitalMode +from owrx.config import Config from queue import Queue, Full, Empty from js8py import Js8Frame from abc import ABC, ABCMeta, abstractmethod @@ -108,22 +109,26 @@ class OpenWebRxClient(Client, metaclass=ABCMeta): class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): - config_keys = [ - "waterfall_colors", + sdr_config_keys = [ + "waterfall_min_level", "waterfall_min_level", "waterfall_max_level", - "waterfall_auto_level_margin", "samp_rate", - "fft_size", - "audio_compression", - "fft_compression", - "max_clients", "start_mod", "start_freq", "center_freq", "initial_squelch_level", "profile_id", "squelch_auto_margin", + ] + + global_config_keys = [ + "waterfall_colors", + "waterfall_auto_level_margin", + "fft_size", + "audio_compression", + "fft_compression", + "max_clients", "frequency_display_precision", ] @@ -132,7 +137,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.dsp = None self.sdr = None - self.configSubs = [] + self.sdrConfigSubs = [] self.connectionProperties = {} try: @@ -142,6 +147,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.close() raise + globalConfig = Config.get().filter(*OpenWebRxReceiverClient.global_config_keys) + self.globalConfigSub = globalConfig.wire(self.write_config) + self.write_config(globalConfig.__dict__()) + self.setSdr() features = FeatureDetector().feature_availability() @@ -154,6 +163,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): CpuUsageThread.getSharedInstance().add_client(self) + def __del__(self): + if hasattr(self, "globalConfigSub"): + self.globalConfigSub.cancel() + def onStateChange(self, state): if state == SdrSource.STATE_RUNNING: self.handleSdrAvailable() @@ -231,8 +244,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.stopDsp() - while self.configSubs: - self.configSubs.pop().cancel() + while self.sdrConfigSubs: + self.sdrConfigSubs.pop().cancel() if self.sdr is not None: self.sdr.removeClient(self) @@ -252,7 +265,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): stack = PropertyStack() stack.addLayer(0, self.sdr.getProps()) stack.addLayer(1, Config.get()) - configProps = stack.filter(*OpenWebRxReceiverClient.config_keys) + configProps = stack.filter(*OpenWebRxReceiverClient.sdr_config_keys) def sendConfig(changes=None): if changes is None: @@ -273,8 +286,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)] self.write_bookmarks(bookmarks) - self.configSubs.append(configProps.wire(sendConfig)) - self.configSubs.append(stack.filter("center_freq", "samp_rate").wire(sendBookmarks)) + self.sdrConfigSubs.append(configProps.wire(sendConfig)) + self.sdrConfigSubs.append(stack.filter("center_freq", "samp_rate").wire(sendBookmarks)) # send initial config sendConfig() @@ -295,8 +308,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.stopDsp() CpuUsageThread.getSharedInstance().remove_client(self) ClientRegistry.getSharedInstance().removeClient(self) - while self.configSubs: - self.configSubs.pop().cancel() + while self.sdrConfigSubs: + self.sdrConfigSubs.pop().cancel() super().close() def stopDsp(self): From a1cbc45b884871635b6f658336cd166575b9e4d5 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Thu, 31 Dec 2020 23:18:01 +0100 Subject: [PATCH 35/37] prevent multiple creation of cpu usage thread --- owrx/cpu.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/owrx/cpu.py b/owrx/cpu.py index 28c9066..9405b8f 100644 --- a/owrx/cpu.py +++ b/owrx/cpu.py @@ -7,11 +7,13 @@ logger = logging.getLogger(__name__) class CpuUsageThread(threading.Thread): sharedInstance = None + creationLock = threading.Lock() @staticmethod def getSharedInstance(): - if CpuUsageThread.sharedInstance is None: - CpuUsageThread.sharedInstance = CpuUsageThread() + with CpuUsageThread.creationLock: + if CpuUsageThread.sharedInstance is None: + CpuUsageThread.sharedInstance = CpuUsageThread() return CpuUsageThread.sharedInstance def __init__(self): From c1245308bd9610fbf503cee1e8ea9dd2a1783ab8 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 1 Jan 2021 23:37:10 +0100 Subject: [PATCH 36/37] make this more robust --- htdocs/openwebrx.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index f884263..594f0b5 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -314,14 +314,16 @@ function scale_px_from_freq(f, range) { } function get_visible_freq_range() { - var out = {}; + if (!bandwidth) return false; var fcalc = function (x) { var canvasWidth = waterfallWidth() * zoom_levels[zoom_level]; return Math.round(((-zoom_offset_px + x) / canvasWidth) * bandwidth) + (center_freq - bandwidth / 2); }; - out.start = fcalc(0); - out.center = fcalc(waterfallWidth() / 2); - out.end = fcalc(waterfallWidth()); + var out = { + start: fcalc(0), + center: fcalc(waterfallWidth() / 2), + end: fcalc(waterfallWidth()), + } out.bw = out.end - out.start; out.hps = out.bw / waterfallWidth(); return out; @@ -426,6 +428,7 @@ var range; function mkscale() { //clear the lower part of the canvas (where frequency scale resides; the upper part is used by filter envelopes): range = get_visible_freq_range(); + if (!range) return; mkenvelopes(range); //when scale changes we will always have to redraw filter envelopes, too scale_ctx.clearRect(0, 22, scale_ctx.canvas.width, scale_ctx.canvas.height - 22); scale_ctx.strokeStyle = "#fff"; @@ -442,9 +445,7 @@ function mkscale() { }; var last_large; var x; - for (; ;) { - x = scale_px_from_freq(marker_hz, range); - if (x > window.innerWidth) break; + while ((x = scale_px_from_freq(marker_hz, range)) <= window.innerWidth) { scale_ctx.beginPath(); scale_ctx.moveTo(x, 22); if (marker_hz % spacing.params.large_marker_per_hz === 0) { //large marker @@ -736,8 +737,7 @@ function on_ws_recv(evt) { if ('max_clients' in config) $('#openwebrx-bar-clients').progressbar().setMaxClients(config['max_clients']); - if (typeof(bandwidth) != 'undefined') - waterfall_init(); + waterfall_init(); var demodulatorPanel = $('#openwebrx-panel-receiver').demodulatorPanel(); demodulatorPanel.setCenterFrequency(center_freq); @@ -751,8 +751,7 @@ function on_ws_recv(evt) { $('#openwebrx-sdr-profiles-listbox').val(currentprofile); } - if (typeof(bandwidth) != 'undefined') - waterfall_clear(); + waterfall_clear(); if ('frequency_display_precision' in config) $('#openwebrx-panel-receiver').demodulatorPanel().setFrequencyPrecision(config['frequency_display_precision']); From 44f45324524db9f8e82cebdc1d91047a0e82c996 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Sat, 2 Jan 2021 02:25:07 +0100 Subject: [PATCH 37/37] add debug logging --- owrx/cpu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/owrx/cpu.py b/owrx/cpu.py index 9405b8f..6b2b82c 100644 --- a/owrx/cpu.py +++ b/owrx/cpu.py @@ -25,6 +25,7 @@ class CpuUsageThread(threading.Thread): super().__init__() def run(self): + logger.debug("cpu usage thread starting up") while self.doRun: try: cpu_usage = self.get_cpu_usage()