From 5bd6f9d08f1ab75a9558a54b088014ec1288fd4a Mon Sep 17 00:00:00 2001 From: brahim Date: Tue, 5 Sep 2023 12:09:21 +0200 Subject: [PATCH] Implementation of acoustic inversion method for suspended sediments high concentrations --- Model/acoustic_data_loader.py | 4 + ...tic_inversion_method_high_concentration.py | 150 ++++++ View/acoustic_data_tab.py | 68 ++- View/acoustic_inversion_tab.py | 469 +++++++++++++++--- View/checkable_combobox.py | 33 ++ View/sample_data_tab.py | 8 +- View/signal_processing_tab.py | 387 ++++++++++----- 7 files changed, 888 insertions(+), 231 deletions(-) create mode 100644 Model/acoustic_inversion_method_high_concentration.py create mode 100644 View/checkable_combobox.py diff --git a/Model/acoustic_data_loader.py b/Model/acoustic_data_loader.py index d9f6b9c..be45708 100644 --- a/Model/acoustic_data_loader.py +++ b/Model/acoustic_data_loader.py @@ -80,6 +80,10 @@ class AcousticDataLoader(): order="F") return r + def compute_r_2D(self): + r2D = np.repeat(self._r, self._time.size, axis=1) + return r2D + def reshape_t(self): t = np.reshape(np.repeat(self._time, self._r.shape[0]), (self._time.shape[0]*self._r.shape[0], 1)) return t diff --git a/Model/acoustic_inversion_method_high_concentration.py b/Model/acoustic_inversion_method_high_concentration.py new file mode 100644 index 0000000..dc5868b --- /dev/null +++ b/Model/acoustic_inversion_method_high_concentration.py @@ -0,0 +1,150 @@ +import numpy as np +import settings as stg + + +class AcousticInversionMethodHighConcentration(): + + """ Thi class compute acoustic inversion method adapted for high suspended sediments concentration + For instance, the case of dam flush downstream Rhone-Isere confluence (07/01/2018) evaluated at ~ 10g/L """ + + def __init__(self): + + pass + + # ========================================== + # Functions + # ========================================== + + # ---------- Computing sound speed ------------- # + def water_velocity(self, T): + """Computing sond speed from Bilaniuk and Wong 1993""" + C = 1.40238744 * 1e3 + 5.03836171 * T - 5.81172916 * 1e-2 * T ** 2 + 3.34638117 * 1e-4 * T ** 3 - \ + 1.48259672 * 1e-6 * T ** 4 + 3.16585020 * 1e-9 * T ** 5 + return C + + # -------- Computing water attenuation coefficient ----------- # + def water_attenuation(self, freq1, freq2, T): + """Computing attenuation from François and Garrison 1982""" + if T > 20: + alpha = (3.964 * 1e-4 - 1.146 * 1e-5 * T + 1.45 * 1e-7 * T ** 2 - 6.5 * 1e-10 * T ** 3) * 1e-3 * \ + (np.log(10) / 20) * (np.array([freq1, freq2]) * 1e-3) ** 2 + else: + alpha = (4.937 * 1e-4 - 2.59 * 1e-5 * T + 9.11 * 1e-7 * T ** 2 - 1.5 * 1e-8 * T ** 3) * 1e-3 * \ + (np.log(10) / 20) * (np.array([freq1, freq2]) * 1e-3) ** 2 + return alpha + + # ---------- Conmpute FBC ---------- + # def compute_FCB(self): + # # print(self.BS_averaged_cross_section_corr.V.shape) + # # print(self.r_2D.shape) + # FCB = np.zeros((256, 4, 1912)) + # for f in range(4): + # # print(self.alpha_w_function(self.Freq[f], self.temperature)) + # FCB[:, f, :] = np.log(self.BS_averaged_cross_section_corr.V[:, f, :]) + np.log(self.r_3D[:, f, :]) + \ + # np.log(2 * self.alpha_w_function(self.Freq[f], self.temperature) * self.r_3D[:, f, :]) + # return FCB + + # ------------- Computing ks ------------- # + + def ks(self, ind): + ks0 = np.array([0.0446681, 0.11490388, 0.35937832, 2.5025668]) + ks = ks0[ind] + return ks + + # ------------- Computing sv ------------- # + + def sv(self): + pass + + # ------------- Computing X ------------- # + def X_exponent(self, ind): + X0 = [3.688664080521131, 3.451418735488537, 0, 3.276577078823426, 0, 0] + X = X0[ind] + return X + + # -------------- Computing Kt -------------- # + def kt_corrected(self, r, water_velocity, RxGain, TxGain, kt_ref): + """Computing the instrument constant Kt that depends on gain and temperature""" + # Cell size + delta_r = r[1] - r[0] + # Pulse length + tau = 2 * delta_r / 1500 + # Sound speed + cel = water_velocity + # Reference pulse length + tau_ref = 13.33333e-6 + # Reference sound speed + c_ref = 1475 + # Gain + gain = 10 ** ((RxGain + TxGain) / 20) + # Computing Kt + kt0 = kt_ref * gain * np.sqrt(tau * cel / (tau_ref * c_ref)) # 1D numpy array + kt = np.reshape(kt0, (1, 2)) # convert to 2d numpy array to compute J_cross_section + return kt + + # ------------- Computing J_cross_section ------------- # + def j_cross_section(self, BS, r2D, kt): + J_cross_section = np.zeros((2, BS.shape[0], BS.shape[2])) # 2 because it's a pair of frequencies + for k in range(2): + J_cross_section[k, :, :] = (3 / (16 * np.pi)) * ((BS[:, k, :]**2 * r2D**2) / kt[k, :, :]**2) + # J_cross_section[J_cross_section == 0] = np.nan + print("compute j_cross_section finished") + return J_cross_section + + # ------------- Computing alpha_s ------------- # + + def alpha_s(self): + pass + + # ------------- Computing interpolation of fine SSC data obtained from water sampling ------------- + # ------------- collected at various depth in the vertical sample ------------- + def M_profile_SCC_fine_interpolated(self): + pass + + # ------------- Computing zeta ------------- # + def zeta(self, ind1, ind2): + # zeta = alpha_s / ((1/r) * (M_profile_SSC_fine)) + zeta0 = np.array([0.04341525, 0.04832906, 0.0847188, np.nan]) + zeta = zeta0[[ind1, ind2]] + return zeta + + # ------------- Computing VBI ------------- # + def VBI_cross_section(self, freq1, freq2, + zeta_freq1, zeta_freq2, + j_cross_section_freq1, j_cross_section_freq2, + r2D, + water_attenuation_freq1, water_attenuation_freq2, + X): + + # print('self.zeta_exp[ind_j].shape', self.zeta_exp[ind_j]) + # print('np.log(self.j_cross_section[:, ind_i, :]).shape', np.log(self.j_cross_section[:, ind_i, :]).shape) + # print('self.r_3D[:, ind_i, :]', self.r_3D[:, ind_i, :].shape) + # print('self.water_attenuation[ind_i]', self.water_attenuation[ind_i]) + # print('self.x_exp[0.3-1 MHz]', self.x_exp['0.3-1 MHz'].values[0]) + # print("start computing VBI") + + logVBI = ((zeta_freq2 * + np.log(j_cross_section_freq1 * np.exp(4 * r2D * water_attenuation_freq1) / + (freq1 ** X)) - + zeta_freq1 * + np.log(j_cross_section_freq2 * np.exp(4 * r2D * water_attenuation_freq2) / + (freq2 ** X))) / + (zeta_freq2 - zeta_freq1)) + + print("compute VBI finished") + + return np.exp(logVBI) + + # ------------- Computing SSC fine ------------- # + def SSC_fine(self, zeta, r2D, VBI, freq, X, j_cross_section): + SSC_fine = (1/zeta) * ( 1/(4 * r2D) * np.log((VBI * freq**X) / j_cross_section) ) + print("compute SSC fine finished") + return SSC_fine + + # ------------- Computing SSC sand ------------- # + def SSC_sand(self, VBI, freq, X, ks): + SSC_sand = (16 * np.pi * VBI * freq ** X) / (3 * ks**2) + print("compute SSC sand finished") + return SSC_sand + + diff --git a/View/acoustic_data_tab.py b/View/acoustic_data_tab.py index bc9db1d..e450345 100644 --- a/View/acoustic_data_tab.py +++ b/View/acoustic_data_tab.py @@ -327,9 +327,9 @@ class AcousticDataTab(QWidget): self.verticalLayout_display_option.addWidget(self.groupbox_xaxis_time) self.gridLayout_groupbox_xaxis_time = QGridLayout(self.groupbox_xaxis_time) - self.label_from = QLabel() - self.label_from.setText("From") - self.gridLayout_groupbox_xaxis_time.addWidget(self.label_from, 0, 0, 1, 1) + self.label_from_time = QLabel() + self.label_from_time.setText("From") + self.gridLayout_groupbox_xaxis_time.addWidget(self.label_from_time, 0, 0, 1, 1) self.label_tmin = QLabel() self.label_tmin.setText("tmin = ") @@ -345,9 +345,9 @@ class AcousticDataTab(QWidget): self.label_tmin_unit.setText("sec") self.gridLayout_groupbox_xaxis_time.addWidget(self.label_tmin_unit, 0, 3, 1, 1) - self.label_to = QLabel() - self.label_to.setText("to") - self.gridLayout_groupbox_xaxis_time.addWidget(self.label_to, 0, 4, 1, 1) + self.label_to_time = QLabel() + self.label_to_time.setText("to") + self.gridLayout_groupbox_xaxis_time.addWidget(self.label_to_time, 0, 4, 1, 1) self.label_tmax = QLabel() self.label_tmax.setText("tmax = ") @@ -356,13 +356,19 @@ class AcousticDataTab(QWidget): self.spinbox_tmax = QDoubleSpinBox() self.spinbox_tmax.setRange(0, 9999) self.gridLayout_groupbox_xaxis_time.addWidget(self.spinbox_tmax, 0, 6, 1, 1) - self.spinbox_tmax.valueChanged.connect(self.update_xaxis_transect_with_BS_raw_data) - self.spinbox_tmax.valueChanged.connect(self.update_xaxis_transect_with_SNR_data) + # self.spinbox_tmax.valueChanged.connect(self.update_xaxis_transect_with_BS_raw_data) + # self.spinbox_tmax.valueChanged.connect(self.update_xaxis_transect_with_SNR_data) self.label_tmax_unit = QLabel() self.label_tmax_unit.setText("sec") self.gridLayout_groupbox_xaxis_time.addWidget(self.label_tmax_unit, 0, 7, 1, 1) + self.pushbutton_apply_transect_boundaries_in_time = QPushButton() + self.pushbutton_apply_transect_boundaries_in_time.setText("Apply") + self.gridLayout_groupbox_xaxis_time.addWidget(self.pushbutton_apply_transect_boundaries_in_time, 0, 7, 1, 1) + self.pushbutton_apply_transect_boundaries_in_time.clicked.connect(self.update_xaxis_transect_with_BS_raw_data) + self.pushbutton_apply_transect_boundaries_in_time.clicked.connect(self.update_xaxis_transect_with_SNR_data) + # --- Group Box Plot x-axis in space --- self.groupbox_xaxis_space = QGroupBox() @@ -372,31 +378,44 @@ class AcousticDataTab(QWidget): self.verticalLayout_display_option.addWidget(self.groupbox_xaxis_space) self.gridLayout_groupbox_xaxis_space = QGridLayout(self.groupbox_xaxis_space) - self.label_from = QLabel() - self.label_from.setText("From") - self.gridLayout_groupbox_xaxis_space.addWidget(self.label_from, 0, 0, 1, 1) + self.label_from_space = QLabel() + self.label_from_space.setText("From ") + self.gridLayout_groupbox_xaxis_space.addWidget(self.label_from_space, 0, 0, 1, 1) + self.label_xmin = QLabel() self.label_xmin.setText("xmin = ") self.gridLayout_groupbox_xaxis_space.addWidget(self.label_xmin, 0, 1, 1, 1) + self.spinbox_xmin = QSpinBox() self.spinbox_xmin.setRange(0, 9999) self.gridLayout_groupbox_xaxis_space.addWidget(self.spinbox_xmin, 0, 2, 1, 1) + self.label_xmin_m = QLabel() self.label_xmin_m.setText("m") self.gridLayout_groupbox_xaxis_space.addWidget(self.label_xmin_m, 0, 3, 1, 1) - self.label_to = QLabel() - self.label_to.setText("to") - self.gridLayout_groupbox_xaxis_space.addWidget(self.label_to, 0, 4, 1, 1) + + self.label_to_space = QLabel() + self.label_to_space.setText("to") + self.gridLayout_groupbox_xaxis_space.addWidget(self.label_to_space, 0, 4, 1, 1) + self.label_xmax = QLabel() self.label_xmax.setText("xmax = ") self.gridLayout_groupbox_xaxis_space.addWidget(self.label_xmax, 0, 5, 1, 1) + self.spinbox_xmax = QSpinBox() self.spinbox_xmax.setRange(0, 9999) self.gridLayout_groupbox_xaxis_space.addWidget(self.spinbox_xmax, 0, 6, 1, 1) + self.label_xmax_m = QLabel() self.label_xmax_m.setText("m") self.gridLayout_groupbox_xaxis_space.addWidget(self.label_xmax_m, 0, 7, 1, 1) + self.pushbutton_apply_transect_boundaries_in_space = QPushButton() + self.pushbutton_apply_transect_boundaries_in_space.setText("Apply") + self.gridLayout_groupbox_xaxis_space.addWidget(self.pushbutton_apply_transect_boundaries_in_space, 0, 8, 1, 1) + self.pushbutton_apply_transect_boundaries_in_space.clicked.connect(self.update_xaxis_transect_with_BS_raw_data) + self.pushbutton_apply_transect_boundaries_in_space.clicked.connect(self.update_xaxis_transect_with_SNR_data) + # --- Group Box bathymetry computation algorithm to detect and plot bottom of transect--- self.groupbox_compute_bathymetry = QGroupBox() @@ -410,30 +429,44 @@ class AcousticDataTab(QWidget): self.combobox_freq_choice = QComboBox() # self.combobox_freq_choice.addItems(['', '0.3 MHz', '0.5 Mhz', '1 MHz', '5 MHz']) self.gridlayout_compute_bathymetry.addWidget(self.combobox_freq_choice, 0, 0, 2, 1) - self.gridlayout_compute_bathymetry.addWidget(self.label_from, 0, 1, 1, 1) + + self.label_from_bathy = QLabel() + self.label_from_bathy.setText("From ") + self.gridlayout_compute_bathymetry.addWidget(self.label_from_bathy, 0, 1, 1, 1) + self.spinbox_depth_min = QSpinBox() self.spinbox_depth_min.setRange(0, 9999) self.gridlayout_compute_bathymetry.addWidget(self.spinbox_depth_min, 0, 2, 1, 1) + self.label_depth_min_unit = QLabel() self.label_depth_min_unit.setText("m") self.gridlayout_compute_bathymetry.addWidget(self.label_depth_min_unit, 0, 3, 1, 1) - self.gridlayout_compute_bathymetry.addWidget(self.label_to, 0, 4, 1, 1) + + self.label_to_bathy = QLabel() + self.label_to_bathy.setText("to ") + self.gridlayout_compute_bathymetry.addWidget(self.label_to_bathy, 0, 4, 1, 1) + self.spinbox_depth_max = QSpinBox() self.spinbox_depth_max.setRange(0, 99999) self.gridlayout_compute_bathymetry.addWidget(self.spinbox_depth_max, 0, 5, 1, 1) + self.label_depth_max_unit = QLabel() self.label_depth_max_unit.setText("m") self.gridlayout_compute_bathymetry.addWidget(self.label_depth_max_unit, 0, 6, 1, 1) + self.label_next_cell = QLabel() self.label_next_cell.setText("Next cell : +/-") self.gridlayout_compute_bathymetry.addWidget(self.label_next_cell, 1, 1, 1, 1) + self.doublespinbox_next_cell = QDoubleSpinBox() self.doublespinbox_next_cell.setRange(0, 99999) self.doublespinbox_next_cell.setDecimals(2) self.gridlayout_compute_bathymetry.addWidget(self.doublespinbox_next_cell, 1, 2, 1, 1) + self.label_next_cell_unit = QLabel() self.label_next_cell_unit.setText("m") self.gridlayout_compute_bathymetry.addWidget(self.label_next_cell_unit, 1, 3, 1, 1) + self.pushbutton_compute_bathymetry_algorithm = QPushButton() self.pushbutton_compute_bathymetry_algorithm.setText("Compute \n&& \nPlot") self.gridlayout_compute_bathymetry.addWidget(self.pushbutton_compute_bathymetry_algorithm, 0, 7, 2, 1) @@ -684,6 +717,7 @@ class AcousticDataTab(QWidget): stg.BS_raw_data = acoustic_data._BS_raw_data stg.BS_raw_data_reshape = acoustic_data.reshape_BS_raw_cross_section() stg.r = acoustic_data._r + stg.r_2D = acoustic_data.compute_r_2D() stg.r_reshape = acoustic_data.reshape_r() stg.time = acoustic_data._time stg.time_reshape = acoustic_data.reshape_t() @@ -861,6 +895,8 @@ class AcousticDataTab(QWidget): np.where(np.round(stg.time, 2) == self.spinbox_tmax.value())[0][0]] stg.t = stg.time[np.where(np.round(stg.time, 2) == self.spinbox_tmin.value())[0][0]: np.where(np.round(stg.time, 2) == self.spinbox_tmax.value())[0][0]] + stg.r_2D = stg.r_2D[:, np.where(np.round(stg.time, 2) == self.spinbox_tmin.value())[0][0]: + np.where(np.round(stg.time, 2) == self.spinbox_tmax.value())[0][0]] for f in range(stg.freq.shape[0]): self.axis_BS[f].cla() diff --git a/View/acoustic_inversion_tab.py b/View/acoustic_inversion_tab.py index e663060..92f8bf1 100644 --- a/View/acoustic_inversion_tab.py +++ b/View/acoustic_inversion_tab.py @@ -1,18 +1,28 @@ import sys -from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication, QVBoxLayout, QHBoxLayout, QGroupBox -from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication, QVBoxLayout, QHBoxLayout, QGroupBox, QComboBox, \ + QGridLayout, QLabel, QPushButton, QSpinBox +from PyQt5.QtCore import QCoreApplication, Qt +from PyQt5.QtGui import QStandardItemModel import numpy as np + +from itertools import combinations + import matplotlib.pyplot as plt from matplotlib.colors import LogNorm, BoundaryNorm, CSS4_COLORS from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolBar - import Translation.constant_string as cs +from checkable_combobox import CheckableComboBox + +import settings as stg + +from Model.acoustic_inversion_method_high_concentration import AcousticInversionMethodHighConcentration + _translate = QCoreApplication.translate @@ -23,22 +33,141 @@ class AcousticInversionTab(QWidget): def __init__(self, widget_tab): super().__init__() -# -# self.verticalLayout_acoustic_inversion_tab = QVBoxLayout(widget_tab) -# -# self.horizontalLayout_Top_acousticInversionTab = QHBoxLayout() -# -# self.groupbox_AcousticInversionOption = QGroupBox() -# # self.groupbox_AcousticInversionOption.setTitle("Acoustic inversion option") -# -# self.horizontalLayout_Top_acousticInversionTab.addWidget(self.groupbox_AcousticInversionOption) -# -# self.verticalLayout_acoustic_inversion_tab.addLayout(self.horizontalLayout_Top_acousticInversionTab, 4) -# -# self.horizontalLayout_Bottom_acousticInversionTab = QHBoxLayout() -# -# self.groupbox_sediment_concentration_2Dplot = QGroupBox() -# # self.groupbox_sediment_concentration_2Dplot.setTitle("Fine and sand sediment concentration") + self.inv_hc = AcousticInversionMethodHighConcentration() + + ### --- General layout of widgets --- + + self.verticalLayoutMain = QVBoxLayout(widget_tab) + + self.horizontalLayoutTop = QHBoxLayout() + self.verticalLayoutMain.addLayout(self.horizontalLayoutTop, 4) # 1O units is 100% , 1 units is 10% + + self.horizontalLayoutBottom = QHBoxLayout() + self.verticalLayoutMain.addLayout(self.horizontalLayoutBottom, 6) + + ### --- Layout of groupbox in the Top horizontal layout box + + # Acoustic inversion Options | Acoustic inversion method Settings parameter + + self.groupbox_acoustic_inversion_options = QGroupBox() + self.horizontalLayoutTop.addWidget(self.groupbox_acoustic_inversion_options, 3) + + self.groupbox_acoustic_inversion_settings_parameter = QGroupBox() + self.horizontalLayoutTop.addWidget(self.groupbox_acoustic_inversion_settings_parameter, 7) + + ### --- Layout of groupbox in the Bottom horizontal layout box + + # Plot SSC 2D field | Plot SSC graph sample vs inversion + + self.groupbox_SSC_2D_field = QGroupBox() + self.horizontalLayoutBottom.addWidget(self.groupbox_SSC_2D_field, 6) + + self.groupbox_SSC_sample_vs_inversion = QGroupBox() + self.horizontalLayoutBottom.addWidget(self.groupbox_SSC_sample_vs_inversion, 4) + + # ===================================================== + # TOP HORIZONTAL BOX LAYOUT + # ===================================================== + + # +++++++++++++++++++++++++++++++++++++++++++++++ + # | Group box Acoustic inversion options | + # +++++++++++++++++++++++++++++++++++++++++++++++ + + self.gridLayout_groupbox_acoustic_inversion_options = QGridLayout(self.groupbox_acoustic_inversion_options) + + self.groupbox_acoustic_inversion_options.setTitle("Acoustic inversion option") + + self.label_acoustic_inversion_method_choice = QLabel() + self.gridLayout_groupbox_acoustic_inversion_options.addWidget( + self.label_acoustic_inversion_method_choice, 0, 0, 1, 1) + self.label_acoustic_inversion_method_choice.setText("Acoustic inversion method : ") + + self.combobox_acoustic_inversion_method_choice = QComboBox() + self.gridLayout_groupbox_acoustic_inversion_options.addWidget( + self.combobox_acoustic_inversion_method_choice, 0, 1, 1, 1) + self.combobox_acoustic_inversion_method_choice.addItems([" ", "Acoustic inversion method 1"]) + self.combobox_acoustic_inversion_method_choice.currentIndexChanged.connect( + self.acoustic_inversion_method_choice) + + self.label_sample_choice = QLabel() + self.gridLayout_groupbox_acoustic_inversion_options.addWidget(self.label_sample_choice, 1, 0, 1, 1) + self.label_sample_choice.setText("Calibration samples : ") + + self.combobox_calibration_samples = CheckableComboBox() + self.gridLayout_groupbox_acoustic_inversion_options.addWidget(self.combobox_calibration_samples, 1, 1, 1, 1) + self.combobox_calibration_samples.currentIndexChanged.connect(self.sample_choice) + + + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + # | Group box Acoustic inversion method settings parameter | + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + self.gridLayout_groupbox_acoustic_inversion_settings_parameter \ + = QGridLayout(self.groupbox_acoustic_inversion_settings_parameter) + + self.groupbox_acoustic_inversion_settings_parameter.setTitle("Acoustic inversion method settings parameter") + + self.label_temperature = QLabel() + self.label_temperature.setText("Temperature : ") + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget(self.label_temperature, 0, 0, 1, 1) + self.spinbox_temperature = QSpinBox() + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget(self.spinbox_temperature, 0, 1, 1, 1) + self.spinbox_temperature.valueChanged.connect(self.temperature_value) + + self.label_frequencies_pairs_to_compute_VBI = QLabel() + self.label_frequencies_pairs_to_compute_VBI.setText("frequencies for VBI : ") + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget( + self.label_frequencies_pairs_to_compute_VBI, 1, 0, 1, 1) + + self.combobox_frequencies_VBI = CheckableComboBox() + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget( + self.combobox_frequencies_VBI, 1, 1, 1, 1) + self.combobox_frequencies_VBI.currentIndexChanged.connect(self.frequencies_pair_choice_to_compute_VBI) + + self.label_frequency_to_compute_SSC = QLabel() + self.label_frequency_to_compute_SSC.setText("frequencies for SSC : ") + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget( + self.label_frequency_to_compute_SSC, 2, 0, 1, 1) + + self.combobox_frequency_SSC = CheckableComboBox() + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget( + self.combobox_frequency_SSC, 2, 1, 1, 1) + self.combobox_frequency_SSC.currentIndexChanged.connect(self.frequency_choice_to_compute_SSC) + + self.pushbutton_run = QPushButton() + self.pushbutton_run.setText("RUN") + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget(self.pushbutton_run, 3, 0, 1, 1) + self.pushbutton_run.clicked.connect(self.compute_acoustic_inversion_method_high_concentration) + + self.pushbutton_plot = QPushButton() + self.pushbutton_plot.setText("PLOT") + self.gridLayout_groupbox_acoustic_inversion_settings_parameter.addWidget(self.pushbutton_plot, 3, 1, 1, 1) + self.pushbutton_plot.clicked.connect(self.plot_SSC_2D_fields) + self.pushbutton_plot.clicked.connect(self.plot_SSC_inverse_VS_measured) + + # ===================================================== + # BOTTOM HORIZONTAL BOX LAYOUT + # ===================================================== + + # +++++++++++++++++++++++++++++++++++++++++++++++ + # | Group box SSC 2D field | + # +++++++++++++++++++++++++++++++++++++++++++++++ + + self.verticalLayout_groupbox_SSC_2D_field = QVBoxLayout(self.groupbox_SSC_2D_field) + + self.groupbox_SSC_2D_field.setTitle("Suspended Sediment Concentration 2D plot") + + self.canvas_SSC_2D_field = None + + # +++++++++++++++++++++++++++++++++++++++++++++++ + # | Group box plot samples vs inversion | + # +++++++++++++++++++++++++++++++++++++++++++++++ + + self.verticalLayout_groupbox_SSC_sample_vs_inversion = QVBoxLayout(self.groupbox_SSC_sample_vs_inversion) + + self.groupbox_SSC_sample_vs_inversion.setTitle("Suspended Sediment Concentration : sample vs inversion") + + self.canvas_SSC_sample_vs_inversion = None # # self.verticalLayout_groupbox_sediment_concentration_2Dplot = QVBoxLayout(self.groupbox_sediment_concentration_2Dplot) # @@ -53,7 +182,7 @@ class AcousticInversionTab(QWidget): # # self.horizontalLayout_Bottom_acousticInversionTab.addWidget(self.canvas_sediments2DPlot) # self.horizontalLayout_Bottom_acousticInversionTab.addWidget(self.groupbox_sediment_concentration_2Dplot, 7) # -# self.groupbox_sediment_concentration_sample_vs_measurement = QGroupBox() +# # # self.groupbox_sediment_concentration_sample_vs_measurement.setTitle( # # "Suspended sediment concentration plot : acoustic inversion theory VS measurements") # @@ -75,68 +204,252 @@ class AcousticInversionTab(QWidget): # self.verticalLayout_acoustic_inversion_tab.addLayout(self.horizontalLayout_Bottom_acousticInversionTab, 6) # # self.retranslate_acoustic_inversion_tab() -# -# # --------------------------------------------------------------------------------------------- -# # -------------------- Functions -------------------- -# # --------------------------------------------------------------------------------------------- -# + + # ---------------------------------------------------------------------------------------------------------------- + # -------------------- Functions -------------------- + # def retranslate_acoustic_inversion_tab(self): # # self.groupbox_AcousticInversionOption.setTitle(_translate("CONSTANT_STRING", cs.ACOUSTIC_INVERSION_OPTIONS)) # self.groupbox_sediment_concentration_2Dplot.setTitle(_translate("CONSTANT_STRING", cs.FINE_AND_SAND_SEDIMENTS_CONCENTRATION_2D_FIELD)) # self.groupbox_sediment_concentration_sample_vs_measurement.setTitle(_translate("CONSTANT_STRING", cs.SUSPENDED_SEDIMENT_CONCENTRATION_PLOT)) -# -# def plot_SSC_fine(self): -# frequency = 0 -# val_min = 1e-2 #np.nanmin(self.model.SSC_fine[:, :]) # -# val_max = 15 #np.nanmax(self.model.SSC_fine[:, :]) # -# # print('val_min=', val_min) -# # print('val_max=', val_max) -# # if val_min == 0: -# # val_min = 0.5 -# # print('val_min update =', val_min) -# pcm_SSC_fine = self.axis_SSC_2Dplot[0].pcolormesh(self.model.dist_BS_section, np.flipud(self.model.BS_averaged_cross_section.r), -# self.model.SSC_fine[:, :], -# cmap='rainbow', norm=LogNorm(vmin=val_min, vmax=val_max), shading='gouraud') -# self.axis_SSC_2Dplot[0].plot(self.model.dist_BS_section, -# np.max(self.model.r_bottom_cross_section) - self.model.r_bottom_cross_section -# + np.min(self.model.r_bottom_cross_section), -# color='k', linewidth=2) -# self.figure_SSC_2Dplot.supxlabel("Distance from left bank (m)", fontsize=10) -# self.figure_SSC_2Dplot.supylabel("Depth (m)", fontsize=10) -# self.figure_SSC_2Dplot.tight_layout() -# -# cbar_SSC_fine = self.figure_SSC_2Dplot.colorbar(pcm_SSC_fine, ax=self.axis_SSC_2Dplot[0], shrink=1, location='right') -# cbar_SSC_fine.set_label(label='Fine SSC (g/L', rotation=270, labelpad=15) -# -# def plot_SSC_sand(self): -# frequency = 0 -# val_min = 1e-2 #np.nanmin(self.model.SSC_fine[:, :]) # -# val_max = 2 #np.nanmax(self.model.SSC_fine[:, :]) # -# # print('val_min=', val_min) -# # print('val_max=', val_max) -# # if val_min == 0: -# # val_min = 0.5 -# # print('val_min update =', val_min) -# pcm_SSC_sand = self.axis_SSC_2Dplot[1].pcolormesh(self.model.dist_BS_section, np.flipud(self.model.BS_averaged_cross_section.r), -# self.model.SSC_sand[:, :], -# cmap='rainbow', norm=LogNorm(vmin=val_min, vmax=val_max), shading='gouraud') -# self.axis_SSC_2Dplot[1].plot(self.model.dist_BS_section, -# np.max(self.model.r_bottom_cross_section) - self.model.r_bottom_cross_section -# + np.min(self.model.r_bottom_cross_section), -# color='k', linewidth=2) -# self.figure_SSC_2Dplot.supxlabel("Distance from left bank (m)", fontsize=10) -# self.figure_SSC_2Dplot.supylabel("Depth (m)", fontsize=10) -# self.figure_SSC_2Dplot.tight_layout() -# -# cbar_SSC_sand = self.figure_SSC_2Dplot.colorbar(pcm_SSC_sand, ax=self.axis_SSC_2Dplot[1], shrink=1, location='right') -# cbar_SSC_sand.set_label(label='Sand SSC (g/L', rotation=270, labelpad=15) -# -# # if __name__ == "__main__": -# # app = QApplication(sys.argv) -# # print("class acoustic inversion tab :", AcousticInversionTab()) -# # sys.exit(app.exec_()) -# -# + + def acoustic_inversion_method_choice(self): + if self.combobox_acoustic_inversion_method_choice.currentIndex() == 1: + + # --- add items in combobox of samples to calibrate acoustic inversion method --- + samples_vertical_line = np.split(stg.samples, np.where(np.diff(stg.sample_time) != 0)[0]+1) + + for s in samples_vertical_line: + self.combobox_calibration_samples.addItem(" - ".join([i for i in s])) + + for i in range(len(samples_vertical_line)): + self.combobox_calibration_samples.setItemChecked(i, False) + + # --- add items in combobox of frequencies for VBI computation --- + for k in combinations(stg.freq_text, 2): + self.combobox_frequencies_VBI.addItem(k[0] + " - " + k[1]) + # print(k) + for i in range(len(list(combinations(stg.freq_text, 2)))): + self.combobox_frequencies_VBI.setItemChecked(i, False) + + def sample_choice(self): + sample_position = [] + for i in range(self.combobox_calibration_samples.count()): + if self.combobox_calibration_samples.itemChecked(i): + sample_position.append(i) + elif (i in sample_position) and (not self.combobox_calibration_samples.itemChecked(i)): + sample_position.remove(i) + + def frequencies_pair_choice_to_compute_VBI(self): + freq_combination = list(combinations(stg.freq, 2)) + frequencies_position = [] + for i in range(self.combobox_frequencies_VBI.count()): + if self.combobox_frequencies_VBI.itemChecked(i): + frequencies_position.append(i) + elif (i in frequencies_position) and (not self.combobox_frequencies_VBI.itemChecked(i)): + frequencies_position.remove(i) + + if len(frequencies_position) != 0: + # print(frequencies_position) + # print(freq_combination[frequencies_position[0]][0], freq_combination[frequencies_position[0]][1]) + stg.frequencies_pair = ( + np.array([[int(np.where(stg.freq == freq_combination[frequencies_position[0]][0])[0][0]), + freq_combination[frequencies_position[0]][0]], + [int(np.where(stg.freq == freq_combination[frequencies_position[0]][1])[0][0]), + freq_combination[frequencies_position[0]][1]]])) + # print(type(stg.frequencies_pair)) + # print(stg.frequencies_pair) + # print(stg.frequencies_pair[1, 0]) + # print(type(stg.frequencies_pair[0]), stg.frequencies_pair[0], + # np.where(stg.freq == stg.frequencies_pair[0])[0][0]) + # print(type(stg.frequencies_pair[1]), stg.frequencies_pair[1], + # np.where(stg.freq == stg.frequencies_pair[1])[0][0]) + + # --- add items in combobox of frequency for SSC computation --- + for k in range(stg.frequencies_pair.shape[0]): + self.combobox_frequency_SSC.addItem(str(1e-6*stg.frequencies_pair[k, 1]) + " MHz") + for i in range(stg.frequencies_pair.shape[0]): + self.combobox_frequency_SSC.setItemChecked(i, False) + + def frequency_choice_to_compute_SSC(self, index): + # print(self.combobox_frequency_SSC.currentText()) + # print(self.combobox_frequency_SSC.currentIndex()) + # print(self.combobox_frequency_SSC.itemChecked(index)) + + + if self.combobox_frequency_SSC.itemChecked(index): + # # itemChecked(index)): # currentIndex() == 0) or (self.combobox_frequency_SSC.currentIndex() == 1): + # print(self.combobox_frequency_SSC.currentText()) + # print(stg.freq_text) + # print(np.where(np.array(stg.freq_text) == self.combobox_frequency_SSC.currentText())) + stg.frequency_to_compute_SSC \ + = np.array([int(np.where(np.array(stg.freq_text) == self.combobox_frequency_SSC.currentText())[0][0]), + stg.freq[int(np.where(np.array(stg.freq_text) == self.combobox_frequency_SSC.currentText())[0][0])]]) + # print("stg.frequency_to_compute_SSC ", stg.frequency_to_compute_SSC) + + def temperature_value(self): + stg.temperature = self.spinbox_temperature.value() + # print(stg.temperature) + + def compute_acoustic_inversion_method_high_concentration(self): + stg.water_attenuation = self.inv_hc.water_attenuation(stg.frequencies_pair[0, 1], stg.frequencies_pair[1, 1], stg.temperature) + # print("water attenuation ", stg.water_attenuation) + + stg.water_velocity = self.inv_hc.water_velocity(stg.temperature) + # print("water velocity ", stg.water_velocity) + + stg.kt_corrected = self.inv_hc.kt_corrected(stg.r, stg.water_velocity, + stg.gain_rx[[int(stg.frequencies_pair[0, 0]), int(stg.frequencies_pair[1, 0])]], + stg.gain_tx[[int(stg.frequencies_pair[0, 0]), int(stg.frequencies_pair[1, 0])]], + stg.kt[[int(stg.frequencies_pair[0, 0]), int(stg.frequencies_pair[1, 0])]]) + # print("kt ", stg.kt_corrected) + # print("kt shape ", stg.kt_corrected.shape) + + stg.kt_corrected_2D = np.repeat(stg.kt_corrected, stg.r.shape[0], axis=0) + # print("kt 2D ", stg.kt_corrected_2D) + # print("kt 2D shape ", stg.kt_corrected_2D.shape) + stg.kt_corrected_3D = np.zeros((stg.kt_corrected_2D.shape[1], stg.kt_corrected_2D.shape[0], stg.t.shape[0])) + # print("stg.t.shape ", stg.t.shape) + # print("kt corrected 3D zeros shape ", stg.kt_corrected_3D.shape) + for k in range(stg.kt_corrected_2D.shape[1]): + stg.kt_corrected_3D[k, :, :] = np.repeat(stg.kt_corrected_2D, stg.t.shape[0], axis=1)[:, k*stg.t.shape[0]:(k+1)*stg.t.shape[0]] + print("kt 3D ", stg.kt_corrected_3D) + print("kt 3D shape ", stg.kt_corrected_3D.shape) + # print("kt 2D", np.repeat(stg.kt_corrected[:, :, np.newaxis], stg.t.shape[0], axis=2)) + + stg.J_cross_section = self.inv_hc.j_cross_section(stg.BS_data[:, [int(stg.frequencies_pair[0, 0]), int(stg.frequencies_pair[0, 0])], :], + stg.r_2D, + stg.kt_corrected_3D) + print("J ", stg.J_cross_section) + print("J sahpe ", stg.J_cross_section.shape) + + stg.X_exponent = self.inv_hc.X_exponent(self.combobox_frequencies_VBI.currentIndex()) + print("X ", stg.X_exponent) + + stg.zeta = self.inv_hc.zeta(int(stg.frequencies_pair[0, 0]), int(stg.frequencies_pair[1, 0])) + print("zeta ", stg.zeta) + + # print("stg.frequencies_pair ", stg.frequencies_pair) + # print("stg.frequencies_pair[0, 0]", int(stg.frequencies_pair[0, 0])) + # print("int(stg.frequencies_pair[1, 0]", int(stg.frequencies_pair[1, 0])) + + stg.ks = self.inv_hc.ks(int(stg.frequency_to_compute_SSC[0])) + print("ks ", stg.ks) + + stg.VBI_cross_section = self.inv_hc.VBI_cross_section(stg.frequencies_pair[0, 1], stg.frequencies_pair[1, 1], + stg.zeta[0], # zeta is already limited to the frequencies pairs so that we just need to select indices 0 and 1 + stg.zeta[1], + stg.J_cross_section[0, :, :], + stg.J_cross_section[1, :, :], + stg.r_2D, + stg.water_attenuation[0], + stg.water_attenuation[1], + stg.X_exponent) + # print("VBI shape ", stg.VBI_cross_section.shape) + # print(int(self.combobox_frequency_SSC.currentIndex())) + # print(stg.zeta[int(self.combobox_frequency_SSC.currentIndex())]) + + stg.SSC_fine = self.inv_hc.SSC_fine(stg.zeta[int(self.combobox_frequency_SSC.currentIndex())], + stg.r_2D, + stg.VBI_cross_section, + stg.frequency_to_compute_SSC[1], + stg.X_exponent, + stg.J_cross_section[self.combobox_frequency_SSC.currentIndex(), :, :]) + # print("SSC fine shape ", stg.SSC_fine.shape) + + stg.SSC_sand = self.inv_hc.SSC_sand(stg.VBI_cross_section, + stg.frequency_to_compute_SSC[1], + stg.X_exponent, + stg.ks) + + # print("SSC sand shape ", stg.SSC_sand.shape) + + def plot_SSC_2D_fields(self): + + if self.canvas_SSC_2D_field == None: + + self.figure_SSC_2D_field, self.axis_SSC_2D_field = plt.subplots(nrows=2, ncols=1, layout="constrained") + self.canvas_SSC_2D_field = FigureCanvas(self.figure_SSC_2D_field) + self.verticalLayout_groupbox_SSC_2D_field.addWidget(self.canvas_SSC_2D_field) + + self.plot_SSC_fine() + self.plot_SSC_sand() + + def plot_SSC_fine(self): + + val_min = 1e-2 #np.nanmin(self.model.SSC_fine[:, :]) # + val_max = 15 #np.nanmax(self.model.SSC_fine[:, :]) # + # print('val_min=', val_min) + # print('val_max=', val_max) + # if val_min == 0: + # val_min = 0.5 + # print('val_min update =', val_min) + + pcm_SSC_fine = self.axis_SSC_2D_field[0].pcolormesh(stg.t, -stg.r, stg.J_cross_section[0, :, :], + cmap='rainbow', + # norm=LogNorm(vmin=val_min, vmax=val_max), + shading='gouraud') + + self.axis_SSC_2D_field[0].plot(stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") + self.figure_SSC_2D_field.supxlabel("Time (sec)", fontsize=10) + self.figure_SSC_2D_field.supylabel("Depth (m)", fontsize=10) + + cbar_SSC_fine = self.figure_SSC_2D_field.colorbar(pcm_SSC_fine, ax=self.axis_SSC_2D_field[0], shrink=1, location='right') + cbar_SSC_fine.set_label(label='Fine SSC (g/L', rotation=270, labelpad=15) + + self.figure_SSC_2D_field.canvas.draw_idle() + + def plot_SSC_sand(self): + + val_min = np.nanmin(stg.VBI_cross_section) #1e-2 #np.nanmin(self.model.SSC_fine[:, :]) # + val_max = np.nanmax(stg.VBI_cross_section) #15 #np.nanmax(self.model.SSC_fine[:, :]) # + # print('val_min=', val_min) + # print('val_max=', val_max) + # if val_min == 0: + # val_min = 0.5 + # print('val_min update =', val_min) + + pcm_SSC_sand = self.axis_SSC_2D_field[1].pcolormesh(stg.t, -stg.r, stg.J_cross_section[1, :, :], + cmap='rainbow', + # vmin=val_min, vmax=val_max, + # norm=LogNorm(vmin=val_min, vmax=val_max), + shading='gouraud') + + self.axis_SSC_2D_field[1].plot(stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") + self.figure_SSC_2D_field.supxlabel("Time (sec)", fontsize=10) + self.figure_SSC_2D_field.supylabel("Depth (m)", fontsize=10) + + cbar_SSC_sand = self.figure_SSC_2D_field.colorbar(pcm_SSC_sand, ax=self.axis_SSC_2D_field[1], shrink=1, location='right') + cbar_SSC_sand.set_label(label='Sand SSC (g/L', rotation=270, labelpad=15) + self.figure_SSC_2D_field.canvas.draw_idle() + + def plot_SSC_inverse_VS_measured(self): + + sample_depth_position = [] + for i in range(stg.sample_depth.shape[0]): + sample_depth_position.append( + np.where(np.abs(stg.r + stg.sample_depth[i]) == np.min(np.abs(stg.r + stg.sample_depth[i])))[0][0]) + + sample_time_position = [] + for j in range(stg.sample_time.shape[0]): + sample_time_position.append( + np.where(np.abs(stg.t - stg.sample_time[j]) == np.min(np.abs(stg.t - stg.sample_time[j])))[0][0]) + + if self.canvas_SSC_sample_vs_inversion == None: + + self.figure_SSC_sample_vs_inversion, self.axis_SSC_sample_vs_inversion = plt.subplots(nrows=1, ncols=1, layout="constrained") + self.canvas_SSC_sample_vs_inversion = FigureCanvas(self.figure_SSC_sample_vs_inversion) + self.verticalLayout_groupbox_SSC_sample_vs_inversion.addWidget(self.canvas_SSC_sample_vs_inversion) + + self.axis_SSC_sample_vs_inversion.plot(stg.Ctot_fine, stg.SSC_fine[sample_depth_position, sample_time_position], ls=" ", marker='v', color='black', label='Fine SSC') + self.axis_SSC_sample_vs_inversion.plot(stg.Ctot_sand, stg.SSC_sand[sample_depth_position, sample_time_position], ls=" ", marker='x', color='black', label='Sand SSC') + self.axis_SSC_sample_vs_inversion.set_xscale('log') + self.axis_SSC_sample_vs_inversion.set_yscale('log') + self.axis_SSC_sample_vs_inversion.plot([0.5e-2, 20], [0.5e-2, 20], color='black', lw=1) + self.axis_SSC_sample_vs_inversion.set_xlabel('Measured SSC (g/l)', weight='bold') + self.axis_SSC_sample_vs_inversion.set_ylabel('Inverse SSC (g/l)', weight='bold') + self.axis_SSC_sample_vs_inversion.legend() + diff --git a/View/checkable_combobox.py b/View/checkable_combobox.py new file mode 100644 index 0000000..d0f31b1 --- /dev/null +++ b/View/checkable_combobox.py @@ -0,0 +1,33 @@ +from PyQt5.QtWidgets import QComboBox +from PyQt5.QtCore import Qt + + +class CheckableComboBox(QComboBox): + def __init__(self): + super().__init__() + self._changed = False + self.view().pressed.connect(self.handleItemPressed) + + def setItemChecked(self, index, checked=False): + item = self.model().item(index, self.modelColumn()) # QStandardItem object + if checked: + item.setCheckState(Qt.Checked) + else: + item.setCheckState(Qt.Unchecked) + + def handleItemPressed(self, index): + item = self.model().itemFromIndex(index) + if item.checkState() == Qt.Checked: + item.setCheckState(Qt.Unchecked) + else: + item.setCheckState(Qt.Checked) + self._changed = True + + def hidePopup(self): + if not self._changed: + super().hidePopup() + self._changed = False + + def itemChecked(self, index): + item = self.model().item(index, self.modelColumn()) + return item.checkState() == Qt.Checked \ No newline at end of file diff --git a/View/sample_data_tab.py b/View/sample_data_tab.py index ed9e406..15e3c39 100644 --- a/View/sample_data_tab.py +++ b/View/sample_data_tab.py @@ -415,8 +415,9 @@ class SampleDataTab(QWidget): # horizontal_header = list(map(str, ["Color", "Sample"] + stg.fine_sediment_columns + # stg.sand_sediment_columns[2:])) horizontal_header = list(itertools.chain(["Color", "Sample"], - list(map(str, stg.fine_sediment_columns.values)), - list(map(str, stg.sand_sediment_columns[2:])))) + list(map(str, stg.fine_sediment_columns[:2])), + list(map(str, stg.fine_sediment_columns[3:])), + list(map(str, stg.sand_sediment_columns[3:])))) for horizontal_header_text in horizontal_header: # print(horizontal_header_text) @@ -447,7 +448,8 @@ class SampleDataTab(QWidget): self.item_checkbox = QTableWidgetItem() self.item_checkbox.setCheckState(Qt.Unchecked) self.tableWidget_sample.setItem(i, 1, self.item_checkbox) - self.item_checkbox.setText("S " + str(i + 1)) + self.item_checkbox.setText("S" + str(i + 1)) + stg.samples.append("S" + str(i + 1)) # print(f"S{i+1} ", self.tableWidget_sample.item(i, 1).checkState()) # --- Fill table with data --- diff --git a/View/signal_processing_tab.py b/View/signal_processing_tab.py index 50aa9d3..5b2a144 100644 --- a/View/signal_processing_tab.py +++ b/View/signal_processing_tab.py @@ -1,7 +1,7 @@ import sys from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QGroupBox, QLabel, QCheckBox, \ - QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, QSlider, QGridLayout + QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, QSlider, QGridLayout, QMessageBox from PyQt5.QtGui import QFont, QIcon, QPixmap from PyQt5.QtCore import Qt, QCoreApplication @@ -83,7 +83,7 @@ class SignalProcessingTab(QWidget): self.pushbutton_load_data = QPushButton() self.horizontalLayout_pushbutton_load_data_plot_bottom_line.addWidget(self.pushbutton_load_data) self.pushbutton_load_data.clicked.connect(self.plot_profile_position_on_transect) - self.pushbutton_load_data.clicked.connect(self.plot_profiles) + self.pushbutton_load_data.clicked.connect(self.plot_profile) self.combobox_frequency = QComboBox() self.horizontalLayout_pushbutton_load_data_plot_bottom_line.addWidget(self.combobox_frequency) @@ -162,26 +162,27 @@ class SignalProcessingTab(QWidget): self.gridLayout_groupbox_acoustic_profile = QGridLayout(self.groupbox_acoustic_profile) - self.checkbox_SNR_criterion = QCheckBox() - self.gridLayout_groupbox_acoustic_profile.addWidget(self.checkbox_SNR_criterion, 0, 0, 1, 1) + # self.checkbox_SNR_criterion = QCheckBox() + self.label_SNR_criterion = QLabel() + self.gridLayout_groupbox_acoustic_profile.addWidget(self.label_SNR_criterion, 0, 0, 1, 1) self.spinbox_SNR_criterion = QSpinBox() self.spinbox_SNR_criterion.setRange(0, 9999) self.spinbox_SNR_criterion.setValue(0) - self.spinbox_SNR_criterion.setDisabled(True) + # self.spinbox_SNR_criterion.setDisabled(True) self.gridLayout_groupbox_acoustic_profile.addWidget(self.spinbox_SNR_criterion, 0, 1, 1, 1) - self.checkbox_SNR_criterion.clicked.connect(self.enable_disable_spinbox_snr_value) + # self.checkbox_SNR_criterion.clicked.connect(self.enable_disable_spinbox_snr_value) + self.spinbox_SNR_criterion.valueChanged.connect(self.remove_point_with_snr_filter) self.pushbutton_snr_filter = QPushButton() self.pushbutton_snr_filter.setText("Apply SNR") - self.pushbutton_snr_filter.setDisabled(True) + # self.pushbutton_snr_filter.setDisabled(True) self.gridLayout_groupbox_acoustic_profile.addWidget(self.pushbutton_snr_filter, 0, 2, 1, 1) - self.spinbox_SNR_criterion.valueChanged.connect(self.remove_point_with_snr_filter) # self.spinbox_SNR_criterion.valueChanged.connect(self.update_plot_profiles) # self.spinbox_SNR_criterion.valueChanged.connect(self.update_plot_profile_position_on_transect) - self.pushbutton_snr_filter.clicked.connect(self.update_plot_profiles) + self.pushbutton_snr_filter.clicked.connect(self.update_plot_profile) # self.pushbutton_snr_filter.clicked.connect(self.remove_point_with_snr_filter) # self.pushbutton_snr_filter.clicked.connect(self.update_plot_profile_position_on_transect) @@ -190,7 +191,7 @@ class SignalProcessingTab(QWidget): self.horizontalLayout_groupbox_window_size = QHBoxLayout(self.groupbox_window_size) self.label_signal_averaging_over = QLabel() - self.label_signal_averaging_over.setText("Signal averaging over ") + self.label_signal_averaging_over.setText("Signal averaging over +/- ") self.horizontalLayout_groupbox_window_size.addWidget(self.label_signal_averaging_over) self.spinbox_average = QSpinBox() self.spinbox_average.setRange(0, 9999) @@ -199,6 +200,15 @@ class SignalProcessingTab(QWidget): self.label_cells = QLabel() self.horizontalLayout_groupbox_window_size.addWidget(self.label_cells) + self.spinbox_average.valueChanged.connect(self.compute_averaged_profile) + + self.pushbutton_average = QPushButton() + self.pushbutton_average.setText("Apply averaging") + # self.pushbutton_snr_filter.setDisabled(True) + self.horizontalLayout_groupbox_window_size.addWidget(self.pushbutton_average) + + self.pushbutton_average.clicked.connect(self.plot_averaged_profile) + # --- Groupbox Rayleigh criterion --- # # self.groupbox_rayleigh_criterion.setTitle("Rayleigh criterion") @@ -566,7 +576,8 @@ class SignalProcessingTab(QWidget): self.slider.valueChanged.connect(self.update_lineEdit_by_moving_slider) self.slider.valueChanged.connect(self.update_plot_profile_position_on_transect) - self.slider.valueChanged.connect(self.update_plot_profiles) + self.slider.valueChanged.connect(self.update_plot_profile) + self.slider.valueChanged.connect(self.update_plot_averaged_profile) self.retranslate_signal_processing_tab() @@ -583,7 +594,7 @@ class SignalProcessingTab(QWidget): self.groupbox_acoustic_profile.setTitle(_translate("CONSTANT_STRING", cs.ACOUSTIC_PROFILE)) # self.checkbox_substract_noise.setText(_translate("CONSTANT_STRING", cs.SUBTRACT_THE_NOISE)) - self.checkbox_SNR_criterion.setText(_translate("CONSTANT_STRING", cs.SNR_CRITERION)) + self.label_SNR_criterion.setText(_translate("CONSTANT_STRING", cs.SNR_CRITERION)) # self.groupbox_window_size.setTitle(_translate("CONSTANT_STRING", cs.WINDOW_SIZE)) # self.label_averageH.setText(_translate("CONSTANT_STRING", cs.HORIZONTAL) + ": +/-") @@ -621,13 +632,13 @@ class SignalProcessingTab(QWidget): def update_lineEdit_by_moving_slider(self): self.lineEdit_slider.setText(str(self.slider.value())) - def enable_disable_spinbox_snr_value(self): - if self.checkbox_SNR_criterion.isChecked(): - self.spinbox_SNR_criterion.setEnabled(True) - self.pushbutton_snr_filter.setEnabled(True) - else: - self.spinbox_SNR_criterion.setDisabled(True) - self.pushbutton_snr_filter.setDisabled(True) + # def enable_disable_spinbox_snr_value(self): + # if self.checkbox_SNR_criterion.isChecked(): + # self.spinbox_SNR_criterion.setEnabled(True) + # self.pushbutton_snr_filter.setEnabled(True) + # else: + # self.spinbox_SNR_criterion.setDisabled(True) + # self.pushbutton_snr_filter.setDisabled(True) def remove_point_with_snr_filter(self): @@ -654,56 +665,56 @@ class SignalProcessingTab(QWidget): self.canvas_plot_profile_position_on_transect = FigureCanvas(self.figure_plot_profile_position_on_transect) self.verticalLayout_groupbox_display_profile_position.addWidget(self.canvas_plot_profile_position_on_transect) - if stg.r_bottom.size == 0: + # if stg.r_bottom.size == 0: - val_min = np.min(stg.BS_data[:, stg.freq_bottom_detection, :]) - val_max = np.max(stg.BS_data[:, stg.freq_bottom_detection, :]) - if val_min == 0: - val_min = 1e-5 + val_min = np.min(stg.BS_data[:, stg.freq_bottom_detection, :]) + val_max = np.max(stg.BS_data[:, stg.freq_bottom_detection, :]) + if val_min == 0: + val_min = 1e-5 - pcm = self.axis_plot_profile_position_on_transect.pcolormesh( - stg.t, -stg.r, stg.BS_data[:, stg.freq_bottom_detection, :], - cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) - - if stg.r_bottom.size != 0: - self.axis_plot_profile_position_on_transect.plot( - stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") + pcm = self.axis_plot_profile_position_on_transect.pcolormesh( + stg.t, -stg.r, stg.BS_data[:, stg.freq_bottom_detection, :], + cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) + if stg.r_bottom.size != 0: self.axis_plot_profile_position_on_transect.plot( - stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, - color='red', linestyle="solid", linewidth=2) + stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") - self.figure_plot_profile_position_on_transect.canvas.draw_idle() + self.axis_plot_profile_position_on_transect.plot( + stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, + color='red', linestyle="solid", linewidth=2) - else: + self.figure_plot_profile_position_on_transect.canvas.draw_idle() - stg.BS_data_section = deepcopy(stg.BS_data) + # else: - for f in range(stg.freq.shape[0]): - for k in range(stg.r_bottom.shape[0]): - # print(k, np.where(stg.r >= stg.r_bottom[k])[0]) - stg.BS_data_section[np.where(stg.r >= stg.r_bottom[k])[0], f, k] \ - = np.nan - # print("----------------------------------------------------------") + # stg.BS_data_section = deepcopy(stg.BS_data) - val_min = np.min(stg.BS_data_section[:, stg.freq_bottom_detection, :]) - val_max = np.max(stg.BS_data_section[:, stg.freq_bottom_detection, :]) - if val_min == 0: - val_min = 1e-5 + # for f in range(stg.freq.shape[0]): + # for k in range(stg.r_bottom.shape[0]): + # # print(k, np.where(stg.r >= stg.r_bottom[k])[0]) + # stg.BS_data_section[np.where(stg.r >= stg.r_bottom[k])[0], f, k] \ + # = np.nan + # # print("----------------------------------------------------------") - pcm = self.axis_plot_profile_position_on_transect.pcolormesh( - stg.t, -stg.r, stg.BS_data_section[:, stg.freq_bottom_detection, :], - cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) - - if stg.r_bottom.size != 0: - self.axis_plot_profile_position_on_transect.plot( - stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") - - self.axis_plot_profile_position_on_transect.plot( - stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, - color='red', linestyle="solid", linewidth=2) - - self.figure_plot_profile_position_on_transect.canvas.draw_idle() + # val_min = np.min(stg.BS_data_section[:, stg.freq_bottom_detection, :]) + # val_max = np.max(stg.BS_data_section[:, stg.freq_bottom_detection, :]) + # if val_min == 0: + # val_min = 1e-5 + # + # pcm = self.axis_plot_profile_position_on_transect.pcolormesh( + # stg.t, -stg.r, stg.BS_data_section[:, stg.freq_bottom_detection, :], + # cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) + # + # if stg.r_bottom.size != 0: + # self.axis_plot_profile_position_on_transect.plot( + # stg.t, -stg.r_bottom, color='black', linewidth=1, linestyle="solid") + # + # self.axis_plot_profile_position_on_transect.plot( + # stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, + # color='red', linestyle="solid", linewidth=2) + # + # self.figure_plot_profile_position_on_transect.canvas.draw_idle() def update_plot_profile_position_on_transect(self): @@ -714,55 +725,55 @@ class SignalProcessingTab(QWidget): # --- Update transect plot --- if self.canvas_plot_profile_position_on_transect != None: + # if stg.r_bottom.size != 0: + + self.axis_plot_profile_position_on_transect.cla() + + val_min = np.min(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) + val_max = np.max(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) + if val_min == 0: + val_min = 1e-5 + + self.axis_plot_profile_position_on_transect.pcolormesh( + stg.t, -stg.r, stg.BS_data[:, self.combobox_frequency.currentIndex(), :], + cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) + if stg.r_bottom.size != 0: - - self.axis_plot_profile_position_on_transect.cla() - - val_min = np.min(stg.BS_data_section[:, self.combobox_frequency.currentIndex(), :]) - val_max = np.max(stg.BS_data_section[:, self.combobox_frequency.currentIndex(), :]) - if val_min == 0: - val_min = 1e-5 - - pcm = self.axis_plot_profile_position_on_transect.pcolormesh( - stg.t, -stg.r, stg.BS_data_section[:, self.combobox_frequency.currentIndex(), :], - cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) - - if stg.r_bottom.size != 0: - self.axis_plot_profile_position_on_transect.plot( - stg.t, -stg.r_bottom, - color='black', linewidth=1, linestyle="solid") - self.axis_plot_profile_position_on_transect.plot( - stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, - color='red', linestyle="solid", linewidth=2) + stg.t, -stg.r_bottom, + color='black', linewidth=1, linestyle="solid") - self.figure_plot_profile_position_on_transect.canvas.draw_idle() + self.axis_plot_profile_position_on_transect.plot( + stg.t[self.slider.value() - 1] * np.ones(stg.r.shape[0]), -stg.r, + color='red', linestyle="solid", linewidth=2) - else: + self.figure_plot_profile_position_on_transect.canvas.draw_idle() - self.axis_plot_profile_position_on_transect.cla() + # else: + # + # self.axis_plot_profile_position_on_transect.cla() + # + # val_min = np.min(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) + # val_max = np.max(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) + # if val_min == 0: + # val_min = 1e-5 + # + # pcm = self.axis_plot_profile_position_on_transect.pcolormesh( + # stg.t, -stg.r, stg.BS_data[:, self.combobox_frequency.currentIndex(), :], + # cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) + # + # if stg.r_bottom.size != 0: + # self.axis_plot_profile_position_on_transect.plot( + # stg.t, -stg.r_bottom, + # color='black', linewidth=1, linestyle="solid") + # + # self.axis_plot_profile_position_on_transect.plot( + # stg.t[self.slider.value()-1] * np.ones(stg.r.shape[0]), -stg.r, + # color='red', linestyle="solid", linewidth=2) + # + # self.figure_plot_profile_position_on_transect.canvas.draw_idle() - val_min = np.min(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) - val_max = np.max(stg.BS_data[:, self.combobox_frequency.currentIndex(), :]) - if val_min == 0: - val_min = 1e-5 - - pcm = self.axis_plot_profile_position_on_transect.pcolormesh( - stg.t, -stg.r, stg.BS_data[:, self.combobox_frequency.currentIndex(), :], - cmap='viridis', norm=LogNorm(vmin=val_min, vmax=val_max)) - - if stg.r_bottom.size != 0: - self.axis_plot_profile_position_on_transect.plot( - stg.t, -stg.r_bottom, - color='black', linewidth=1, linestyle="solid") - - self.axis_plot_profile_position_on_transect.plot( - stg.t[self.slider.value()-1] * np.ones(stg.r.shape[0]), -stg.r, - color='red', linestyle="solid", linewidth=2) - - self.figure_plot_profile_position_on_transect.canvas.draw_idle() - - def plot_profiles(self): + def plot_profile(self): # --- Raw profile --- @@ -786,51 +797,46 @@ class SignalProcessingTab(QWidget): self.figure_profile.canvas.draw_idle() - # --- Raw averaged profile --- + # # --- Raw averaged profile --- + # + # self.figure_averaged_profile, self.axis_averaged_profile \ + # = plt.subplots(nrows=1, ncols=stg.freq.shape[0], layout='constrained') + # self.canvas_averaged_profile = FigureCanvas(self.figure_averaged_profile) + # self.verticalLayout_groupbox_plot_averaged_profile.addWidget(self.canvas_averaged_profile) + # + # for f in range(stg.freq.shape[0]): + # self.axis_averaged_profile[f].cla() + # self.axis_averaged_profile[f].plot(stg.BS_data[:, f, self.slider.value() - 1], -stg.r, + # linestyle='solid', color='k', linewidth=1) + # self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) - self.figure_averaged_profile, self.axis_averaged_profile \ - = plt.subplots(nrows=1, ncols=stg.freq.shape[0], layout='constrained') - self.canvas_averaged_profile = FigureCanvas(self.figure_averaged_profile) - self.verticalLayout_groupbox_plot_averaged_profile.addWidget(self.canvas_averaged_profile) + # # --- Raw FCB profile --- + # + # self.figure_FCB_profile, self.axis_FCB_profile \ + # = plt.subplots(nrows=1, ncols=stg.freq.shape[0], layout='constrained') + # self.canvas_FCB_profile = FigureCanvas(self.figure_FCB_profile) + # self.verticalLayout_groupbox_plot_FCB_profile.addWidget(self.canvas_FCB_profile) - for f in range(stg.freq.shape[0]): - self.axis_averaged_profile[f].cla() - self.axis_averaged_profile[f].plot(stg.BS_data[:, f, self.slider.value() - 1], -stg.r, - linestyle='solid', color='k', linewidth=1) - self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + def update_plot_profile(self): - # --- Raw FCB profile --- + # if self.checkbox_SNR_criterion.isChecked(): - self.figure_FCB_profile, self.axis_FCB_profile \ - = plt.subplots(nrows=1, ncols=stg.freq.shape[0], layout='constrained') - self.canvas_FCB_profile = FigureCanvas(self.figure_FCB_profile) - self.verticalLayout_groupbox_plot_FCB_profile.addWidget(self.canvas_FCB_profile) + # self.remove_point_with_snr_filter() - def update_plot_profiles(self): - - if self.checkbox_SNR_criterion.isChecked(): - - # self.remove_point_with_snr_filter() + if stg.BS_data_filter_snr.size == 0: for f in range(stg.freq.shape[0]): self.axis_profile[f].cla() - self.axis_profile[f].plot(stg.BS_data_filter_snr[:, f, self.slider.value()-1], -stg.r, - linestyle='solid', color='k', linewidth=1) - - # if stg.r_bottom.size != 0: - # self.axis_profile[f].plot( - # np.array([0, np.nanmax(stg.BS_data[:, stg.freq_bottom_detection, self.slider.value() - 1])]), - # -stg.r_bottom[self.slider.value() - 1] * np.ones(2), - # linestyle='dashed', color='red', linewidth=1) - + self.axis_profile[f].plot(stg.BS_data[:, f, self.slider.value()-1], -stg.r, + linestyle='solid', color='k', linewidth=1) self.axis_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) else: for f in range(stg.freq.shape[0]): self.axis_profile[f].cla() - self.axis_profile[f].plot(stg.BS_data[:, f, self.slider.value()-1], -stg.r, - linestyle='solid', color='k', linewidth=1) + self.axis_profile[f].plot(stg.BS_data_filter_snr[:, f, self.slider.value()-1], -stg.r, + linestyle='solid', color='k', linewidth=1) # if stg.r_bottom.size != 0: # self.axis_profile[f].plot( @@ -840,10 +846,123 @@ class SignalProcessingTab(QWidget): self.axis_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + # else: + # + # for f in range(stg.freq.shape[0]): + # self.axis_profile[f].cla() + # self.axis_profile[f].plot(stg.BS_data[:, f, self.slider.value()-1], -stg.r, + # linestyle='solid', color='k', linewidth=1) + + # if stg.r_bottom.size != 0: + # self.axis_profile[f].plot( + # np.array([0, np.nanmax(stg.BS_data[:, stg.freq_bottom_detection, self.slider.value() - 1])]), + # -stg.r_bottom[self.slider.value() - 1] * np.ones(2), + # linestyle='dashed', color='red', linewidth=1) + + # self.axis_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + self.figure_profile.canvas.draw_idle() def compute_averaged_profile(self): - pass + filter_convolve = np.ones(self.spinbox_average.value()) + + if stg.BS_data_filter_snr.size == 0: + stg.BS_data_averaged = np.zeros((stg.r.shape[0], stg.freq.shape[0], stg.t.shape[0]-self.spinbox_average.value()+1)) + for f in range(stg.freq.shape[0]): + for i in range(stg.r.shape[0]): + stg.BS_data_averaged[i, f, :] = np.convolve(stg.BS_data[i, f, :], filter_convolve, mode='valid') + # stg.BS_data_averaged = np.concatenate((stg.BS_data[:, :, :np.int(self.spinbox_average.value()/2)], + # stg.BS_data_averaged, + # stg.BS_data[:, :, -np.int(self.spinbox_average.value()/2):]), + # axis=2) + # print(stg.BS_data_averaged.shape) + else: + stg.BS_data_averaged = np.zeros((stg.r.shape[0], stg.freq.shape[0], stg.t.shape[0]-self.spinbox_average.value()+1)) + for f in range(stg.freq.shape[0]): + for i in range(stg.r.shape[0]): + stg.BS_data_averaged[i, f, :] = np.convolve(stg.BS_data_filter_snr[i, f, :], filter_convolve, + mode='valid') + + self.label_cells.clear() + self.label_cells.setText("cells = +/- " + str((self.spinbox_average.value() // 2)*(1/stg.nb_profiles_per_sec)) + " sec") + + def plot_averaged_profile(self): + # fig, ax = plt.subplots(nrows=1, ncols=1) + # ax.pcolormesh(stg.t[8:2232], -stg.r, stg.BS_data_averaged[:, 0, :], + # cmap='viridis', + # norm=LogNorm(vmin=1e-5, vmax=np.max(stg.BS_data_averaged[:, 0, :]))) + # plt.show() + + if self.canvas_averaged_profile != None: + for f in range(stg.freq.shape[0]): + self.axis_averaged_profile[f].cla() + + self.figure_averaged_profile, self.axis_averaged_profile \ + = plt.subplots(nrows=1, ncols=stg.freq.shape[0], layout='constrained') + self.canvas_averaged_profile = FigureCanvas(self.figure_averaged_profile) + self.verticalLayout_groupbox_plot_averaged_profile.addWidget(self.canvas_averaged_profile) + + if stg.BS_data_filter_snr.size == 0: + + BS_concatenate = stg.BS_data_averaged = np.concatenate((stg.BS_data[:, :, :np.int(self.spinbox_average.value()/2)], + stg.BS_data_averaged, + stg.BS_data[:, :, -np.int(self.spinbox_average.value()/2):]), + axis=2) + for f in range(stg.freq.shape[0]): + self.axis_averaged_profile[f].cla() + self.axis_averaged_profile[f].plot(BS_concatenate[:, f, self.slider.value()-1], -stg.r, + linestyle='solid', color='k', linewidth=1) + self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + else: + BS_concatenate = stg.BS_data_averaged = np.concatenate((stg.BS_data_filter_snr[:, :, :np.int(self.spinbox_average.value() / 2)], + stg.BS_data_averaged, + stg.BS_data_filter_snr[:, :, -np.int(self.spinbox_average.value() / 2):]), + axis=2) + for f in range(stg.freq.shape[0]): + self.axis_averaged_profile[f].cla() + self.axis_averaged_profile[f].plot(BS_concatenate[:, f, self.slider.value() - 1], -stg.r, + linestyle='solid', color='k', linewidth=1) + self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + + self.figure_averaged_profile.canvas.draw_idle() + + def update_plot_averaged_profile(self): + if self.canvas_averaged_profile == None: + + msgBox = QMessageBox() + msgBox.setWindowTitle("Plot averaged profile Error") + msgBox.setIcon(QMessageBox.Warning) + msgBox.setText("Compute acoustic backscatter averaged data") + msgBox.setStandardButtons(QMessageBox.Ok) + msgBox.exec() + + else: + + if stg.BS_data_filter_snr.size == 0: + + BS_concatenate = stg.BS_data_averaged = np.concatenate( + (stg.BS_data[:, :, :np.int(self.spinbox_average.value() / 2)], + stg.BS_data_averaged, + stg.BS_data[:, :, -np.int(self.spinbox_average.value() / 2):]), + axis=2) + for f in range(stg.freq.shape[0]): + self.axis_averaged_profile[f].cla() + self.axis_averaged_profile[f].plot(BS_concatenate[:, f, self.slider.value() - 1], -stg.r, + linestyle='solid', color='k', linewidth=1) + self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + else: + BS_concatenate = stg.BS_data_averaged = np.concatenate( + (stg.BS_data_filter_snr[:, :, :np.int(self.spinbox_average.value() / 2)], + stg.BS_data_averaged, + stg.BS_data_filter_snr[:, :, -np.int(self.spinbox_average.value() / 2):]), + axis=2) + for f in range(stg.freq.shape[0]): + self.axis_averaged_profile[f].cla() + self.axis_averaged_profile[f].plot(BS_concatenate[:, f, self.slider.value() - 1], -stg.r, + linestyle='solid', color='k', linewidth=1) + self.axis_averaged_profile[f].set_ylim(-np.max(stg.r), np.min(stg.r)) + + self.figure_averaged_profile.canvas.draw_idle() # def plot_transect_bottom_with_profile_position(self, profile_position): # frequency = self.model.Freq[0]