Skip to main content
LARDON processes raw detector data through two parallel pipelines that can be run independently or together. The TPC/charge pipeline is activated with the -trk flag, and the PDS/light pipeline is activated with -pds. When both flags are provided, a final TPC–PDS matching step is performed. Each detector module is processed independently during signal processing and per-module reconstruction. Cross-module steps run after all individual modules have been processed.

TPC/Charge Pipeline (-trk)

The charge pipeline is orchestrated in workflow.py. For each module, charge_signal_proc handles signal processing and charge_reco handles the actual reconstruction. After all modules are processed, charge_reco_whole performs detector-level stitching and timing.
1

Read raw data per module

Raw ADC waveforms are read from file for the current module. Dead or masked channels are excluded from processing via dc.alive_chan.
2

Signal processing (per module)

The charge_signal_proc function applies a multi-stage noise removal chain. See Signal Processing for the full breakdown.In order:
  1. Raw pedestal computation (ped.compute_pedestal(noise_type='raw'))
  2. Two iterations of filtered pedestal + mask refinement
  3. FFT low-pass filter (noise.FFT_low_pass)
  4. Zero-padding if the sample count became odd after FFT
  5. Two more iterations of filtered pedestal + mask refinement (pass 2)
  6. Shield coupling removal for PDVD TDE View 0 (noise.shield_coupling)
  7. Coherent noise removal (noise.coherent_noise)
  8. Microphonic noise removal (noise.median_filter)
  9. Final pedestal computation and mask refinement
3

Hit finding

hf.find_hits() extracts hit objects from signal regions of interest (ROI) in each channel. An R-tree spatial index is built immediately after with clu.hits_rtree([cf.imod]) for efficient proximity lookups during tracking.
4

2D track finding (Hough transform + Kalman filter)

trk2d.find_tracks_hough([cf.imod]) searches each wire view independently for track candidates using a Hough transform to seed tracks, followed by a Kalman-style filter to extend and validate them. Tracks are found in the position–drift-time (X–Z) plane.
5

2D track stitching within module

stitch.stitch2D_in_module([cf.imod]) merges 2D track segments belonging to the same physical track within a module, using alignment and proximity thresholds.
6

3D track reconstruction (3 views)

trk3d.find_track_3D_rtree_new([cf.imod]) combines 2D tracks from all three wire views using R-tree-based Z-overlap searches and graph-based connected component analysis to build 3D trajectories.
7

3D track reconstruction with missing view

trk3d.find_3D_tracks_with_missing_view([cf.imod]) handles cases where one wire view has no matched 2D track. It attempts to reconstruct a 3D track from the remaining two views using charge and Z-overlap criteria.
8

Single-hit (blip) finder

sh.single_hit_finder([cf.imod]) runs after track reconstruction and searches for isolated charge depositions (blips) in hits not assigned to any track. DBSCAN clustering is used to group nearby free hits across views.
9

Cross-module 3D track stitching

Runs in charge_reco_whole after all modules have been processed individually.
  • PDHD: stitch3D_across_modules([0,1]), then stitch3D_across_modules([2,3]), then stitch3D_across_cathode([[0,1],[2,3]])
  • PDVD: stitch3D_across_modules([0,1]), then stitch3D_across_modules([2,3]), then stitch3D_across_cathode([[2,3],[0,1]])
Tracks near module boundaries are tested for compatibility and merged into cross-module tracks.
10

Track timing computation

tmg.compute_all_track_timing() computes absolute timing information for all reconstructed 3D tracks, using anode-crossing and cathode-crossing constraints.

PDS/Light Pipeline (-pds)

The light pipeline is orchestrated by pds_signal_proc and pds_reco in workflow.py. It operates on two types of PDS data simultaneously: streaming (continuous readout) and self-triggered data.
PDS reconstruction is only configured for pdvd and cbbot. It does not work for pdhd, and is not configured for cbtop, cb1top, cb1bot, dp, or 50l.
1

PDS signal processing

pds_signal_proc applies three steps:
  1. First pedestal pass: ped.compute_pedestal_pds(first=True) — removes the waveform median and builds a signal mask using raw_adc_thresh = 200 ADC.
  2. Median filter: noise.median_filter_pds() — flattens the baseline using a sliding median window of 4000 samples.
  3. Second pedestal pass: ped.compute_pedestal_pds(first=False) — refines the pedestal after filtering.
Both streaming and triggered PDS data are processed in parallel within each call.
2

PDS hit/peak finding

hf.find_all_pds_peak() searches for peaks in both streaming and triggered PDS data. The same collection-type hit-finding algorithm used for TPC is applied, with amplitude thresholds of [5, 8] × RMS, a minimum duration of 50 samples, and left/right padding of 30/50 samples.
3

Light clustering

clu.light_clustering() groups PDS peaks across channels that are coincident in time. Waveforms are first aligned by cross-correlating peak timestamps (with a search window of 50 PDS ticks), then peaks within 5 μs of each other are grouped into light clusters.

TPC–PDS Matching

When both -trk and -pds are active, match_charge_and_pds calls mat.matching_trk_pds() to associate reconstructed 3D tracks with light clusters. Tracks are classified into categories (anode-crossers, cathode-crossers, unknown), and each category is matched against light clusters within a configurable time tolerance window (default ±5 μs). Only unambiguous one-to-one matches are recorded. See PDS Reconstruction for details.

Function Call Sequence

The following shows the order of workflow function calls as executed by the main event loop:
# Per-module loop
for imod in range(n_modules):
    charge_signal_proc(deb, is_online)   # signal processing
    charge_reco(deb, is_online)          # hit finding, 2D+3D tracking, blips

# PDS pipeline (independent of module loop)
pds_signal_proc()                        # pedestal, median filter, pedestal
pds_reco()                               # peak finding, light clustering

# Whole-detector steps (after all modules)
charge_reco_whole(is_online)             # cross-module stitching, timing

# TPC–PDS matching
match_charge_and_pds()                   # match tracks to light clusters

Build docs developers (and LLMs) love