001 /*
002 * Zmanim Java API
003 * Copyright (C) 2004-2010 Eliyahu Hershfeld
004 *
005 * This library is free software; you can redistribute it and/or modify it under the terms of the
006 * GNU Lesser General Public License as published by the Free Software Foundation; either
007 * version 2.1 of the License, or (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
010 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011 * General Public License for more details.
012 *
013 * You should have received a copy of the GNU Lesser General Public License along with this program; if
014 * not, write to the Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA
015 * 02111-1307, USA or connect to: http://www.fsf.org/copyleft/gpl.html
016 */
017 package net.sourceforge.zmanim.util;
018
019 import java.util.Calendar;
020
021 import net.sourceforge.zmanim.AstronomicalCalendar;
022
023 /**
024 * Implementation of sunrise and sunset methods to calculate astronomical times.
025 * This implementation is a port of the C++ algorithm written by Ken Bloom for
026 * the sourceforge.net <a href="http://sourceforge.net/projects/zmanim/">Zmanim</a>
027 * project. Ken's algorithm is based on the US Naval Almanac algorithm. Added to
028 * Ken's code is adjustment of the zenith to account for elevation. Originally released
029 * under the GPL, it has been released under the LGPL as of April 8, 2010.
030 *
031 * @author © Chanoch (Ken) Bloom 2003 - 2004
032 * @author © Eliyahu Hershfeld 2004 - 2010
033 * @version 1.1
034 */
035 public class ZmanimCalculator extends AstronomicalCalculator {
036 private String calculatorName = "US Naval Almanac Algorithm";
037 public String getCalculatorName(){
038 return calculatorName; //"US Naval Almanac Algorithm";
039 }
040
041 /**
042 * @see net.sourceforge.zmanim.util.AstronomicalCalculator#getUTCSunrise(AstronomicalCalendar,
043 * double, boolean)
044 */
045 public double getUTCSunrise(AstronomicalCalendar astronomicalCalendar,
046 /*GeoLocation geoLocation,*/ double zenith, boolean adjustForElevation) {
047 // zenith = adjustZenithForElevation(astronomicalCalendar, zenith,
048 // geoLocation.getElevation());
049 // double elevationAdjustment = this.getElevationAdjustment(zenith,
050 // geoLocation.getElevation());
051 // double refractionAdjustment = this.getRefraction(zenith);
052 // zenith = zenith + elevationAdjustment + refractionAdjustment;
053 if(adjustForElevation){
054 zenith = adjustZenith(zenith, astronomicalCalendar.getGeoLocation().getElevation());
055 } else {
056 zenith = adjustZenith(zenith, 0);
057 }
058
059 // step 1: First calculate the day of the year
060 // NOT NEEDED in this implementation
061
062 // step 2: convert the longitude to hour value and calculate an
063 // approximate time
064 double lngHour = astronomicalCalendar.getGeoLocation().getLongitude() / 15;
065
066 double t = astronomicalCalendar.getCalendar().get(Calendar.DAY_OF_YEAR)
067 + ((6 - lngHour) / 24); // use 18 for
068 // sunset instead
069 // of 6
070
071 // step 3: calculate the sun's mean anomaly
072 double m = (0.9856 * t) - 3.289;
073
074 // step 4: calculate the sun's true longitude
075 double l = m + (1.916 * Math.sin(Math.toRadians(m)))
076 + (0.020 * Math.sin(Math.toRadians(2 * m))) + 282.634;
077 while (l < 0) {
078 double Lx = l + 360;
079 l = Lx;
080 }
081 while (l >= 360) {
082 double Lx = l - 360;
083 l = Lx;
084 }
085
086 // step 5a: calculate the sun's right ascension
087 double RA = Math.toDegrees(Math.atan(0.91764 * Math.tan(Math
088 .toRadians(l))));
089
090 while (RA < 0) {
091 double RAx = RA + 360;
092 RA = RAx;
093 }
094 while (RA >= 360) {
095 double RAx = RA - 360;
096 RA = RAx;
097 }
098
099 // step 5b: right ascension value needs to be in the same quadrant as L
100 double lQuadrant = Math.floor(l / 90) * 90;
101 double raQuadrant = Math.floor(RA / 90) * 90;
102 RA = RA + (lQuadrant - raQuadrant);
103
104 // step 5c: right ascension value needs to be converted into hours
105 RA /= 15;
106
107 // step 6: calculate the sun's declination
108 double sinDec = 0.39782 * Math.sin(Math.toRadians(l));
109 double cosDec = Math.cos(Math.asin(sinDec));
110
111 // step 7a: calculate the sun's local hour angle
112 double cosH = (Math.cos(Math.toRadians(zenith)) - (sinDec * Math
113 .sin(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude()))))
114 / (cosDec * Math.cos(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude())));
115
116 // the following line would throw an Exception if the sun never rose.
117 // this is not needed since the calculation will return a Double.NaN
118 // if (cosH > 1) throw new Exception("doesnthappen");
119
120 // FOR SUNSET use the following instead of the above if statement.
121 // if (cosH < -1)
122
123 // step 7b: finish calculating H and convert into hours
124 double H = 360 - Math.toDegrees(Math.acos(cosH));
125
126 // FOR SUNSET remove "360 - " from the above
127
128 H = H / 15;
129
130 // step 8: calculate local mean time
131
132 double T = H + RA - (0.06571 * t) - 6.622;
133
134 // step 9: convert to UTC
135 double UT = T - lngHour;
136 while (UT < 0) {
137 double UTx = UT + 24;
138 UT = UTx;
139 }
140 while (UT >= 24) {
141 double UTx = UT - 24;
142 UT = UTx;
143 }
144 return UT;
145 }
146
147 /**
148 * @see net.sourceforge.zmanim.util.AstronomicalCalculator#getUTCSunset(AstronomicalCalendar,
149 * double, boolean)
150 */
151 public double getUTCSunset(AstronomicalCalendar astronomicalCalendar,
152 /*GeoLocation geoLocation,*/ double zenith, boolean adjustForElevation) {
153 // zenith = adjustZenithForElevation(astronomicalCalendar, zenith,
154 // geoLocation.getElevation());
155 // double elevationAdjustment = this.getElevationAdjustment(zenith,
156 // geoLocation.getElevation());
157 // double refractionAdjustment = this.getRefraction(zenith);
158 // zenith = zenith + elevationAdjustment + refractionAdjustment;
159
160 if(adjustForElevation){
161 zenith = adjustZenith(zenith, astronomicalCalendar.getGeoLocation().getElevation());
162 } else {
163 zenith = adjustZenith(zenith, 0);
164 }
165
166 // step 1: First calculate the day of the year
167 // int calendarDayOfYear = calelendar.DAY_OF_YEAR;
168
169 // int N=theday - date(1,1,theday.year()) + 1;
170 int N = astronomicalCalendar.getCalendar().get(Calendar.DAY_OF_YEAR);
171
172 // step 2: convert the longitude to hour value and calculate an
173 // approximate time
174 double lngHour = astronomicalCalendar.getGeoLocation().getLongitude() / 15;
175
176 double t = N + ((18 - lngHour) / 24);
177
178 // step 3: calculate the sun's mean anomaly
179 double M = (0.9856 * t) - 3.289;
180
181 // step 4: calculate the sun's true longitude
182 double L = M + (1.916 * Math.sin(Math.toRadians(M)))
183 + (0.020 * Math.sin(Math.toRadians(2 * M))) + 282.634;
184 while (L < 0) {
185 double Lx = L + 360;
186 L = Lx;
187 }
188 while (L >= 360) {
189 double Lx = L - 360;
190 L = Lx;
191 }
192
193 // step 5a: calculate the sun's right ascension
194 double RA = Math.toDegrees(Math.atan(0.91764 * Math.tan(Math
195 .toRadians(L))));
196 while (RA < 0) {
197 double RAx = RA + 360;
198 RA = RAx;
199 }
200 while (RA >= 360) {
201 double RAx = RA - 360;
202 RA = RAx;
203 }
204
205 // step 5b: right ascension value needs to be in the same quadrant as L
206 double Lquadrant = Math.floor(L / 90) * 90;
207 double RAquadrant = Math.floor(RA / 90) * 90;
208 RA = RA + (Lquadrant - RAquadrant);
209
210 // step 5c: right ascension value needs to be converted into hours
211 RA /= 15;
212
213 // step 6: calculate the sun's declination
214 double sinDec = 0.39782 * Math.sin(Math.toRadians(L));
215 double cosDec = Math.cos(Math.asin(sinDec));
216
217 // step 7a: calculate the sun's local hour angle
218 double cosH = (Math.cos(Math.toRadians(zenith)) - (sinDec * Math
219 .sin(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude()))))
220 / (cosDec * Math.cos(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude())));
221
222 // the following line would throw an Exception if the sun never set.
223 // this is not needed since the calculation will return a Double.NaN
224 // if (cosH < -1) throw new ZmanimException("doesnthappen");
225
226 // step 7b: finish calculating H and convert into hours
227 double H = Math.toDegrees(Math.acos(cosH));
228 H = H / 15;
229
230 // step 8: calculate local mean time
231
232 double T = H + RA - (0.06571 * t) - 6.622;
233
234 // step 9: convert to UTC
235 double UT = T - lngHour;
236 while (UT < 0) {
237 double UTx = UT + 24;
238 UT = UTx;
239 }
240 while (UT >= 24) {
241 double UTx = UT - 24;
242 UT = UTx;
243 }
244 return UT;
245 }
246 }