diff --git a/ana/pedestal/text-summary.py b/ana/pedestal/text-summary.py index 11c1c54a..4291005c 100644 --- a/ana/pedestal/text-summary.py +++ b/ana/pedestal/text-summary.py @@ -7,7 +7,7 @@ parser.add_argument('pedestals', help='decoded pedestal CSV file to summarize') args = parser.parse_args() -samples = pd.read_csv(args.pedestals).groupby(['link','channel']) +samples = pd.read_csv(args.pedestals).groupby(['i_link','channel']) summary = pd.DataFrame({ 'mean': samples.adc.mean(), @@ -17,4 +17,6 @@ # could sort index so the channels appear in non dictionary order # but can't figure out the correct key function right now -print(summary.to_string()) +# ignore channels where the standard deviation is exactly zero +# these are broken or turned off +print(summary[summary['std'] > 0].to_string()) diff --git a/ana/plot-fw-histograms.py b/ana/plot-fw-histograms.py index 8c4d39c0..59286295 100644 --- a/ana/plot-fw-histograms.py +++ b/ana/plot-fw-histograms.py @@ -19,6 +19,7 @@ parser.add_argument('--stc', type=int, help='only plot input STC if given') parser.add_argument('--roc-adc-th', type=int, help='digitalhalf_#.adc_th setting on the ROCs being used') parser.add_argument('--trigger-th', type=int, help='trigger threshold being used') +parser.add_argument('--raw-counts', action='store_true', help='plot raw counts instead of scaling by collection time to estimate the rate') args = parser.parse_args() if args.output is None: @@ -28,6 +29,8 @@ data = json.load(file, object_hook=uhi.io.json.object_hook) h = hist.Hist(data) +if not args.raw_counts: + h /= data['metadata']['collection_time'] if args.stc is not None and len(h.axes) > 1: h[f'STC{args.stc}',:].plot(yerr = args.error_bars) plt.annotate( @@ -54,5 +57,8 @@ rotation = 90, ) plt.yscale('log') -plt.ylabel('Events / bin') +if args.raw_counts: + plt.ylabel('Events / bin') +else: + plt.ylabel('Rate / Hz') plt.savefig(args.output, bbox_inches='tight') diff --git a/app/tool/trig/FWHistoPool.cxx b/app/tool/trig/FWHistoPool.cxx index 96c15f52..72949a01 100644 --- a/app/tool/trig/FWHistoPool.cxx +++ b/app/tool/trig/FWHistoPool.cxx @@ -62,13 +62,14 @@ std::array FWHistoPool::read(int ihist) { } nlohmann::json FWHistoPool::to_json(const std::array& data, - int ihist) { + int ihist, double collection_time) { nlohmann::json hist; hist["uhi_schema"] = 1; hist["writer_info"]["pftool.FWHistoPool"]["version"] = pflib::version::debug(); hist["writer_info"]["trigpath-firmware"]["version"] = FW_VERSION; hist["metadata"]["_variance_known"] = true; + hist["metadata"]["collection_time"] = collection_time; nlohmann::json axis; axis["type"] = "regular"; @@ -88,19 +89,21 @@ nlohmann::json FWHistoPool::to_json(const std::array& data, axis["metadata"]["label"] = name + " Encoded Sum"; hist["axes"] = {axis}; - hist["storage"]["type"] = "int"; + hist["storage"]["type"] = "double"; hist["storage"]["values"] = data; return hist; } nlohmann::json FWHistoPool::to_json( - const std::array, 8>& data) { + const std::array, 8>& data, + double collection_time) { nlohmann::json hist; hist["uhi_schema"] = 1; hist["writer_info"]["pftool.FWHistoPool"]["version"] = pflib::version::debug(); hist["writer_info"]["trigpath-firmware"]["version"] = FW_VERSION; hist["metadata"]["_variance_known"] = true; + hist["metadata"]["collection_time"] = collection_time; nlohmann::json cat; cat["type"] = "category_str"; @@ -122,7 +125,7 @@ nlohmann::json FWHistoPool::to_json( reg["metadata"]["label"] = "Encoded Sum"; hist["axes"] = {cat, reg}; - hist["storage"]["type"] = "int"; + hist["storage"]["type"] = "double"; hist["storage"]["values"] = data; return hist; } diff --git a/app/tool/trig/FWHistoPool.h b/app/tool/trig/FWHistoPool.h index 07413aca..308ba6e5 100644 --- a/app/tool/trig/FWHistoPool.h +++ b/app/tool/trig/FWHistoPool.h @@ -79,10 +79,13 @@ class FWHistoPool { * * @param[in] hist histogram to serialize into UHI JSON * @param[in] ihist histogram index to include in labeling + * @param[in] collection_time time in s that data was collected, + * included in the histograms 'metadata' in the JSON for scaling + * the plot later if desired * @return JSON representation of histogram */ static nlohmann::json to_json(const std::array& hist, - int ihist); + int ihist, double collection_time); /** * convert the input set of many histograms into a JSON @@ -105,10 +108,14 @@ class FWHistoPool { * ``` * * @param[in] data set of histograms to serialize into JSON + * @param[in] collection_time time in s that data was collected, + * included in the histograms 'metadata' in the JSON for scaling + * the plot later if desired * @return JSON representation of list of histograms */ static nlohmann::json to_json( - const std::array, 8>& data); + const std::array, 8>& data, + double collection_time); }; #endif diff --git a/app/tool/trig/histo.cxx b/app/tool/trig/histo.cxx index b607061f..10622b98 100644 --- a/app/tool/trig/histo.cxx +++ b/app/tool/trig/histo.cxx @@ -3,6 +3,7 @@ */ #include "histo.h" +#include #include #include "FWHistoPool.h" @@ -10,11 +11,30 @@ #include "pflib/utility/string_format.h" using pflib::utility::string_format; +using the_clock = std::chrono::high_resolution_clock; +using a_time_point = std::chrono::time_point; +static std::optional time_of_last_clear{}; + +ENABLE_LOGGING(); + +std::optional get_collection_time(a_time_point now) { + using namespace std::literals; + if (time_of_last_clear) { + return (now - time_of_last_clear.value()) / 1.0s; + } else { + pflib_log(warn) << "There hasn't be a CLEAR recently," + " so we don't know the collection time and" + " the histograms are probably saturated!"; + return {}; + } +} + void histo(const std::string& cmd, Target* tgt) { static FWHistoPool hist_pool{0}; if (cmd == "CLEAR") { hist_pool.clear(); + time_of_last_clear = the_clock::now(); } if (cmd == "DEBUG") { @@ -26,9 +46,24 @@ void histo(const std::string& cmd, Target* tgt) { if (cmd == "READ") { static int ihist = 0; ihist = pftool::readline_int("Which histogram?", ihist); + auto now = the_clock::now(); std::array hist = hist_pool.read(ihist); + std::optional collection_time = get_collection_time(now); + pflib_log(info) << "accumulated histogram for " + << collection_time.value_or(0) << "s"; + bool raw_counts = true; + if (collection_time) { + raw_counts = + pftool::readline_bool("Show raw counts (y) or rate (n)?", raw_counts); + } for (std::size_t i{0}; i < hist.size(); i++) { - printf("%3d %u\n", i, hist[i]); + printf("%3ld ", i); + if (raw_counts) { + printf("%u", hist[i]); + } else { + printf("%0.4e", hist[i] / collection_time.value()); + } + printf("\n"); } if (pftool::readline_bool("Store histogram in JSON file for plotting?", false)) { @@ -38,7 +73,7 @@ void histo(const std::string& cmd, Target* tgt) { if (not file.is_open()) { PFEXCEPTION_RAISE("FileOpen", "Unable to open " + path); } - file << FWHistoPool::to_json(hist, ihist); + file << FWHistoPool::to_json(hist, ihist, collection_time.value_or(0)); } } @@ -47,28 +82,34 @@ void histo(const std::string& cmd, Target* tgt) { // we read BEFORE asking what to write to enable a RESET->DUMP to // be able to happen quickly std::array, 8> hists; - std::array total; - total.fill(0); + auto now = the_clock::now(); for (int ihist{0}; ihist < hists.size(); ihist++) { hists[ihist] = hist_pool.read(ihist); } + std::optional collection_time = get_collection_time(now); + pflib_log(info) << "accumulated histogram for " + << collection_time.value_or(0) << "s"; + if (pftool::readline_bool("Show histograms in terminal?", true)) { + bool raw_counts = true; + if (collection_time) { + raw_counts = pftool::readline_bool("Show raw counts (y) or rate (n)?", + raw_counts); + } printf("bin : %10u %10u %10u %10u %10u %10u %10u %10u\n", 0, 1, 2, 3, 4, 5, 6, 7); for (std::size_t i{0}; i < hists[0].size(); i++) { - printf("%3d :", i); + printf("%3ld :", i); for (int ihist{0}; ihist < hists.size(); ihist++) { - printf(" %10u", hists[ihist][i]); - total[ihist] += hists[ihist][i]; + if (raw_counts) { + printf(" %10u", hists[ihist][i]); + } else { + printf(" %10.4e", hists[ihist][i] / collection_time.value()); + } } printf("\n"); } - printf("tot :"); - for (int ihist{0}; ihist < total.size(); ihist++) { - printf(" %10u", total[ihist]); - } - printf("\n"); } if (pftool::readline_bool("Store histograms in JSON file for plotting?", @@ -78,7 +119,7 @@ void histo(const std::string& cmd, Target* tgt) { if (not file.is_open()) { PFEXCEPTION_RAISE("FileOpen", "Unable to open " + path); } - file << FWHistoPool::to_json(hists); + file << FWHistoPool::to_json(hists, collection_time.value_or(0)); } } } diff --git a/app/tool/trig/trig.cxx b/app/tool/trig/trig.cxx index 50432d2c..9492b86f 100644 --- a/app/tool/trig/trig.cxx +++ b/app/tool/trig/trig.cxx @@ -11,6 +11,7 @@ #include "pflib/packing/SingleECONTCaptureFrame.h" #include "self_trig.h" #include "timein.h" +#include "watch_run.h" using pflib::packing::SingleECONTCaptureFrame; #include @@ -156,6 +157,7 @@ auto menu_trig = "SETUP", "apply time offset parameters deduced from TIMEIN and/or SELF_TRIG", setup) + ->line("WATCH_RUN", "collect data following self-trigger", watch_run) ->line("ELINK_SPY", "spy on the six TRIG elinks", trig) ->line("EVENT_SPY", "attempt to read the last captured event", trig); diff --git a/app/tool/trig/watch_run.cxx b/app/tool/trig/watch_run.cxx new file mode 100644 index 00000000..41fe9a01 --- /dev/null +++ b/app/tool/trig/watch_run.cxx @@ -0,0 +1,162 @@ +#include "watch_run.h" + +#include "decode_multi_sample.h" +#include "pflib/TRIG.h" +#include "pflib/packing/Hex.h" +#include "pflib/packing/MultiSampleECONDEventPacket.h" +#include "pflib/packing/SingleECONTCaptureFrame.h" +#include "pflib/packing/TrigAlgoOutput.h" +#include "pflib/utility/string_format.h" +using pflib::packing::MultiSampleECONDEventPacket; +using pflib::packing::SingleECONTCaptureFrame; + +ENABLE_LOGGING(); + +void watch_run(pflib::Target* tgt) { + auto trig = tgt->trig(); + if (!trig) return; + /** + * TRIG.WATCH_RUN + * + * watch the self trigger and write out the + * data that it collects + */ + + int n_events = pftool::readline_int("Number of events to wait for?", 100); + auto path{pftool::readline_path("watch-run", ".csv")}; + std::ofstream file{path}; + if (not file.is_open()) { + pflib_log(fatal) << "unabel to open " << path; + return; + } + + static int i_roc = pftool::state.iroc; + i_roc = pftool::readline_int("ROC to readout: ", pftool::state.iroc); + + static std::string channel_str = "0,1,2,3,4,5,6,7"; + channel_str = pftool::readline( + "Comma-separated list of channels in that ROC to readout:", channel_str); + std::stringstream channel_stream{channel_str}; + std::string channel; + std::vector channels; + while (getline(channel_stream, channel, ',')) { + channels.push_back(std::stoi(channel)); + } + const auto& mapping{tgt->getRocErxMapping()}; + + file << "i_event,i_sample"; + for (int ch : channels) { + file << ",ch_" << ch << "_Tp" + << ",ch_" << ch << "_Tc" + << ",ch_" << ch << "_adc_tm1" + << ",ch_" << ch << "_adc" + << ",ch_" << ch << "_toa" + << ",ch_" << ch << "_tot"; + } + + // TODO: expand deduction to ECON-T2 EcalSMM + static const std::vector> i_roc_to_stcs = { + {6, 7, 4, 5}, + {3, 2, 1, 0}, + }; + + if (i_roc > 1) { + pflib_log(warn) + << "untested using ECON-T2, will not run without further software dev"; + return; + } + + auto stc_indices = i_roc_to_stcs.at(i_roc); + for (int i_stc : stc_indices) { + file << ",stc" << i_stc; + } + file << '\n'; + + bool l1aen, extl1a; + tgt->fc().fc_enables_read(l1aen, extl1a); + bool og_single_shot = trig->get_enable_single_shot(); + trig->enable_single_shot(true); + tgt->fc().fc_enables(true, true); + + tgt->setup_run(1, Target::DaqFormat::ECOND_SW_HEADERS, 1); + + int self_trigger_count = trig->get_self_trigger_count(); + for (int i_event{0}; i_event < n_events; i_event++) { + if (i_event % 100 == 0 and i_event > 99) { + // status on every 100 events after the first 100 + pflib_log(info) << i_event << " events collected"; + } + + trig->reset_single_shot(); + int i100us{0}; + do { + usleep(100); + i100us++; + } while (not trig->single_shot_fired() and i100us < 10000); + + if (not trig->single_shot_fired()) { + pflib_log(warn) + << "waiting for 1s and did not see a self-trigger, skipping event " + << i_event; + continue; + } + + // capture data output, using daq last to advance readout pointer + std::vector trg_charge_event = trig->read_event(); + std::vector charge_algo_output_raw = trig->read_algo_output(); + std::vector daq_charge_event = tgt->read_event(); + + // decode after capturing all data so decoding errors don't cause + // readout pointer misalignment + std::vector trg_charge = + decode_multi_sample(trig->get_l1a_per_ror(), + trg_charge_event); + + /* + std::vector charge_algo_output = + decode_multi_sample(trig->get_l1a_per_ror(), + charge_algo_output_raw); + */ + + pflib::packing::MultiSampleECONDEventPacket daq_charge(2); + daq_charge.from(daq_charge_event); + + // serialize + for (int i_sample{0}; i_sample < trig->get_l1a_per_ror(); i_sample++) { + file << i_event << ',' << i_sample; + for (int ch : channels) { + auto [i_erx, i_ch] = mapping.toErxChannel(i_roc, ch); + auto sample{daq_charge.samples.at(i_sample).channel(i_erx, i_ch)}; + file << ',' << sample.Tp() << ',' << sample.Tc() << ',' + << sample.adc_tm1() << ',' << sample.adc() << ',' << sample.toa() + << ',' << sample.tot(); + } + for (int i_stc : stc_indices) { + file << ',' << trg_charge[i_sample].stc_sum(i_stc, 0); + } + file << '\n'; + } + + int new_self_trigger_count = trig->get_self_trigger_count(); + if (new_self_trigger_count != self_trigger_count + 1) { + // self trigger counter is 16bits and so we may have wrapped around + // if its getting spammed + int diff{0}; + if (new_self_trigger_count < self_trigger_count) { + // wrap around happend + diff = (0xffff - self_trigger_count) + new_self_trigger_count; + } else { + // no wrap around + diff = new_self_trigger_count - self_trigger_count - 1; + } + pflib_log(info) + << "single-shot gate ignored " << diff + << " self-triggers while acquiring, decoding, and serializing data" + << " for event " << i_event; + } + self_trigger_count = new_self_trigger_count; + } + + tgt->fc().fc_enables(l1aen, extl1a); + trig->enable_single_shot(og_single_shot); +} diff --git a/app/tool/trig/watch_run.h b/app/tool/trig/watch_run.h new file mode 100644 index 00000000..05f9200b --- /dev/null +++ b/app/tool/trig/watch_run.h @@ -0,0 +1,2 @@ +#include "../pftool.h" +void watch_run(pflib::Target* tgt); diff --git a/config/hgcroc/umn-cosmic-init.yaml b/config/hgcroc/umn-cosmic-init.yaml new file mode 100644 index 00000000..e06114c7 --- /dev/null +++ b/config/hgcroc/umn-cosmic-init.yaml @@ -0,0 +1,155 @@ +digitalhalf_0: + adc_th: 16 +ch_0: + adc_pedestal: 210 + trim_inv: 14 +ch_1: + adc_pedestal: 210 + trim_inv: 61 +ch_2: + adc_pedestal: 210 + trim_inv: 59 +ch_3: + adc_pedestal: 210 + trim_inv: 37 +ch_4: + adc_pedestal: 210 + # was not leveled? +ch_5: + adc_pedestal: 210 + trim_inv: 1 +ch_6: + adc_pedestal: 210 + trim_inv: 27 +ch_7: + adc_pedestal: 210 + dacb: 1 + sign_dac: 1 +ch_8: + channel_off: 1 +ch_9: + channel_off: 1 +ch_10: + channel_off: 1 +ch_11: + channel_off: 1 +ch_12: + channel_off: 1 +ch_13: + channel_off: 1 +ch_14: + channel_off: 1 +ch_15: + channel_off: 1 +ch_16: + channel_off: 1 +ch_17: + channel_off: 1 +ch_18: + channel_off: 1 +ch_19: + channel_off: 1 +ch_20: + channel_off: 1 +ch_21: + channel_off: 1 +ch_22: + channel_off: 1 +ch_23: + channel_off: 1 +ch_24: + channel_off: 1 +ch_25: + channel_off: 1 +ch_26: + channel_off: 1 +ch_27: + channel_off: 1 +ch_28: + channel_off: 1 +ch_29: + channel_off: 1 +ch_30: + channel_off: 1 +ch_31: + channel_off: 1 +ch_32: + channel_off: 1 +ch_33: + channel_off: 1 +ch_34: + channel_off: 1 +ch_35: + channel_off: 1 +ch_36: + channel_off: 1 +ch_37: + channel_off: 1 +ch_38: + channel_off: 1 +ch_39: + channel_off: 1 +ch_40: + channel_off: 1 +ch_41: + channel_off: 1 +ch_42: + channel_off: 1 +ch_43: + channel_off: 1 +ch_44: + channel_off: 1 +ch_45: + channel_off: 1 +ch_46: + channel_off: 1 +ch_47: + channel_off: 1 +ch_48: + channel_off: 1 +ch_49: + channel_off: 1 +ch_50: + channel_off: 1 +ch_51: + channel_off: 1 +ch_52: + channel_off: 1 +ch_53: + channel_off: 1 +ch_54: + channel_off: 1 +ch_55: + channel_off: 1 +ch_56: + channel_off: 1 +ch_57: + channel_off: 1 +ch_58: + channel_off: 1 +ch_59: + channel_off: 1 +ch_60: + channel_off: 1 +ch_61: + channel_off: 1 +ch_62: + channel_off: 1 +ch_63: + channel_off: 1 +ch_64: + channel_off: 1 +ch_65: + channel_off: 1 +ch_66: + channel_off: 1 +ch_67: + channel_off: 1 +ch_68: + channel_off: 1 +ch_69: + channel_off: 1 +ch_70: + channel_off: 1 +ch_71: + channel_off: 1