From 973e4522f90bf5f7c4b06c3cbfe65b9d9220b8b0 Mon Sep 17 00:00:00 2001 From: Josh Bailey Date: Sun, 7 Jan 2024 20:37:05 +0000 Subject: [PATCH 1/5] prevent waterfall stalling at very high refresh rates (e.g. stare mode). --- gamutrfwaterfall/gamutrfwaterfall/waterfall.py | 14 ++++++++++++-- gamutrfwaterfall/tests/test_waterfall.py | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gamutrfwaterfall/gamutrfwaterfall/waterfall.py b/gamutrfwaterfall/gamutrfwaterfall/waterfall.py index a7d924d9..05633a36 100644 --- a/gamutrfwaterfall/gamutrfwaterfall/waterfall.py +++ b/gamutrfwaterfall/gamutrfwaterfall/waterfall.py @@ -271,7 +271,12 @@ def argument_parser(): type=int, help="Waterfall width (maximum)", ) - + parser.add_argument( + "--refresh", + default=5, + type=int, + help="Waterfall refresh time", + ) return parser @@ -899,6 +904,7 @@ def waterfall( waterfall_height, waterfall_width, batch, + refresh, zmqr, ): style.use("fast") @@ -980,6 +986,7 @@ def sig_handler(_sig=None, _frame=None): if need_reset_fig: reset_fig(config, state) need_reset_fig = False + last_gap = time.time() while True: scan_configs, scan_df = zmqr.read_buff( scan_fres=config.freq_resolution * 1e6 @@ -1017,6 +1024,8 @@ def sig_handler(_sig=None, _frame=None): ) continue results.append((scan_configs, scan_df)) + if results and time.time() - last_gap > (refresh / 2): + break if need_reconfig: continue if results: @@ -1085,7 +1094,7 @@ def main(): engine = "agg" batch = True savefig_path = os.path.join(tempdir, "waterfall.png") - flask = FlaskHandler(savefig_path, args.port) + flask = FlaskHandler(savefig_path, args.port, refresh=args.refresh) flask.start() zmqr = ZmqReceiver( @@ -1108,6 +1117,7 @@ def main(): args.waterfall_height, args.waterfall_width, batch, + args.refresh, zmqr, ) diff --git a/gamutrfwaterfall/tests/test_waterfall.py b/gamutrfwaterfall/tests/test_waterfall.py index 1cee4e4c..2f0c299b 100755 --- a/gamutrfwaterfall/tests/test_waterfall.py +++ b/gamutrfwaterfall/tests/test_waterfall.py @@ -93,6 +93,7 @@ def test_run_waterfall(self): 5, # args.height, 10, # args.waterfall_height, 100, # args.waterfall_width, + 5, # args.refresh, True, # args.batch zmqr, ) From 332a38bdc3540bce0583a548f87c6731d7e74d5e Mon Sep 17 00:00:00 2001 From: Josh Bailey Date: Sun, 7 Jan 2024 22:40:44 +0000 Subject: [PATCH 2/5] better vkfft batch size default. --- orchestrator.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/orchestrator.yml b/orchestrator.yml index 4474fbb1..c36e87af 100644 --- a/orchestrator.yml +++ b/orchestrator.yml @@ -68,7 +68,7 @@ services: - --tune-dwell-ms=0 - --tune-step-fft=512 - --db_clamp_floor=-150 - - --fft_batch_size=256 + - --fft_batch_size=32 - --mqtt_server=mqtt - --no-compass - --use_external_gps @@ -78,6 +78,7 @@ services: - --inference_model_name=mini2_snr - --n_inference=10 - --n_image=10 + - --vkfft # - --external_gps_server=1.2.3.4 # - --external_gps_server_port=8888 # - --inference_output_dir=/logs/inference From 1cd08d9ef988d7fe6a4b94821e020ebb8d68a9d0 Mon Sep 17 00:00:00 2001 From: Josh Bailey Date: Sun, 7 Jan 2024 22:41:11 +0000 Subject: [PATCH 3/5] aggregate multiple frames from zmq. --- gamutrflib/gamutrflib/zmqbucket.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/gamutrflib/gamutrflib/zmqbucket.py b/gamutrflib/gamutrflib/zmqbucket.py index 89960758..196ca768 100644 --- a/gamutrflib/gamutrflib/zmqbucket.py +++ b/gamutrflib/gamutrflib/zmqbucket.py @@ -177,14 +177,15 @@ def read_new_frame_df(self, df, discard_time): self.fftbuffer = pd.concat([self.fftbuffer, df]) if self.fftbuffer["sweep_start"].nunique() > 1: min_sweep_start = self.fftbuffer["sweep_start"].min() + max_sweep_start = self.fftbuffer["sweep_start"].max() frame_df = self.fftbuffer[ - self.fftbuffer["sweep_start"] == min_sweep_start + self.fftbuffer["sweep_start"] != max_sweep_start ].copy() frame_df["tune_count"] = ( frame_df["tune_count"].max() - frame_df["tune_count"].min() ) self.fftbuffer = self.fftbuffer[ - self.fftbuffer["sweep_start"] != min_sweep_start + self.fftbuffer["sweep_start"] == max_sweep_start ] scan_config = self.scan_configs[min_sweep_start] del self.scan_configs[min_sweep_start] @@ -264,15 +265,20 @@ def healthy(self): return False def read_buff(self, log=None, discard_time=0, scan_fres=0): - results = [scanner.read_buff(log, discard_time) for scanner in self.scanners] - if self.last_results: - for i, result in enumerate(results): - _scan_config, df = result - if df is not None: - self.last_results[i] = result - logging.info("%s got scan result", self.scanners[i]) - else: - self.last_results = results + while True: + results = [scanner.read_buff(log, discard_time) for scanner in self.scanners] + new_results = 0 + if self.last_results: + for i, result in enumerate(results): + _scan_config, df = result + if df is not None: + self.last_results[i] = result + new_results += 1 + logging.info("%s got scan result", self.scanners[i]) + else: + self.last_results = results + if new_results == 0: + break scan_configs = [] dfs = [] From 936e74bb7579d7f05966529ed3acdf937f89ee01 Mon Sep 17 00:00:00 2001 From: Josh Bailey Date: Sun, 7 Jan 2024 23:10:15 +0000 Subject: [PATCH 4/5] more waterfall rendering improvements. --- auto_label/auto_label.py | 42 +++++++++---------- gamutrflib/gamutrflib/zmqbucket.py | 19 +++++---- .../gamutrfwaterfall/waterfall.py | 8 +++- orchestrator.yml | 2 +- tests/test_grscan.py | 4 +- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/auto_label/auto_label.py b/auto_label/auto_label.py index 69d94543..310321dd 100644 --- a/auto_label/auto_label.py +++ b/auto_label/auto_label.py @@ -1,39 +1,38 @@ -import os +import os from argparse import ArgumentParser -import numpy as np +import numpy as np import cv2 as cv - def label(filename): - img = cv.imread(filename) imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # opening and closing # https://docs.opencv.org/3.4/d9/d61/tutorial_py_morphological_ops.html # https://docs.opencv.org/3.4/d3/dbe/tutorial_opening_closing_hats.html - kernel = np.ones((5,5),np.uint8) + kernel = np.ones((5, 5), np.uint8) imgray = cv.morphologyEx(imgray, cv.MORPH_OPEN, kernel) imgray = cv.morphologyEx(imgray, cv.MORPH_CLOSE, kernel) - # bilateral smoothing # https://docs.opencv.org/3.4/dc/dd3/tutorial_gausian_median_blur_bilateral_filter.html bilateral_kernel = 8 - imgray = cv.bilateralFilter(imgray, bilateral_kernel, bilateral_kernel * 2, bilateral_kernel / 2) + imgray = cv.bilateralFilter( + imgray, bilateral_kernel, bilateral_kernel * 2, bilateral_kernel / 2 + ) ret, thresh = cv.threshold(imgray, 140, 255, 0) contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) - - # remove internal contours + + # remove internal contours # https://docs.opencv.org/4.x/d9/d8b/tutorial_py_contours_hierarchy.html good_contours = [] for i in range(len(contours)): - if hierarchy[0,i,3] == -1: + if hierarchy[0, i, 3] == -1: good_contours.append(contours[i]) contours = good_contours - # draw rectangels over all contours + # draw rectangels over all contours for cnt in contours: # Contour approximation https://docs.opencv.org/4.x/dd/d49/tutorial_py_contour_features.html # epsilon = 0.01*cv.arcLength(cnt,True) @@ -42,14 +41,14 @@ def label(filename): # Convex hull # cnt = cv.convexHull(cnt) - x,y,w,h = cv.boundingRect(cnt) - cv.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) - + x, y, w, h = cv.boundingRect(cnt) + cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) - cv.imshow('Image',img) + cv.imshow("Image", img) cv.waitKey(0) cv.destroyAllWindows() + def main(): parser = ArgumentParser() parser.add_argument( @@ -62,20 +61,21 @@ def main(): filepath = args.filepath - # get files + # get files files = [] if os.path.isfile(filepath): files.append(filepath) elif os.path.isdir(filepath): - files.extend([os.path.join(filepath, filename) for filename in os.listdir(filepath)]) + files.extend( + [os.path.join(filepath, filename) for filename in os.listdir(filepath)] + ) else: - raise ValueError('filepath must be existing directory or filename') - + raise ValueError("filepath must be existing directory or filename") + # loop through files for img_filename in files: label(img_filename) - if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/gamutrflib/gamutrflib/zmqbucket.py b/gamutrflib/gamutrflib/zmqbucket.py index 196ca768..034c1978 100644 --- a/gamutrflib/gamutrflib/zmqbucket.py +++ b/gamutrflib/gamutrflib/zmqbucket.py @@ -227,7 +227,8 @@ def read_buff(self, log, discard_time): lines = self.txtbuf_to_lines(log) if lines: df = self.lines_to_df(lines) - scan_config, frame_df = self.read_new_frame_df(df, discard_time) + if df is not None: + scan_config, frame_df = self.read_new_frame_df(df, discard_time) return scan_config, frame_df @@ -266,15 +267,17 @@ def healthy(self): def read_buff(self, log=None, discard_time=0, scan_fres=0): while True: - results = [scanner.read_buff(log, discard_time) for scanner in self.scanners] + results = [ + scanner.read_buff(log, discard_time) for scanner in self.scanners + ] new_results = 0 if self.last_results: - for i, result in enumerate(results): - _scan_config, df = result - if df is not None: - self.last_results[i] = result - new_results += 1 - logging.info("%s got scan result", self.scanners[i]) + for i, result in enumerate(results): + _scan_config, df = result + if df is not None: + self.last_results[i] = result + new_results += 1 + logging.info("%s got scan result", self.scanners[i]) else: self.last_results = results if new_results == 0: diff --git a/gamutrfwaterfall/gamutrfwaterfall/waterfall.py b/gamutrfwaterfall/gamutrfwaterfall/waterfall.py index 05633a36..2af5bec0 100644 --- a/gamutrfwaterfall/gamutrfwaterfall/waterfall.py +++ b/gamutrfwaterfall/gamutrfwaterfall/waterfall.py @@ -988,10 +988,16 @@ def sig_handler(_sig=None, _frame=None): need_reset_fig = False last_gap = time.time() while True: + gap_time = time.time() - last_gap + if results and gap_time > refresh / 2: + break scan_configs, scan_df = zmqr.read_buff( scan_fres=config.freq_resolution * 1e6 ) if scan_df is None: + if batch and gap_time < refresh / 2: + time.sleep(0.1) + continue break last_config = make_config( scan_configs, @@ -1024,8 +1030,6 @@ def sig_handler(_sig=None, _frame=None): ) continue results.append((scan_configs, scan_df)) - if results and time.time() - last_gap > (refresh / 2): - break if need_reconfig: continue if results: diff --git a/orchestrator.yml b/orchestrator.yml index c36e87af..a3c79fc2 100644 --- a/orchestrator.yml +++ b/orchestrator.yml @@ -78,7 +78,7 @@ services: - --inference_model_name=mini2_snr - --n_inference=10 - --n_image=10 - - --vkfft + - --no-vkfft # - --external_gps_server=1.2.3.4 # - --external_gps_server_port=8888 # - --inference_output_dir=/logs/inference diff --git a/tests/test_grscan.py b/tests/test_grscan.py index ef77b61a..85589540 100644 --- a/tests/test_grscan.py +++ b/tests/test_grscan.py @@ -95,7 +95,9 @@ def test_fake_uhd(self): get_source("ettus", 1e3, 10, 1024, 1024, uhd_lib=FakeUHD()) def test_fake_soapy(self): - sources, _, workaround = get_source("SoapyAIRT", 1e3, 10, 1024, 1024, soapy_lib=FakeSoapy()) + sources, _, workaround = get_source( + "SoapyAIRT", 1e3, 10, 1024, 1024, soapy_lib=FakeSoapy() + ) tb = FakeTb(sources, workaround, 100e6) tb.start() From 3157caa0686423f57358f0bc820771f6e233fba4 Mon Sep 17 00:00:00 2001 From: Josh Bailey Date: Sun, 7 Jan 2024 23:41:40 +0000 Subject: [PATCH 5/5] add small batch. --- orchestrator.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orchestrator.yml b/orchestrator.yml index a3c79fc2..c639db71 100644 --- a/orchestrator.yml +++ b/orchestrator.yml @@ -68,7 +68,7 @@ services: - --tune-dwell-ms=0 - --tune-step-fft=512 - --db_clamp_floor=-150 - - --fft_batch_size=32 + - --fft_batch_size=16 - --mqtt_server=mqtt - --no-compass - --use_external_gps @@ -78,7 +78,7 @@ services: - --inference_model_name=mini2_snr - --n_inference=10 - --n_image=10 - - --no-vkfft + - --vkfft # - --external_gps_server=1.2.3.4 # - --external_gps_server_port=8888 # - --inference_output_dir=/logs/inference