source: branches/FACT++_lidctrl_usb/src/makeschedule.cc@ 19582

Last change on this file since 19582 was 18583, checked in by tbretz, 8 years ago
Fixed some typos in the docu.
File size: 24.2 KB
Line 
1#include "externals/Prediction.h"
2
3#include <boost/algorithm/string/join.hpp>
4
5#include "Database.h"
6
7#include "tools.h"
8#include "Time.h"
9#include "Configuration.h"
10
11using namespace std;
12using namespace Nova;
13
14// -----------------------------------------------------------------------
15
16void SetupConfiguration(Configuration &conf)
17{
18 po::options_description control("Makeschedule");
19 control.add_options()
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)")
32 ;
33
34 po::positional_options_description p;
35 p.add("date", 1); // The first positional options
36
37 conf.AddOptions(control);
38 conf.SetArgumentPositions(p);
39}
40
41void PrintUsage()
42{
43 cout <<
44 "makeschedule - Creates an automatic schedule\n"
45 "\n"
46 "Usage: makeschedule [yyyy-mm-dd]\n";
47 cout << endl;
48}
49
50void PrintHelp()
51{
52#ifdef HAVE_ROOT
53 cout <<
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"
57 "\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 "
61 "be observed.\n"
62 "\n"
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"
70 "\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"
77 "\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"
81 "\n"
82 "\n"
83 "Examples:\n"
84 "\n"
85 " makeschedule 2016-12-24\n"
86 "\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"
89 "\n"
90 " makeschedule --source='Mrk 421' --source='Mrk 501' --source='Crab'\n"
91 "\n"
92 "Use only the mentioned sources to calculate the schedule.\n"
93 "\n"
94 " makeschedule --source=Crab --setup.Crab.max-zd=30\n"
95 "\n"
96 "Limit the zenith distance of Crab into the range [0;30]deg.\n"
97 "\n"
98 " makeschedule --source=Crab --setup.Crab.max-current=50\n"
99 "\n"
100 "Limit the maximum estimated current of Crab at 50uA.\n"
101 "\n"
102 " makeschedule --source='IC 310' '--setup.IC 310.penalty=1.2'\n"
103 "\n"
104 "Multiply IC310's estimated relative threshold by a factor 1.2\n"
105 "\n";
106 cout << endl;
107#endif
108}
109
110
111struct MyDouble
112{
113 double val;
114 bool valid;
115 MyDouble(Configuration &conf, const string &str) : val(0)
116 {
117 valid = conf.Has(str);
118 if (valid)
119 val = conf.Get<double>(str);
120 }
121 MyDouble() : val(0), valid(false) {}
122};
123
124/*
125struct MinMax
126{
127 MyDouble min;
128 MyDouble max;
129 MinMax(Configuration &conf, const string &str)
130 {
131 min = MyDouble(conf, str+".min");
132 max = MyDouble(conf, str+".max");
133 }
134 MinMax() {}
135};
136*/
137
138struct Source
139{
140 // Global limits
141 static double max_current;
142 static double max_zd;
143
144 // Source descrition
145 string name;
146 uint16_t key;
147 EquPosn equ;
148
149 // Source specific limits
150 MyDouble maxzd;
151 MyDouble maxcurrent;
152 double penalty;
153
154 // Possible observation time
155 double begin;
156 double end;
157
158 // Threshold (sorting reference)
159 double threshold;
160
161 double duration() const { return end-begin; };
162
163 // Pre-observations (e.g. ratescan)
164 vector<string> preobs;
165
166 Source(const string &n="", uint16_t k=-1) : name(n), key(k), begin(0), threshold(std::numeric_limits<double>::max()) { }
167
168 //bool IsSpecial() const { return threshold==std::numeric_limits<double>::max(); }
169
170 double zd(const double &jd) const
171 {
172 return 90-GetHrzFromEqu(equ, jd).alt;
173 }
174
175 bool valid(const SolarObjects &so) const
176 {
177 const HrzPosn hrz = GetHrzFromEqu(equ, so.fJD);
178 const double current = FACT::PredictI(so, equ);
179
180 if (current>max_current)
181 return false;
182
183 if (hrz.alt<=0 || 90-hrz.alt>max_zd)
184 return false;
185
186 if (maxzd.valid && 90-hrz.alt>maxzd.val)
187 return false;
188
189 if (maxcurrent.valid && current>maxcurrent.val)
190 return false;
191
192 return true;
193 }
194
195 bool IsRangeValid(const double &jd_begin, const double &jd_end) const
196 {
197 const uint32_t n = nearbyint((jd_end-jd_begin)*24*60);
198 for (uint32_t i=0; i<n; i++)
199 if (!valid(SolarObjects(jd_begin+i/24./60.)))
200 return false;
201
202 return true;
203 }
204
205 double getThreshold(const SolarObjects &so) const
206 {
207 const HrzPosn hrz = GetHrzFromEqu(equ, so.fJD);
208 const double current = FACT::PredictI(so, equ);
209
210 const double ratio = pow(cos((90-hrz.alt)*M_PI/180), -2.664);
211
212 return penalty*ratio*pow(current/6.2, 0.394);
213 }
214
215 bool calcThreshold(const SolarObjects &so)
216 {
217 const HrzPosn hrz = GetHrzFromEqu(equ, so.fJD);
218 const double current = FACT::PredictI(so, equ);
219
220 if (current>max_current)
221 return false;
222
223 if (hrz.alt<=0 || 90-hrz.alt>max_zd)
224 return false;
225
226 if (maxzd.valid && 90-hrz.alt>maxzd.val)
227 return false;
228
229 if (maxcurrent.valid && current>maxcurrent.val)
230 return false;
231
232 const double ratio = pow(cos((90-hrz.alt)*M_PI/180), -2.664);
233 threshold = penalty*ratio*pow(current/6.2, 0.394);
234
235 return true;
236 }
237};
238
239double Source::max_zd;
240double Source::max_current;
241
242bool SortByThreshold(const Source &i, const Source &j) { return i.threshold<j.threshold; }
243
244bool RescheduleFirstSources(vector<Source> &obs)
245{
246 if (obs.size()<2 || obs[0].duration()>=40./24/60 || obs[0].name=="SLEEP" || obs[1].name=="SLEEP")
247 return false;
248
249 cout << "First source [" << obs[0].name << "] detected < 40min" << endl;
250
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;
253
254 // Check that:
255 // - the duration for the shrunken source obs[1] is still above 40min
256 // - obs[0] does not exceed 60deg at the end of its new window
257 // - obs[0] does not exceed any limit within the new window
258
259 if (obs1_duration>=40./24/60 && obs[0].IsRangeValid(obs[0].end, obs0_end))
260 {
261 obs[0].end = obs0_end;
262 obs[1].begin = obs0_end;
263
264 cout << "First source [" << obs[0].name << "] extended to 40min" << endl;
265
266 return false;
267 }
268
269 // Try to remove the first source, check if second source fullfills all limits
270 if (obs[1].IsRangeValid(obs[0].begin, obs[0].end))
271 {
272 cout << "First source [" << obs[0].name << "] removed" << endl;
273
274 obs[1].begin = obs[0].begin;
275 obs.erase(obs.begin());
276
277 return true;
278 }
279
280 // Try to remove the second source, check if the first source fullfills all limits
281 if (obs[0].IsRangeValid(obs[1].begin, obs[1].end))
282 {
283 cout << "Second source [" << obs[1].name << "] removed" << endl;
284
285 obs[0].end = obs[1].end;
286 obs.erase(obs.begin()+1);
287
288 if (obs.size()==0 || obs[0].name!=obs[1].name)
289 return true;
290
291 obs[0].end = obs[1].end;
292 obs.erase(obs.begin()+1);
293
294 cout << "Combined first two indentical sources [" << obs[0].name << "] into one observation" << endl;
295
296 return true;
297 }
298
299 cout << "No reschedule possible within limit." << endl;
300
301 return false;
302}
303
304bool RescheduleLastSources(vector<Source> &obs)
305{
306 // If observation time is smaller than 40min for the first source
307 // extend it to 40min if zenith angle will not go above 60deg.
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")
310 return false;
311
312 cout << "Last source [" << obs[last].name << "] detected < 40min" << endl;
313
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;
316
317 // Check that:
318 // - the duration for the shrunken source obs[1] is still above 40min
319 // - obs[0] does not exceed 60deg at the end of its new window
320 // - obs[0] does not exceed any limit within the new window
321
322 if (obs1_duration>=40./24/60 && obs[last].IsRangeValid(obs0_begin, obs[last].begin))
323 {
324 obs[last].begin = obs0_begin;
325 obs[last-1].end = obs0_begin;
326
327 cout << "Last source [" << obs[last].name << "] extended to 40min" << endl;
328
329 return false;
330 }
331
332 // Try to remove the last source, check if second source fullfills all limits
333 if (obs[last-1].IsRangeValid(obs[last].begin, obs[last].end))
334 {
335 cout << "Last source [" << obs[last].name << "] removed" << endl;
336
337 obs[last-1].end = obs[last].end;
338 obs.pop_back();
339
340 return true;
341 }
342
343 // Try to remove the second last source, check if the first source fullfills all limits
344 if (obs[last].IsRangeValid(obs[last-1].begin, obs[last-1].end))
345 {
346 cout << "Second last source [" << obs[last-1].name << "] removed" << endl;
347
348 obs[last].begin = obs[last-1].begin;
349 obs.erase(obs.begin()+obs.size()-2);
350
351 if (obs.size()==0 || obs[last-1].name!=obs[last-2].name)
352 return true;
353
354 obs[last-2].end = obs[last-1].end;
355 obs.pop_back();
356
357 cout << "Combined last two indentical sources [" << obs[last-1].name << "] into one observation" << endl;
358
359 return true;
360 }
361
362 cout << "No reschedule possible within limit." << endl;
363
364 return false;
365}
366
367bool RescheduleIntermediateSources(vector<Source> &obs)
368{
369 for (size_t i=1; i<obs.size()-1; i++)
370 {
371 if (obs[i].duration()>=40./24/60)
372 continue;
373
374 if (obs[i-1].name=="SLEEP" && obs[i+1].name=="SLEEP")
375 continue;
376
377 cout << "Intermediate source [" << obs[i].name << "] detected < 40min" << endl;
378
379 double intersection = -1;
380
381 if (obs[i-1].name=="SLEEP")
382 intersection = obs[i].begin;
383
384 if (obs[i+1].name=="SLEEP")
385 intersection = obs[i].end;
386
387 if (obs[i-1].name==obs[i+1].name)
388 intersection = obs[i].begin;
389
390 if (intersection<0)
391 {
392 const uint32_t n = nearbyint((obs[i].end-obs[i].begin)*24*60);
393 for (uint32_t ii=0; ii<n; ii++)
394 {
395 const double jd = obs[i].begin+ii/24./60.;
396
397 const SolarObjects so(jd);
398 if (obs[i-1].getThreshold(so)>=obs[i+1].getThreshold(so))
399 {
400 intersection = jd;
401 break;
402 }
403 }
404 }
405
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)))
408 {
409 cout << "No reschedule possible within limits." << endl;
410 continue;
411 }
412
413 cout << "Intermediate source [" << obs[i].name << "] removed" << endl;
414
415 const bool underflow = obs[i-1].duration()*24*60<40 || obs[i+1].duration()*24*60<40;
416
417 obs[i-1].end = intersection;
418 obs[i+1].begin = intersection;
419 obs.erase(obs.begin()+i);
420
421 i--;
422
423 if (obs.size()>1 && obs[i].name==obs[i+1].name)
424 {
425 obs[i].end = obs[i+1].end;
426 obs.erase(obs.begin()+i+1);
427
428 cout << "Combined two surrounding indentical sources [" << obs[i].name << "] into one observation" << endl;
429
430 i--;
431
432 continue;
433 }
434
435 if (underflow)
436 cout << "WARNING - Neighbor source < 40min as well." << endl;
437 }
438 return false;
439}
440
441void RemoveMiniSources(vector<Source> &obs)
442{
443 for (size_t i=1; i<obs.size()-1; i++)
444 {
445 if (obs[i].duration()>=5./24/60)
446 continue;
447
448 if (obs[i-1].name=="SLEEP" && obs[i+1].name=="SLEEP")
449 continue;
450
451 cout << "Mini source [" << obs[i].name << "] detected < 5min" << endl;
452
453 if (obs[i-1].name=="SLEEP" && obs[i+1].name=="SLEEP")
454 {
455 obs[i-1].end = obs[i+2].begin;
456
457 obs.erase(obs.begin()+i+1);
458 obs.erase(obs.begin()+i);
459
460 i -= 2;
461
462 cout << "Combined two surrounding sleep into one" << endl;
463
464 continue;
465 }
466
467 if (obs[i-1].name=="SLEEP")
468 {
469 obs[i-1].end = obs[i+1].begin;
470 obs.erase(obs.begin()+i);
471 i--;
472
473 cout << "Extended previous sleep" << endl;
474
475 continue;
476 }
477
478 if (obs[i+1].name=="SLEEP")
479 {
480 obs[i+1].begin = obs[i-1].end;
481 obs.erase(obs.begin()+i);
482
483 cout << "Extended following sleep" << endl;
484
485 i--;
486 continue;
487 }
488 }
489}
490
491void CheckStartupAndShutdown(vector<Source> &obs)
492{
493 if (obs.front().name=="SLEEP")
494 {
495 obs.erase(obs.begin());
496 cout << "Detected sleep after startup... removed." << endl;
497 }
498
499 if (obs.back().name=="SLEEP")
500 {
501 obs.pop_back();
502 cout << "Detected sleep before shutdown... removed." << endl;
503 }
504}
505
506void Print(const vector<Source> &obs, double startup_offset)
507{
508 cout << Time(obs[0].begin-startup_offset).GetAsStr() << " STARTUP\n";
509 for (const auto& src: obs)
510 {
511 string tm = Time(src.begin).GetAsStr();
512 if (src.preobs.size()>0)
513 {
514 for (const auto& pre: src.preobs)
515 {
516 cout << tm << " " << pre << "\n";
517 tm = " ";
518 }
519 }
520
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));
525 cout << "]";
526
527 if (src.duration()*24*60<40)
528 cout << " (!)";
529
530 cout << "\n";
531 }
532 cout << Time(obs.back().end).GetAsStr() << " SHUTDOWN" << endl;
533}
534
535int FillSql(Database &db, int enter, const vector<Source> &obs, double startup_offset)
536{
537 const string query0 = "SELECT COUNT(*) FROM Schedule WHERE DATE(ADDTIME(fStart, '-12:00')) = '"+Time(obs[0].begin).GetAsStr("%Y-%m-%d")+"'";
538
539 const mysqlpp::StoreQueryResult res0 = db.query(query0).store();
540
541 if (res0.num_rows()!=1)
542 {
543 cout << "Check for schedule size failed." << endl;
544 return 10;
545 }
546
547 if (uint32_t(res0[0][0])!=0)
548 {
549 cout << "Schedule not empty." << endl;
550 return 11;
551 }
552
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])));
557
558 ostringstream str;
559 str << "INSERT INTO Schedule (fStart, fUser, fMeasurementID, fMeasurementTypeKEY, fSourceKEY) VALUES ";
560
561 str << "('" << Time(obs[0].begin-startup_offset).GetAsStr() << "', 'auto', 0, " << types["Startup"] << ", NULL),\n"; // [Startup]\n";
562 for (const auto& src: obs)
563 {
564 string tm = Time(src.begin).GetAsStr();
565
566 /*
567 if (src.preobs.size()>0)
568 {
569 for (const auto& pre: src.preobs)
570 {
571 str << tm << " " << pre << "\n";
572 tm = " ";
573 }
574 }*/
575
576 if (src.name!="SLEEP")
577 str << "('" << tm << "', 'auto', 0, " << types["Data"] << ", " << src.key << "),\n"; // [Data: " << src.name << "]\n";
578 else
579 str << "('" << tm << "', 'auto', 0, " << types["Sleep"] << ", NULL),\n"; // [Sleep]\n";
580 }
581
582 str << "('" << Time(obs.back().end).GetAsStr() << "', 'auto', 0, " << types["Shutdown"] << ", NULL)";// [Shutdown]";
583
584 if (enter<0)
585 {
586 cout << str.str() << endl;
587 return 0;
588 }
589
590 db.query(str.str()).exec();
591
592 cout << "Schedule entered successfully into database." << endl;
593 return 0;
594}
595
596int main(int argc, const char* argv[])
597{
598// gROOT->SetBatch();
599
600 Configuration conf(argv[0]);
601 conf.SetPrintUsage(PrintUsage);
602 SetupConfiguration(conf);
603
604 if (!conf.DoParse(argc, argv, PrintHelp))
605 return 127;
606
607 // ------------------ Eval config ---------------------
608
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.");
612
613 Time time;
614 if (conf.Has("date"))
615 time.SetFromStr(conf.Get<string>("date")+" 12:00:00");
616
617 if (enter && floor(time.JD())<ceil(Time().JD()))
618 throw runtime_error("Only future schedules can be entered into the database.");
619
620 Source::max_current = conf.Get<double>("max-current");
621 Source::max_zd = conf.Get<double>("max-zd");
622
623 const double startup_offset = conf.Get<double>("startup.offset")/60/24;
624
625 const double angle_sun_set = conf.Get<double>("data-taking.start");
626 const double angle_sun_rise = conf.Get<double>("data-taking.end");
627
628 if (startup_offset<0 || startup_offset>120)
629 throw runtime_error("Only values [0;120] are allowed for startup.offset");
630
631 if (angle_sun_set>-6)
632 throw runtime_error("datataking.start not allowed before sun at -6deg");
633
634 if (angle_sun_rise>-6)
635 throw runtime_error("datataking.end not allowed after sun at -6deg");
636
637 // -12: nautical
638 // Sun set with the same date than th provided date
639 // Sun rise on the following day
640 const RstTime sun_set = GetSolarRst(floor(time.JD())-0.5, angle_sun_set);
641 const RstTime sun_rise = GetSolarRst(floor(time.JD())+0.5, angle_sun_rise);
642
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;
645
646 cout << "\n";
647
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";
651
652 cout << "\n";
653
654 cout << "Global maximum current: " << Source::max_current << " uA/pix\n";
655 cout << "Global zenith distance: " << Source::max_zd << " deg\n";
656
657 cout << "\n";
658
659 // ------------- Get Sources from databasse ---------------------
660
661 const vector<string> ourcenames = conf.Vec<string>("source");
662 const vector<string> sourcenames = conf.Vec<string>("source");
663 cout << "Nsources = " << sourcenames.size() << "\n";
664
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, "', '")+"')";
668
669 const string sourcedb = conf.Get<string>("source-database");
670 const mysqlpp::StoreQueryResult res =
671 Database(sourcedb).query(query).store();
672
673 // ------------------ Eval config ---------------------
674
675 vector<Source> sources;
676 for (const auto &row: res)
677 {
678 const string name = string(row[0]);
679
680 Source src(name, row[1]);
681
682 src.equ.ra = double(row[2])*15;
683 src.equ.dec = double(row[3]);
684
685 src.maxzd = MyDouble(conf, "setup."+name+".max-zd");
686 src.maxcurrent = MyDouble(conf, "setup."+name+".max-current");
687 src.penalty = conf.Has("setup."+name+".penalty") ?
688 conf.Get<double>("setup."+name+".penalty") : 1;
689
690 src.preobs = conf.Vec<string>("preobs."+name);
691
692
693 cout << "[" << name << "]";
694
695 if (src.maxzd.valid)
696 cout << " Zd<" << src.maxzd.val;
697 if (src.penalty!=1)
698 cout << " Penalty=" << src.penalty;
699
700 cout << " " << boost::algorithm::join(src.preobs, "+") << endl;
701
702 /*
703 RstTime t1 = GetObjectRst(floor(sunset)-1, src.equ);
704 RstTime t2 = GetObjectRst(floor(sunset), src.equ);
705
706 src.rst.transit = t1.transit<floor(sunset) ? t2.transit : t1.transit;
707 src.rst.rise = t1.rise>src.rst.transit ? t2.rise : t1.rise;
708 src.rst.set = t1.set <src.rst.transit ? t2.set : t1.set;
709 */
710
711 sources.emplace_back(src);
712 }
713 cout << endl;
714
715 // -------------------------------------------------------------------------
716
717 vector<Source> obs;
718
719 const uint32_t n = nearbyint((sunrise-sunset)*24*60);
720 for (uint32_t i=0; i<n; i++)
721 {
722 const double jd = sunset + i/24./60.;
723
724 const SolarObjects so(jd);
725
726 vector<Source> vis;
727 for (auto& src: sources)
728 {
729 if (src.calcThreshold(so))
730 vis.emplace_back(src);
731 }
732
733 // In case no source was found, add a sleep source
734 Source src("SLEEP");
735 vis.emplace_back(src);
736
737 // Source has higher priority if minimum observation time not yet fullfilled
738 sort(vis.begin(), vis.end(), SortByThreshold);
739
740 if (obs.size()>0 && obs.back().name==vis[0].name)
741 continue;
742
743 vis[0].begin = jd;
744 obs.emplace_back(vis[0]);
745 }
746
747 if (obs.size()==0)
748 {
749 cout << "No source found." << endl;
750 return 1;
751 }
752
753 // -------------------------------------------------------------------------
754
755 for (auto it=obs.begin(); it<obs.end()-1; it++)
756 it[0].end = it[1].begin;
757 obs.back().end = sunrise;
758
759 // -------------------------------------------------------------------------
760
761 Print(obs, startup_offset);
762 cout << endl;
763
764 // -------------------------------------------------------------------------
765
766 while (RescheduleFirstSources(obs));
767 while (RescheduleLastSources(obs));
768 while (RescheduleIntermediateSources(obs));
769
770 RemoveMiniSources(obs);
771 CheckStartupAndShutdown(obs);
772
773 // ---------------------------------------------------------------------
774
775 cout << endl;
776 Print(obs, startup_offset);
777 cout << endl;
778
779 // ---------------------------------------------------------------------
780
781 if (!enter)
782 return 0;
783
784 const string scheduledb = conf.Get<string>("schedule-database");
785
786 Database db(scheduledb);
787
788 if (enter>0)
789 db.query("LOCK TABLES Schedule WRITE");
790
791 const int rc = FillSql(db, enter, obs, startup_offset);
792
793 if (enter>0)
794 db.query("UNLOCK TABLES");
795
796 // ---------------------------------------------------------------------
797
798 return rc;
799}
Note: See TracBrowser for help on using the repository browser.