Skip to content

Commit 285b759

Browse files
authored
Merge branch 'main' into 305-lpgbt-temperature
2 parents 3c02668 + b2e7f4f commit 285b759

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1959
-972
lines changed

CMakeLists.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,25 +114,25 @@ set(pflib_src
114114
src/pflib/GPIO.cxx
115115
src/pflib/Elinks.cxx
116116
src/pflib/Parameters.cxx
117+
src/pflib/DAQ.cxx
117118
src/pflib/lpGBT.cxx
118119
src/pflib/lpgbt/lpGBT_ConfigTransport_I2C.cxx
119120
src/pflib/lpgbt/lpGBT_Registers.cxx
120121
src/pflib/lpgbt/lpGBT_Utility.cxx
121122
src/pflib/lpgbt/lpGBT_standard_configs.cxx
122123
src/pflib/lpgbt/I2C.cxx
123124
src/pflib/lpgbt/GPIO.cxx
124-
src/pflib/TargetFiberless.cxx
125125
src/pflib/GPIO_HcalHGCROCZCU.cxx
126126
src/pflib/FastControlCMS_MMap.cxx
127127
src/pflib/ECOND_Formatter.cxx
128128
src/pflib/zcu/UIO.cxx
129-
src/pflib/zcu/Elinks_zcu.cxx
130129
src/pflib/zcu/lpGBT_ICEC_ZCU_Simple.cxx
131130
src/pflib/zcu/zcu_elinks.cxx
132-
src/pflib/zcu/zcu_DAQ.cxx
131+
src/pflib/zcu/zcu_daq.cxx
133132
src/pflib/zcu/zcu_optolink.cxx
134-
src/pflib/zcu/HcalBackplaneZCU.cxx
135-
src/pflib/zcu/EcalSMMTarget.cxx
133+
src/pflib/zcu/HcalBackplane.cxx
134+
src/pflib/zcu/EcalSMM.cxx
135+
src/pflib/zcu/HGCROCBoardFiberless.cxx
136136
src/pflib/Ecal.cxx
137137
src/pflib/Bias.cxx
138138
)
@@ -144,8 +144,8 @@ if (${Rogue_FOUND})
144144
src/pflib/bittware/bittware_elinks.cxx
145145
src/pflib/bittware/bittware_daq.cxx
146146
src/pflib/bittware/bittware_FastControl.cxx
147-
src/pflib/bittware/HcalBackplaneBittware.cxx
148-
src/pflib/bittware/EcalSMMTarget.cxx
147+
src/pflib/bittware/HcalBackplane.cxx
148+
src/pflib/bittware/EcalSMM.cxx
149149
)
150150
endif()
151151

ana/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# pflib Analysis
22
These analysis scripts are meant to be _simple_ and _fast_.
33

4-
They use `pandas` and `matplotlib`.
4+
They use `pandas`, `matplotlib` and `astropy`.
55
It is **not recommended** to do these analyses on the ZCU itself
66
due to disk space constraints, but you've been warned.
77

88
```
99
python3 -m venv venv
1010
. venv/bin/activate
11-
pip install pandas matplotlib
11+
pip install pandas matplotlib astropy
1212
```

ana/charge/parameter-time-scan.py

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@
1010
import update_path
1111
from plt_gen import plt_gen
1212
from get_params import get_params
13+
from astropy.stats import sigma_clipped_stats
1314

1415
import os
1516

1617
from pathlib import Path
1718
import argparse
1819

1920
import matplotlib.pyplot as plt
21+
import matplotlib.ticker as ticker
22+
23+
from scipy.optimize import curve_fit
2024
import numpy as np
25+
import pandas as pd
2126

2227
from read import read_pflib_csv
2328

