• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Based on sunrisesunsetlib-java:
5  * Copyright 2008-2009 Mike Reedell / LuckyCatLabs.
6  *
7  * Original project and source can be found at:
8  * http://mikereedell.github.com/sunrisesunsetlib-java/
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22 
23 package com.android.wallpaper.grass;
24 
25 import java.util.Calendar;
26 import java.util.TimeZone;
27 
28 import android.location.Location;
29 
30 class SunCalculator {
31     /** Astronomical sunrise/set is when the sun is 18 degrees below the horizon. */
32     static final double ZENITH_ASTRONOMICAL = 108;
33 
34     /** Nautical sunrise/set is when the sun is 12 degrees below the horizon. */
35     static final double ZENITH_NAUTICAL = 102;
36 
37     /** Civil sunrise/set (dawn/dusk) is when the sun is 6 degrees below the horizon. */
38     static final double ZENITH_CIVIL = 96;
39 
40     /** Official sunrise/set is when the sun is 50' below the horizon. */
41     static final double ZENITH_OFFICIAL = 90.8333;
42 
43     private Location mLocation;
44     private TimeZone mTimeZone;
45 
SunCalculator(Location location, String timeZoneIdentifier)46     SunCalculator(Location location, String timeZoneIdentifier) {
47         mLocation = location;
48         mTimeZone = TimeZone.getTimeZone(timeZoneIdentifier);
49     }
50 
setLocation(Location location)51     public void setLocation(Location location) {
52         mLocation = location;
53     }
54 
55     /**
56      * Computes the sunrise time for the given zenith at the given date.
57      *
58      * @param solarZenith <code>Zenith</code> enum corresponding to the type
59      *        of sunrise to compute.
60      * @param date <code>Calendar</code> object representing the date to
61      *        compute the sunrise for.
62      * @return the sunrise time
63      */
computeSunriseTime(double solarZenith, Calendar date)64     public double computeSunriseTime(double solarZenith, Calendar date) {
65         return computeSolarEventTime(solarZenith, date, true);
66     }
67 
68     /**
69      * Computes the sunset time for the given zenith at the given date.
70      *
71      * @param solarZenith <code>Zenith</code> enum corresponding to the type of
72      *        sunset to compute.
73      * @param date <code>Calendar</code> object representing the date to compute
74      *        the sunset for.
75      * @return the sunset time
76      */
computeSunsetTime(double solarZenith, Calendar date)77     public double computeSunsetTime(double solarZenith, Calendar date) {
78         return computeSolarEventTime(solarZenith, date, false);
79     }
80 
timeToHours(double time)81     public static int timeToHours(double time) {
82         int hour = (int) Math.floor(time);
83         int minute = (int) Math.round((time - hour) * 60);
84         if (minute == 60) {
85             hour++;
86         }
87         return hour;
88     }
89 
timeToMinutes(double time)90     public static int timeToMinutes(double time) {
91         int hour = (int) Math.floor(time);
92         int minute = (int) Math.round((time - hour) * 60);
93         if (minute == 60) {
94             minute = 0;
95         }
96         return minute;
97     }
98 
timeToDayFraction(double time)99     public static float timeToDayFraction(double time) {
100         int hour = (int) Math.floor(time);
101         int minute = (int) Math.round((time - hour) * 60);
102         if (minute == 60) {
103             minute = 0;
104             hour++;
105         }
106         return (hour * 60 + minute) / 1440.0f;
107     }
108 
timeToString(double time)109     public static String timeToString(double time) {
110         StringBuffer buffer = new StringBuffer();
111         int hour = (int) Math.floor(time);
112         int minute = (int) Math.round((time - hour) * 60);
113         if (minute == 60) {
114             minute = 0;
115             hour++;
116         }
117         buffer.append(hour).append(':').append(minute < 10 ? "0" + minute : minute);
118         return buffer.toString();
119     }
120 
121     private double computeSolarEventTime(double solarZenith, Calendar date, boolean isSunrise) {
122         date.setTimeZone(mTimeZone);
123         double longitudeHour = getLongitudeHour(date, isSunrise);
124         double meanAnomaly = getMeanAnomaly(longitudeHour);
125         double sunTrueLong = getSunTrueLongitude(meanAnomaly);
126         double cosineSunLocalHour = getCosineSunLocalHour(sunTrueLong, solarZenith);
127         if ((cosineSunLocalHour < -1.0) || (cosineSunLocalHour > 1.0)) {
128             return 0;
129         }
130 
131         double sunLocalHour = getSunLocalHour(cosineSunLocalHour, isSunrise);
132         double localMeanTime = getLocalMeanTime(sunTrueLong, longitudeHour, sunLocalHour);
133         return getLocalTime(localMeanTime, date);
134     }
135 
136     /**
137      * Computes the base longitude hour, lngHour in the algorithm.
138      *
139      * @return the longitude of the location of the solar event divided by 15 (deg/hour), in
140      *         <code>double</code> form.
141      */
142     private double getBaseLongitudeHour() {
143         return mLocation.getLongitude() / 15.0;
144     }
145 
146     /**
147      * Computes the longitude time, t in the algorithm.
148      *
149      * @return longitudinal time in <code>double</code> form.
150      */
151     private double getLongitudeHour(Calendar date, Boolean isSunrise) {
152         int offset = 18;
153         if (isSunrise) {
154             offset = 6;
155         }
156         double dividend = offset - getBaseLongitudeHour();
157         double addend = dividend / 24.0;
158         return getDayOfYear(date) + addend;
159     }
160 
161     /**
162      * Computes the mean anomaly of the Sun, M in the algorithm.
163      *
164      * @return the suns mean anomaly, M, in <code>double</code> form.
165      */
166     private static double getMeanAnomaly(double longitudeHour) {
167         return 0.9856 * longitudeHour - 3.289;
168     }
169 
170     /**
171      * Computes the true longitude of the sun, L in the algorithm, at the given
172      * location, adjusted to fit in the range [0-360].
173      *
174      * @param meanAnomaly the suns mean anomaly.
175      * @return the suns true longitude, in <code>double</code> form.
176      */
177     private static double getSunTrueLongitude(double meanAnomaly) {
178         final double meanRadians = Math.toRadians(meanAnomaly);
179         double sinMeanAnomaly = Math.sin(meanRadians);
180         double sinDoubleMeanAnomaly = Math.sin((meanRadians * 2.0));
181 
182         double firstPart = meanAnomaly + sinMeanAnomaly * 1.916;
183         double secondPart = sinDoubleMeanAnomaly * 0.020 + 282.634;
184         double trueLongitude = firstPart + secondPart;
185 
186         if (trueLongitude > 360) {
187             trueLongitude = trueLongitude - 360.0;
188         }
189         return trueLongitude;
190     }
191 
192     /**
193      * Computes the suns right ascension, RA in the algorithm, adjusting for
194      * the quadrant of L and turning it into degree-hours. Will be in the
195      * range [0,360].
196      *
197      * @param sunTrueLong Suns true longitude, in <code>double</code>
198      * @return suns right ascension in degree-hours, in <code>double</code> form.
199      */
getRightAscension(double sunTrueLong)200     private static double getRightAscension(double sunTrueLong) {
201         double tanL = Math.tan(Math.toRadians(sunTrueLong));
202 
203         double innerParens = Math.toDegrees(tanL) * 0.91764;
204         double rightAscension = Math.atan(Math.toRadians(innerParens));
205         rightAscension = Math.toDegrees(rightAscension);
206 
207         if (rightAscension < 0.0) {
208             rightAscension = rightAscension + 360.0;
209         } else if (rightAscension > 360.0) {
210             rightAscension = rightAscension - 360.0;
211         }
212 
213         double ninety = 90.0;
214         double longitudeQuadrant = (int) (sunTrueLong / ninety);
215         longitudeQuadrant = longitudeQuadrant * ninety;
216 
217         double rightAscensionQuadrant = (int) (rightAscension / ninety);
218         rightAscensionQuadrant = rightAscensionQuadrant * ninety;
219 
220         double augend = longitudeQuadrant - rightAscensionQuadrant;
221         return (rightAscension + augend) / 15.0;
222     }
223 
getCosineSunLocalHour(double sunTrueLong, double zenith)224     private double getCosineSunLocalHour(double sunTrueLong, double zenith) {
225         double sinSunDeclination = getSinOfSunDeclination(sunTrueLong);
226         double cosineSunDeclination = getCosineOfSunDeclination(sinSunDeclination);
227 
228         final double zenithInRads = Math.toRadians(zenith);
229         final double latitude = Math.toRadians(mLocation.getLatitude());
230 
231         double cosineZenith = Math.cos(zenithInRads);
232         double sinLatitude = Math.sin(latitude);
233         double cosLatitude = Math.cos(latitude);
234 
235         double sinDeclinationTimesSinLat = sinSunDeclination * sinLatitude;
236         double dividend = cosineZenith - sinDeclinationTimesSinLat;
237         double divisor = cosineSunDeclination * cosLatitude;
238 
239         return dividend / divisor;
240     }
241 
getSinOfSunDeclination(double sunTrueLong)242     private static double getSinOfSunDeclination(double sunTrueLong) {
243         double sinTrueLongitude = Math.sin(Math.toRadians(sunTrueLong));
244         return sinTrueLongitude * 0.39782;
245     }
246 
getCosineOfSunDeclination(double sinSunDeclination)247     private static double getCosineOfSunDeclination(double sinSunDeclination) {
248         double arcSinOfSinDeclination = Math.asin(sinSunDeclination);
249         return Math.cos(arcSinOfSinDeclination);
250     }
251 
getSunLocalHour(double cosineSunLocalHour, Boolean isSunrise)252     private static double getSunLocalHour(double cosineSunLocalHour, Boolean isSunrise) {
253         double arcCosineOfCosineHourAngle = Math.acos(cosineSunLocalHour);
254         double localHour = Math.toDegrees(arcCosineOfCosineHourAngle);
255         if (isSunrise) {
256             localHour = 360.0 - localHour;
257         }
258         return localHour / 15.0;
259     }
260 
getLocalMeanTime(double sunTrueLong, double longitudeHour, double sunLocalHour)261     private static double getLocalMeanTime(double sunTrueLong, double longitudeHour,
262             double sunLocalHour) {
263 
264         double rightAscension = getRightAscension(sunTrueLong);
265         double innerParens = longitudeHour * 0.06571;
266         double localMeanTime = sunLocalHour + rightAscension - innerParens;
267         localMeanTime = localMeanTime - 6.622;
268 
269         if (localMeanTime < 0.0) {
270             localMeanTime = localMeanTime + 24.0;
271         } else if (localMeanTime > 24.0) {
272             localMeanTime = localMeanTime - 24.0;
273         }
274         return localMeanTime;
275     }
276 
getLocalTime(double localMeanTime, Calendar date)277     private double getLocalTime(double localMeanTime, Calendar date) {
278         double utcTime = localMeanTime - getBaseLongitudeHour();
279         double utcOffSet = getUTCOffSet(date);
280         double utcOffSetTime = utcTime + utcOffSet;
281         return adjustForDST(utcOffSetTime, date);
282     }
283 
adjustForDST(double localMeanTime, Calendar date)284     private double adjustForDST(double localMeanTime, Calendar date) {
285         double localTime = localMeanTime;
286         if (mTimeZone.inDaylightTime(date.getTime())) {
287             localTime++;
288         }
289         if (localTime > 24.0) {
290             localTime = localTime - 24.0;
291         }
292         return localTime;
293     }
294 
295     /**
296      * ****** UTILITY METHODS (Should probably go somewhere else. *****************
297      */
298 
getDayOfYear(Calendar date)299     private static double getDayOfYear(Calendar date) {
300         return date.get(Calendar.DAY_OF_YEAR);
301     }
302 
getUTCOffSet(Calendar date)303     private static double getUTCOffSet(Calendar date) {
304         int offSetInMillis = date.get(Calendar.ZONE_OFFSET);
305         return offSetInMillis / 3600000;
306     }
307 }