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    }