Dev/stuff from pyctbgui (#273)
Build on RHEL8 / build (push) Successful in 2m23s
Build on RHEL9 / build (push) Successful in 2m35s
Run tests using data on local RHEL8 / build (push) Failing after 3m19s

Matterhorn10 Transform 
some other Transformations from pyctbGUI 
added method get_reading_mode for easier error handling in decoders 


## TODO: 

- proper error handling for all other decoders 
- proper documentation for all other decoders 
- refactoring all other decoders to store hard coded values in a Struct
ChipSpecification
This commit is contained in:
2026-02-19 16:12:44 +01:00
committed by GitHub
parent 5dbc746462
commit 2139e5843c
19 changed files with 411 additions and 51 deletions
+3 -1
View File
@@ -18,6 +18,9 @@ class CtbRawFile(_aare.CtbRawFile):
super().__init__(fname)
self._chunk_size = chunk_size
self._transform = transform
if self._transform:
if hasattr(self._transform, "compatibility") and callable(getattr(self._transform, "compatibility")):
self._transform.compatibility(self.master.reading_mode)
def read_frame(self, frame_index: int | None = None ) -> tuple:
@@ -45,7 +48,6 @@ class CtbRawFile(_aare.CtbRawFile):
if header.shape == (1,):
header = header[0]
if self._transform:
res = self._transform(data)
if isinstance(res, tuple):
+1 -1
View File
@@ -5,7 +5,7 @@ from . import _aare
from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
from ._aare import DetectorType
from ._aare import DetectorType, ReadoutMode
from ._aare import hitmap
from ._aare import ROI
from ._aare import corner
+82 -11
View File
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: MPL-2.0
import numpy as np
from . import _aare
from aare import ReadoutMode
from aare._aare import Matterhorn10
class AdcSar04Transform64to16:
def __call__(self, data):
@@ -24,6 +25,14 @@ class Moench05Transform:
return np.take(data.view(np.uint16), self.pixel_map)
class Moench03Transform:
def __init__(self):
self.pixel_map = _aare.GenerateMoench03PixelMap()
def __call__(self, data):
return np.take(data.view(np.uint16), self.pixel_map)
class Moench05Transform1g:
#Could be moved to C++ without changing the interface
def __init__(self):
@@ -40,19 +49,81 @@ class Moench05TransformOld:
def __call__(self, data):
return np.take(data.view(np.uint16), self.pixel_map)
class Matterhorn02Transform:
class Moench04AnalogTransform:
#Could be moved to C++ without changing the interface
def __init__(self):
self.pixel_map = _aare.GenerateMH02FourCounterPixelMap()
self.pixel_map = _aare.GenerateMoench04AnalogPixelMap()
def __call__(self, data):
counters = int(data.size / 48**2 / 2)
if counters == 1:
return np.take(data.view(np.uint16), self.pixel_map[0])
else:
return np.take(data.view(np.uint16), self.pixel_map[0:counters])
return np.take(data.view(np.uint16), self.pixel_map)
class Matterhorn02TransceiverTransform:
#Could be moved to C++ without changing the interface
def __init__(self):
self.pixel_map = _aare.GenerateMH02SingleCounterPixelMap()
def __call__(self, data):
return np.take(data.view(np.uint16), self.pixel_map)
class Matterhorn10Transform:
"""
Transforms Matterhorn10 chip data from a buffer of bytes (uint8_t)
to a numpy array of uint8, uint16 depending on dynamic range.
Assumes data taken with transceiver samples only.
:param dynamic_range: How many bits a pixel is encoded dynamic range (4, 8, or 16)
:type dynamic_range: int
:param num_counters: num counters used (1 to 4)
:type num_counters: int
.. note::
A matterhorn chip has 256 columns and 256 rows.
A matterhornchip with dynamic range 16 and 2 counters thus requires
256*256*16*2/(2*64) = 1024 transceiver samples. (Per default 2 channels are enabled per transceiver sample, each channel storing 64 bits)
"""
def __init__(self, dynamic_range : int, num_counters : int):
self.pixel_map = _aare.GenerateMatterhorn10PixelMap(dynamic_range, num_counters)
self.dynamic_range = dynamic_range
self.num_counters = num_counters
def compatibility(self, readingmode : ReadoutMode):
"""
checks if Matterhorn10Transform is compatible with given parameters
:param readingmode: Reading mode set
:type readingmode: ReadoutMode
:raises ValueError: if not compatible
"""
if(readingmode != ReadoutMode.TRANSCEIVER_ONLY):
raise ValueError(f"Incompatible Transformation. Matterhorn10Transform only requires transceiver samples. However reading mode is {readingmode}.")
pass
def data_compatibility(self, data):
"""
checks if data is compatible for transformation
:param data: data to be transformed, expected to be a 1D numpy array of uint8
:type data: np.ndarray
:raises ValueError: if not compatible
"""
expected_size = (Matterhorn10.nRows*Matterhorn10.nCols*self.num_counters*self.dynamic_range)//8 # read_frame returns data in uint8_t
if(data.size != expected_size):
raise ValueError(f"Data size {data.size} does not match expected size {expected_size} for Matterhorn10 with dynamic range {self.dynamic_range} and num_counters {self.num_counters}.")
pass
def __call__(self, data):
self.data_compatibility(data)
if self.dynamic_range == 16:
return np.take(data.view(np.uint16), self.pixel_map)
elif self.dynamic_range == 8:
return np.take(data.view(np.uint8), self.pixel_map)
else: #dynamic range 4
return np.take(_aare.expand4to8bit(data.view(np.uint8)), self.pixel_map)
class Mythen302Transform:
"""
Transform Mythen 302 test chip data from a buffer of bytes (uint8_t)
@@ -95,7 +166,7 @@ class Mythen302Transform:
moench05 = Moench05Transform()
moench05_1g = Moench05Transform1g()
moench05_old = Moench05TransformOld()
matterhorn02 = Matterhorn02Transform()
matterhorn02 = Matterhorn02TransceiverTransform()
adc_sar_04_64to16 = AdcSar04Transform64to16()
adc_sar_05_64to16 = AdcSar05Transform64to16()
adc_sar_05_06_07_08_64to16 = AdcSar05060708Transform64to16()