@@ -30,7 +35,7 @@
3035
parser.add_argument('-od', '--output_directory', type=Path, help='directory to which to print.')
3136
parser.add_argument('-r', '--repository', type=bool, help='True if you would like to indicate that the input is a repository with csv files rather than a single csv file.')
3237
plot_types = ['SCATTER', 'HEATMAP']
33-
plot_funcs = ['ADC-TIME', 'TOT-TIME', 'TOT', 'TOT-EFF', 'PARAMS', 'MULTI-CHANNEL']
38+
plot_funcs = ['ADC-TIME', 'ADC-ALL-CHANNELS', 'TOT-TIME', 'TOT', 'TOT-EFF', 'PARAMS', 'MULTI-CHANNEL']
3439
parser.add_argument('-pt','--plot_type', choices=plot_types, default=plot_types[0], type=str, help=f'Plotting type. Options: {", ".join(plot_types)}')
3540
parser.add_argument('-pf','--plot_function', choices=plot_funcs, default=plot_types[0], type=str, help=f'Plotting function. Options: {", ".join(plot_types)}')
3641
parser.add_argument('-xl','--xlabel', default='time [ns]', type=str, help=f'What to label the x-axis with.')
@@ -71,10 +76,197 @@ def set_xticks (
7176
xmin = 25*np.floor(xmin/25)
7277
xmax = 25*np.ceil(xmax/25)
7378
ax.set_xticks(ticks = np.arange(xmin, xmax+1, 25), minor=False)
74-
ax.set_xticks(ticks = np.arange(xmin, xmax, 25/16), minor=True)
79+
ax.set_xticks(ticks = np.arange(xmin, xmax, 25/16), minor=True)
80+
81+
#################### HELPER FUNCTIONS ########################
82+
83+
def linear(x, k, m):
84+
return k*x + m
7585

7686
#################### PLOTTING FUNCTIONS ########################
7787

88+
def adc_all_channels(
89+
samples,
90+
run_params,
91+
ax_holder,
92+
plot_params="all", # "all" or "one"
93+
param_index=6 # used only when plot_params="one"
94+
):
95+
"""
96+
Plot max ADC vs channels, compute RMS trends, and fit dependence on parameters.
97+
"""
98+
99+
def compute_rms(ch_df, pedestal):
100+
"""RMS for a single channel dataframe."""
101+
return np.sqrt(((ch_df['adc'] - pedestal)**2).mean())
102+
103+
def compute_all_rms(df, pedestal):
104+
"""Return {channel: rms}"""
105+
rms = df.groupby('channel').apply(lambda c: compute_rms(c, pedestal))
106+
return rms.reset_index(name='rms')
107+
108+
def conditional_legend(ax, max_labels=37, **kwargs):
109+
"""Only draw legend if below threshold."""
110+
handles, labels = ax.get_legend_handles_labels()
111+
if len(labels) < max_labels:
112+
ax.legend(**kwargs)
113+
114+
def save_and_close(fig, fname):
115+
fig.savefig(fname, dpi=400)
116+
plt.close(fig)
117+
118+
charges_group = samples.groupby("nr channels")
119+
fig_rms, ax_rms = plt.subplots(1, 1)
120+
ax_rms.set_xlabel('Channels activated')
121+
ax_rms.set_ylabel('Average RMS of non-activated channels')
122+
123+
link = 0
124+
central_val = 54 if link == 1 else 18
125+
126+
activated_channels_list = []
127+
avg_rms_list = []
128+
129+
runs = len(charges_group)
130+
activated_channels_list = [[] for _ in range(runs - 1)]
131+
avg_rms_list = [[] for _ in range(runs - 1)]
132+
133+
parameter_values = set()
134+
135+
# Loop over charge groups
136+
for i, (charge_id, charge_df) in enumerate(charges_group):
137+
print(f'Running {i+1} out of {runs}')
138+
# Pedestal case
139+
if i == 0:
140+
fig, ax = plt.subplots()
141+
df = charge_df[charge_df['nr channels'] == 0]
142+
143+
df = df[df['channel'] < 36] if link == 0 else df[df['channel'] >= 36]
144+
145+
mean, med, std = sigma_clipped_stats(df['adc'], sigma=3)
146+
pedestal = mean
147+
148+
ax.scatter(df['channel'], df['adc'], s=5, color='r', lw=1)
149+
ax.set_ylabel('ADC')
150+
ax.set_xlabel('Channel')
151+
152+
save_and_close(fig, 'Pedestal.png')
153+
continue
154+
155+
fig, ax1 = plt.subplots()
156+
fig_time, ax_time = plt.subplots()
157+
158+
ax1.set_ylabel(r'$\Delta$ADC')
159+
ax1.set_xlabel('Channel')
160+
ax1.xaxis.set_minor_locator(ticker.AutoMinorLocator())
161+
162+
ax_time.set_ylabel('ADC')
163+
ax_time.set_xlabel('Time')
164+
165+
# Parameter grouping
166+
try:
167+
param_group, param_name = get_params(charge_df, 0)
168+
except Exception:
169+
param_group = [(None, charge_df)]
170+
171+
cmap = plt.get_cmap('viridis')
172+
173+
for j, (param_id, param_df) in enumerate(param_group):
174+
175+
# --- Parameter selection ---
176+
if plot_params == "one":
177+
if j != param_index:
178+
continue
179+
180+
try:
181+
key = param_name.split('.')[1]
182+
val = param_df[param_name].iloc[0]
183+
except Exception:
184+
key = "Channels flashed, voltage [mVpp]"
185+
val = (4, 2000)
186+
187+
parameter_values.add(val)
188+
189+
max_diff = (
190+
param_df
191+
.groupby('channel')['adc']
192+
.apply(lambda s: (s - pedestal).abs().max())
193+
.reset_index(name='max_diff')
194+
)
195+
196+
# Get RMS
197+
rms_df = compute_all_rms(param_df, pedestal)
198+
rms_vals = rms_df['rms'].tolist()
199+
avg_rms = 0
200+
count = 0
201+
for k in range(central_val - 18, central_val + 18):
202+
if central_val - (i - 1) <= k <= central_val + (i - 1):
203+
continue
204+
avg_rms += rms_vals[k]
205+
count += 1
206+
207+
if count > 0:
208+
avg_rms /= count
209+
avg_rms_list[i - 1].append(avg_rms)
210+
activated_channels_list[i - 1].append(i)
211+
212+
merged = max_diff.merge(rms_df, on='channel')
213+
ax1.set_ylim(-10, merged['max_diff'].max() + 10)
214+
ax1.scatter(
215+
merged['channel'], merged['max_diff'],
216+
label=f'{key} = {val}',
217+
s=5, color=cmap(j / len(param_group)), lw=1
218+
)
219+
if link == 0:
220+
link_df = param_df[param_df['channel'] < 36]
221+
elif link == 1:
222+
link_df = param_df[param_df['channel'] >= 36]
223+
for k, (ch_id, ch_df) in enumerate(link_df.groupby('channel')):
224+
ax_time.scatter(
225+
ch_df['time'], ch_df['adc'],
226+
label=f'ch{ch_id}',
227+
s=5, color=plt.get_cmap('tab20')(k / 20)
228+
)
229+
230+
# Pedestal and link lines
231+
ax1.axhline(y=0, color='k', linestyle='--', linewidth=0.8)
232+
ax1.axvline(x=35.5, color='r', linestyle='--', alpha=0.5)
233+
234+
conditional_legend(ax1, fontsize=8)
235+
conditional_legend(ax_time, fontsize=4, ncols=3)
236+
237+
save_and_close(fig, f'adc_channels_{i}.png')
238+
save_and_close(fig_time, f'adc_time_{i}.png')
239+
240+
# slope/intercept plots
241+
activated_T = list(map(list, zip(*activated_channels_list)))
242+
rms_T = list(map(list, zip(*avg_rms_list)))
243+
parameter_values = sorted(parameter_values)
244+
245+
if len(activated_T) > 1:
246+
slopes = []
247+
intercepts = []
248+
249+
for xvals, yvals in zip(activated_T, rms_T):
250+
popt, _ = curve_fit(linear, xvals, yvals)
251+
slopes.append(popt[0])
252+
intercepts.append(popt[1])
253+
254+
ax_rms.scatter(xvals, yvals, s=8)
255+
xfit = np.linspace(min(xvals), max(xvals), 100)
256+
ax_rms.plot(xfit, linear(xfit, *popt),
257+
label=f'Fit: y={popt[0]:.3f}x + {popt[1]:.3f}, CALIB={parameter_values[len(slopes)-1]}')
258+
259+
conditional_legend(ax_rms, fontsize=8)
260+
save_and_close(fig_rms, 'avg_rms.png')
261+
262+
fig_par, ax_par = plt.subplots()
263+
ax_par.scatter(parameter_values, slopes, label='slopes')
264+
ax_par.scatter(parameter_values, intercepts, label='intercepts')
265+
ax_par.legend()
266+
ax_par.set_xlabel('CALIB')
267+
ax_par.set_title('Slope and intercepts from fits')
268+
save_and_close(fig_par, 'slope_intercept.png')
269+
78270
def time(
79271
samples,
80272
run_params,
@@ -374,6 +566,10 @@ def multiparams(
374566

375567
############################## MAIN ###################################
376568

569+
if args.plot_function == 'ADC-ALL-CHANNELS':
570+
plt_gen(adc_all_channels, samples, run_params, args.output,
571+
xlabel = args.xlabel, ylabel = args.ylabel)
572+
377573
if args.plot_function == 'ADC-TIME' and args.plot_type == 'SCATTER':
378574
plt_gen(partial(time, yval = 'adc'), samples, run_params, args.output,
379575
xlabel = args.xlabel, ylabel = args.ylabel)

app/tool/algorithm/get_calibs.cxx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include "get_calibs.h"
2+
3+
#include <algorithm>
4+
#include <cmath>
5+
6+
#include "../daq_run.h"
7+
#include "pflib/utility/efficiency.h"
8+
#include "pflib/utility/string_format.h"
9+
10+
namespace pflib::algorithm {
11+
12+
template <class EventPacket>
13+
std::array<int, 72> get_calibs(Target* tgt, ROC& roc, int& target_adc) {
14+
static auto the_log_{::pflib::logging::get("get_calibs")};
15+
std::array<int, 72> calibs;
16+
/// reserve a vector of the appropriate size to avoid repeating allocation
17+
/// time for all 72 channels
18+
DecodeAndBuffer<EventPacket> buffer{1, 2};
19+
for (int ch{0}; ch < 72; ch++) {
20+
// Set up for highrange charge injection on channel
21+
pflib_log(info) << "Getting calib for channel " << ch;
22+
int i_link = ch / 36;
23+
auto refvol_page =
24+
pflib::utility::string_format("REFERENCEVOLTAGE_%d", i_link);
25+
auto ch_page = pflib::utility::string_format("CH_%d", ch);
26+
// We don't want TOT to trigger because our ADC will go to -1. We therefore
27+
// set the TOA_VREF to 0. This is not pretty but I couldn't find any other
28+
// parameter to set to turn of the TOT overwriting the ADC.
29+
auto test_param_handle = roc.testParameters()
30+
.add(refvol_page, "INTCTEST", 1)
31+
.add(refvol_page, "CHOICE_CINJ", 1)
32+
.add(ch_page, "HIGHRANGE", 1)
33+
.add(refvol_page, "TOA_VREF", 0)
34+
.apply();
35+
// Here we're starting at a value well above the saturation of the adc
36+
// Would prefer to set this in reference to the pedestal for extra safety.
37+
int calib = 1000;
38+
int nr_bx = 3;
39+
while (true) {
40+
pflib_log(info) << "Testing calib = " << calib;
41+
auto calib_handle =
42+
roc.testParameters().add(refvol_page, "CALIB", calib).apply();
43+
usleep(10);
44+
std::vector<int> adcs;
45+
auto central_charge_to_l1a = tgt->fc().fc_get_setup_calib();
46+
// We need to scan different BXs because the max adc is not neccessarilly
47+
// in the first one. Need to add phase scan here as well. Currently the bx
48+
// scan is not working for some reason. Needs a fix.
49+
// for (int bx = 0; bx < nr_bx; bx++) {
50+
// tgt->fc().fc_setup_calib(central_charge_to_l1a + bx);
51+
// usleep(10);
52+
// tgt->daq_run("CHARGE", buffer, 1, 100);
53+
// auto data = buffer.get_buffer();
54+
// for (std::size_t i{0}; i < data.size(); i++) {
55+
// adcs.push_back(data[i].channel(ch).adc());
56+
// }
57+
//}
58+
daq_run(tgt, "CHARGE", buffer, 1, 100);
59+
auto data = buffer.get_buffer();
60+
for (std::size_t i{0}; i < data.size(); i++) {
61+
if constexpr (std::is_same_v<
62+
EventPacket,
63+
pflib::packing::MultiSampleECONDEventPacket>) {
64+
adcs.push_back(
65+
data[i].samples[data[i].i_soi].channel(i_link, ch).adc());
66+
} else if constexpr (std::is_same_v<
67+
EventPacket,
68+
pflib::packing::SingleROCEventPacket>) {
69+
adcs.push_back(data[i].channel(ch).adc());
70+
} else {
71+
PFEXCEPTION_RAISE("BadConf",
72+
"Unable to get adc for the cofigured format");
73+
}
74+
}
75+
int max_adc = *std::max_element(adcs.begin(), adcs.end());
76+
if (std::abs(max_adc - target_adc) <= 2) {
77+
calibs[ch] = calib;
78+
pflib_log(info) << "Final calib = " << calib;
79+
break;
80+
} else if (max_adc > target_adc) {
81+
calib -= 50;
82+
continue;
83+
} else if (max_adc < target_adc) {
84+
calib += 1;
85+
}
86+
}
87+
}
88+
pflib_log(info) << "Calib retrieved for all channels";
89+
return calibs;
90+
}
91+
92+
template std::array<int, 72> get_calibs<pflib::packing::SingleROCEventPacket>(
93+
Target* tgt, ROC& roc, int& target_adc);
94+
95+
template std::array<int, 72>
96+
get_calibs<pflib::packing::MultiSampleECONDEventPacket>(Target* tgt, ROC& roc,
97+
int& targt_adc);
98+
99+
} // namespace pflib::algorithm

0 commit comments

Comments
 (0)