001 /*
002 * Zmanim Java API
003 * Copyright (C) 2004-2007 Eliyahu Hershfeld
004 *
005 * This program is free software; you can redistribute it and/or modify it under the terms of the
006 * GNU General Public License as published by the Free Software Foundation; either version 2 of the
007 * 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 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 net.sourceforge.zmanim.AstronomicalCalendar;
020
021 /**
022 * An abstract class that all sun time calculating classes extend. This allows
023 * the algorithm used to be changed at runtime, easily allowing comparison the
024 * results of using different algorithms.
025 *
026 * @author © Eliyahu Hershfeld 2004 - 2007
027 * @version 1.1
028 */
029 public abstract class AstronomicalCalculator {
030 private double refraction = 34 / 60d;
031
032 // private double refraction = 34.478885263888294 / 60d;
033 private double solarRadius = 16 / 60d;
034
035 /**
036 * getDefault method returns the default sun times calculation engine.
037 *
038 * @return AstronomicalCalculator the default class for calculating sunrise
039 * and sunset. In the current implementation the default calculator
040 * returned is the {@link SunTimesCalculator}.
041 */
042 public static AstronomicalCalculator getDefault() {
043 return new SunTimesCalculator();
044 }
045
046 /**
047 *
048 * @return the descriptive name of the algorithm.
049 */
050 public abstract String getCalculatorName();
051
052 /**
053 * A method that calculates UTC sunrise as well as any time based on an
054 * angle above or below sunrise. This abstract method is implemented by the
055 * classes that extend this class.
056 *
057 * @param astronomicalCalendar
058 * Used to calculate day of year.
059 * @param zenith
060 * the azimuth below the vertical zenith of 90 degrees. for
061 * sunrise typically the {@link #adjustZenith zenith} used for
062 * the calculation uses geometric zenith of 90° and
063 * {@link #adjustZenith adjusts} this slightly to account for
064 * solar refraction and the sun's radius. Another example would
065 * be {@link AstronomicalCalendar#getBeginNauticalTwilight()}
066 * that passes {@link AstronomicalCalendar#NAUTICAL_ZENITH} to
067 * this method.
068 * @return The UTC time of sunrise in 24 hour format. 5:45:00 AM will return
069 * 5.75.0. If an error was encountered in the calculation (expected behavior for some locations such as near
070 * the poles, {@link java.lang.Double.NaN} will be returned.
071 */
072 public abstract double getUTCSunrise(
073 AstronomicalCalendar astronomicalCalendar, double zenith, boolean adjustForElevation);
074
075 /**
076 * A method that calculates UTC sunset as well as any time based on an angle
077 * above or below sunset. This abstract method is implemented by the classes
078 * that extend this class.
079 *
080 * @param astronomicalCalendar
081 * Used to calculate day of year.
082 * @param zenith
083 * the azimuth below the vertical zenith of 90°. For
084 * sunset typically the {@link #adjustZenith zenith} used for the
085 * calculation uses geometric zenith of 90° and
086 * {@link #adjustZenith adjusts} this slightly to account for
087 * solar refraction and the sun's radius. Another example would
088 * be {@link AstronomicalCalendar#getEndNauticalTwilight()} that
089 * passes {@link AstronomicalCalendar#NAUTICAL_ZENITH} to this
090 * method.
091 * @return The UTC time of sunset in 24 hour format. 5:45:00 AM will return
092 * 5.75.0. If an error was encountered in the calculation (expected behavior for some locations such as near
093 * the poles, {@link java.lang.Double.NaN} will be returned.
094 */
095 public abstract double getUTCSunset(
096 AstronomicalCalendar astronomicalCalendar, double zenith, boolean adjustForElevation);
097
098 /**
099 * Method to return the adjustment to the zenith required to account for the
100 * elevation. Since a person at a higher elevation can see farther below the
101 * horizon, the calculation for sunrise / sunset is calculated below the
102 * horizon used at sea level. This is only used for sunrise and sunset and
103 * not times above or below it such as
104 * {@link AstronomicalCalendar#getBeginNauticalTwilight() nautical twilight}
105 * since those calculations are based on the level of available light at the
106 * given dip below the horizon, something that is not affected by elevation,
107 * the adjustment should only made if the zenith == 90°
108 * {@link #adjustZenith adjusted} for refraction and solar radius.<br />
109 * The algorithm used is:
110 *
111 * <pre>
112 * elevationAdjustment = Math.toDegrees(Math.acos(earthRadiusInMeters
113 * / (earthRadiusInMeters + elevationMeters)));
114 * </pre>
115 *
116 * The source of this algorthitm is <a
117 * href="http://www.calendarists.com">Calendrical Calculations</a> by
118 * Edward M. Reingold and Nachum Dershowitz. An alternate algorithm that
119 * produces an almost identical (but not accurate) result found in Ma'aglay
120 * Tzedek by Moishe Kosower and other sources is:
121 *
122 * <pre>
123 * elevationAdjustment = 0.0347 * Math.sqrt(elevationMeters);
124 * </pre>
125 *
126 * @param elevation
127 * elevation in Meters.
128 * @return the adjusted zenith
129 */
130 double getElevationAdjustment(double elevation) {
131 double earthRadius = 6356.9;
132 //double elevationAdjustment = 0.0347 * Math.sqrt(elevation);
133 double elevationAdjustment = Math.toDegrees(Math.acos(earthRadius
134 / (earthRadius + (elevation / 1000))));
135 return elevationAdjustment;
136
137 }
138
139 /**
140 * Adjusts the zenith to account for solar refraction, solar radius and
141 * elevation. The value for Sun's zenith and true rise/set Zenith (used in
142 * this class and subclasses) is the angle that the center of the Sun makes
143 * to a line perpendicular to the Earth's surface. If the Sun were a point
144 * and the Earth were without an atmosphere, true sunset and sunrise would
145 * correspond to a 90° zenith. Because the Sun is not a point, and
146 * because the atmosphere refracts light, this 90° zenith does not, in
147 * fact, correspond to true sunset or sunrise, instead the centre of the
148 * Sun's disk must lie just below the horizon for the upper edge to be
149 * obscured. This means that a zenith of just above 90° must be used.
150 * The Sun subtends an angle of 16 minutes of arc (this can be changed via
151 * the {@link #setSolarRadius(double)} method , and atmospheric refraction
152 * accounts for 34 minutes or so (this can be changed via the
153 * {@link #setRefraction(double)} method), giving a total of 50 arcminutes.
154 * The total value for ZENITH is 90+(5/6) or 90.8333333° for true
155 * sunrise/sunset. Since a person at an elevation can see blow the horizon
156 * of a person at sea level, this will also adjust the zenith to account for
157 * elevation if available.
158 *
159 * @return The zenith adjusted to include the
160 * {@link #getSolarRadius sun's radius},
161 * {@link #getRefraction refraction} and
162 * {@link #getElevationAdjustment elevation} adjustment.
163 */
164 double adjustZenith(double zenith, double elevation) {
165 if (zenith == AstronomicalCalendar.GEOMETRIC_ZENITH) {
166 zenith = zenith
167 + (getSolarRadius() + getRefraction() + getElevationAdjustment(elevation));
168 }
169
170 return zenith;
171 }
172
173 /**
174 * Method to get the refraction value to be used when calculating sunrise
175 * and sunset. The default value is 34 arc minutes. The <a
176 * href="http://emr.cs.iit.edu/home/reingold/calendar-book/second-edition/errata.pdf">Errata
177 * and Notes for Calendrical Calculations: The Millenium Eddition</a> by
178 * Edward M. Reingold and Nachum Dershowitz lists the actual refraction
179 * value as 34.478885263888294 or approximately 34' 29". The refraction value as well
180 * as the solarRadius and elevation adjustment are added to the zenith of
181 * sunrise and sunset.
182 *
183 * @return The refraction in arc minutes.
184 */
185 double getRefraction() {
186 return refraction;
187 }
188
189 /**
190 * A method to allow overriding the default refraction of the calculator.
191 * @param refraction
192 * The refraction in arc minutes.
193 * @see #getRefraction()
194 */
195 public void setRefraction(double refraction) {
196 this.refraction = refraction;
197 }
198
199 /**
200 * Method to get the sun's radius. The default value is 16 arc minutes. This
201 * will probably never be changed unless someone calculates that it is
202 * different than the commonly used 16 arc minutes.
203 *
204 * @return The sun's radius in arc minutes.
205 */
206 double getSolarRadius() {
207 return solarRadius;
208 }
209
210 /**
211 * Method to set the sun's radius. The default value is 16 arc minutes. This
212 * will probably never be changed unless someone calculates that it is
213 * different than the commonly used 16 arc minutes.
214 *
215 * @param solarRadius
216 * The sun's radius in arc minutes.
217 */
218 public void setSolarRadius(double solarRadius) {
219 this.solarRadius = solarRadius;
220 }
221 }