Source code for semi_cr.core.lab.station.routing_v1.conversions
# Contains device-side transformations that wrap routed quantities:
from dataclasses import dataclass
from typing import Any, Callable
import math
[docs]
class ConvertedVoltageQuantity:
"""
Device-side voltage quantity backed by an instrument-side voltage source.
Convention:
V_device = conversion_factor * V_instrument
"""
def __init__(
self,
instrument_voltage,
conversion_factor: float,
):
if conversion_factor == 0:
raise ValueError("conversion_factor cannot be zero.")
self.instrument_voltage = instrument_voltage
self.conversion_factor = float(conversion_factor)
[docs]
def set(self, device_voltage: float) -> None:
"""
Set the desired voltage at the device.
The required instrument voltage is calculated automatically.
"""
instrument_voltage = (
float(device_voltage) / self.conversion_factor
)
self.instrument_voltage.set(instrument_voltage)
[docs]
def get(self) -> float:
"""
Return the instrument readback expressed as device-side voltage.
"""
instrument_voltage = self.instrument_voltage.get()
return (
float(instrument_voltage) * self.conversion_factor
)
def __call__(self) -> float:
return self.get()
[docs]
@dataclass
class CurrentFromTransimpedanceVoltage:
voltage: Any
transimpedance_gain: Callable[[], float]
[docs]
def measure(self) -> float:
voltage = float(self.voltage.measure())
gain = float(self.transimpedance_gain())
if not math.isfinite(gain):
raise ValueError(
"Transimpedance gain must be finite; "
f"received {gain!r}."
)
if gain == 0.0:
raise ValueError(
"Transimpedance gain cannot be zero."
)
return voltage / gain
[docs]
def attach_converted_voltage_quantity(
device,
source_pin_name: str = "source",
voltage_quantity_name: str = "dc_voltage",
conversion_module=None,
) -> None:
"""
Replace the source's public voltage quantity with a device-side,
converted voltage quantity while preserving the original routed
instrument voltage.
"""
if conversion_module is None:
return
source = getattr(device, source_pin_name)
instrument_voltage = getattr(source, voltage_quantity_name)
conversion_factor = float(conversion_module.voltage_conversion_factor())
# Expose dc_voltage in device-side units.
setattr(
source,
voltage_quantity_name,
ConvertedVoltageQuantity(
instrument_voltage=instrument_voltage,
conversion_factor=conversion_factor,
),
)
[docs]
def attach_transimpedance_current_quantity(
device,
drain_pin_name: str = "drain",
voltage_quantity_name: str = "dc_voltage",
transimpedance_module= None,
):
if transimpedance_module is None:
raise ValueError(
"A transimpedance module is required."
)
drain = getattr(device, drain_pin_name)
voltage = getattr(drain, voltage_quantity_name)
drain.dc_current = CurrentFromTransimpedanceVoltage(
voltage=voltage,
transimpedance_gain=transimpedance_module.gain_v_per_a,
)