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