Browse Source

Fix abs(s) to dB translation,
add thread locks for pyplot (just in case),
add simple transmission measurement option for S11 & S22,
add reload button to cover case of matplotlib errors

ricet8ur 2 years ago
parent
commit
061107340e
2 changed files with 146 additions and 86 deletions
  1. 145 84
      source/frontend/front.py
  2. 1 2
      source/frontend/show_amplitude_echart.py

+ 145 - 84
source/frontend/front.py

@@ -9,6 +9,10 @@ from .draw_smith_utils import draw_smith_circle, plot_abs_s_gridlines, plot_im_z
 from .show_amplitude_echart import plot_interact_abs_from_f
 from .show_amplitude_echart import plot_interact_abs_from_f
 from .data_parsing_utils import parse_snp_header, read_data, count_columns, prepare_snp, unpack_data
 from .data_parsing_utils import parse_snp_header, read_data, count_columns, prepare_snp, unpack_data
 
 
+# see https://docs.streamlit.io/streamlit-cloud/troubleshooting#limitations-and-known-issues
+# if you're using Matplotlib you should wrap your code with locks
+from matplotlib.backends.backend_agg import RendererAgg
+
 
 
 def plot_smith(r, i, g, r_cut, i_cut):
 def plot_smith(r, i, g, r_cut, i_cut):
     # maintaining state again (remember options for this session)
     # maintaining state again (remember options for this session)
@@ -39,95 +43,109 @@ def plot_smith(r, i, g, r_cut, i_cut):
 
 
     (show_excluded_points, show_grid, show_Abs_S_gridlines,
     (show_excluded_points, show_grid, show_Abs_S_gridlines,
      show_Re_Z_gridlines, show_Im_Z_gridlines) = st.session_state.smith_options
      show_Re_Z_gridlines, show_Im_Z_gridlines) = st.session_state.smith_options
-    fig = plt.figure(figsize=(10, 10))
-    ax = fig.add_subplot()
-    ax.axis('equal')
-    minor_ticks = np.arange(-1.1, 1.1, 0.05)
-    ax.set_xticks(minor_ticks, minor=True)
-    ax.set_yticks(minor_ticks, minor=True)
-    ax.grid(which='major', color='grey', linewidth=1.5)
-    ax.grid(which='minor', color='grey', linewidth=0.5, linestyle=':')
-    plt.xlabel('$Re(S)$', color='gray', fontsize=16, fontname="Cambria")
-    plt.ylabel('$Im(S)$', color='gray', fontsize=16, fontname="Cambria")
-    plt.title('Smith chart', fontsize=24, fontname="Cambria")
-
-    # unit circle
-    draw_smith_circle(ax, 0, 0, 1, '#1946BA')
-
-    if not show_grid:
-        ax.axis('off')
-
-    if show_Abs_S_gridlines:
-        # imshow is extremely slow, so draw it in place
-        plot_abs_s_gridlines(ax)
-
-    if show_Re_Z_gridlines:
-        plot_re_z_gridlines(ax)
-
-    if show_Im_Z_gridlines:
-        plot_im_z_gridlines(ax)
-
-    # input data points
-    if show_excluded_points:
-        ax.plot(r, i, '+', ms=8, mew=2, color='#b6c7f4')
-
-    # choosen data points
-    ax.plot(r_cut, i_cut, '+', ms=8, mew=2, color='#1946BA')
-
-    # S-circle approximation by calc
-    radius = abs(g[1] - g[0] / g[2]) / 2
-    x = ((g[1] + g[0] / g[2]) / 2).real
-    y = ((g[1] + g[0] / g[2]) / 2).imag
-    draw_smith_circle(ax, x, y, radius, color='#FF8400')
-
-    XLIM = [-1.3, 1.3]
-    YLIM = [-1.3, 1.3]
-    ax.set_xlim(XLIM)
-    ax.set_ylim(YLIM)
-    try:
-        st.pyplot(fig)
-    except:
-        st.write('Unexpected plot error')
+
+    # _lock to fix matplotlib crashing maybe?
+    _lock = RendererAgg.lock
+    with _lock:
+        # normal matplotlib usage
+        fig = plt.figure(figsize=(10, 10))
+        ax = fig.add_subplot()
+        ax.axis('equal')
+        minor_ticks = np.arange(-1.1, 1.1, 0.05)
+        ax.set_xticks(minor_ticks, minor=True)
+        ax.set_yticks(minor_ticks, minor=True)
+        ax.grid(which='major', color='grey', linewidth=1.5)
+        ax.grid(which='minor', color='grey', linewidth=0.5, linestyle=':')
+        plt.xlabel('$Re(S)$', color='gray', fontsize=16, fontname="Cambria")
+        plt.ylabel('$Im(S)$', color='gray', fontsize=16, fontname="Cambria")
+        plt.title('Smith chart', fontsize=24, fontname="Cambria")
+
+        # unit circle
+        draw_smith_circle(ax, 0, 0, 1, '#1946BA')
+
+        if not show_grid:
+            ax.axis('off')
+
+        if show_Abs_S_gridlines:
+            # imshow is extremely slow, so draw it in place
+            plot_abs_s_gridlines(ax)
+
+        if show_Re_Z_gridlines:
+            plot_re_z_gridlines(ax)
+
+        if show_Im_Z_gridlines:
+            plot_im_z_gridlines(ax)
+
+        # input data points
+        if show_excluded_points:
+            ax.plot(r, i, '+', ms=8, mew=2, color='#b6c7f4')
+
+        # choosen data points
+        ax.plot(r_cut, i_cut, '+', ms=8, mew=2, color='#1946BA')
+
+        # S-circle approximation by calc
+        radius = abs(g[1] - g[0] / g[2]) / 2
+        x = ((g[1] + g[0] / g[2]) / 2).real
+        y = ((g[1] + g[0] / g[2]) / 2).imag
+        draw_smith_circle(ax, x, y, radius, color='#FF8400')
+
+        XLIM = [-1.3, 1.3]
+        YLIM = [-1.3, 1.3]
+        ax.set_xlim(XLIM)
+        ax.set_ylim(YLIM)
+        try:
+            st.pyplot(fig)
+        except:
+            st.write('Unexpected plot error')
+            reload_plot=st.button('Reload')
+            if reload_plot:
+                st.experimental_rerun()
 
 
 
 
 # plot abs(S) vs f chart with pyplot
 # plot abs(S) vs f chart with pyplot
 def plot_abs_vs_f(f, r, i, fitted_mag_s):
 def plot_abs_vs_f(f, r, i, fitted_mag_s):
