added readback method for fil_trans. untested.
CI for csaxs_bec / test (push) Successful in 1m20s

This commit is contained in:
x01dc
2026-01-08 11:33:53 +01:00
committed by x12sa
parent e7e49b4916
commit e300fcaf27
@@ -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 exposurebox filter combination.
Determines stage positions via dev.<axis>.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 = [] # 05 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")