# Copyright (c) ETH Zurich, SIS ID and HVL D-ITET
#
"""
Sensors that are used by the devices implemented in the CCB
"""
import logging
import typing
from abc import ABC, abstractmethod
from dataclasses import dataclass
import numpy as np
import numpy.typing as npt
from hvl_ccb.utils.typing import ConvertableTypes
from hvl_ccb.utils.validation import validate_number
from .unit import Temperature
from .utils import GetAttr, SetAttr, convert_value_to_str, preserve_type
logger = logging.getLogger(__name__)
[docs]
class Sensor(ABC):
@abstractmethod
@preserve_type
def convert(self, value: ConvertableTypes) -> ConvertableTypes:
pass # pragma: no cover
[docs]
@dataclass
class LEM4000S(Sensor):
CONVERSION = GetAttr(5000, "CONVERSION")
shunt = SetAttr(1.2, "shunt", limits=(1e-6, None), validator=validate_number)
calibration_factor = SetAttr(
1,
"calibration_factor",
limits=(0.9, 1.1),
absolut=True,
validator=validate_number,
)
@preserve_type
def convert(self, value: ConvertableTypes) -> ConvertableTypes:
value = typing.cast(npt.NDArray, value)
conversion = self.CONVERSION * self.calibration_factor
value_ret = value / self.shunt * conversion
value_str, value_ret_str = convert_value_to_str(value, value_ret)
logger.info(
f"LEM400S: Convert a secondary current of {value_str} A to a "
f"primary current of {value_ret_str} A."
)
return value_ret
[docs]
@dataclass
class LMT70A(Sensor):
"""
Converts the output voltage (V) to the measured temperature (default °C)
when using a TI Precision Analog Temperature Sensor LMT70(A)
"""
temperature_unit = SetAttr(
Temperature.CELSIUS, "temperature_unit", limits=None, validator=Temperature
)
# look up table from datasheet
# first column: temperature in degree celsius
# second column: voltage in volt
# https://www.ti.com/lit/ds/symlink/lmt70a.pdf?ts=1631590373860
LUT = GetAttr(
np.array([
[-55.0, 1.375219],
[-50.0, 1.350441],
[-40.0, 1.300593],
[-30.0, 1.250398],
[-20.0, 1.199884],
[-10.0, 1.14907],
[0.0, 1.097987],
[10.0, 1.046647],
[20.0, 0.99505],
[30.0, 0.943227],
[40.0, 0.891178],
[50.0, 0.838882],
[60.0, 0.78636],
[70.0, 0.733608],
[80.0, 0.680654],
[90.0, 0.62749],
[100.0, 0.574117],
[110.0, 0.520551],
[120.0, 0.46676],
[130.0, 0.412739],
[140.0, 0.358164],
[150.0, 0.302785],
]),
"LUT",
)
@preserve_type
def convert(self, value: ConvertableTypes) -> ConvertableTypes:
"""
NaN is returned for values that are not covered by the look up table
:param value: output voltage of the sensor.
:raise TypeError: for non convertable data types
:return: measured temperature (default °C)
"""
# cast necessary because of wrapper, which changes the type of value
value = typing.cast(npt.NDArray, value)
try:
validate_number(
"value", value, (self.LUT[-1, 1], self.LUT[0, 1]), logger=logger
)
except ValueError:
mask = np.any([value < self.LUT[-1, 1], value > self.LUT[0, 1]], axis=0)
value[mask] = np.NaN
logger.info(
"LMT70A: Use linear interpolation of lookup table provided "
"in datasheet of LMT70A"
)
value_ret = np.interp(value, self.LUT[::-1, 1], self.LUT[::-1, 0])
value_ret = Temperature.convert(
value_ret, source=Temperature.CELSIUS, target=self.temperature_unit
)
value_str, value_ret_str = convert_value_to_str(value, value_ret)
logger.info(
f"LMT70A: Convert a voltage of {value_str} V to "
f"a temperature of {value_ret_str} {self.temperature_unit.name}."
)
return value_ret