1.3 ADC/TDC 信号处理:从物理量到数字化
1. 目的
- 掌握模拟电信号(时间、幅度)如何进行数字化(保存为整数道值)。
- 理解基线偏移(Pedestal)、超界(Overflow)。
- 有效事件的选择(cut)
2. Digitization (数字化)
在模拟代码中,事件的变量(如 $q_L/q_R$, $t_L/t_R$)通常是用 double 保存的浮点数。但在真实实验中,探测器输出的连续模拟信号经过电子学模块(ADC / TDC / QDC)后,会被量化为离散的整数(Channel / 道值),这个过程就叫数字化。
A. ADC / QDC (模拟/电荷-数字转换器)
ADC / QDC 用于记录输入信号的大小(ADC 测电压幅度,QDC 测电荷积分)。
假设一个 12-bit 的 ADC,硬件输入量程为 0-4V。 它的转换公式可以直白地写为:
$$ \mathrm{Raw\_ADC} = \mathrm{Int}(\text{信号幅度} \times \mathrm{Gain} + \mathrm{Pedestal} + \text{噪声涨落}) $$
具体参数说明:
- 信号幅度 (Input Signal): 实际进入 ADC 的模拟电压(例如 2.5V)。
- Gain (增益/转换系数): 把电压映射到道值的比例。
理想 0–4 V 量程对应 4096 个码,Gain=4096/4000=1.024 ch/mV。未加 pedestal 时,2.5 V 对应 2560 ch。
- Pedestal (基线/直流偏置):
- 因为真实的零信号总带有电子学噪声,噪声有正有负。如果基线设在 0 ch,负向噪声就会被 ADC 丢弃(无法记录负数)。
可加入直流偏置把 pedestal 均值移到正道值,如 140 ch,以减少负向噪声被量程截断;这不保证任意噪声样本都在量程内。
无物理信号时,读数仍有分布。其均值用来扣除 pedestal offset,宽度反映噪声;是否近似 Gaussian 应从数据检查。扣除均值不等于消除了逐事例噪声。

