61 if (!file.is_open()) {
62 LOGP(error,
"SectorEdgeFluctuations: cannot open file: {}",
filename);
71 while (std::getline(file, line)) {
75 const auto firstNonSpace = line.find_first_not_of(
" \t\r\n");
76 if (firstNonSpace == std::string::npos || line[firstNonSpace] ==
'#') {
81 std::vector<std::string> tokens;
83 std::stringstream ss(line);
85 while (std::getline(ss, tok,
',')) {
87 const auto s = tok.find_first_not_of(
" \t\r\n");
88 const auto e = tok.find_last_not_of(
" \t\r\n");
89 tokens.push_back((s == std::string::npos) ?
"" : tok.substr(s, e - s + 1));
94 if (tokens.size() < 5) {
95 LOGP(warning,
"SectorEdgeFluctuations: skipping malformed line {}", lineNum);
102 run = std::stoi(tokens[0]);
104 LOGP(warning,
"SectorEdgeFluctuations: cannot parse run number on line {}", lineNum);
111 interval.startTimeMS = std::stoll(tokens[1]);
112 interval.endTimeMS = std::stoll(tokens[2]);
114 LOGP(warning,
"SectorEdgeFluctuations: cannot parse timestamps on line {}", lineNum);
119 if (interval.endTimeMS < interval.startTimeMS) {
120 LOGP(warning,
"SectorEdgeFluctuations: end < start on line {}, skipping", lineNum);
126 for (
size_t i = 5;
i < tokens.size(); ++
i) {
127 std::string sectorStr = tokens[
i];
131 const auto eqPos = sectorStr.find(
'=');
132 if (eqPos != std::string::npos) {
134 scale = std::stof(sectorStr.substr(eqPos + 1));
136 LOGP(warning,
"SectorEdgeFluctuations: cannot parse scale in '{}' on line {}, using 1.0", tokens[
i], lineNum);
138 sectorStr = sectorStr.substr(0, eqPos);
143 LOGP(warning,
"SectorEdgeFluctuations: unknown sector '{}' on line {}, skipping token", sectorStr, lineNum);
148 auto dup = std::find_if(interval.sectors.begin(), interval.sectors.end(), [sectorId](
const std::pair<int, float>& p) { return p.first == sectorId; });
149 if (dup != interval.sectors.end()) {
152 interval.sectors.emplace_back(sectorId, scale);
156 if (interval.sectors.empty()) {
159 for (
int s = 0; s < nSec; ++s) {
160 interval.sectors.emplace_back(s, 1.0f);
163 mIntervals[run].push_back(std::move(interval));
168 for (
auto& [run, intervals] : mIntervals) {
170 return a.startTimeMS < b.startTimeMS;
174 LOGP(info,
"SectorEdgeFluctuations: loaded {} intervals for {} run(s) from '{}' ({} lines skipped)", nLoaded, mIntervals.size(),
filename, nSkipped);
180 const auto runIt = mIntervals.find(run);
181 if (runIt == mIntervals.end()) {
189 std::map<int, std::pair<float, Long64_t>> sectorBestScale;
191 const auto& intervals = runIt->second;
192 const auto endIt = std::upper_bound(intervals.begin(), intervals.end(), timestampMS, [](Long64_t ts,
const SectorEdgeInterval& iv) { return ts < iv.startTimeMS; });
194 for (
auto it = intervals.begin(); it != endIt; ++it) {
195 if (it->endTimeMS < timestampMS) {
198 for (
const auto& [sector, scale] : it->sectors) {
199 auto sit = sectorBestScale.find(sector);
200 if (sit == sectorBestScale.end() || it->endTimeMS > sit->second.second) {
201 sectorBestScale[sector] = {scale, it->endTimeMS};
206 std::vector<std::pair<int, float>>
result;
207 result.reserve(sectorBestScale.size());
208 for (
const auto& [sector, scaleAndEnd] : sectorBestScale) {
209 result.emplace_back(sector, scaleAndEnd.first);