瀏覽代碼

interp update

spacexerq 1 周之前
父節點
當前提交
cc14a71bd0
共有 2 個文件被更改,包括 60 次插入49 次删除
  1. 20 34
      apps/gui/src/tabs/spectroscopy_tab.py
  2. 40 15
      services/seq-interp/src/interfaces/rf_exporter.py

+ 20 - 34
apps/gui/src/tabs/spectroscopy_tab.py

@@ -389,8 +389,8 @@ class SpectroscopyTab(QWidget):
         )
         p1 = self._raw_widget.getPlotItem()
         p1.setTitle("Raw Signal")
-        p1.setLabel("bottom", "Time", units="ms")
-        p1.setLabel("left",   "Amplitude", units="V")
+        p1.setLabel("bottom", "Время", units="мс", color=p["text"], size="9pt")
+        p1.setLabel("left",   "Амплитуда", units="В", color=p["text"], size="9pt")
         p1.showGrid(x=True, y=True, alpha=0.2)
         p1.addLegend(offset=(10, 5))
         self._raw_real = p1.plot(pen=pg.mkPen(_C_RAW_REAL, width=1.5), name="Real")
@@ -411,8 +411,8 @@ class SpectroscopyTab(QWidget):
         )
         p4 = self._raw_fft_widget.getPlotItem()
         p4.setTitle("Raw FFT")
-        p4.setLabel("bottom", "Frequency", units="MHz")
-        p4.setLabel("left",   "Magnitude", units="V")
+        p4.setLabel("bottom", "Частота", units="МГц", color=p["text"], size="9pt")
+        p4.setLabel("left",   "Амплитуда", units="В", color=p["text"], size="9pt")
         p4.showGrid(x=True, y=True, alpha=0.2)
         p4.addLegend(offset=(10, 5))
         self._raw_fft_mag = p4.plot(
@@ -438,8 +438,8 @@ class SpectroscopyTab(QWidget):
         )
         p2 = self._dem_widget.getPlotItem()
         p2.setTitle("Demodulated FID")
-        p2.setLabel("bottom", "Time", units="ms")
-        p2.setLabel("left",   "Amplitude", units="a.u.")
+        p2.setLabel("bottom", "Время", units="мс", color=p["text"], size="9pt")
+        p2.setLabel("left",   "Амплитуда", units="отн. ед.", color=p["text"], size="9pt")
         p2.showGrid(x=True, y=True, alpha=0.2)
         p2.addLegend(offset=(10, 5))
         self._dem_real = p2.plot(pen=pg.mkPen(_C_DEM_REAL, width=1.5), name="Real")
@@ -461,8 +461,8 @@ class SpectroscopyTab(QWidget):
         )
         p3 = self._sp_widget.getPlotItem()
         p3.setTitle("NMR Spectrum")
-        p3.setLabel("bottom", "Frequency", units="MHz")
-        p3.setLabel("left",   "Magnitude", units="a.u.")
+        p3.setLabel("bottom", "Частота", units="МГц", color=p["text"], size="9pt")
+        p3.setLabel("left",   "Амплитуда", units="отн. ед.", color=p["text"], size="9pt")
         p3.showGrid(x=True, y=True, alpha=0.2)
         p3.addLegend(offset=(10, 5))
         self._sp_mag   = p3.plot(pen=pg.mkPen(_C_SPEC_MAG, width=1.5), name="Magnitude")
@@ -829,37 +829,22 @@ class SpectroscopyTab(QWidget):
     def _on_raw_mouse(self, pos) -> None:
         self._update_hover(
             self._raw_widget,
-            self._raw_vl,
-            self._raw_hl,
-            self._raw_hover,
-            pos,
-            "t",
-            "ms",
-            "a",
+            self._raw_vl, self._raw_hl, self._raw_hover,
+            pos, "t", "мс", "A", "В",
         )
 
     def _on_raw_fft_mouse(self, pos) -> None:
         self._update_hover(
             self._raw_fft_widget,
-            self._raw_fft_vl,
-            self._raw_fft_hl,
-            self._raw_fft_hover,
-            pos,
-            "f",
-            "MHz",
-            "m",
+            self._raw_fft_vl, self._raw_fft_hl, self._raw_fft_hover,
+            pos, "f", "МГц", "A", "В",
         )
 
     def _on_dem_mouse(self, pos) -> None:
         self._update_hover(
             self._dem_widget,
-            self._dem_vl,
-            self._dem_hl,
-            self._dem_hover,
-            pos,
-            "t",
-            "ms",
-            "a",
+            self._dem_vl, self._dem_hl, self._dem_hover,
+            pos, "t", "мс", "A", "отн.",
         )
 
     def _on_sp_mouse(self, pos) -> None:
