// EventTime.cc // Contact person: Gabriel D. Orebi Gann // See EventTime.hh for more details //———————————————————————// /* EventTime.cc * * REVISION HISTORY: * 07-09-2012 : Nuno Barros - Modified the class to no longer load the database in the constructor, * but rather the first time it is accessed. This way, the DATE database field * can be modified in the macro file. It adds a performance penalty * on first "use", but then the performance penalty is negligible. * 12-09-2012 : Nuno Barros - Modified indentation to comply with the rules (2 spaces instead of tab). * 19-10-2016 : Nuno Barros - Explicitly casted pow to take doubles as input to avoid errors in older compilers * - Removed generic calls on CLHEP and std namespace, since they create a lot of shadowing warnings * - Removed superfluous include statements * 2017-09-17 : Anthony LaTorre - Update the integer packing of the date and * time to match the format used by the mtc server in zdab files. */ #include #include extern "C" { #include } namespace RAT { // declare static members DBLinkPtr EventTime::fDateBank; bool EventTime::fDBLoaded = false; int EventTime::fTimeOffsetDaysDB; int EventTime::fTimeOffsetSecsDB; double EventTime::fTimeOffsetNsecsDB; void EventTime::LoadDB() { detail << "EventTime: Loading and caching DB constants" << newline; // Get start time for this run from ratdb // We need to know which run we are simulating fDateBank = DB::Get()->GetLink("RUN"); fTimeOffsetDaysDB = fDateBank->GetI("start_day"); fTimeOffsetSecsDB = fDateBank->GetI("start_sec"); fTimeOffsetNsecsDB = fDateBank->GetD("start_nsc"); fDBLoaded = true; Init(); } EventTime::EventTime() { // Initialize to zero. Will later be loaded the from DB if (!fDBLoaded) { fTimeOffsetDays = 0; fTimeOffsetSecs = 0; fTimeOffsetNsecs = 0.0; } // Offset of 00:00 01 Jan 2010 from JulianDate_count start (which started at noon, hence 0.5) fJDOffset = 2455197.50; fDays = 0; fSecs = 0; fNsecs = 0.; fYear = 0; fMonth = 0; fDay = 0; } void EventTime::Init() { if (!fDBLoaded) LoadDB(); fTimeOffsetDays = fTimeOffsetDaysDB; fTimeOffsetSecs = fTimeOffsetSecsDB; fTimeOffsetNsecs = fTimeOffsetNsecsDB; // Offset of 00:00 01 Jan 2010 from JulianDate_count start (which started at noon, hence 0.5) fJDOffset = 2455197.50; fDays = 0; fSecs = 0; fNsecs = 0.; fYear = 0; fMonth = 0; fDay = 0; } void EventTime::SetStart() { if (!fDBLoaded) LoadDB(); EventTime::Init(); fDays = fTimeOffsetDays; fSecs = fTimeOffsetSecs; fNsecs = fTimeOffsetNsecs; EventTime::SetDate(); } void EventTime::SetTimeEV(RAT::DS::EV *ev) { if (!fDBLoaded) LoadDB(); fDays = ev->GetUniversalTime().GetDays(); fSecs = ev->GetUniversalTime().GetSeconds(); fNsecs = ev->GetUniversalTime().GetNanoSeconds(); } void EventTime::SetTimeMC(RAT::DS::MC *mc) { if (!fDBLoaded) LoadDB(); fDays = mc->GetUniversalTime().GetDays(); fSecs = mc->GetUniversalTime().GetSeconds(); fNsecs = mc->GetUniversalTime().GetNanoSeconds(); } void EventTime::SetDate() { if (!fDBLoaded) LoadDB(); EventTime::GetDateSNO(fDays, fSecs, fNsecs); } void EventTime::IncrementUT(double deltaT) { if (!fDBLoaded) LoadDB(); // Increment ns fNsecs = fNsecs + deltaT; EventTime::RollOver(); EventTime::SetDate(); } void EventTime::IncrementUT(EventTime ev1) { if (!fDBLoaded) LoadDB(); // Increment ns fNsecs = fNsecs + ev1.fNsecs; fSecs = fSecs + ev1.fSecs; fDays = fDays + ev1.fDays; EventTime::RollOver(); EventTime::SetDate(); } // Is this event before ev2? // The event that occurs earlier in time should have a smaller UT bool EventTime::TimeOrder(EventTime ev2) { if (!fDBLoaded) LoadDB(); EventTime differ; int differd = fDays - ev2.fDays; int differs = fSecs - ev2.fSecs; double differns = fNsecs - ev2.fNsecs; differ.SetUTDays(differd); differ.SetUTSecs(differs); differ.SetUTNsecs(differns); differ.RollOver(); if(differ.GetUTDays() > 0)return false; if(differ.GetUTDays() < 0)return true; if(differ.GetUTSecs() > 0)return false; if(differ.GetUTSecs() < 0)return true; if(differ.GetUTNsecs() > 0.)return false; if(differ.GetUTNsecs() < 0.)return true; return false; } bool EventTime::TimeOrder(EventTime *ev2) { if (!fDBLoaded) LoadDB(); EventTime differ; int differd = fDays - ev2->fDays; int differs = fSecs - ev2->fSecs; double differns = fNsecs - ev2->fNsecs; differ.SetUTDays(differd); differ.SetUTSecs(differs); differ.SetUTNsecs(differns); differ.RollOver(); if(differ.GetUTDays() > 0)return false; if(differ.GetUTDays() < 0)return true; if(differ.GetUTSecs() > 0)return false; if(differ.GetUTSecs() < 0)return true; if(differ.GetUTNsecs() > 0.)return false; if(differ.GetUTNsecs() < 0.)return true; return false; } // Roll ns -> s -> days // AND check none of these are negative void EventTime::RollOver() { if (!fDBLoaded) LoadDB(); int rollsecs = 0; int rolldays = 0; rollsecs = (int)(fNsecs/1.0e+09); fNsecs = fNsecs - (double)(1.0e+09*rollsecs); fSecs += rollsecs; rolldays = (int)(fSecs/86400); fSecs = fSecs - (86400*rolldays); fDays += rolldays; if(fNsecs<0.){ fNsecs += 1.0e09; fSecs -= 1; } if(fSecs < 0){ fSecs += 86400; fDays -= 1; } } // Take diff of the times of 2 EventTime objects // NEGATIVE MEANS EV1 CAME FIRST IN TIME EventTime EventTime::CompareUT(EventTime ev1, EventTime ev2) { int diffd, diffs; double diffns; EventTime diff; // Check which event came first in time: bool ev1first = ev1.TimeOrder(ev2); // ev1 came first, so |time diff| = ev2 - ev1 if(ev1first){ diffd = ev2.fDays - ev1.fDays; diffs = ev2.fSecs - ev1.fSecs; diffns = ev2.fNsecs - ev1.fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = -1*diff.GetUTDays(); diffs = -1*diff.GetUTSecs(); diffns = -1.0*diff.GetUTNsecs(); } if(!ev1first){ diffd = ev1.fDays - ev2.fDays; diffs = ev1.fSecs - ev2.fSecs; diffns = ev1.fNsecs - ev2.fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = diff.GetUTDays(); diffs = diff.GetUTSecs(); diffns = diff.GetUTNsecs(); } diff.SetUT(diffd, diffs, diffns); diff.SetDate(); return diff; } // Take diff of the times of this EventTime object from another // Assuming current EventTime occurred after ev2 (otherwise, result is <0) EventTime EventTime::CompareUT(EventTime ev2) { int diffd, diffs; double diffns; EventTime diff; // Check which event came first in time: bool evfirst = EventTime::TimeOrder(ev2); // ev1 came first, so time diff = ev2 - ev1 if(evfirst){ diffd = ev2.fDays - fDays; diffs = ev2.fSecs - fSecs; diffns = ev2.fNsecs - fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = -1*diff.GetUTDays(); diffs = -1*diff.GetUTSecs(); diffns = -1.0*diff.GetUTNsecs(); } if(!evfirst){ diffd = fDays - ev2.fDays; diffs = fSecs - ev2.fSecs; diffns = fNsecs - ev2.fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = diff.GetUTDays(); diffs = diff.GetUTSecs(); diffns = diff.GetUTNsecs(); } diff.SetUT(diffd, diffs, diffns); diff.SetDate(); return diff; } // Same thing for pointers EventTime EventTime::CompareUT(EventTime *ev1, EventTime *ev2) { int diffd, diffs; double diffns; EventTime diff; // Check which event came first in time: bool ev1first = ev1->TimeOrder(ev2); // ev1 came first, so |time diff| = ev2 - ev1 if(ev1first){ diffd = ev2->fDays - ev1->fDays; diffs = ev2->fSecs - ev1->fSecs; diffns = ev2->fNsecs - ev1->fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = -1*diff.GetUTDays(); diffs = -1*diff.GetUTSecs(); diffns = -1.0*diff.GetUTNsecs(); } if(!ev1first){ diffd = ev1->fDays - ev2->fDays; diffs = ev1->fSecs - ev2->fSecs; diffns = ev1->fNsecs - ev2->fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = diff.GetUTDays(); diffs = diff.GetUTSecs(); diffns = diff.GetUTNsecs(); } diff.SetUT(diffd, diffs, diffns); diff.SetDate(); return diff; } EventTime EventTime::CompareUT(EventTime *ev2) { int diffd, diffs; double diffns; EventTime diff; // Check which event came first in time: bool evfirst = EventTime::TimeOrder(ev2); // ev1 came first, so time diff = ev2 - ev1 if(evfirst){ diffd = ev2->fDays - fDays; diffs = ev2->fSecs - fSecs; diffns = ev2->fNsecs - fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = -1*diff.GetUTDays(); diffs = -1*diff.GetUTSecs(); diffns = -1.0*diff.GetUTNsecs(); } if(!evfirst){ diffd = fDays - ev2->fDays; diffs = fSecs - ev2->fSecs; diffns = fNsecs - ev2->fNsecs; diff.SetUT(diffd, diffs, diffns); diff.RollOver(); diffd = diff.GetUTDays(); diffs = diff.GetUTSecs(); diffns = diff.GetUTNsecs(); } diff.SetUT(diffd, diffs, diffns); diff.SetDate(); return diff; } double EventTime::GetFullUTNsecs() { if (!fDBLoaded) LoadDB(); double total = (double)fNsecs + (double)(1.0e+09 * fSecs) + (double)(86400.0 * 1.0e+09 * fDays); return total; } uint32_t EventTime::GetIntDate() { /* Returns the date formatted as an integer. The format is the same as the * mtc server formats the date in the run header record, i.e. * * date |= (year - 1852) << 24; * date |= month << 16; * date |= day << 8; * date |= is daylight savings time ? 1 : 0 * * Note: The funny year 1852 thing is due to a misunderstanding of how the * UNIX broken down time structure worked when writing the mtc server code. * I originally assumed that the year was represented as the calendar year, * and so subtracted the year 2000 from the current year when storing it in * the date so that it was guaranteed to be 8 bytes. However, it turns out * that the UNIX broken down time represents the year as the number of years * since 1900, so what happens when the year is 2017 is: * * tm.tm_year = 117; * rhdr.Date |= ((uint8_t) (tm.tm_year - 2000)) << 24; * * 117 - 2000 = -1883 * * This gets converted to an unsigned byte and becomes: * * (uint8_t) -1883 = 165 * * Therefore, the original year can be calculated as 1852 + 165 = 2017. * * This scheme will work until 2107. */ if (!fDBLoaded) LoadDB(); struct tm tm; tm.tm_year = fYear - 1900; tm.tm_mon = fMonth - 1; tm.tm_mday = fDay; tm.tm_isdst = 0; tm.tm_hour = fSecs/3600; tm.tm_min = (fSecs - tm.tm_hour*3600)/60; tm.tm_sec = fSecs - tm.tm_hour*3600 - tm.tm_min*60; time_t unix_time = timegm(&tm); /* Convert from UTC -> Sudbury Time. */ unix_time -= 5*3600; /* Convert back into broken down time. */ gmtime_r(&unix_time, &tm); uint32_t date = 0; date |= ((uint8_t) (tm.tm_year - 2000)) << 24; date |= ((uint8_t) tm.tm_mon) << 16; date |= ((uint8_t) tm.tm_mday) << 8; date |= ((uint8_t) tm.tm_isdst); return date; } // Return date in format "yyyy / mm / dd" std::string EventTime::GetStrDate() { if (!fDBLoaded) LoadDB(); std::string date; date = dformat("%4i/%2i/%2i",fYear,fMonth,fDay); return date; } uint32_t EventTime::GetIntTime() { /* Returns the time formatted as an integer. The format is the same as the * mtc server formats the time in the run header record, i.e. * * time |= hour << 24; * time |= min << 16; * time |= sec << 8; * * This time format should only be used to set the time in the RAT::DS::Run * object. */ if (!fDBLoaded) LoadDB(); struct tm tm; tm.tm_year = fYear - 1900; tm.tm_mon = fMonth - 1; tm.tm_mday = fDay; tm.tm_isdst = 0; tm.tm_hour = fSecs/3600; tm.tm_min = (fSecs - tm.tm_hour*3600)/60; tm.tm_sec = fSecs - tm.tm_hour*3600 - tm.tm_min*60; time_t unix_time = timegm(&tm); /* Convert from UTC -> Sudbury Time. */ unix_time -= 5*3600; /* Convert back into broken down time. */ gmtime_r(&unix_time, &tm); uint32_t time = 0; time |= ((uint8_t) tm.tm_hour) << 24; time |= ((uint8_t) tm.tm_min) << 16; time |= ((uint8_t) tm.tm_sec) << 8; return time; } // Return time in format "hh:mm:ss:cc" where cc = centisecs std::string EventTime::GetStrTime() { if (!fDBLoaded) LoadDB(); int sec = fSecs; int hour = int(sec/3600); sec = sec - 3600*hour; int min = int(sec/60); sec = sec - 60*min; int cent = int(fNsecs / 1e+07); if(cent>99 || sec>99 || min>99)printf("Something is off in the time conversion\n"); std::string time; time = dformat("%2i:%2i:%2i:%2i",hour,min,sec,cent); return time; } // ******************************************** // Diff options for JD conversion (all agree): // ******************************************** void EventTime::GetDateSNO(int UTdays, int UTsecs, double UTnsecs) { if (!fDBLoaded) LoadDB(); double d2010 = (double)(int)fJDOffset + 1.; double jdd = d2010 + double(UTdays) + double(UTsecs)/86400. + UTnsecs/(86400.*1.0e+09); int i = int(jdd); double a = double(int((i - 1867216.25) /36524.25 )); double b = double(i + 1 + a - int(a/4)); double c = double(b + 1524); double d = double(int((c-122.1)/365.25)); double e = double(int(365.25*d)); double g = double(int((c-e)/30.6001)); int id = (int)(c-e-int(30.6001*g)); int im = 0; if(g<13.5){im = (int)(g-1.);} else{ im = (int)(g-13.);} int iy=0; if(im>=3){ iy = (int)(d-4716.);} else{iy = (int)(d-4715.);} fYear = iy; fMonth = im; fDay = id; } int EventTime::GetUTDay(int year, int month, int day, int secs, double nsecs, int &UTdays, int &UTsecs, double &UTnsecs) { int value = EventTime::GetUTDaySNO(year, month, day, secs, nsecs, UTdays, UTsecs, UTnsecs); return value; } int EventTime::GetUTDaySNO(int year, int month, int day, int secs, double nsecs, int &UTdays, int &UTsecs, double &UTnsecs) { int yy = year; int mm = month; if(month<3){ mm += 12; yy -= 1; } int a = int(yy/100); int leap = 2 -a +int(a/4); int c = int(365.25*yy)-694025; int e = int(30.6001*(mm+1)); int d1900 = 2415019; int julday = leap+c+e+day+d1900; double jd = double(julday) - int(fJDOffset) + double(secs)/86400. + nsecs/(86400.*1.0e+09); UTdays = int(jd); double rem = 86400.0*(jd - double(UTdays)); UTsecs = int(rem); rem = 1.0e9*(rem - UTsecs); UTnsecs = rem; return UTdays; } int EventTime::GetUTDayNAVY(int year, int month, int day, int secs, double nsecs, int &UTdays, int &UTsecs, double &UTnsecs) { int I = year; int J = month; int K = day; int julday = K-32075+1461*(I+4800+(J-14)/12)/4+367*(J-2-(J-14)/12*12) /12-3*((I+4900+(J-14)/12)/100)/4; double jd = double(julday) - int(fJDOffset) -1. + double(secs)/86400. + nsecs/(86400.*1.0e+09); UTdays = int(jd); double rem = 86400.0*(jd - double(UTdays)); UTsecs = int(rem); rem = 1.0e9*(rem - UTsecs); UTnsecs = rem; return UTdays; } } // namespace RAT