-    fig = plt.figure(figsize=(10, 10))
-    s = np.abs(np.array(r) + 1j * np.array(i))
-    if st.session_state.legendselection == '|S| (dB)':
-        m = np.min(np.where(s == 0, np.inf, s))
-        s = list(20 * np.where(s == 0, np.log10(m), np.log10(s)))
-        m = np.min(np.where(s == 0, np.inf, fitted_mag_s))
-        fitted_mag_s = list(
-            20 * np.where(s == 0, np.log10(m), np.log10(fitted_mag_s)))
-    s = list(s)
-    min_f = min(f)
-    max_f = max(f)
-    xlim = [min_f - abs(max_f - min_f) * 0.1, max_f + abs(max_f - min_f) * 0.1]
-    min_s = min(s)
-    max_s = max(s)
-    ylim = [min_s - abs(max_s - min_s) * 0.5, max_s + abs(max_s - min_s) * 0.5]
-    ax = fig.add_subplot()
-    ax.set_xlim(xlim)
-    ax.set_ylim(ylim)
-    ax.grid(which='major', color='k', linewidth=1)
-    ax.grid(which='minor', color='grey', linestyle=':', linewidth=0.5)
-    plt.xlabel(r'$f,\; 1/c$', color='gray', fontsize=16, fontname="Cambria")
-    if st.session_state.legendselection == '|S| (dB)':
-        plt.ylabel('$|S|$ (dB)', color='gray', fontsize=16, fontname="Cambria")
-        plt.title('|S| (dB) vs frequency', fontsize=24, fontname="Cambria")
-    else:
-        plt.ylabel('$|S|$', color='gray', fontsize=16, fontname="Cambria")
-        plt.title('|S| vs frequency', fontsize=24, fontname="Cambria")
+    # _lock to fix matplotlib crashing maybe?
+    _lock = RendererAgg.lock
+    with _lock:
+        fig = plt.figure(figsize=(10, 10))
+        s = np.abs(np.array(r) + 1j * np.array(i))
+        if st.session_state.legendselection == '|S| (dB)':
+            m = np.max(np.where(s == 0, 10**-50, s)) # where 10**-50 ~ 0
+            s = list(20 * np.where(s == 0, np.log10(m), np.log10(s)))
+            m = np.max(np.where(s == 0, 10**-50, fitted_mag_s)) # where 10**-50 ~ 0
+            fitted_mag_s = list(
+                20 * np.where(s == 0, np.log10(m), np.log10(fitted_mag_s)))
+        s = list(s)
+        min_f = min(f)
+        max_f = max(f)
+        xlim = [min_f - abs(max_f - min_f) * 0.1, max_f + abs(max_f - min_f) * 0.1]
+        min_s = min(s)
+        max_s = max(s)
+        ylim = [min_s - abs(max_s - min_s) * 0.5, max_s + abs(max_s - min_s) * 0.5]
+        ax = fig.add_subplot()
+        ax.set_xlim(xlim)
+        ax.set_ylim(ylim)
+        ax.grid(which='major', color='k', linewidth=1)
+        ax.grid(which='minor', color='grey', linestyle=':', linewidth=0.5)
+        plt.xlabel(r'$f,\; 1/c$', color='gray', fontsize=16, fontname="Cambria")
+        if st.session_state.legendselection == '|S| (dB)':
+            plt.ylabel('$|S|$ (dB)', color='gray', fontsize=16, fontname="Cambria")
+            plt.title('|S| (dB) vs frequency', fontsize=24, fontname="Cambria")
+        else:
+            plt.ylabel('$|S|$', color='gray', fontsize=16, fontname="Cambria")
+            plt.title('|S| vs frequency', fontsize=24, fontname="Cambria")
 
 
-    ax.plot(f, s, '+', ms=8, mew=2, color='#1946BA')
+        ax.plot(f, s, '+', ms=8, mew=2, color='#1946BA')
 
 
-    ax.plot(f, fitted_mag_s, '-', linewidth=3, color='#FF8400')
+        ax.plot(f, fitted_mag_s, '-', linewidth=3, color='#FF8400')
 
 
-    try:
-        st.pyplot(fig)
-    except:
-        st.write('Unexpected plot error')
+        try:
+            st.pyplot(fig)
+        except:
+            st.write('Unexpected plot error')
+            reload_plot=st.button('Reload')
+            if reload_plot:
+                st.experimental_rerun()
 
 
 def run(calc_function):
 def run(calc_function):
     # info
     # info
