diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/filter_transmission.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/filter_transmission.py index fafb010..f1ef3f5 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/filter_transmission.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/filter_transmission.py @@ -2,6 +2,9 @@ """ cSAXS exposure-box filter transmission utilities. +This method has been created based on previous spec implementations +The translation was mainly done by copilot AI. + Implements fil_trans physics: - Per-material attenuation-length tables loaded from package 'filter_data/'. - Linear interpolation of attenuation length vs. energy (eV). @@ -137,7 +140,7 @@ class cSAXSFilterTransmission: # ----------------------------- # Public API # ----------------------------- - def fil_trans( + def fil_trans_select( self, transmission: float, energy_kev: Optional[float] = None, @@ -498,3 +501,103 @@ class cSAXSFilterTransmission: # Optionally: achieved transmission (approx., using the selected combination value) achieved_T = comb["transmission"] print(f"\nAchieved transmission (approx.): {achieved_T:9.3e} at {energy_kev:.3f} keV") + + + + def fil_trans_report(self, tol: float = 0.1) -> None: + """ + Report the currently active exposure‑box filter combination. + Determines stage positions via dev..readback.get() + with a tolerance window of ±tol relative to nominal positions. + """ + self._ensure_internal_state() + + # Resolve device object + dev = getattr(self.client, "dev", None) + if dev is None: + dev = self.client.device_manager + + # Try getting current energy + try: + energy_kev = float(self.client.device_manager.mokev.read()) + except Exception: + print("WARNING: Could not read current beam energy. Assuming 6.2 keV.") + energy_kev = 6.2 + + print("\nCurrent filter transmission report") + print("-" * 60) + print(f"Photon energy : {energy_kev:.3f} keV") + print(f"Tolerance : ±{tol:.3f}") + print("-" * 60) + + indices = [] # 0–5 per unit + + for unit_idx, axis_name in enumerate(self._AXES): + axis_obj = getattr(dev, axis_name, None) + if axis_obj is None: + print(f"ERROR: Device axis '{axis_name}' not found.") + return + + # Read readback + try: + rb = float(axis_obj.readback.get()) + except Exception: + print(f"ERROR: readback unavailable for axis {axis_name}") + return + + # Match readback to nearest nominal + pos_list = self._POSITIONS_USER[unit_idx] + best_idx = None + for i, nominal in enumerate(pos_list): + if nominal is None: + continue + if abs(rb - nominal) <= tol: + best_idx = i + break + + if best_idx is None: + print(f"Unit {unit_idx+1}: readback {rb:.3f} does not match any known position.") + return + + indices.append(best_idx) + + # Build combination dictionary like internally used ones + code = "".join(str(i + 1) for i in indices) + print(f"\nMatched filter code: {code}") + + # Build full combination record for transmission calculation + units = [ + self._FILTERS[u * self._PER_UNIT : (u + 1) * self._PER_UNIT] + for u in range(self._UNITS) + ] + + materials = [] + total_T = 1.0 + + for u, pos_idx in enumerate(indices): + (m1, t1), (m2, t2), enabled = units[u][pos_idx] + T = self._position_transmission(units[u][pos_idx], energy_kev) + if T is None: + print(f"Unit {u+1}: position disabled") + T = 1.0 + total_T *= T + + if m2 != "none" and t2 > 0: + materials.append(((m1, t1), (m2, t2))) + else: + materials.append(((m1, t1), None)) + + # Print detailed report + print(f"Total transmission: {total_T:.6e}\n") + + for u, matinfo in enumerate(materials, start=1): + (m1, t1), second = matinfo + pos = indices[u - 1] + 1 + if second: + m2, t2 = second + print(f" unit {u}: #{pos} {t1:4.0f} µm {m1:<4} + {t2:4.0f} µm {m2:<4}") + else: + if m1 != "none" and t1 > 0: + print(f" unit {u}: #{pos} {t1:4.0f} µm {m1:<4}") + else: + print(f" unit {u}: #{pos} ----- out")