1 |
#include "l1menu/tools.h"
|
2 |
|
3 |
#include <sstream>
|
4 |
#include <exception>
|
5 |
#include <map>
|
6 |
#include <stdexcept>
|
7 |
#include <algorithm>
|
8 |
#include "l1menu/ITrigger.h"
|
9 |
#include "l1menu/IEvent.h"
|
10 |
#include "l1menu/TriggerTable.h"
|
11 |
|
12 |
std::vector<std::string> l1menu::getThresholdNames( const l1menu::ITrigger& trigger )
|
13 |
{
|
14 |
std::vector<std::string> returnValue;
|
15 |
|
16 |
//
|
17 |
// I don't know how many thresholds there are, so I'll try and get every possible one and catch
|
18 |
// the exception when I eventually hit a threshold that doesn't exist.
|
19 |
//
|
20 |
|
21 |
std::stringstream stringConverter;
|
22 |
// I plan in the future to implement cross triggers with "leg1threshold1", "leg2threshold1" etcetera,
|
23 |
// so I'll loop over those possible prefixes.
|
24 |
for( size_t legNumber=0; true; ++legNumber ) // Loop continuously until the exception handler calls "break"
|
25 |
{
|
26 |
size_t thresholdNumber;
|
27 |
try
|
28 |
{
|
29 |
// Loop over all possible numbers of thresholds
|
30 |
for( thresholdNumber=1; true; ++thresholdNumber ) // Loop continuously until I hit an exception
|
31 |
{
|
32 |
stringConverter.str("");
|
33 |
if( legNumber!=0 ) stringConverter << "leg" << legNumber; // For triggers with only one leg I don't want to prefix anything.
|
34 |
|
35 |
stringConverter << "threshold" << thresholdNumber;
|
36 |
|
37 |
trigger.parameter(stringConverter.str());
|
38 |
// If the threshold doesn't exist the statement above will throw an exception, so
|
39 |
// I've reached this far then the threshold name must exist.
|
40 |
returnValue.push_back( stringConverter.str() );
|
41 |
}
|
42 |
|
43 |
}
|
44 |
catch( std::exception& error )
|
45 |
{
|
46 |
// If this exception is from the first threshold tried then the prefix (e.g. "leg1") does not
|
47 |
// exist, so I know I've finished. If it isn't from the first threshold then there could be
|
48 |
// other prefixes (e.g. "leg2") that have thresholds that can be modified, in which case I
|
49 |
// need to continue.
|
50 |
if( thresholdNumber==1 && legNumber!=0 ) break;
|
51 |
}
|
52 |
}
|
53 |
|
54 |
return returnValue;
|
55 |
}
|
56 |
|
57 |
std::vector<std::string> l1menu::getNonThresholdParameterNames( const l1menu::ITrigger& trigger )
|
58 |
{
|
59 |
std::vector<std::string> returnValue;
|
60 |
|
61 |
// It'll be easier to get the threshold names and then copy
|
62 |
// everything that's not in there to the return value.
|
63 |
std::vector<std::string> allParameterNames=trigger.parameterNames();
|
64 |
std::vector<std::string> thresholdNames=getThresholdNames(trigger);
|
65 |
|
66 |
for( const auto& parameterName : allParameterNames )
|
67 |
{
|
68 |
const auto& iFindResult=std::find( thresholdNames.begin(), thresholdNames.end(), parameterName );
|
69 |
// If the current parameter name isn't one of the thresholds add
|
70 |
// it the vector which I'm going to return.
|
71 |
if( iFindResult==thresholdNames.end() ) returnValue.push_back(parameterName);
|
72 |
}
|
73 |
|
74 |
return returnValue;
|
75 |
}
|
76 |
|
77 |
void l1menu::setTriggerThresholdsAsTightAsPossible( const l1menu::IEvent& event, l1menu::ITrigger& trigger, float tolerance )
|
78 |
{
|
79 |
std::vector<std::string> thresholdNames=l1menu::getThresholdNames( trigger );
|
80 |
std::map<std::string,float> tightestPossibleThresholds;
|
81 |
|
82 |
// First set all of the thresholds to zero
|
83 |
for( const auto& thresholdName : thresholdNames ) trigger.parameter(thresholdName)=0;
|
84 |
|
85 |
// Now run through each threshold at a time and figure out how low it can be and still
|
86 |
// pass the event.
|
87 |
for( const auto& thresholdName : thresholdNames )
|
88 |
{
|
89 |
// Note that this is a reference, so when this is changed the trigger is modified
|
90 |
float& threshold=trigger.parameter(thresholdName);
|
91 |
|
92 |
float lowThreshold=0;
|
93 |
float highThreshold=500;
|
94 |
// See if an indication of the range of the trigger has been set
|
95 |
try // These calls will throw an exception if no suggestion has been set
|
96 |
{
|
97 |
lowThreshold=l1menu::TriggerTable::instance().getSuggestedLowerEdge( trigger.name(), thresholdName );
|
98 |
highThreshold=l1menu::TriggerTable::instance().getSuggestedUpperEdge( trigger.name(), thresholdName );
|
99 |
highThreshold*=5; // Make sure the high threshold is very high, to catch all tails
|
100 |
}
|
101 |
catch( std::exception& error ) { /* No indication set. Do nothing and just use the defaults I set previously. */ }
|
102 |
|
103 |
threshold=lowThreshold;
|
104 |
bool lowTest=trigger.apply( event );
|
105 |
|
106 |
threshold=highThreshold;
|
107 |
bool highTest=trigger.apply( event );
|
108 |
|
109 |
if( lowTest==highTest ) std::runtime_error( "l1menu::setTriggerThresholdsAsTightAsPossible() - couldn't find a set of thresholds to pass the given event.");
|
110 |
|
111 |
// Try and find the turn on point by bisection
|
112 |
while( highThreshold-lowThreshold > tolerance )
|
113 |
{
|
114 |
threshold=(highThreshold+lowThreshold)/2;
|
115 |
bool midTest=trigger.apply(event);
|
116 |
|
117 |
if( lowTest==midTest && highTest!=midTest ) lowThreshold=threshold;
|
118 |
else if( highTest==midTest ) highThreshold=threshold;
|
119 |
else throw std::runtime_error( std::string("Something fucked up while testing ")+trigger.name() );
|
120 |
}
|
121 |
|
122 |
// Record what this value was for the parameter named
|
123 |
tightestPossibleThresholds[thresholdName]=highThreshold;
|
124 |
// Then set back to zero ready to test the other thresholds
|
125 |
threshold=0;
|
126 |
}
|
127 |
|
128 |
//
|
129 |
// Now that I've figured out what all of the thresholds need to be, run
|
130 |
// through and set the trigger up with these thresholds.
|
131 |
//
|
132 |
for( const auto& parameterValuePair : tightestPossibleThresholds )
|
133 |
{
|
134 |
trigger.parameter(parameterValuePair.first)=parameterValuePair.second;
|
135 |
}
|
136 |
}
|