1863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy/* 2863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Copyright (C) 2009 The Android Open Source Project 3863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 4863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Based on sunrisesunsetlib-java: 5863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Copyright 2008-2009 Mike Reedell / LuckyCatLabs. 6863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 7863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Original project and source can be found at: 8863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * http://mikereedell.github.com/sunrisesunsetlib-java/ 9863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 10863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Licensed under the Apache License, Version 2.0 (the "License"); 11863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * you may not use this file except in compliance with the License. 12863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * You may obtain a copy of the License at 13863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 14863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * http://www.apache.org/licenses/LICENSE-2.0 15863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 16863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Unless required by applicable law or agreed to in writing, software 17863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * distributed under the License is distributed on an "AS IS" BASIS, 18863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * See the License for the specific language governing permissions and 20863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * limitations under the License. 21863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 22863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 23863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guypackage com.android.wallpaper.grass; 24863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 25863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guyimport java.util.Calendar; 26863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guyimport java.util.TimeZone; 27863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 28863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guyimport android.location.Location; 29863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 30863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guyclass SunCalculator { 31863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** Astronomical sunrise/set is when the sun is 18 degrees below the horizon. */ 32863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy static final double ZENITH_ASTRONOMICAL = 108; 33863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 34863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** Nautical sunrise/set is when the sun is 12 degrees below the horizon. */ 35863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy static final double ZENITH_NAUTICAL = 102; 36863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 37863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** Civil sunrise/set (dawn/dusk) is when the sun is 6 degrees below the horizon. */ 38863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy static final double ZENITH_CIVIL = 96; 39863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 40863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** Official sunrise/set is when the sun is 50' below the horizon. */ 41863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy static final double ZENITH_OFFICIAL = 90.8333; 42863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 43863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private Location mLocation; 44863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private TimeZone mTimeZone; 45863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 46863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy SunCalculator(Location location, String timeZoneIdentifier) { 47863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy mLocation = location; 48863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy mTimeZone = TimeZone.getTimeZone(timeZoneIdentifier); 49863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 50863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 51863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public void setLocation(Location location) { 52863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy mLocation = location; 53863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 54863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 55863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 56863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the sunrise time for the given zenith at the given date. 57863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 58863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param solarZenith <code>Zenith</code> enum corresponding to the type 59863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * of sunrise to compute. 60863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param date <code>Calendar</code> object representing the date to 61863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * compute the sunrise for. 62863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return the sunrise time 63863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 64863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public double computeSunriseTime(double solarZenith, Calendar date) { 65863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return computeSolarEventTime(solarZenith, date, true); 66863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 67863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 68863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 69863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the sunset time for the given zenith at the given date. 70863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 71863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param solarZenith <code>Zenith</code> enum corresponding to the type of 72863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * sunset to compute. 73863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param date <code>Calendar</code> object representing the date to compute 74863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * the sunset for. 75863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return the sunset time 76863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 77863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public double computeSunsetTime(double solarZenith, Calendar date) { 78863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return computeSolarEventTime(solarZenith, date, false); 79863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 80863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 81863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public static int timeToHours(double time) { 82863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int hour = (int) Math.floor(time); 83863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int minute = (int) Math.round((time - hour) * 60); 84863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (minute == 60) { 85863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy hour++; 86863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 87863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return hour; 88863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 89863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 90863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public static int timeToMinutes(double time) { 91863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int hour = (int) Math.floor(time); 92863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int minute = (int) Math.round((time - hour) * 60); 93863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (minute == 60) { 94863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy minute = 0; 95863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 96863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return minute; 97863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 98863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 99863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public static float timeToDayFraction(double time) { 100863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int hour = (int) Math.floor(time); 101863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int minute = (int) Math.round((time - hour) * 60); 102863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (minute == 60) { 103863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy minute = 0; 104863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy hour++; 105863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 106863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return (hour * 60 + minute) / 1440.0f; 107863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 108863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 109863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy public static String timeToString(double time) { 110863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy StringBuffer buffer = new StringBuffer(); 111863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int hour = (int) Math.floor(time); 112863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int minute = (int) Math.round((time - hour) * 60); 113863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (minute == 60) { 114863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy minute = 0; 115863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy hour++; 116863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 117863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy buffer.append(hour).append(':').append(minute < 10 ? "0" + minute : minute); 118863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return buffer.toString(); 119863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 120863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 121863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double computeSolarEventTime(double solarZenith, Calendar date, boolean isSunrise) { 122863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy date.setTimeZone(mTimeZone); 123863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double longitudeHour = getLongitudeHour(date, isSunrise); 124863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double meanAnomaly = getMeanAnomaly(longitudeHour); 125863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sunTrueLong = getSunTrueLongitude(meanAnomaly); 126863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double cosineSunLocalHour = getCosineSunLocalHour(sunTrueLong, solarZenith); 127863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if ((cosineSunLocalHour < -1.0) || (cosineSunLocalHour > 1.0)) { 128863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return 0; 129863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 130863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 131863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sunLocalHour = getSunLocalHour(cosineSunLocalHour, isSunrise); 132863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double localMeanTime = getLocalMeanTime(sunTrueLong, longitudeHour, sunLocalHour); 133863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return getLocalTime(localMeanTime, date); 134863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 135863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 136863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 137863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the base longitude hour, lngHour in the algorithm. 138863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 139863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return the longitude of the location of the solar event divided by 15 (deg/hour), in 140863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * <code>double</code> form. 141863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 142863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double getBaseLongitudeHour() { 143863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return mLocation.getLongitude() / 15.0; 144863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 145863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 146863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 147863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the longitude time, t in the algorithm. 148863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 149863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return longitudinal time in <code>double</code> form. 150863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 151863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double getLongitudeHour(Calendar date, Boolean isSunrise) { 152863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int offset = 18; 153863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (isSunrise) { 154863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy offset = 6; 155863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 156863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double dividend = offset - getBaseLongitudeHour(); 157863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double addend = dividend / 24.0; 158863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return getDayOfYear(date) + addend; 159863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 160863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 161863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 162863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the mean anomaly of the Sun, M in the algorithm. 163863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 164863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return the suns mean anomaly, M, in <code>double</code> form. 165863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 166863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getMeanAnomaly(double longitudeHour) { 167863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return 0.9856 * longitudeHour - 3.289; 168863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 169863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 170863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 171863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the true longitude of the sun, L in the algorithm, at the given 172863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * location, adjusted to fit in the range [0-360]. 173863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 174863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param meanAnomaly the suns mean anomaly. 175863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return the suns true longitude, in <code>double</code> form. 176863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 177863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getSunTrueLongitude(double meanAnomaly) { 178863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy final double meanRadians = Math.toRadians(meanAnomaly); 179863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinMeanAnomaly = Math.sin(meanRadians); 180863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinDoubleMeanAnomaly = Math.sin((meanRadians * 2.0)); 181863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 182863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double firstPart = meanAnomaly + sinMeanAnomaly * 1.916; 183863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double secondPart = sinDoubleMeanAnomaly * 0.020 + 282.634; 184863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double trueLongitude = firstPart + secondPart; 185863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 186863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (trueLongitude > 360) { 187863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy trueLongitude = trueLongitude - 360.0; 188863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 189863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return trueLongitude; 190863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 191863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 192863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 193863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * Computes the suns right ascension, RA in the algorithm, adjusting for 194863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * the quadrant of L and turning it into degree-hours. Will be in the 195863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * range [0,360]. 196863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * 197863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @param sunTrueLong Suns true longitude, in <code>double</code> 198863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * @return suns right ascension in degree-hours, in <code>double</code> form. 199863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 200863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getRightAscension(double sunTrueLong) { 201863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double tanL = Math.tan(Math.toRadians(sunTrueLong)); 202863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 203863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double innerParens = Math.toDegrees(tanL) * 0.91764; 204863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double rightAscension = Math.atan(Math.toRadians(innerParens)); 205863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy rightAscension = Math.toDegrees(rightAscension); 206863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 207863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (rightAscension < 0.0) { 208863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy rightAscension = rightAscension + 360.0; 209863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } else if (rightAscension > 360.0) { 210863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy rightAscension = rightAscension - 360.0; 211863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 212863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 213863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double ninety = 90.0; 214863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double longitudeQuadrant = (int) (sunTrueLong / ninety); 215863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy longitudeQuadrant = longitudeQuadrant * ninety; 216863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 217863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double rightAscensionQuadrant = (int) (rightAscension / ninety); 218863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy rightAscensionQuadrant = rightAscensionQuadrant * ninety; 219863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 220863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double augend = longitudeQuadrant - rightAscensionQuadrant; 221863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return (rightAscension + augend) / 15.0; 222863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 223863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 224863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double getCosineSunLocalHour(double sunTrueLong, double zenith) { 225863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinSunDeclination = getSinOfSunDeclination(sunTrueLong); 226863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double cosineSunDeclination = getCosineOfSunDeclination(sinSunDeclination); 227863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 228863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy final double zenithInRads = Math.toRadians(zenith); 229863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy final double latitude = Math.toRadians(mLocation.getLatitude()); 230863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 231863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double cosineZenith = Math.cos(zenithInRads); 232863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinLatitude = Math.sin(latitude); 233863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double cosLatitude = Math.cos(latitude); 234863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 235863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinDeclinationTimesSinLat = sinSunDeclination * sinLatitude; 236863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double dividend = cosineZenith - sinDeclinationTimesSinLat; 237863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double divisor = cosineSunDeclination * cosLatitude; 238863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 239863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return dividend / divisor; 240863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 241863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 242863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getSinOfSunDeclination(double sunTrueLong) { 243863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sinTrueLongitude = Math.sin(Math.toRadians(sunTrueLong)); 244863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return sinTrueLongitude * 0.39782; 245863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 246863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 247863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getCosineOfSunDeclination(double sinSunDeclination) { 248863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double arcSinOfSinDeclination = Math.asin(sinSunDeclination); 249863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return Math.cos(arcSinOfSinDeclination); 250863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 251863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 252863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getSunLocalHour(double cosineSunLocalHour, Boolean isSunrise) { 253863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double arcCosineOfCosineHourAngle = Math.acos(cosineSunLocalHour); 254863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double localHour = Math.toDegrees(arcCosineOfCosineHourAngle); 255863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (isSunrise) { 256863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localHour = 360.0 - localHour; 257863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 258863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return localHour / 15.0; 259863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 260863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 261863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getLocalMeanTime(double sunTrueLong, double longitudeHour, 262863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double sunLocalHour) { 263863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 264863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double rightAscension = getRightAscension(sunTrueLong); 265863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double innerParens = longitudeHour * 0.06571; 266863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double localMeanTime = sunLocalHour + rightAscension - innerParens; 267863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localMeanTime = localMeanTime - 6.622; 268863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 269863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (localMeanTime < 0.0) { 270863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localMeanTime = localMeanTime + 24.0; 271863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } else if (localMeanTime > 24.0) { 272863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localMeanTime = localMeanTime - 24.0; 273863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 274863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return localMeanTime; 275863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 276863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 277863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double getLocalTime(double localMeanTime, Calendar date) { 278863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double utcTime = localMeanTime - getBaseLongitudeHour(); 279863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double utcOffSet = getUTCOffSet(date); 280863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double utcOffSetTime = utcTime + utcOffSet; 281863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return adjustForDST(utcOffSetTime, date); 282863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 283863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 284863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private double adjustForDST(double localMeanTime, Calendar date) { 285863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy double localTime = localMeanTime; 286863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (mTimeZone.inDaylightTime(date.getTime())) { 287863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localTime++; 288863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 289863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy if (localTime > 24.0) { 290863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy localTime = localTime - 24.0; 291863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 292863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return localTime; 293863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 294863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 295863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy /** 296863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy * ****** UTILITY METHODS (Should probably go somewhere else. ***************** 297863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy */ 298863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 299863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getDayOfYear(Calendar date) { 300863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return date.get(Calendar.DAY_OF_YEAR); 301863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 302863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy 303863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy private static double getUTCOffSet(Calendar date) { 304863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy int offSetInMillis = date.get(Calendar.ZONE_OFFSET); 305863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy return offSetInMillis / 3600000; 306863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy } 307863fd1d0c9c7ade2c8eb29ae3d642377c0fec673Romain Guy}