- Range (量程):
- 12-bit:
0 ~ 4095ch - 14-bit:
0 ~ 16383ch
- 12-bit:
- 本模拟把超量程值记为最大码。真实模块可能另有 overflow 状态或编码,应按模块说明处理。
B. TDC (时间-数字转换器)
TDC 用于记录 Start 和 Stop 两个信号之间的时间差。
假设这个 TDC 的时间测量量程是 0-100ns,数字编码为 12-bit (0-4095 ch)。 它的转换公式为:
$$ \mathrm{Raw\_TDC} = \mathrm{Int}\left( (T_{\mathrm{stop}} - T_{\mathrm{start}} + \mathrm{Delay}) \times \mathrm{转换系数} \right) $$
具体参数说明:
- 转换系数: 把时间差换算成道值的系数。
- 在这个例子中,100ns 对应 4096 个道址,转换系数就是 $4096 / 100\mathrm{ns} \approx 40.96 \text{ ch/ns}$。
- Delay:
- TDC 只能测正的时间差(0-100ns)。但在实际连线中,因为电缆长度或触发逻辑,
Stop信号有可能比Start信号先到(时间差变成负的,比如 -10ns),TDC 就无法记录。 - 在硬件或逻辑上给信号强行加上一段固定的 Delay(比如 +30ns),把原本 -10ns 的时间差整体平移到 +20ns,让它稳稳地落在 0-100ns 的合法量程内。
- TDC 只能测正的时间差(0-100ns)。但在实际连线中,因为电缆长度或触发逻辑,
- Range (量程):
- 12-bit:
0 ~ 4095ch - 14-bit:
0 ~ 16383ch
- 12-bit:
- 本模拟把超量程值记为最大码。真实模块可能另有 overflow 状态或编码,应按模块说明处理。
3. 物理过程
- 束流轰击靶以产生带电粒子、n、$\gamma$等粒子,重带电粒子碎片被靶后 Dipole magnet偏走,而部分轻带电粒子(LP: light particles,主要为质子)没有被完全偏走,仍然进入中子探测器。
真实数据获取 (DAQ) 中,触发逻辑决定了我们记录什么事件:
- 物理触发 (Trig_Phys):中子,Gamma或LP 击中探测器。(
pid=0, 1,2) - 束流/时钟触发用于监测基线。本模拟把 pid=3 设为无物理信号事件;真实的时钟触发仍可能与粒子信号偶然重叠。
4. 代码:基于 1.1 代码进行修改
定义数字化参数与分支
// --- 新增变量 ---
Int_t iAL, iAR; // QDC道值 (0-4095)
Int_t itL, itR; // TDC道值 (0-4095)
// 复用已有 pid:0:G, 1:N, 2:LP, 3:Pedestal
// --- 数字化与电子学参数 ---
const Double_t ADC_Gain = 5.0; // 增益
const Double_t TDC_Gain = 25.0; // 转换系数 (如40ps/ch)
const Double_t Ped_L = 140.0, Ped_R = 130.0; // 基线
const Double_t Noise = 8.5; // 电子学噪声
const Double_t Trig_Dly = 50.0; // 延迟
const Int_t Max_ADC = 4095; // 12-bit
const Int_t Threshold = 180; // 触发阈值(道值)
// --- Branch 映射 (省略原有部分) ---
// pid 分支已在原模拟中建立,不再重复声明。
opt->Branch("iAL", &iAL, "iAL/I"); // ... 其余同理
事件生成 ($E_n \to E_p \to E_{ee}$)
// 1. 抽样生成 pid
Double_t rnd = gr->Uniform();
if (rnd < 0.10) pid = 3; // 空触发/时钟触发
else if (rnd < 0.20) pid = 2; // LP (质子)
else pid = (gr->Uniform() < 0.3) ? 0 : 1;
// 2. 物理过程修改
...
if (pid == 2) {
Eee = gr->Gaus(50.0, 5.0); // 此处直接指定 LP 的示意光输出分布(MeVee)
}
if (pid == 3) {
Eee = 0.0; // Pedestal 无能量
}
数字化 (Digitization) 与 触发逻辑 (Trigger)
...
// 1. ADC: 幅度 * 增益 + 基线 + 噪声
iAL = (Int_t)(AL* ADC_Gain + Ped_L + gr->Gaus(0, Noise));
iAR = (Int_t)(AR * ADC_Gain + Ped_R + gr->Gaus(0, Noise));
// 2. TDC: (时间 + 延迟) * 转换系数
if (pid == 3) {
itL = Max_ADC; itR = Max_ADC; // 无物理信号,TDC默认溢出
} else {
itL = (Int_t)((tL + Trig_Dly) * TDC_Gain );
itR = (Int_t)((tR + Trig_Dly) * TDC_Gain );
}
// 3. 超界截断处理
if (iAL > Max_ADC) iAL = Max_ADC; if (iAL < 0) iAL = 0;
// ... iAR, itL, itR 同理 ...
// 4. DAQ 触发逻辑 (物理符合触发 OR 时钟触发)
bool Trig_Phys = (iAL > Threshold) && (iAR > Threshold);
bool Trig_Clock = (pid == 3);
if (Trig_Phys || Trig_Clock) {
opt->Fill(); // 只有满足触发条件才存入 Tree
}
道值、量程与状态
12-bit ADC 有 4096 个码。理想 0–4 V 量程的步距为 \(4/4096\) V,约 0.977 mV;码间距不是完整的仪器精度。Pedestal 是无物理信号时的基线分布,均值为 pedestal offset、宽度反映噪声,增加 offset 不保证所有噪声样本都为正。
本例约定超量程信号饱和到最大码;真实 ADC/TDC 对 overflow、无命中和无效时间的编码应查对应模块。TDC 的符号和有效区间还取决于 common-start/common-stop 等读出方式。
%jsroot on
数字化信号处理:有效范围与 Dithering
先确定有效道值
用空触发或无束流数据画出 ADC 谱,拟合 pedestal 得到均值 $\mu_{ped}$ 和宽度 $\sigma_{ped}$。以 $\mu_{ped}+3\sigma_{ped}$ 为阈值,是抑制噪声的常用起点,再根据低幅度信号与噪声的分离情况调整。对 Gaussian 噪声,超过这一阈值的单侧概率约为 0.135%;它描述噪声的越阈比例,不是所选事例的信号纯度。
计算组合量前,排除缺失、overflow 及不在有效范围内的读数。例如,一端 TDC 无效时,两端平均时间也没有意义。本模拟可先用以下条件选择两端时间:
TCut valid_tdc = "itL>0 && itL<4095 && itR>0 && itR<4095";
这里的 0、4095 对应前面模拟的编码约定;实验数据按模块的状态与有效码范围设置。幅度扣除 pedestal 后,再检查开方、对数和倒数所需的输入条件。
刻度和组合运算前进行 Dithering
本讲义将 dithering 作为整数 ADC/TDC 道值进入逐事件连续刻度前的标准步骤。整数道值代表一个量化区间;直接缩放、求差或取平均后,新 histogram 的 bin 宽度与离散步距可能不匹配,从而产生人为的锯齿、条纹或空 bin。
前面的模拟采用向下取整,$N$ 对应 $[N,N+1)$。在一个道宽内分布变化较慢时,采用道内均匀近似:
$$A_{cont}=N+u,\qquad u\sim U(0,1).$$
先由有效整数道值生成连续值,再用该值进行刻度和组合运算。每个事例的每路测量只随机化一次,后续计算复用它;原始整数和状态仍保留。Dithering 不增加仪器分辨率,也不确定单个事例在道内的真实位置。
比较两端平均时间
下面对同一批整数时间读数比较两种处理:左图取道中心 $N+0.5$ 后求平均,右图先分别 dithering 再求平均。使用相同的 histogram 范围和 bin 宽度。道中心与道内均匀取值具有相同的期望值,因而不会把半个道的整体平移混入比较。
TRandom3 rng(1301);
TH1D *hcenter = new TH1D("hcenter","Bin-center values;Mean time (channel);Counts",200,30,70);
TH1D *hdither = new TH1D("hdither","Uniform within-bin model;Mean time (channel);Counts",200,30,70);
for (int i=0; i<100000; ++i) {
int left = std::floor(rng.Gaus(50,5));
int right = std::floor(rng.Gaus(50,5));
hcenter->Fill(((left+0.5)+(right+0.5))/2);
hdither->Fill(((left+rng.Uniform())+(right+rng.Uniform()))/2);
}
TCanvas *c1 = new TCanvas("c1","Quantization",900,400);
c1->Divide(2,1);
c1->cd(1); hcenter->Draw();
c1->cd(2); hdither->Draw();
c1->Draw();
cout << "Bin centers: mean=" << hcenter->GetMean() << ", RMS=" << hcenter->GetStdDev() << endl;
cout << "Dither model: mean=" << hdither->GetMean() << ", RMS=" << hdither->GetStdDev() << endl;
Bin centers: mean=49.9874, RMS=3.55508 Dither model: mean=49.9884, RMS=3.56175
图中的量化结构
左图的两端平均时间仍以 0.5 channel 为间隔,而 histogram 的 bin 宽度为 0.2 channel,因而出现交替的空 bin 和高计数 bin。它们来自量化步距与新 binning 的不匹配,不是物理峰。
右图在求平均前对每端做了 dithering,离散码不再集中映射到少数 bin,量化条纹明显减弱。打印的均值和 RMS 用于比较整体位置和宽度;后者包含道内均匀近似的贡献。
用于后续分析
逐事件处理按“有效性检查 → dithering → 刻度与组合量计算”的顺序进行。先生成并保存各路连续值,再由同一组数值求时间差、平均时间、能量等量,不在每次计算或画图时重新加随机数。固定随机种子便于复现示例。
