001 /*
002 * Zmanim Java API
003 * Copyright (C) 2004-2011 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 - 2011
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 this.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 double adjustedZenith = zenith;
054 if(adjustForElevation){
055 adjustedZenith = adjustZenith(zenith, astronomicalCalendar.getGeoLocation().getElevation());
056 } else {
057 adjustedZenith = adjustZenith(zenith, 0);
058 }
059
060 // step 1: First calculate the day of the year
061 // NOT NEEDED in this implementation
062
063 // step 2: convert the longitude to hour value and calculate an
064 // approximate time
065 double lngHour = astronomicalCalendar.getGeoLocation().getLongitude() / 15;
066
067 double t = astronomicalCalendar.getCalendar().get(Calendar.DAY_OF_YEAR)
068 + ((6 - lngHour) / 24); // use 18 for
069 // sunset instead
070 // of 6
071
072 // step 3: calculate the sun's mean anomaly
073 double m = (0.9856 * t) - 3.289;
074
075 // step 4: calculate the sun's true longitude
076 double l = m + (1.916 * Math.sin(Math.toRadians(m)))
077 + (0.020 * Math.sin(Math.toRadians(2 * m))) + 282.634;
078 while (l < 0) {
079 double Lx = l + 360;
080 l = Lx;
081 }
082 while (l >= 360) {
083 double Lx = l - 360;
084 l = Lx;
085 }
086
087 // step 5a: calculate the sun's right ascension
088 double RA = Math.toDegrees(Math.atan(0.91764 * Math.tan(Math
089 .toRadians(l))));
090
091 while (RA < 0) {
092 double RAx = RA + 360;
093 RA = RAx;
094 }
095 while (RA >= 360) {
096 double RAx = RA - 360;
097 RA = RAx;
098 }
099
100 // step 5b: right ascension value needs to be in the same quadrant as L
101 double lQuadrant = Math.floor(l / 90) * 90;
102 double raQuadrant = Math.floor(RA / 90) * 90;
103 RA = RA + (lQuadrant - raQuadrant);
104
105 // step 5c: right ascension value needs to be converted into hours
106 RA /= 15;
107
108 // step 6: calculate the sun's declination
109 double sinDec = 0.39782 * Math.sin(Math.toRadians(l));
110 double cosDec = Math.cos(Math.asin(sinDec));
111
112 // step 7a: calculate the sun's local hour angle
113 double cosH = (Math.cos(Math.toRadians(adjustedZenith)) - (sinDec * Math
114 .sin(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude()))))
115 / (cosDec * Math.cos(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude())));
116
117 // the following line would throw an Exception if the sun never rose.
118 // this is not needed since the calculation will return a Double.NaN
119 // if (cosH > 1) throw new Exception("doesnthappen");
120
121 // FOR SUNSET use the following instead of the above if statement.
122 // if (cosH < -1)
123
124 // step 7b: finish calculating H and convert into hours
125 double H = 360 - Math.toDegrees(Math.acos(cosH));
126
127 // FOR SUNSET remove "360 - " from the above
128
129 H = H / 15;
130
131 // step 8: calculate local mean time
132
133 double T = H + RA - (0.06571 * t) - 6.622;
134
135 // step 9: convert to UTC
136 double UT = T - lngHour;
137 while (UT < 0) {
138 double UTx = UT + 24;
139 UT = UTx;
140 }
141 while (UT >= 24) {
142 double UTx = UT - 24;
143 UT = UTx;
144 }
145 return UT;
146 }
147
148 /**
149 * @see net.sourceforge.zmanim.util.AstronomicalCalculator#getUTCSunset(AstronomicalCalendar,
150 * double, boolean)
151 */
152 public double getUTCSunset(AstronomicalCalendar astronomicalCalendar,
153 /*GeoLocation geoLocation,*/ double zenith, boolean adjustForElevation) {
154 // zenith = adjustZenithForElevation(astronomicalCalendar, zenith,
155 // geoLocation.getElevation());
156 // double elevationAdjustment = this.getElevationAdjustment(zenith,
157 // geoLocation.getElevation());
158 // double refractionAdjustment = this.getRefraction(zenith);
159 // zenith = zenith + elevationAdjustment + refractionAdjustment;
160 double adjustedZenith = zenith;
161 if(adjustForElevation){
162 adjustedZenith = adjustZenith(zenith, astronomicalCalendar.getGeoLocation().getElevation());
163 } else {
164 adjustedZenith = adjustZenith(zenith, 0);
165 }
166
167 // step 1: First calculate the day of the year
168 // int calendarDayOfYear = calelendar.DAY_OF_YEAR;
169
170 // int N=theday - date(1,1,theday.year()) + 1;
171 int N = astronomicalCalendar.getCalendar().get(Calendar.DAY_OF_YEAR);
172
173 // step 2: convert the longitude to hour value and calculate an
174 // approximate time
175 double lngHour = astronomicalCalendar.getGeoLocation().getLongitude() / 15;
176
177 double t = N + ((18 - lngHour) / 24);
178
179 // step 3: calculate the sun's mean anomaly
180 double M = (0.9856 * t) - 3.289;
181
182 // step 4: calculate the sun's true longitude
183 double L = M + (1.916 * Math.sin(Math.toRadians(M)))
184 + (0.020 * Math.sin(Math.toRadians(2 * M))) + 282.634;
185 while (L < 0) {
186 double Lx = L + 360;
187 L = Lx;
188 }
189 while (L >= 360) {
190 double Lx = L - 360;
191 L = Lx;
192 }
193
194 // step 5a: calculate the sun's right ascension
195 double RA = Math.toDegrees(Math.atan(0.91764 * Math.tan(Math
196 .toRadians(L))));
197 while (RA < 0) {
198 double RAx = RA + 360;
199 RA = RAx;
200 }
201 while (RA >= 360) {
202 double RAx = RA - 360;
203 RA = RAx;
204 }
205
206 // step 5b: right ascension value needs to be in the same quadrant as L
207 double Lquadrant = Math.floor(L / 90) * 90;
208 double RAquadrant = Math.floor(RA / 90) * 90;
209 RA = RA + (Lquadrant - RAquadrant);
210
211 // step 5c: right ascension value needs to be converted into hours
212 RA /= 15;
213
214 // step 6: calculate the sun's declination
215 double sinDec = 0.39782 * Math.sin(Math.toRadians(L));
216 double cosDec = Math.cos(Math.asin(sinDec));
217
218 // step 7a: calculate the sun's local hour angle
219 double cosH = (Math.cos(Math.toRadians(adjustedZenith)) - (sinDec * Math
220 .sin(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude()))))
221 / (cosDec * Math.cos(Math.toRadians(astronomicalCalendar.getGeoLocation().getLatitude())));
222
223 // the following line would throw an Exception if the sun never set.
224 // this is not needed since the calculation will return a Double.NaN
225 // if (cosH < -1) throw new ZmanimException("doesnthappen");
226
227 // step 7b: finish calculating H and convert into hours
228 double H = Math.toDegrees(Math.acos(cosH));
229 H = H / 15;
230
231 // step 8: calculate local mean time
232
233 double T = H + RA - (0.06571 * t) - 6.622;
234
235 // step 9: convert to UTC
236 double UT = T - lngHour;
237 while (UT < 0) {
238 double UTx = UT + 24;
239 UT = UTx;
240 }
241 while (UT >= 24) {
242 double UTx = UT - 24;
243 UT = UTx;
244 }
245 return UT;
246 }
247 }