summaryrefslogtreecommitdiff
path: root/lisp/calendar/solar.el
diff options
context:
space:
mode:
authorGlenn Morris <rgm@gnu.org>2008-03-08 23:07:56 +0000
committerGlenn Morris <rgm@gnu.org>2008-03-08 23:07:56 +0000
commita603b7dbaa2dbe6a751096b0f90b2666654b5eec (patch)
treedb2bfc46eda725933ae144cb147a5c37bf45303c /lisp/calendar/solar.el
parentf0a51f2a94e9010ed827c7dfe8c337403e7d0fd9 (diff)
downloademacs-a603b7dbaa2dbe6a751096b0f90b2666654b5eec.tar.gz
(calendar-location-name, calendar-latitude)
(calendar-longitude, solar-setup, solar-sin-degrees) (solar-cosine-degrees, solar-tangent-degrees, solar-xy-to-quadrant) (solar-degrees-to-quadrant, solar-atn2, solar-arccos) (solar-sunrise-and-sunset, solar-moment, solar-daylight) (solar-exact-local-noon, solar-julian-ut-centuries) (solar-ephemeris-time, solar-date-next-longitude) (solar-horizontal-coordinates, solar-equatorial-coordinates) (solar-ecliptic-coordinates, solar-data-list, solar-longitude) (solar-ephemeris-correction, solar-sidereal-time, solar-date-to-et) (sunrise-sunset, solar-seasons-data, solar-equinoxes/solstices): Doc fixes. (solar-horizontal-coordinates, solar-equatorial-coordinates) (solar-ecliptic-coordinates): Rename argument `for-sunrise-sunset'. (solar-ecliptic-coordinates): Use unless. (calendar-sunrise-sunset, diary-sunrise-sunset, diary-sabbath-candles): Use or.
Diffstat (limited to 'lisp/calendar/solar.el')
-rw-r--r--lisp/calendar/solar.el215
1 files changed, 102 insertions, 113 deletions
diff --git a/lisp/calendar/solar.el b/lisp/calendar/solar.el
index ecb5321c5a7..fe61c0d8562 100644
--- a/lisp/calendar/solar.el
+++ b/lisp/calendar/solar.el
@@ -55,7 +55,6 @@
;;; Code:
-(defvar date)
(defvar displayed-month)
(defvar displayed-year)
@@ -87,7 +86,6 @@ would give military-style times like `21:07 (UTC)'."
(defcustom calendar-latitude nil
"Latitude of `calendar-location-name' in degrees.
-
The value can be either a decimal fraction (one place of accuracy is
sufficient), + north, - south, such as 40.7 for New York City, or the value
can be a vector [degrees minutes north/south] such as [40 50 north] for New
@@ -106,7 +104,6 @@ This variable should be set in `site-start'.el."
(defcustom calendar-longitude nil
"Longitude of `calendar-location-name' in degrees.
-
The value can be either a decimal fraction (one place of accuracy is
sufficient), + east, - west, such as -73.9 for New York City, or the value
can be a vector [degrees minutes east/west] such as [73 55 west] for New
@@ -140,9 +137,9 @@ This variable should be set in `site-start'.el."
(if (numberp calendar-longitude)
(if (> calendar-longitude 0) "E" "W")
(if (equal (aref calendar-longitude 2) 'east) "E" "W"))))
- "Expression evaluating to name of `calendar-longitude', `calendar-latitude'.
-For example, \"New York City\". Default value is just the latitude, longitude
-pair.
+ "Expression evaluating to the name of the calendar location.
+For example, \"New York City\". The default value is just the
+variable `calendar-latitude' paired with the variable `calendar-longitude'.
This variable should be set in `site-start'.el."
:type 'sexp
@@ -191,7 +188,7 @@ Needed for polar areas, in order to know whether the day lasts 0 or 24 hours.")
(defsubst calendar-latitude ()
- "Convert calendar-latitude to a signed decimal fraction, if needed."
+ "Ensure the variable `calendar-latitude' is a signed decimal fraction."
(if (numberp calendar-latitude)
calendar-latitude
(let ((lat (+ (aref calendar-latitude 0)
@@ -201,7 +198,7 @@ Needed for polar areas, in order to know whether the day lasts 0 or 24 hours.")
(- lat)))))
(defsubst calendar-longitude ()
- "Convert calendar-longitude to a signed decimal fraction, if needed."
+ "Ensure the variable `calendar-longitude' is a signed decimal fraction."
(if (numberp calendar-longitude)
calendar-longitude
(let ((long (+ (aref calendar-longitude 0)
@@ -211,7 +208,7 @@ Needed for polar areas, in order to know whether the day lasts 0 or 24 hours.")
(- long)))))
(defun solar-setup ()
- "Prompt user for latitude, longitude, and time zone."
+ "Prompt for `calendar-longitude', `calendar-latitude', `calendar-time-zone'."
(beep)
(or calendar-longitude
(setq calendar-longitude
@@ -235,22 +232,25 @@ Returns nil if nothing was entered."
(string-to-number x))))
(defun solar-sin-degrees (x)
+ "Return sin of X degrees."
(sin (degrees-to-radians (mod x 360.0))))
(defun solar-cosine-degrees (x)
+ "Return cosine of X degrees."
(cos (degrees-to-radians (mod x 360.0))))
(defun solar-tangent-degrees (x)
+ "Return tangent of X degrees."
(tan (degrees-to-radians (mod x 360.0))))
(defun solar-xy-to-quadrant (x y)
- "Determines the quadrant of the point X, Y."
+ "Determine the quadrant of the point X, Y."
(if (> x 0)
(if (> y 0) 1 4)
(if (> y 0) 2 3)))
(defun solar-degrees-to-quadrant (angle)
- "Determines the quadrant of ANGLE."
+ "Determine the quadrant of ANGLE degrees."
(1+ (floor (mod angle 360) 90)))
(defun solar-arctan (x quad)
@@ -262,13 +262,13 @@ Returns nil if nothing was entered."
(t deg))))
(defun solar-atn2 (x y)
- "Arctan of point X, Y."
+ "Arctangent of point X, Y."
(if (zerop x)
(if (> y 0) 90 270)
(solar-arctan (/ y x) (solar-xy-to-quadrant x y))))
(defun solar-arccos (x)
- "Arcos of X."
+ "Arccosine of X."
(let ((y (sqrt (- 1 (* x x)))))
(solar-atn2 x y)))
@@ -307,11 +307,11 @@ Parameters are the midday TIME and the LATITUDE, LONGITUDE of the location.
TIME is a pair with the first component being the number of Julian centuries
elapsed at 0 Universal Time, and the second component being the universal
time. For instance, the pair corresponding to November 28, 1995 at 16 UT is
-\(-0.040945 16), -0.040945 being the number of julian centuries elapsed between
+\(-0.040945 16), -0.040945 being the number of Julian centuries elapsed between
Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT.
HEIGHT is the angle the center of the sun has over the horizon for the contact
-we are trying to find. For sunrise and sunset, it is usually -0.61 degrees,
+we are trying to find. For sunrise and sunset, it is usually -0.61 degrees,
accounting for the edge of the sun being on the horizon.
Coordinates are included because this function is called with latitude=1
@@ -339,11 +339,11 @@ being TIME.
TIME is a pair with the first component being the number of Julian centuries
elapsed at 0 Universal Time, and the second component being the universal
time. For instance, the pair corresponding to November 28, 1995 at 16 UT is
-\(-0.040945 16), -0.040945 being the number of julian centuries elapsed between
+\(-0.040945 16), -0.040945 being the number of Julian centuries elapsed between
Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT.
HEIGHT is the angle the center of the sun has over the horizon for the contact
-we are trying to find. For sunrise and sunset, it is usually -0.61 degrees,
+we are trying to find. For sunrise and sunset, it is usually -0.61 degrees,
accounting for the edge of the sun being on the horizon.
Uses binary search."
@@ -395,14 +395,13 @@ Format used is given by `calendar-time-display-form'."
(defun solar-daylight (time)
- "Printable form for time expressed in hours."
+ "Printable form for TIME expressed in hours."
(format "%d:%02d"
(floor time)
(floor (* 60 (- time (floor time))))))
(defun solar-exact-local-noon (date)
- "Date and Universal Time of local noon at *local date* date.
-
+ "Date and Universal Time of local noon at *local date* DATE.
The date may be different from the one asked for, but it will be the right
local date. The second component of date should be an integer."
(let* ((nd date)
@@ -426,7 +425,6 @@ local date. The second component of date should be an integer."
(defun solar-sunrise-sunset (date)
"List of *local* times of sunrise, sunset, and daylight on Gregorian DATE.
-
Corresponding value is nil if there is no sunrise/sunset."
;; First, get the exact moment of local noon.
(let* ((exact-local-noon (solar-exact-local-noon date))
@@ -478,21 +476,20 @@ Corresponding value is nil if there is no sunrise/sunset."
(car (cdr (cdr l))))))
(defun solar-julian-ut-centuries (date)
- "Number of Julian centuries elapsed since 1 Jan, 2000 at noon U.T. for Gregorian DATE."
+ "Number of Julian centuries since 1 Jan, 2000 at noon UT for Gregorian DATE."
(/ (- (calendar-absolute-from-gregorian date)
(calendar-absolute-from-gregorian '(1 1.5 2000)))
36525.0))
-(defun solar-ephemeris-time(time)
+(defun solar-ephemeris-time (time)
"Ephemeris Time at moment TIME.
-
TIME is a pair with the first component being the number of Julian centuries
elapsed at 0 Universal Time, and the second component being the universal
time. For instance, the pair corresponding to November 28, 1995 at 16 UT is
-\(-0.040945 16), -0.040945 being the number of julian centuries elapsed between
+\(-0.040945 16), -0.040945 being the number of Julian centuries elapsed between
Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT.
-Result is in julian centuries of ephemeris time."
+Result is in Julian centuries of ephemeris time."
(let* ((t0 (car time))
(ut (car (cdr time)))
(t1 (+ t0 (/ (/ ut 24.0) 36525)))
@@ -501,18 +498,14 @@ Result is in julian centuries of ephemeris time."
(+ t1 (/ (/ dt 86400) 36525))))
(defun solar-date-next-longitude (d l)
- "First moment on or after Julian day number D when sun's longitude is a
-multiple of L degrees at calendar-location-name with that location's
-local time (including any daylight saving rules).
-
-L must be an integer divisor of 360.
-
-Result is in local time expressed astronomical (Julian) day numbers.
-
-The values of calendar-daylight-savings-starts,
-calendar-daylight-savings-starts-time, calendar-daylight-savings-ends,
-calendar-daylight-savings-ends-time, calendar-daylight-time-offset, and
-calendar-time-zone are used to interpret local time."
+ "First time after day D when solar longitude is a multiple of L degrees.
+D is a Julian day number. L must be an integer divisor of 360.
+The result is for `calendar-location-name', and is in local time
+\(including any daylight saving rules) expressed in astronomical (Julian)
+day numbers. The values of `calendar-daylight-savings-starts',
+`calendar-daylight-savings-starts-time', `calendar-daylight-savings-ends',
+`calendar-daylight-savings-ends-time', `calendar-daylight-time-offset',
+and `calendar-time-zone' are used to interpret local time."
(let* ((long)
(start d)
(start-long (solar-longitude d))
@@ -536,24 +529,23 @@ calendar-time-zone are used to interpret local time."
(setq end-long long)))
(/ (+ start end) 2.0)))
-(defun solar-horizontal-coordinates
- (time latitude longitude for-sunrise-sunset)
+(defun solar-horizontal-coordinates (time latitude longitude sunrise-flag)
"Azimuth and height of the sun at TIME, LATITUDE, and LONGITUDE.
-
-TIME is a pair with the first component being the number of Julian centuries
-elapsed at 0 Universal Time, and the second component being the universal
-time. For instance, the pair corresponding to November 28, 1995 at 16 UT is
-\(-0.040945 16), -0.040945 being the number of julian centuries elapsed between
-Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT.
-
-The azimuth is given in degrees as well as the height (between -180 and 180)."
- (let* ((ut (car (cdr time)))
- (ec (solar-equatorial-coordinates time for-sunrise-sunset))
+TIME is a pair with the first component being the number of
+Julian centuries elapsed at 0 Universal Time, and the second
+component being the universal time. For instance, the pair
+corresponding to November 28, 1995 at 16 UT is (-0.040945 16),
+-0.040945 being the number of Julian centuries elapsed between
+Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT. SUNRISE-FLAG
+is passed to `solar-ecliptic-coordinates'. Azimuth and
+height (between -180 and 180) are both in degrees."
+ (let* ((ut (cadr time))
+ (ec (solar-equatorial-coordinates time sunrise-flag))
(st (+ solar-sidereal-time-greenwich-midnight
(* ut 1.00273790935)))
;; Hour angle (in degrees).
(ah (- (* st 15) (* 15 (car ec)) (* -1 (calendar-longitude))))
- (de (car (cdr ec)))
+ (de (cadr ec))
(azimuth (solar-atn2 (- (* (solar-cosine-degrees ah)
(solar-sin-degrees latitude))
(* (solar-tangent-degrees de)
@@ -567,24 +559,26 @@ The azimuth is given in degrees as well as the height (between -180 and 180)."
(if (> height 180) (setq height (- height 360)))
(list azimuth height)))
-(defun solar-equatorial-coordinates (time for-sunrise-sunset)
+(defun solar-equatorial-coordinates (time sunrise-flag)
"Right ascension (in hours) and declination (in degrees) of the sun at TIME.
-
-TIME is a pair with the first component being the number of Julian centuries
-elapsed at 0 Universal Time, and the second component being the universal
-time. For instance, the pair corresponding to November 28, 1995 at 16 UT is
-\(-0.040945 16), -0.040945 being the number of julian centuries elapsed between
-Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT."
+TIME is a pair with the first component being the number of
+Julian centuries elapsed at 0 Universal Time, and the second
+component being the universal time. For instance, the pair
+corresponding to November 28, 1995 at 16 UT is (-0.040945 16),
+-0.040945 being the number of Julian centuries elapsed between
+Jan 1, 2000 at 12 UT and November 28, 1995 at 0 UT. SUNRISE-FLAG is passed
+to `solar-ecliptic-coordinates'."
(let* ((tm (solar-ephemeris-time time))
- (ec (solar-ecliptic-coordinates tm for-sunrise-sunset)))
+ (ec (solar-ecliptic-coordinates tm sunrise-flag)))
(list (solar-right-ascension (car ec) (car (cdr ec)))
(solar-declination (car ec) (car (cdr ec))))))
-(defun solar-ecliptic-coordinates (time for-sunrise-sunset)
- "Apparent longitude of the sun, ecliptic inclination, (both in degrees)
-equation of time (in hours) and nutation in longitude (in seconds)
-at moment `time', expressed in julian centuries of Ephemeris Time
-since January 1st, 2000, at 12 ET."
+(defun solar-ecliptic-coordinates (time sunrise-flag)
+ "Return solar longitude, ecliptic inclination, equation of time, nutation.
+Values are for TIME in Julian centuries of Ephemeris Time since
+January 1st, 2000, at 12 ET. Longitude and inclination are in
+degrees, equation of time in hours, and nutation in seconds of longitude.
+If SUNRISE-FLAG is non-nil, only calculate longitude and inclination."
(let* ((l (+ 280.46645
(* 36000.76983 time)
(* 0.0003032 time time))) ; sun mean longitude
@@ -610,35 +604,31 @@ since January 1st, 2000, at 12 ET."
(omega (+ 125.04
(* -1934.136 time)))
;; nut = nutation in longitude, measured in seconds of angle.
- (nut (if (not for-sunrise-sunset)
- (+ (* -17.20 (solar-sin-degrees omega))
- (* -1.32 (solar-sin-degrees (* 2 l)))
- (* -0.23 (solar-sin-degrees (* 2 ml)))
- (* 0.21 (solar-sin-degrees (* 2 omega))))
- nil))
- (ecc (if (not for-sunrise-sunset)
- (+ 0.016708617
- (* -0.000042037 time)
- (* -0.0000001236 time time)) ; eccentricity of earth's orbit
- nil))
- (app (+ L
+ (nut (unless sunrise-flag
+ (+ (* -17.20 (solar-sin-degrees omega))
+ (* -1.32 (solar-sin-degrees (* 2 l)))
+ (* -0.23 (solar-sin-degrees (* 2 ml)))
+ (* 0.21 (solar-sin-degrees (* 2 omega))))))
+ (ecc (unless sunrise-flag ; eccentricity of earth's orbit
+ (+ 0.016708617
+ (* -0.000042037 time)
+ (* -0.0000001236 time time))))
+ (app (+ L ; apparent longitude of sun
-0.00569
(* -0.00478
- (solar-sin-degrees omega)))) ; apparent longitude of sun
- (y (if (not for-sunrise-sunset)
- (* (solar-tangent-degrees (/ i 2))
- (solar-tangent-degrees (/ i 2)))
- nil))
+ (solar-sin-degrees omega))))
+ (y (unless sunrise-flag
+ (* (solar-tangent-degrees (/ i 2))
+ (solar-tangent-degrees (/ i 2)))))
;; Equation of time, in hours.
- (time-eq (if (not for-sunrise-sunset)
+ (time-eq (unless sunrise-flag
(/ (* 12 (+ (* y (solar-sin-degrees (* 2 l)))
(* -2 ecc (solar-sin-degrees m))
(* 4 ecc y (solar-sin-degrees m)
(solar-cosine-degrees (* 2 l)))
(* -0.5 y y (solar-sin-degrees (* 4 l)))
(* -1.25 ecc ecc (solar-sin-degrees (* 2 m)))))
- 3.1415926535)
- nil)))
+ 3.1415926535))))
(list app i time-eq nut)))
(defconst solar-data-list
@@ -691,16 +681,16 @@ since January 1st, 2000, at 12 ET."
(10 2.21 46941.14)
(10 3.59 -68.29)
(10 1.50 21463.25)
- (10 2.55 157208.40)))
+ (10 2.55 157208.40))
+ "Data used for calculation of solar longitude.")
(defun solar-longitude (d)
"Longitude of sun on astronomical (Julian) day number D.
-Accurary is about 0.0006 degree (about 365.25*24*60*0.0006/360 = 1 minutes).
-
-The values of calendar-daylight-savings-starts,
-calendar-daylight-savings-starts-time, calendar-daylight-savings-ends,
-calendar-daylight-savings-ends-time, calendar-daylight-time-offset, and
-calendar-time-zone are used to interpret local time."
+Accuracy is about 0.0006 degree (about 365.25*24*60*0.0006/360 = 1 minutes).
+The values of `calendar-daylight-savings-starts',
+`calendar-daylight-savings-starts-time', `calendar-daylight-savings-ends',
+`calendar-daylight-savings-ends-time', `calendar-daylight-time-offset', and
+`calendar-time-zone' are used to interpret local time."
(let* ((a-d (calendar-absolute-from-astro d))
;; Get Universal Time.
(date (calendar-astro-from-absolute
@@ -736,11 +726,9 @@ calendar-time-zone are used to interpret local time."
(mod (radians-to-degrees (+ longitude aberration nutation)) 360.0)))
(defun solar-ephemeris-correction (year)
- "Ephemeris time minus Universal Time during Gregorian year.
-Result is in days.
-
-For the years 1800-1987, the maximum error is 1.9 seconds.
-For the other years, the maximum error is about 30 seconds."
+ "Ephemeris time minus Universal Time during Gregorian YEAR.
+Result is in days. For the years 1800-1987, the maximum error is
+1.9 seconds. For the other years, the maximum error is about 30 seconds."
(cond ((and (<= 1988 year) (< year 2020))
(/ (+ year -2000 67.0) 60.0 60.0 24.0))
((and (<= 1900 year) (< year 1988))
@@ -797,9 +785,7 @@ For the other years, the maximum error is about 30 seconds."
(/ second 60.0 60.0 24.0)))))
(defun solar-sidereal-time (t0)
- "Sidereal time (in hours) in Greenwich.
-
-At T0=Julian centuries of universal time.
+ "Sidereal time (in hours) in Greenwich at T0 Julian centuries.
T0 must correspond to 0 hours UT."
(let* ((mean-sid-time (+ 6.6973746
(* 2400.051337 t0)
@@ -822,17 +808,16 @@ T0 must correspond to 0 hours UT."
(defun solar-date-to-et (date ut)
"Ephemeris Time at Gregorian DATE at Universal Time UT (in hours).
-Expressed in julian centuries of Ephemeris Time."
+Expressed in Julian centuries of Ephemeris Time."
(let ((t0 (solar-julian-ut-centuries date)))
(solar-ephemeris-time (list t0 ut))))
;;;###autoload
(defun sunrise-sunset (&optional arg)
"Local time of sunrise and sunset for today. Accurate to a few seconds.
-If called with an optional prefix argument, prompt for date.
-
-If called with an optional double prefix argument, prompt for longitude,
-latitude, time zone, and date, and always use standard time.
+If called with an optional prefix argument ARG, prompt for date.
+If called with an optional double prefix argument, prompt for
+longitude, latitude, time zone, and date, and always use standard time.
This function is suitable for execution in a .emacs file."
(interactive "p")
@@ -896,33 +881,38 @@ This function is suitable for execution in a .emacs file."
(if pop-up-windows
"Type \\[delete-other-windows] to remove temp window."
"Type \\[switch-to-buffer] RET to remove temp window.")
- "Type \\[switch-to-buffer-other-window] RET to restore old contents of temp window."))))))
+ "Type \\[switch-to-buffer-other-window] RET to restore old \
+contents of temp window."))))))
(defun calendar-sunrise-sunset ()
"Local time of sunrise and sunset for date under cursor.
Accurate to a few seconds."
(interactive)
- (if (not (and calendar-latitude calendar-longitude calendar-time-zone))
+ (or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(let ((date (calendar-cursor-to-date t)))
(message "%s: %s"
(calendar-date-string date t t)
(solar-sunrise-sunset-string date))))
+(defvar date)
+
+;; To be called from list-sexp-diary-entries, where DATE is bound.
(defun diary-sunrise-sunset ()
"Local time of sunrise and sunset as a diary entry.
Accurate to a few seconds."
- (if (not (and calendar-latitude calendar-longitude calendar-time-zone))
+ (or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(solar-sunrise-sunset-string date))
+;; To be called from list-sexp-diary-entries, where DATE is bound.
(defun diary-sabbath-candles (&optional mark)
"Local time of candle lighting diary entry--applies if date is a Friday.
No diary entry if there is no sunset on that date.
An optional parameter MARK specifies a face or single-character string to
use when highlighting the day in the calendar."
- (if (not (and calendar-latitude calendar-longitude calendar-time-zone))
+ (or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(if (= (% (calendar-absolute-from-gregorian date) 7) 5) ; Friday
(let* ((sunset (car (cdr (solar-sunrise-sunset date))))
@@ -960,15 +950,14 @@ use when highlighting the day in the calendar."
(12 287.11 31931.756)
(12 320.81 34777.259)
(9 227.73 1222.114)
- (8 15.45 16859.074)))
+ (8 15.45 16859.074))
+ "Data for solar equinox/solstice calculations.")
(defun solar-equinoxes/solstices (k year)
"Date of equinox/solstice K for YEAR.
K=0, spring equinox; K=1, summer solstice; K=2, fall equinox;
-K=3, winter solstice.
-RESULT is a gregorian local date.
-
-Accurate to less than a minute between 1951 and 2050."
+K=3, winter solstice. RESULT is a Gregorian local date.
+Accurate to within a minute between 1951 and 2050."
(let* ((JDE0 (solar-mean-equinoxes/solstices k year))
(T (/ (- JDE0 2451545.0) 36525))
(W (- (* 35999.373 T) 2.47))