001 /*
002 * $Id: Time.java,v 1.3 2012/02/19 17:35:39 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.data.adde.sgp4;
032
033 import java.text.DateFormat;
034 import java.text.DecimalFormat;
035 import java.text.SimpleDateFormat;
036 import java.util.Calendar;
037 import java.util.GregorianCalendar;
038 import java.util.TimeZone;
039
040 /**
041 * Used to keep track of a time and convert to multiple formats.
042 * @author Shawn E. Gano
043 */
044 public class Time implements java.io.Serializable
045 {
046 // time units
047 /**
048 * year type
049 */
050 public final static int YEAR=Calendar.YEAR;
051 /**
052 * month type
053 */
054 public final static int MONTH=Calendar.MONTH;
055 /**
056 * date type
057 */
058 public final static int DATE=Calendar.DATE;
059 /**
060 * hour type
061 */
062 public final static int HOUR=Calendar.HOUR;
063 /**
064 * Hour type
065 */
066 public final static int HOUR_OF_DAY=Calendar.HOUR_OF_DAY;
067 /**
068 * Minute type
069 */
070 public final static int MINUTE=Calendar.MINUTE;
071 /**
072 * Second type
073 */
074 public final static int SECOND=Calendar.SECOND;
075 /**
076 * Millisecond type
077 */
078 public final static int MILLISECOND=Calendar.MILLISECOND;
079
080 // main calendar
081 private GregorianCalendar currentTime;
082 // other date formats
083 private double mjd; // Modified Julian date
084 private double mjde; // Modified Julian Day Ephemeris time
085
086 // decimal formats
087 private final static DecimalFormat fmt4Dig = new DecimalFormat("0000");
088 private final static DecimalFormat fmt2Dig = new DecimalFormat("00");
089
090 // output format
091 private DateFormat dateFormat=null;
092
093 // time zone
094 private final static TimeZone tz=TimeZone.getTimeZone("UTC"); // default internal timezone
095 private TimeZone tzStringFormat = TimeZone.getTimeZone("UTC"); // SEG removed static April 16 2009
096
097
098 public static void main(String args[])
099 {
100 Time t = new Time(2007,9,1,11,59,0.0);
101 System.out.println("Jul Date:" + t.getJulianDate() + ", string:" + t.getDateTimeStr());
102 // System.out.println("MJD :" + t.getMJD());
103 // System.out.println("MJDE :" + t.getMJDE());
104 // System.out.println("DT: " + t.deltaT( t.getMJD() )*60*60*24 );
105 // 2454344.5
106 t.addSeconds(120.0); // add 60 sec
107 System.out.println("Jul Date + 120 sec:" + t.getJulianDate()+ ", string:" + t.getDateTimeStr());
108
109 t.add(Time.HOUR,12);
110 System.out.println("Jul Date + 12 hour:" + t.getJulianDate()+ ", string:" + t.getDateTimeStr());
111
112 }
113
114
115 /**
116 * Default Constructor
117 */
118 public Time()
119 {
120 // create new calendar with default timezone
121 currentTime = new GregorianCalendar(tz);
122
123 // update other time formates
124 updateTimeMeasures();
125 }
126
127
128 /**
129 * Constructor with given calendar date (UT)
130 *
131 * @param year year
132 * @param month month (1-12)
133 * @param day day of month
134 * @param hour hour 0-24
135 * @param min minute
136 * @param sec second and fraction of second (accurate up to 1 millisecond)
137 */
138 public Time(int year, int month, int day, int hour, int min, double sec)
139 {
140 int secInt = new Double( Math.floor(sec) ).intValue();
141 int millisec = new Double( Math.round((sec - Math.floor(sec))*1000.0) ).intValue();
142
143 currentTime = new GregorianCalendar(tz); // set default timezone
144
145 currentTime.set(Calendar.YEAR,year);
146 currentTime.set(Calendar.MONTH,month-1);
147 currentTime.set(Calendar.DATE,day);
148 currentTime.set(Calendar.HOUR_OF_DAY,hour);
149 currentTime.set(Calendar.MINUTE,min);
150 currentTime.set(Calendar.SECOND,secInt);
151 currentTime.set(Calendar.MILLISECOND,millisec);
152
153 // update other time formats
154 updateTimeMeasures();
155 }
156
157
158 /**
159 * Updates the time to current system time
160 */
161 public void update2CurrentTime()
162 {
163 // update to current time (which is faster?)
164 //currentTime =new GregorianCalendar(tz);
165 currentTime.setTimeInMillis( System.currentTimeMillis() );
166 //currentTime.setTime( new Date() );
167
168 // update other time formats
169 updateTimeMeasures();
170 } // update2CurrentTime
171
172 /**
173 * Set the current time (UT) to the number of milliseconds
174 * @param milliseconds number of millisconds as in from the function Calendar.getTimeInMillis()
175 */
176 public void set(long milliseconds)
177 {
178 currentTime.setTimeInMillis(milliseconds);
179
180 // update other time formats
181 updateTimeMeasures();
182 } // set
183
184 /**
185 * Add specified value in specified time unit to current time
186 *
187 * @param unit int Time unit
188 * @param val int Time increment
189 */
190 public void add(int unit, int val)
191 {
192 currentTime.add(unit, val);
193
194 // update other time formats
195 updateTimeMeasures();
196 }
197
198 /**
199 * Add specified seconds to current time
200 *
201 * @param seconds number of seconds to add to current time (can be fractional)
202 */
203 public void addSeconds(double seconds)
204 {
205 // multiply input by 1000 then round off and add this number of milliseconds to date
206 int millis2Add = new Double(Math.round( seconds*1000 )).intValue();
207
208 currentTime.add(Calendar.MILLISECOND, millis2Add);
209
210 // update other time formats
211 updateTimeMeasures();
212 }
213
214 /**
215 * Updates the Julian and Julian Epehermis Dates using Current GregorianCalendar
216 */
217 private void updateTimeMeasures()
218 {
219 mjd = calcMjd(currentTime);
220 mjde = mjd + deltaT(mjd);
221 }
222
223 /**
224 * Gets the Julian Date (UT)
225 * @return Returns the Julian Date
226 */
227 public double getJulianDate()
228 {
229 return mjd + 2400000.5;
230 }
231
232 /**
233 * Gets the Modified Julian Date (Julian date minus 2400000.5) (UT)
234 * @return Returns the Modified Julian Date
235 */
236 public double getMJD()
237 {
238 return mjd ;
239 }
240
241 /**
242 * Gets the Modified Julian Ephemeris Date (Julian date minus 2400000.5) (TT)
243 * @return Returns the Modified Julian Ephemeris Date
244 */
245 public double getMJDE()
246 {
247 return mjde;
248 }
249
250 /**
251 * Sets timezone for the output string to use via the function getDateTimeStr()
252 * @param aTzStringFormat time zone to format output strings with
253 */
254 public void setTzStringFormat(TimeZone aTzStringFormat)
255 {
256 tzStringFormat = aTzStringFormat;
257 }
258
259
260 /**
261 * Set SimpleDateFormat for displaying date/time string
262 * @param dateFormat SimpleDateFormat
263 */
264 public void setDateFormat(SimpleDateFormat dateFormat)
265 {
266 this.dateFormat=dateFormat;
267 }
268
269 /**
270 * Set SimpleDateFormat string
271 * ISSUE - only valid after Jan 1, 1970
272 *@param formatStr String format for simple date format to use for creating strings of the date
273 */
274 public void setDateFormat(java.lang.String formatStr)
275 {
276 if ((formatStr!=null)&&(formatStr.length()>0))
277 {
278 dateFormat=new SimpleDateFormat(formatStr);
279 }
280 }
281
282 /**
283 * Gets the date format
284 * @return date format
285 */
286 public DateFormat getDateFormat()
287 {
288 return dateFormat;
289 }
290
291 /**
292 * Returns the specified field
293 * @param field int The specified field
294 * @return int The field value
295 */
296 public final int get(int field)
297 {
298 return currentTime.get(field);
299 }
300
301 /*
302 * Get the UTC date/time string in the format of yyyy-mm-dd hh:mm:ss
303 * If the dateFormat is not set or if the date is before Jan 1, 1970
304 * otherwise the empty string "" will be returned.)
305 * @return java.lang.String
306 */
307 public String getDateTimeStr()
308 {
309 String retStr="";
310
311 if ((dateFormat!=null) &&( getJulianDate() >= 2440587.5))
312 {
313 dateFormat.setTimeZone(tzStringFormat);
314 retStr=dateFormat.format( currentTime.getTime() );
315 }
316 else
317 {
318 StringBuffer strBuf = new StringBuffer(fmt4Dig.format(get(Time.YEAR)));
319 strBuf.append("-");
320 strBuf.append(fmt2Dig.format(get(Time.MONTH)+1));
321 strBuf.append("-");
322 strBuf.append(fmt2Dig.format(get(Time.DATE)));
323 strBuf.append(" ");
324 strBuf.append(fmt2Dig.format(get(Time.HOUR_OF_DAY)));
325 strBuf.append(":");
326 strBuf.append(fmt2Dig.format(get(Time.MINUTE)));
327 strBuf.append(":");
328 strBuf.append(fmt2Dig.format(get(Time.SECOND)));
329 strBuf.append(" ");
330 strBuf.append( tz.getID() );
331 retStr=strBuf.toString();
332 }
333 return retStr;
334 }
335
336 // ============================== STATIC Functions ====================================
337
338 /**
339 * Calculate Modified Julian Date from calendar object
340 *
341 * @param cal Calendar object
342 * @return Modified Julian Date (UT)
343 */
344 public static double calcMjd(Calendar cal)
345 {
346 double sec = cal.get(Calendar.SECOND) + cal.get(Calendar.MILLISECOND)/1000.0;
347 return calcMjd(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH)+1,cal.get(Calendar.DATE),cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), sec);
348 }
349
350 /**
351 * Calculate Modified Julian Date from calendar date and time elements
352 *
353 * @param year calendar year
354 * @param month calendar month
355 * @param day calendar day
356 * @param hour calendar hour (0-24)
357 * @param min calendar min
358 * @param sec calendar sec
359 * @return Modified Julian Date (UT)
360 */
361 public static double calcMjd(int year, int month, int day, int hour, int min, double sec)
362 {
363 // Variables
364 long MjdMidnight;
365 double FracOfDay;
366 int b;
367
368 if (month<=2)
369 { month+=12; --year;}
370
371 if ( (10000L*year+100L*month+day) <= 15821004L )
372 {
373 b = -2 + ((year+4716)/4) - 1179; // Julian calendar
374 }
375 else
376 {
377 b = (year/400)-(year/100)+(year/4); // Gregorian calendar
378 }
379
380 MjdMidnight = 365L*year - 679004L + b + (int) (30.6001*(month+1)) + day;
381 FracOfDay = (hour+min/60.0+sec/3600.0) / 24.0;
382
383 return MjdMidnight + FracOfDay;
384 } //calcMjd
385
386
387 /**
388 * Return TT minus UT.
389 *
390 * <p>Up to 1983 Ephemeris Time (ET) was used in place of TT, between
391 * 1984 and 2000 Temps Dynamique Terrestrial (TDT) was used in place of TT.
392 * The three time scales, while defined differently, form a continuous time
393 * scale for most purposes. TT has a fixed offset from TAI (Temps Atomique
394 * International).
395 *
396 * <p>This method returns the difference TT - UT in days. Usually this
397 * would be looked up in a table published after the fact. Here we use
398 * polynomial fits for the distant past, for the future and also for the
399 * time where the table exists. Except for 1987 to 2015, the expressions
400 * are taken from
401 * Jean Meeus, 1991, <I>Astronomical Algorithms</I>, Willmann-Bell, Richmond VA, p.73f.
402 * For the present (1987 to 2015 we use our own graphical linear fit to the
403 * data 1987 to 2001 from
404 * USNO/RAL, 2001, <I>Astronomical Almanach 2003</I>, U.S. Government Printing Office, Washington DC, Her Majesty's Stationery Office, London, p.K9:
405 *
406 * <p>t = Ep - 2002
407 * <p>DeltaT/s = 9.2 * t / 15 + 65
408 *
409 * <p>Close to the present (1900 to 1987) we use Schmadl and Zech:
410 *
411 * <p>t = (Ep - 1900) / 100
412 * <p>DeltaT/d = -0.000020 + 0.000297 * t
413 * + 0.025184 * t<sup>2</sup> - 0.181133 * t<sup>3</sup><BR>
414 * + 0.553040 * t<sup>4</sup> - 0.861938 * t<sup>5</sup>
415 * + 0.677066 * t<sup>6</sup> - 0.212591 * t<sup>7</sup>
416 *
417 * <p>This work dates from 1988 and the equation is supposed to be valid only
418 * to 1987, but we extend its use into the near future. For the 19th
419 * century we use Schmadl and Zech:
420 *
421 * <p>t = (Ep - 1900) / 100
422 * <p>DeltaT/d = -0.000009 + 0.003844 * t
423 * + 0.083563 * t<sup>2</sup> + 0.865736 * t<sup>3</sup><BR>
424 * + 4.867575 * t<sup>4</sup> + 15.845535 * t<sup>5</sup>
425 * + 31.332267 * t<sup>6</sup> + 38.291999 * t<sup>7</sup><BR>
426 * + 28.316289 * t<sup>8</sup> + 11.636204 * t<sup>9</sup>
427 * + 2.043794 * t<sup>10</sup>
428 *
429 * <p>Stephenson and Houlden are credited with the equations for times before
430 * 1600. First for the period 948 to 1600:
431 *
432 * <p>t = (Ep - 1850) / 100
433 * <p>DeltaT/s = 22.5 * t<sup>2</sup>
434 *
435 * <p>and before 948:
436 *
437 * <p>t = (Ep - 948) / 100
438 * <p>DeltaT/s = 1830 - 405 * t + 46.5 * t<sup>2</sup>
439 *
440 * <p>This leaves no equation for times between 1600 and 1800 and beyond
441 * 2015. For such times we use the equation of Morrison and Stephenson:
442 *
443 * <p>t = Ep - 1810
444 * <p>DeltaT/s = -15 + 0.00325 * t<sup>2</sup>
445 *
446 * @param givenMJD Modified Julian Date (UT)
447 * @return TT minus UT in days
448 */
449
450 public static double deltaT(double givenMJD)
451 {
452 double theEpoch; /* Julian Epoch */
453 double t; /* Time parameter used in the equations. */
454 double D; /* The return value. */
455
456 givenMJD -= 50000;
457
458 theEpoch = 2000. + (givenMJD - 1545.) / 365.25;
459
460 /* For 1987 to 2015 we use a graphical linear fit to the annual tabulation
461 * from USNO/RAL, 2001, Astronomical Almanach 2003, p.K9. We use this up
462 * to 2015 about as far into the future as it is based on data in the past.
463 * The result is slightly higher than the predictions from that source. */
464
465 if (1987 <= theEpoch && 2015 >= theEpoch)
466 {
467 t = (theEpoch - 2002.);
468 D = 9.2 * t / 15. + 65.;
469 D /= 86400.;
470 }
471
472 /* For 1900 to 1987 we use the equation from Schmadl and Zech as quoted in
473 * Meeus, 1991, Astronomical Algorithms, p.74. This is precise within
474 * 1.0 second. */
475
476 else if (1900 <= theEpoch && 1987 > theEpoch)
477 {
478 t = (theEpoch - 1900.) / 100.;
479 D = -0.212591 * t * t * t * t * t * t * t
480 + 0.677066 * t * t * t * t * t * t
481 - 0.861938 * t * t * t * t * t
482 + 0.553040 * t * t * t * t
483 - 0.181133 * t * t * t
484 + 0.025184 * t * t
485 + 0.000297 * t
486 - 0.000020;
487 }
488
489 /* For 1800 to 1900 we use the equation from Schmadl and Zech as quoted in
490 * Meeus, 1991, Astronomical Algorithms, p.74. This is precise within 1.0
491 * second. */
492
493 else if (1800 <= theEpoch && 1900 > theEpoch)
494 {
495 t = (theEpoch - 1900.) / 100.;
496 D = 2.043794 * t * t * t * t * t * t * t * t * t * t
497 + 11.636204 * t * t * t * t * t * t * t * t * t
498 + 28.316289 * t * t * t * t * t * t * t * t
499 + 38.291999 * t * t * t * t * t * t * t
500 + 31.332267 * t * t * t * t * t * t
501 + 15.845535 * t * t * t * t * t
502 + 4.867575 * t * t * t * t
503 + 0.865736 * t * t * t
504 + 0.083563 * t * t
505 + 0.003844 * t
506 - 0.000009;
507 }
508
509 /* For 948 to 1600 we use the equation from Stephenson and Houlden as
510 * quoted in Meeus, 1991, Astronomical Algorithms, p.73. */
511
512 else if (948 <= theEpoch && 1600 >= theEpoch)
513 {
514 t = (theEpoch - 1850.) / 100.;
515 D = 22.5 * t * t;
516 D /= 86400.;
517 }
518
519 /* Before 948 we use the equation from Stephenson and Houlden as quoted
520 * in Meeus, 1991, Astronomical Algorithms, p.73. */
521
522 else if (948 > theEpoch)
523 {
524 t = (theEpoch - 948.) / 100.;
525 D = 46.5 * t * t - 405. * t + 1830.;
526 D /= 86400.;
527 }
528
529 /* Else (between 1600 and 1800 and after 2010) we use the equation from
530 * Morrison and Stephenson, quoted as eqation 9.1 in Meeus, 1991,
531 * Astronomical Algorithms, p.73. */
532
533 else
534 {
535 t = theEpoch - 1810.;
536 D = 0.00325 * t * t - 15.;
537 D /= 86400.;
538 }
539
540 return D; // in days
541 } // deltaT
542
543 public GregorianCalendar getCurrentGregorianCalendar()
544 {
545 return currentTime;
546 }
547
548 // function to take a given Julian date and parse it to a Gerorian Calendar
549 public static GregorianCalendar convertJD2Calendar(double jd)
550 {
551 /**
552 * Calculate calendar date for Julian date field this.jd
553 */
554 Double jd2 = new Double(jd + 0.5);
555 long I = jd2.longValue();
556 double F = jd2.doubleValue() - (double) I;
557 long A = 0;
558 long B = 0;
559
560 if (I > 2299160)
561 {
562 Double a1 = new Double(((double) I - 1867216.25) / 36524.25);
563 A = a1.longValue();
564 Double a3 = new Double((double) A / 4.0);
565 B = I + 1 + A - a3.longValue();
566 }
567 else
568 {
569 B = I;
570 }
571
572 double C = (double) B + 1524;
573 Double d1 = new Double((C - 122.1) / 365.25);
574 long D = d1.longValue();
575 Double e1 = new Double(365.25 * (double) D);
576 long E = e1.longValue();
577 Double g1 = new Double((double) (C - E) / 30.6001);
578 long G = g1.longValue();
579 Double h = new Double((double) G * 30.6001);
580 long da = (long) C - E - h.longValue();
581
582 Integer date = new Integer((int) da); // DATE
583
584 Integer month;
585 Integer year;
586
587 if (G < 14L)
588 {
589 month = new Integer((int) (G - 2L));
590 }
591 else
592 {
593 month = new Integer((int) (G - 14L));
594 }
595
596 if (month.intValue() > 1)
597 {
598 year = new Integer((int) (D - 4716L));
599 }
600 else
601 {
602 year = new Integer((int) (D - 4715L));
603 }
604
605 // Calculate fractional part as hours, minutes, and seconds
606 Double dhr = new Double(24.0 * F);
607 Integer hour = new Integer(dhr.intValue());
608 Double dmin = new Double((dhr.doubleValue() - (double) dhr.longValue()) * 60.0);
609 Integer minute = new Integer(dmin.intValue());
610
611 Double dsec = new Double((dmin.doubleValue() - (double) dmin.longValue()) * 60.0);
612 Integer second=new Integer(dsec.intValue());
613
614 //int ms = (int)((dsec.doubleValue() - (double) second.longValue()) * 1000.0);
615 // rounding fix - e-mailed to SEG by Hani A. Altwaijry 28 May 2009
616 int ms = (int) Math.round((dsec.doubleValue() - (double) second.longValue()) * 1000.0);
617
618 // create Calendar object
619 GregorianCalendar newTime = new GregorianCalendar(tz); // set default timezone
620
621 newTime.set(Calendar.YEAR,year);
622 newTime.set(Calendar.MONTH,month);
623 newTime.set(Calendar.DATE,date);
624 newTime.set(Calendar.HOUR_OF_DAY,hour);
625 newTime.set(Calendar.MINUTE,minute);
626 newTime.set(Calendar.SECOND,second);
627 newTime.set(Calendar.MILLISECOND,ms);
628
629 return newTime;
630
631 } // convertJD2Calendar
632
633 // function used to take a given Julian Date and parse it as a string using the current settings
634 public String convertJD2String(double jd)
635 {
636 // convert to calendar
637 GregorianCalendar newTime = convertJD2Calendar(jd);
638
639 // format as String -- ASSUMES dateFromat is not NULL!!
640 dateFormat.setTimeZone(tzStringFormat);
641 String retStr=dateFormat.format( newTime.getTime() );
642
643 return retStr;
644
645
646 } // convertJD2String
647
648
649
650
651 }