3 #include <boost/algorithm/string/join.hpp> 18 po::options_description control(
"Makeschedule");
20 (
"date", var<string>(),
"SQL time (UTC), e.g. '2016-12-24' (equiv. '2016-12-24 12:00:00' to '2016-12-25 11:59:59')")
21 (
"source-database", var<string>()->required(),
"Database link as in\n\tuser:password@server[:port]/database.")
22 (
"schedule-database", var<string>(),
"Database link as in\n\tuser:password@server[:port]/database.")
23 (
"max-current", var<double>(90),
"Global maximum current limit in uA")
24 (
"max-zd", var<double>(75),
"Global zenith distance limit in degree")
25 (
"source", vars<string>(),
"List of all TeV sources to be included, names according to the database")
26 (
"setup.*", var<double>(),
"Setup for the sources to be observed")
27 (
"preobs.*", vars<string>(),
"Prescheduled observations")
28 (
"startup.offset", var<double>(15),
"Determines how many minutes the startup is scheduled before data-taking.start [0;120]")
29 (
"data-taking.start", var<double>(-12),
"Begin of data-taking in degree of sun below horizon")
30 (
"data-taking.end", var<double>(-13.75),
"End of data-taking in degree of sun below horizon")
31 (
"enter-schedule-into-database", var<bool>(),
"Enter schedule into database (required schedule-database, false: dry-run)")
34 po::positional_options_description p;
44 "makeschedule - Creates an automatic schedule\n" 46 "Usage: makeschedule [yyyy-mm-dd]\n";
54 "First for each minute of the night a list is calculated of all " 55 "selected sources fulfiling all global and all source specific " 56 "constraints, e.g. on the zenith distance or the current.\n" 58 "The remaining source list is sorted by the relative threshold, " 59 "while the threshold is weighted with a user defined source " 60 "specific penalty. The first source of the list is taken to " 63 "In a next step the first and last source of the resulting schedule " 64 "are evaluated. If their observation time is below 40', it is tried " 65 "to extend it to 40min. If this violates one of the criteria mentioned " 66 "above or gives an observation time for the neighbouring source of " 67 "less than 40min, try to replace it by the neighbouring source. " 68 "If this also does not fulfil the requirements, the original " 69 "schedule remains unchanged.\n" 71 "Now a similar check is run for all intermediate sources. They are " 72 "checked (from the beginning to the end, one by one), if they have " 73 "an observation time of less than 40min. In this case, it is tried " 74 "to remove them. The observation of the two neighbouring sources is " 75 "extended to their penalized point of equal relative threshold. " 76 "If this solution would not fulfil all criteria, no change is made.\n" 78 "In a last step, all remaining sources with less than 5min " 79 "observation time are replaced with sleep and sleep after startup " 80 "or before shutdown are removed.\n" 85 " makeschedule 2016-12-24\n" 87 "Calculate the Christmas schedule for 2016 using all TeV sources from the\n" 88 "database. If the date is omitted the current date is used.\n" 90 " makeschedule --source='Mrk 421' --source='Mrk 501' --source='Crab'\n" 92 "Use only the mentioned sources to calculate the schedule.\n" 94 " makeschedule --source=Crab --setup.Crab.max-zd=30\n" 96 "Limit the zenith distance of Crab into the range [0;30]deg.\n" 98 " makeschedule --source=Crab --setup.Crab.max-current=50\n" 100 "Limit the maximum estimated current of Crab at 50uA.\n" 102 " makeschedule --source='IC 310' '--setup.IC 310.penalty=1.2'\n" 104 "Multiply IC310's estimated relative threshold by a factor 1.2\n" 117 valid = conf.
Has(str);
119 val = conf.
Get<
double>(
str);
161 double duration()
const {
return end-
begin; };
166 Source(
const string &n=
"", uint16_t k=-1) : name(n), key(k), begin(0), threshold(
std::numeric_limits<double>::max()) { }
170 double zd(
const double &jd)
const 180 if (current>max_current)
183 if (hrz.alt<=0 || 90-hrz.alt>max_zd)
186 if (maxzd.
valid && 90-hrz.alt>maxzd.
val)
189 if (maxcurrent.
valid && current>maxcurrent.
val)
197 const uint32_t n = nearbyint((jd_end-jd_begin)*24*60);
198 for (uint32_t
i=0;
i<n;
i++)
210 const double ratio = pow(cos((90-hrz.alt)*M_PI/180), -2.664);
212 return penalty*ratio*pow(current/6.2, 0.394);
220 if (current>max_current)
223 if (hrz.alt<=0 || 90-hrz.alt>max_zd)
226 if (maxzd.
valid && 90-hrz.alt>maxzd.
val)
229 if (maxcurrent.
valid && current>maxcurrent.
val)
232 const double ratio = pow(cos((90-hrz.alt)*M_PI/180), -2.664);
233 threshold = penalty*ratio*pow(current/6.2, 0.394);
246 if (obs.size()<2 || obs[0].duration()>=40./24/60 || obs[0].name==
"SLEEP" || obs[1].name==
"SLEEP")
249 cout <<
"First source [" << obs[0].name <<
"] detected < 40min" << endl;
251 const double obs1_duration = obs[1].end - obs[0].begin - 40./24/60;
252 const double obs0_end = obs[0].begin + 40./24/60;
259 if (obs1_duration>=40./24/60 && obs[0].IsRangeValid(obs[0].
end, obs0_end))
261 obs[0].end = obs0_end;
262 obs[1].begin = obs0_end;
264 cout <<
"First source [" << obs[0].name <<
"] extended to 40min" << endl;
270 if (obs[1].IsRangeValid(obs[0].
begin, obs[0].end))
272 cout <<
"First source [" << obs[0].name <<
"] removed" << endl;
274 obs[1].begin = obs[0].begin;
275 obs.erase(obs.begin());
281 if (obs[0].IsRangeValid(obs[1].begin, obs[1].end))
283 cout <<
"Second source [" << obs[1].name <<
"] removed" << endl;
285 obs[0].end = obs[1].end;
286 obs.erase(obs.begin()+1);
288 if (obs.size()==0 || obs[0].name!=obs[1].name)
291 obs[0].end = obs[1].end;
292 obs.erase(obs.begin()+1);
294 cout <<
"Combined first two indentical sources [" << obs[0].name <<
"] into one observation" << endl;
299 cout <<
"No reschedule possible within limit." << endl;
308 const int last = obs.size()-1;
309 if (obs.size()<2 || obs[last].duration()>=40./24/60 || obs[last].name==
"SLEEP" || obs[last-1].name==
"SLEEP")
312 cout <<
"Last source [" << obs[last].name <<
"] detected < 40min" << endl;
314 const double obs1_duration = obs[last].end - 40./24/60 - obs[last-1].begin;
315 const double obs0_begin = obs[last].end - 40./24/60;
322 if (obs1_duration>=40./24/60 && obs[last].IsRangeValid(obs0_begin, obs[last].
begin))
324 obs[last].begin = obs0_begin;
325 obs[last-1].end = obs0_begin;
327 cout <<
"Last source [" << obs[last].name <<
"] extended to 40min" << endl;
333 if (obs[last-1].IsRangeValid(obs[last].begin, obs[last].
end))
335 cout <<
"Last source [" << obs[last].name <<
"] removed" << endl;
337 obs[last-1].end = obs[last].end;
344 if (obs[last].IsRangeValid(obs[last-1].begin, obs[last-1].end))
346 cout <<
"Second last source [" << obs[last-1].name <<
"] removed" << endl;
348 obs[last].begin = obs[last-1].begin;
349 obs.erase(obs.begin()+obs.size()-2);
351 if (obs.size()==0 || obs[last-1].name!=obs[last-2].name)
354 obs[last-2].end = obs[last-1].end;
357 cout <<
"Combined last two indentical sources [" << obs[last-1].name <<
"] into one observation" << endl;
362 cout <<
"No reschedule possible within limit." << endl;
369 for (
size_t i=1;
i<obs.size()-1;
i++)
371 if (obs[
i].duration()>=40./24/60)
374 if (obs[
i-1].name==
"SLEEP" && obs[
i+1].name==
"SLEEP")
377 cout <<
"Intermediate source [" << obs[
i].name <<
"] detected < 40min" << endl;
379 double intersection = -1;
381 if (obs[
i-1].name==
"SLEEP")
382 intersection = obs[
i].begin;
384 if (obs[
i+1].name==
"SLEEP")
385 intersection = obs[
i].end;
387 if (obs[
i-1].name==obs[
i+1].name)
388 intersection = obs[
i].begin;
392 const uint32_t n = nearbyint((obs[
i].
end-obs[
i].
begin)*24*60);
393 for (uint32_t ii=0; ii<n; ii++)
395 const double jd = obs[
i].begin+ii/24./60.;
398 if (obs[
i-1].getThreshold(so)>=obs[
i+1].getThreshold(so))
406 if ((obs[
i-1].name!=
"SLEEP" && !obs[
i-1].IsRangeValid(obs[
i-1].
end, intersection)) ||
407 (obs[
i+1].name!=
"SLEEP" && !obs[
i+1].IsRangeValid(intersection, obs[
i+1].
begin)))
409 cout <<
"No reschedule possible within limits." << endl;
413 cout <<
"Intermediate source [" << obs[
i].name <<
"] removed" << endl;
415 const bool underflow = obs[
i-1].duration()*24*60<40 || obs[
i+1].duration()*24*60<40;
417 obs[
i-1].end = intersection;
418 obs[
i+1].begin = intersection;
419 obs.erase(obs.begin()+
i);
423 if (obs.size()>1 && obs[
i].name==obs[
i+1].name)
425 obs[
i].end = obs[
i+1].end;
426 obs.erase(obs.begin()+
i+1);
428 cout <<
"Combined two surrounding indentical sources [" << obs[
i].name <<
"] into one observation" << endl;
436 cout <<
"WARNING - Neighbor source < 40min as well." << endl;
443 for (
size_t i=1;
i<obs.size()-1;
i++)
445 if (obs[
i].duration()>=5./24/60)
448 if (obs[
i-1].name==
"SLEEP" && obs[
i+1].name==
"SLEEP")
451 cout <<
"Mini source [" << obs[
i].name <<
"] detected < 5min" << endl;
453 if (obs[
i-1].name==
"SLEEP" && obs[
i+1].name==
"SLEEP")
455 obs[
i-1].end = obs[
i+2].begin;
457 obs.erase(obs.begin()+
i+1);
458 obs.erase(obs.begin()+
i);
462 cout <<
"Combined two surrounding sleep into one" << endl;
467 if (obs[
i-1].name==
"SLEEP")
469 obs[
i-1].end = obs[
i+1].begin;
470 obs.erase(obs.begin()+
i);
473 cout <<
"Extended previous sleep" << endl;
478 if (obs[
i+1].name==
"SLEEP")
480 obs[
i+1].begin = obs[
i-1].end;
481 obs.erase(obs.begin()+
i);
483 cout <<
"Extended following sleep" << endl;
493 if (obs.front().name==
"SLEEP")
495 obs.erase(obs.begin());
496 cout <<
"Detected sleep after startup... removed." << endl;
499 if (obs.back().name==
"SLEEP")
502 cout <<
"Detected sleep before shutdown... removed." << endl;
506 void Print(
const vector<Source> &obs,
double startup_offset)
509 for (
const auto& src: obs)
512 if (src.preobs.size()>0)
514 for (
const auto& pre: src.preobs)
516 cout << tm <<
" " << pre <<
"\n";
521 cout << tm <<
" " << src.name <<
" [";
522 cout << src.duration()*24*60 <<
"'";
523 if (src.name!=
"SLEEP")
524 cout <<
Tools::Form(
"; %.1f/%.1f", src.zd(src.begin), src.zd(src.end));
527 if (src.duration()*24*60<40)
532 cout <<
Time(obs.back().end).GetAsStr() <<
" SHUTDOWN" << endl;
535 int FillSql(
Database &db,
int enter,
const vector<Source> &obs,
double startup_offset)
537 const string query0 =
"SELECT COUNT(*) FROM Schedule WHERE DATE(ADDTIME(fStart, '-12:00')) = '"+
Time(obs[0].
begin).
GetAsStr(
"%Y-%m-%d")+
"'";
539 const mysqlpp::StoreQueryResult res0 = db.query(query0).store();
541 if (res0.num_rows()!=1)
543 cout <<
"Check for schedule size failed." << endl;
547 if (uint32_t(res0[0][0])!=0)
549 cout <<
"Schedule not empty." << endl;
553 const mysqlpp::StoreQueryResult res1 = db.query(
"SELECT fMeasurementTypeName, fMeasurementTypeKEY FROM MeasurementType").store();
554 map<string, uint32_t> types;
555 for (
const auto &row: res1)
556 types.insert(make_pair(
string(row[0]), uint32_t(row[1])));
559 str <<
"INSERT INTO Schedule (fStart, fUser, fMeasurementID, fMeasurementTypeKEY, fSourceKEY) VALUES ";
561 str <<
"('" <<
Time(obs[0].begin-startup_offset).
GetAsStr() <<
"', 'auto', 0, " << types[
"Startup"] <<
", NULL),\n";
562 for (
const auto& src: obs)
576 if (src.name!=
"SLEEP")
577 str <<
"('" << tm <<
"', 'auto', 0, " << types[
"Data"] <<
", " << src.key <<
"),\n";
579 str <<
"('" << tm <<
"', 'auto', 0, " << types[
"Sleep"] <<
", NULL),\n";
582 str <<
"('" <<
Time(obs.back().end).GetAsStr() <<
"', 'auto', 0, " << types[
"Shutdown"] <<
", NULL)";
586 cout << str.str() << endl;
590 db.query(str.str()).exec();
592 cout <<
"Schedule entered successfully into database." << endl;
596 int main(
int argc,
const char* argv[])
609 const int enter = conf.
Has(
"enter-schedule-into-database") ? (conf.
Get<
bool>(
"enter-schedule-into-database") ? 1 : -1) : 0;
610 if (enter && !conf.
Has(
"schedule-database"))
611 throw runtime_error(
"enter-schedule-into-database required schedule-database.");
614 if (conf.
Has(
"date"))
617 if (enter && floor(time.
JD())<ceil(
Time().
JD()))
618 throw runtime_error(
"Only future schedules can be entered into the database.");
623 const double startup_offset = conf.
Get<
double>(
"startup.offset")/60/24;
625 const double angle_sun_set = conf.
Get<
double>(
"data-taking.start");
626 const double angle_sun_rise = conf.
Get<
double>(
"data-taking.end");
628 if (startup_offset<0 || startup_offset>120)
629 throw runtime_error(
"Only values [0;120] are allowed for startup.offset");
631 if (angle_sun_set>-6)
632 throw runtime_error(
"datataking.start not allowed before sun at -6deg");
634 if (angle_sun_rise>-6)
635 throw runtime_error(
"datataking.end not allowed after sun at -6deg");
643 const double sunset = ceil(sun_set.set*24*60) /24/60 + 1e-9;
644 const double sunrise = floor(sun_rise.rise*24*60)/24/60 + 1e-9;
648 cout <<
"Date: " <<
Time(floor(sunset)).
GetAsStr() <<
"\n";
649 cout <<
"Set: " <<
Time(sunset).
GetAsStr() <<
" [" <<
Time(sun_set.set) <<
"]\n";
650 cout <<
"Rise: " <<
Time(sunrise).
GetAsStr() <<
" [" <<
Time(sun_rise.rise) <<
"]\n";
655 cout <<
"Global zenith distance: " << Source::max_zd <<
" deg\n";
661 const vector<string> ourcenames = conf.
Vec<
string>(
"source");
662 const vector<string> sourcenames = conf.
Vec<
string>(
"source");
663 cout <<
"Nsources = " << sourcenames.size() <<
"\n";
665 string query =
"SELECT fSourceName, fSourceKEY, fRightAscension, fDeclination FROM Source WHERE fSourceTypeKEY=1";
666 if (sourcenames.size()>0)
667 query +=
" AND fSourceName IN ('" + boost::algorithm::join(sourcenames,
"', '")+
"')";
669 const string sourcedb = conf.
Get<
string>(
"source-database");
670 const mysqlpp::StoreQueryResult res =
671 Database(sourcedb).query(query).store();
675 vector<Source> sources;
676 for (
const auto &row: res)
678 const string name = string(row[0]);
682 src.
equ.ra = double(row[2])*15;
683 src.
equ.dec = double(row[3]);
687 src.
penalty = conf.
Has(
"setup."+name+
".penalty") ?
688 conf.
Get<
double>(
"setup."+name+
".penalty") : 1;
690 src.
preobs = conf.
Vec<
string>(
"preobs."+name);
693 cout <<
"[" << name <<
"]";
698 cout <<
" Penalty=" << src.
penalty;
700 cout <<
" " << boost::algorithm::join(src.
preobs,
"+") << endl;
711 sources.emplace_back(src);
719 const uint32_t n = nearbyint((sunrise-sunset)*24*60);
720 for (uint32_t
i=0;
i<n;
i++)
722 const double jd = sunset +
i/24./60.;
727 for (
auto& src: sources)
729 if (src.calcThreshold(so))
730 vis.emplace_back(src);
735 vis.emplace_back(src);
740 if (obs.size()>0 && obs.back().name==vis[0].name)
744 obs.emplace_back(vis[0]);
749 cout <<
"No source found." << endl;
755 for (
auto it=obs.begin(); it<obs.end()-1; it++)
757 obs.back().end = sunrise;
761 Print(obs, startup_offset);
776 Print(obs, startup_offset);
784 const string scheduledb = conf.
Get<
string>(
"schedule-database");
789 db.query(
"LOCK TABLES Schedule WRITE");
791 const int rc =
FillSql(db, enter, obs, startup_offset);
794 db.query(
"UNLOCK TABLES");
bool RescheduleFirstSources(vector< Source > &obs)
void Print(const vector< Source > &obs, double startup_offset)
int main(int argc, const char *argv[])
bool RescheduleIntermediateSources(vector< Source > &obs)
bool SortByThreshold(const Source &i, const Source &j)
MyDouble(Configuration &conf, const string &str)
Adds some functionality to boost::posix_time::ptime for our needs.
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
Source(const string &n="", uint16_t k=-1)
std::vector< T > Vec(const std::string &var)
void SetArgumentPositions(const po::positional_options_description &desc)
bool valid(const SolarObjects &so) const
void CheckStartupAndShutdown(vector< Source > &obs)
bool calcThreshold(const SolarObjects &so)
bool Has(const std::string &var)
void AddOptions(const po::options_description &opt, bool visible=true)
double zd(const double &jd) const
Warning because the service this data corrsponds to might have been last updated longer ago than Local time
void RemoveMiniSources(vector< Source > &obs)
Commandline parsing, resource file parsing and database access.
void SetFromStr(const std::string &str, const char *fmt="%Y-%m-%d %H:%M:%S")
int FillSql(Database &db, int enter, const vector< Source > &obs, double startup_offset)
RstTime GetSolarRst(double jd, const LnLatPosn &obs, double hrz=LN_SOLAR_STANDART_HORIZON)
HrzPosn GetHrzFromEqu(const EquPosn &equ, const LnLatPosn &obs, double jd)
static double max_current
void SetupConfiguration(Configuration &conf)
double getThreshold(const SolarObjects &so) const
std::string GetAsStr(const char *fmt="%Y-%m-%d %H:%M:%S") const
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
double PredictI(const Nova::SolarObjects &so, const Nova::EquPosn &srcEqu)
bool RescheduleLastSources(vector< Source > &obs)
bool IsRangeValid(const double &jd_begin, const double &jd_end) const