@@ -868,10 +853,7 @@ class SpectroscopyTab(QWidget):
             self._sp_vl,
             self._sp_hl,
             self._sp_hover,
-            pos,
-            "f",
-            "MHz",
-            "m",
+            pos, "f", "МГц", "A", "отн.",
         )
 
     # -- Helpers -----------------------------------------------------------
@@ -898,6 +880,7 @@ class SpectroscopyTab(QWidget):
         x_name: str,
         x_unit: str,
         y_name: str,
+        y_unit: str = "",
     ) -> None:
         if not widget.sceneBoundingRect().contains(pos):
             self._hide_hover(vline, hline, label)
@@ -909,11 +892,14 @@ class SpectroscopyTab(QWidget):
         dx = (x_range[1] - x_range[0]) * 0.015
         dy = (y_range[1] - y_range[0]) * 0.04
 
+        x_str = f"{x_name} = {pt.x():.4f} {x_unit}"
+        y_str = f"{y_name} = {pt.y():.4g}" + (f" {y_unit}" if y_unit else "")
+
         vline.setVisible(True)
         hline.setVisible(True)
         vline.setPos(pt.x())
         hline.setPos(pt.y())
-        label.setText(f"{x_name}={pt.x():.4f} {x_unit}\n{y_name}={pt.y():.4g}")
+        label.setText(f"{x_str}\n{y_str}")
         label.setPos(pt.x() + dx, pt.y() + dy)
         label.setVisible(True)
 

+ 40 - 15
services/seq-interp/src/interfaces/rf_exporter.py

@@ -8,22 +8,47 @@ class RFExporter:
 
     @staticmethod
     def _radio_ampl_convertation(rf_ampl, t_rf, rf_raster_local):
-        out_rf_list = []
-        rf_ampl_raster = 127
-        rf_ampl_maximum = np.abs(max(rf_ampl)) if len(rf_ampl) else 0
+        """
+        Resample the RF waveform to a dense raster grid.
+
+        pypulseq represents block (hard) pulses as just 2 samples with equal
+        complex amplitude at t_start and t_end.  The old loop-based approach
+        treated every large time-gap as a zero region, which caused block pulses
+        to disappear entirely.
+
+        np.interp handles all pulse shapes correctly:
+          - Shaped pulses  : many samples → linear interpolation (good approx)
+          - Block pulses   : 2 equal samples → constant amplitude throughout
+          - Inter-pulse gaps (amplitude ≈ 0) : np.interp fills with ~0
+        """
+        rf_ampl = np.asarray(rf_ampl, dtype=complex)
+        t_rf    = np.asarray(t_rf,    dtype=float)
+
+        if len(rf_ampl) == 0:
+            return []
+
+        rf_ampl_maximum = float(np.max(np.abs(rf_ampl)))
         if rf_ampl_maximum == 0:
-            return out_rf_list
-
-        proportional_cf_rf = rf_ampl_raster / rf_ampl_maximum
-        num_zeroes = 0
-        for rf_iter in range(len(rf_ampl) - 1):
-            if abs(t_rf[rf_iter] - t_rf[rf_iter + 1]) > 2 * rf_raster_local:
-                num_zeroes += int(np.abs((t_rf[rf_iter] - t_rf[rf_iter + 1]) / rf_raster_local))
-            else:
-                out_rf_list += [0] * num_zeroes
-                num_zeroes = 0
-                out_rf_list.append(round(rf_ampl[rf_iter].real * proportional_cf_rf))
-                out_rf_list.append(round(rf_ampl[rf_iter].imag * proportional_cf_rf))
+            return []
+
+        proportional_cf_rf = 127.0 / rf_ampl_maximum
+
+        # Dense time grid aligned to raster
+        t_start   = float(t_rf[0])
+        t_end     = float(t_rf[-1])
+        n_samples = max(1, round((t_end - t_start) / rf_raster_local))
+        t_dense   = np.linspace(t_start, t_end, n_samples)
+
+        # Linear interpolation — exact for block pulses (constant), good approx
+        # for shaped pulses.  Extrapolation is clamped to boundary values.
+        real_dense = np.interp(t_dense, t_rf, rf_ampl.real)
+        imag_dense = np.interp(t_dense, t_rf, rf_ampl.imag)
+
+        out_rf_list: list[int] = []
+        for r, i in zip(real_dense, imag_dense):
+            out_rf_list.append(round(r * proportional_cf_rf))
+            out_rf_list.append(round(i * proportional_cf_rf))
+
         return out_rf_list
 
     def export(self, waveforms: dict, params: dict, output_dir: str):