@@ -271,6 +289,7 @@ def run(calc_function):
         column_count, validator_status = count_columns(data)
         column_count, validator_status = count_columns(data)
 
 
     f, r, i = [], [], []
     f, r, i = [], [], []
+    pair_count = 1
     if validator_status == "data parsed":
     if validator_status == "data parsed":
         input_ports_pair = 1
         input_ports_pair = 1
         if column_count > 3:
         if column_count > 3:
@@ -301,11 +320,11 @@ def run(calc_function):
         # make accessible a specific range of numerical data choosen with interactive plot
         # make accessible a specific range of numerical data choosen with interactive plot
         # line id, line id
         # line id, line id
         interval_start, interval_end = plot_interact_abs_from_f(f,r,i)
         interval_start, interval_end = plot_interact_abs_from_f(f,r,i)
-    
         f_cut, r_cut, i_cut = [], [], []
         f_cut, r_cut, i_cut = [], [], []
         if validator_status == "data parsed":
         if validator_status == "data parsed":
             f_cut, r_cut, i_cut = (x[interval_start:interval_end]
             f_cut, r_cut, i_cut = (x[interval_start:interval_end]
                                    for x in (f, r, i))
                                    for x in (f, r, i))
+            # show cutting result
             with st.expander("Selected data interval as .s1p"):
             with st.expander("Selected data interval as .s1p"):
                 st_ace(value="# Hz S RI R 50\n" +
                 st_ace(value="# Hz S RI R 50\n" +
                        ''.join(f'{f_cut[x]} {r_cut[x]} {i_cut[x]}\n'
                        ''.join(f'{f_cut[x]} {r_cut[x]} {i_cut[x]}\n'
@@ -314,7 +333,7 @@ def run(calc_function):
                        auto_update=True,
                        auto_update=True,
                        placeholder="Selection is empty",
                        placeholder="Selection is empty",
                        height="150px")
                        height="150px")
-    
+            # can't find a circle without 3 points
             if len(f_cut) < 3:
             if len(f_cut) < 3:
                 validator_status = "Choosen interval is too small, add more points"
                 validator_status = "Choosen interval is too small, add more points"
 
 
@@ -376,6 +395,48 @@ def run(calc_function):
 
 
         st.latex('f_L =' + f'{format(fl, precision)}' + r'\text{ }Hz')
         st.latex('f_L =' + f'{format(fl, precision)}' + r'\text{ }Hz')
 
 
+        # in case of .snp with n>=2
+        if pair_count >= 4:
+            if 'make_transmission_measurement' not in st.session_state:
+                st.session_state['make_transmission_measurement'] = False
+
+            st.session_state['make_transmission_measurement'] = st.checkbox(
+                "Make transmission measurement on S11 & S22",
+                value=st.session_state['make_transmission_measurement'])
+
+            if st.session_state['make_transmission_measurement']:
+                def find_k(port_pair:int):
+                    f, r, i, validator_status = unpack_data(data,
+                                (port_pair - 1) * 2 + 1,
+                                column_count,
+                                input_ref_resistance,
+                                select_measurement_parameter,
+                                select_data_representation)
+                    if validator_status!='data parsed':
+                        st.write(validator_status)
+                        return 0,0
+                    else:
+                        f = [x * hz for x in f]
+                        f, r, i = (x[interval_start:interval_end]
+                                       for x in (f, r, i))
+                        Q0, sigmaQ0, QL, sigmaQL, k, ks, circle_params, fl, fitted_mag_s = calc_function(
+                        f, r, i, check_coupling_loss)
+                        return QL,k
+                # S11
+                QL1,k1=find_k(1)
+                # S22
+                QL2,k2=find_k(4)
+                QL=(QL1+QL2)/2
+                k_in = k1*(1+k2)/(1-k1*k2)
+                k_out = k2*(1+k1)/(1-k1*k2)
+                k=k_in+k_out
+                Q0 = QL*(1+k)
+                if Q0 <= 0 or QL <= 0:
+                    st.write("Negative Q detected, fitting may be inaccurate!")
+                show_result_in_latex('Q_0', Q0)
+                show_result_in_latex('Q_L', QL)
+                show_result_in_latex(r'\kappa', k)
+                
         with st.expander("Show static abs(S) plot"):
         with st.expander("Show static abs(S) plot"):
             plot_abs_vs_f(f_cut, r_cut, i_cut, fitted_mag_s)
             plot_abs_vs_f(f_cut, r_cut, i_cut, fitted_mag_s)
 
 

+ 1 - 2
source/frontend/show_amplitude_echart.py

@@ -16,9 +16,8 @@ def plot_interact_abs_from_f(f, r, i):
     s = np.abs(np.array(r) + 1j * np.array(i))
     s = np.abs(np.array(r) + 1j * np.array(i))
     abs_S = list(s)
     abs_S = list(s)
     # case (s[x] = 0) => (log10(s[x]) = log10(min(s)))
     # case (s[x] = 0) => (log10(s[x]) = log10(min(s)))
-    m = np.min(np.where(s == 0, np.inf, s))
+    m = np.max(np.where(s == 0, 10**-50, s)) # where 10**-50 ~ 0
     db_abs_S = list(20 * np.where(s == 0, np.log10(m), np.log10(s)))
     db_abs_S = list(20 * np.where(s == 0, np.log10(m), np.log10(s)))
-
     # echarts for datazoom https://discuss.streamlit.io/t/streamlit-echarts/3655
     # echarts for datazoom https://discuss.streamlit.io/t/streamlit-echarts/3655
     # datazoom https://echarts.apache.org/examples/en/editor.html?c=line-draggable&lang=ts
     # datazoom https://echarts.apache.org/examples/en/editor.html?c=line-draggable&lang=ts
     # axis pointer values https://echarts.apache.org/en/option.html#axisPointer
     # axis pointer values https://echarts.apache.org/en/option.html#axisPointer