@@ -1508,6 +1508,94 @@ def pulse_mask(self, labelled=True, field=None):
15081508 raise ValueError (f"{ field = !r} parameter was not 'fel'/'ppl'/None" )
15091509
15101510
1511+ class XtdfPulses (PulsePattern ):
1512+ """An interface to pulses from XTDF sources.
1513+
1514+ The custom fast MHz detectors used at European XFEL send their data
1515+ via the XFEL Train Data Format (XTDF), which contains information
1516+ about the pulse pattern recorded by these detectors.
1517+
1518+ This component exposes this information via the pulse pattern
1519+ interface. As XTDF detectors generally enumerate pulses
1520+ independently, it also allows to correlate its pulse ID with those
1521+ obtained from other global sources, e.g. the machine information
1522+ from [XrayPulses][extra.components.Xraypulses].
1523+
1524+ Args:
1525+ detector (SourceData): Instrument source of an XTDF source.
1526+ ppt_anchor (int or PulsePattern, optional): Constant offset to
1527+ add to XTDF pulse IDs, or PulsePattern-based component to
1528+ correlate every train individually. No correlation is done
1529+ by default.
1530+ first_lit_cell (int, optional): Which cell coincides with the
1531+ first pulse from passed ppt_anchor, ignored unless a
1532+ PulsePattern component is passed.
1533+ include_cells (bool, optional): Whether to include cell IDs
1534+ in the pulse index generated by this component, True by
1535+ default.
1536+ """
1537+
1538+ def __init__ (self , xtdf_source , ppt_anchor = None , first_lit_cell = 0 ,
1539+ include_cells = True ):
1540+ super ().__init__ (xtdf_source , xtdf_source ['image.pulseId' ])
1541+
1542+ self ._ppt_anchor = ppt_anchor
1543+ self ._first_lit_cell = first_lit_cell
1544+ self ._cell_key = xtdf_source ['image.cellId' ] if include_cells else None
1545+
1546+ def __repr__ (self ):
1547+ return "<{} using {}>" .format (type (self ).__name__ , self ._source .source )
1548+
1549+ def _get_train_ids (self ):
1550+ return self ._source .train_ids
1551+
1552+ def _get_pulse_ids (self ):
1553+ pulse_ids = self ._key .ndarray ().astype (np .int32 ).ravel ()
1554+
1555+ if isinstance (self ._ppt_anchor , int ):
1556+ pulse_ids += self ._ppt_anchor
1557+
1558+ elif isinstance (self ._ppt_anchor , PulsePattern ):
1559+ first , last = self ._key .train_index_bounds (False )
1560+
1561+ # TODO: Must be aligned!
1562+ for i , (train_id , anchor_pids ) in enumerate (
1563+ self ._ppt_anchor .pulse_ids ().groupby ('trainId' )
1564+ ):
1565+ pulse_ids [first [i ]:last [i ]] += pids .iloc [0 ] - pulse_ids [
1566+ first [i ] + self ._first_lit_cell ]
1567+
1568+ elif self ._ppt_anchor is not None :
1569+ raise TypeError (type (self ._ppt_anchor ))
1570+
1571+ index_levels = {
1572+ 'trainId' : self ._key .train_id_coordinates (),
1573+ 'pulseIndex' : np .concatenate ([
1574+ np .arange (count , dtype = np .int32 ) for count
1575+ in self ._key .data_counts (labelled = False )])}
1576+
1577+ if self ._cell_key is not None :
1578+ index_levels ['cellId' ] = self ._cell_key .ndarray ().ravel ()
1579+
1580+ import pandas as pd
1581+ index = pd .MultiIndex .from_arrays (
1582+ list (index_levels .values ()), names = list (index_levels .keys ()))
1583+
1584+ return pd .Series (data = pulse_ids , index = index , dtype = np .int32 )
1585+
1586+
1587+ class AgipdPulses (XtdfPulses ):
1588+ pass
1589+
1590+
1591+ class LpdPulses (XtdfPulses ):
1592+ pass
1593+
1594+
1595+ class DsscPulses (XtdfPulses ):
1596+ pass
1597+
1598+
15111599class DldPulses (PulsePattern ):
15121600 """An interface to pulses from DLD reconstruction.
15131601
0 commit comments