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 }