Add reference to new Calendrical Calculations book.
[bpt/emacs.git] / lisp / calendar / solar.el
index 406107b..f9183a4 100644 (file)
@@ -1,6 +1,6 @@
 ;;; solar.el --- calendar functions for solar events.
 
-;; Copyright (C) 1992, 1993, 1995 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1993, 1995, 1997 Free Software Foundation, Inc.
 
 ;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
 ;;     Denis B. Roegel <Denis.Roegel@loria.fr>
 ;;    2. Equinox/solstice times will be accurate to the minute for years
 ;;       1951--2050.  For other years the times will be within +/- 1 minute.
 
+;; Technical details of all the calendrical calculations can be found in
+;; ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold,
+;; Cambridge University Press (1997).
+
 ;; Comments, corrections, and improvements should be sent to
 ;;  Edward M. Reingold               Department of Computer Science
 ;;  (217) 333-6733                   University of Illinois at Urbana-Champaign
@@ -63,7 +67,7 @@
 (require 'cal-julian)
 
 ;;;###autoload
-(defvar calendar-time-display-form
+(defcustom calendar-time-display-form
   '(12-hours ":" minutes am-pm
     (if time-zone " (") time-zone (if time-zone ")"))
   "*The pseudo-pattern that governs the way a time of day is formatted.
@@ -77,10 +81,12 @@ For example, the form
   '(24-hours \":\" minutes
     (if time-zone \" (\") time-zone (if time-zone \")\"))
 
-would give military-style times like `21:07 (UTC)'.")
+would give military-style times like `21:07 (UTC)'."
+  :type 'sexp
+  :group 'calendar)
 
 ;;;###autoload
-(defvar calendar-latitude nil
+(defcustom calendar-latitude nil
   "*Latitude of `calendar-location-name' in degrees.
 
 The value can be either a decimal fraction (one place of accuracy is
@@ -88,10 +94,19 @@ 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
 York City.
 
-This variable should be set in site-local.el.")
+This variable should be set in `site-start'.el."
+  :type '(choice (const nil)
+                (number :tag "Exact")
+                (vector :value [0 0 north]
+                        (integer :tag "Degrees")
+                        (integer :tag "Minutes")
+                        (choice :tag "Position"
+                                (const north)
+                                (const south))))
+  :group 'calendar)
 
 ;;;###autoload
-(defvar calendar-longitude nil
+(defcustom calendar-longitude nil
   "*Longitude of `calendar-location-name' in degrees.
 
 The value can be either a decimal fraction (one place of accuracy is
@@ -99,7 +114,16 @@ 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
 York City.
 
-This variable should be set in site-start.el.")
+This variable should be set in `site-start'.el."
+  :type '(choice (const nil)
+                (number :tag "Exact")
+                (vector :value [0 0 west]
+                        (integer :tag "Degrees")
+                        (integer :tag "Minutes")
+                        (choice :tag "Position"
+                                (const east)
+                                (const west))))
+  :group 'calendar)
 
 (defsubst calendar-latitude ()
   "Convert calendar-latitude to a signed decimal fraction, if needed."
@@ -122,7 +146,7 @@ This variable should be set in site-start.el.")
         (- long)))))
 
 ;;;###autoload
-(defvar calendar-location-name
+(defcustom calendar-location-name
   '(let ((float-output-format "%.1f"))
      (format "%s%s, %s%s"
              (if (numberp calendar-latitude)
@@ -139,13 +163,15 @@ 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'.
+  "*Expression evaluating to name of `calendar-longitude', `calendar-latitude'.
 For example, \"New York City\".  Default value is just the latitude, longitude
 pair.
 
-This variable should be set in site-start.el.")
+This variable should be set in `site-start'.el."
+  :type 'sexp
+  :group 'calendar)
 
-(defvar solar-error 0.5
+(defcustom solar-error 0.5
 "*Tolerance (in minutes) for sunrise/sunset calculations.
 
 A larger value makes the calculations for sunrise/sunset faster, but less
@@ -156,7 +182,9 @@ It is useless to set the value smaller than 4*delta, where delta is the
 accuracy in the longitude of the sun (given by the function
 `solar-ecliptic-coordinates') in degrees since (delta/360) x (86400/60) = 4 x
 delta.  At present, delta = 0.01 degrees, so the value of the variable
-`solar-error' should be at least 0.04 minutes (about 2.5 seconds).")
+`solar-error' should be at least 0.04 minutes (about 2.5 seconds)."
+  :type 'number
+  :group 'calendar)
 
 (defvar solar-n-hemi-seasons
   '("Vernal Equinox" "Summer Solstice" "Autumnal Equinox" "Winter Solstice")
@@ -232,19 +260,20 @@ Returns nil if nothing was entered."
 
 (defun solar-atn2 (x y)
    "Arctan of point X, Y."
-   (if (= y 0)
-       (if (> x 0) 90 270)
-     (solar-arctan (/ x y) y)))
+   (if (= x 0)
+       (if (> y 0) 90 270)
+     (solar-arctan (/ y x) x)))
 
 (defun solar-arccos (x)
-  "Arcos of X."
-  (let ((y (sqrt (- 1 (* x x)))))
-    (solar-arctan (/ y x) (solar-xy-to-quadrant x y))))
+     "Arcos of X."
+     (let ((y (sqrt (- 1 (* x x)))))
+       (solar-atn2 x y)))
 
 (defun solar-arcsin (y)
-  "Arcsin of Y."
-  (let ((x (sqrt (- 1 (* y y)))))
-    (solar-arctan (/ y x) (solar-xy-to-quadrant x y))))
+     "Arcsin of Y."
+     (let ((x (sqrt (- 1 (* y y)))))
+       (solar-atn2 x y)
+       ))
 
 (defsubst solar-degrees-to-hours (degrees)
   "Convert DEGREES to hours."
@@ -276,7 +305,7 @@ 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.
 
 Coordinates are included because this function is called with latitude=10
@@ -290,8 +319,9 @@ degrees to find out if polar regions have 24 hours of sun or only night."
           (setq day-length 24)
           (setq day-length 0))
         (setq day-length (- set-time rise-time)))
-    (list (+ rise-time (/ calendar-time-zone 60.0)) 
-          (+ set-time (/ calendar-time-zone 60.0)) day-length)))
+    (list (if rise-time (+ rise-time (/ calendar-time-zone 60.0)) nil)
+          (if set-time (+ set-time (/ calendar-time-zone 60.0)) nil)
+          day-length)))
 
 (defun solar-moment (direction latitude longitude time)
   "Sunrise/sunset at location.
@@ -301,7 +331,7 @@ 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.
 
 Uses binary search."
@@ -336,7 +366,7 @@ Uses binary search."
                     (if (< hut -0.61) (setq utmin utmoment))
                     (if (> hut -0.61) (setq utmax utmoment))
                    )
-                (setq possible 0)) the sun never rises
+                (setq possible 0)) the sun never rises
                 (setq possible 0)) ; the sun never sets
      (if (equal possible 0) nil utmoment)))
 
@@ -364,7 +394,7 @@ Format used is given by `calendar-time-display-form'."
 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)
-         (ut (- 12.0 (/ calendar-longitude 15)))
+         (ut (- 12.0 (/ (calendar-longitude) 15)))
          (te (solar-time-equation date ut)))
     (setq ut (- ut te))
     (if (>= ut 24)
@@ -398,7 +428,7 @@ Corresponding value is nil if there is no sunrise/sunset."
                  (solar-sunrise-and-sunset 
                   (list t0 (car (cdr exact-local-noon)))
                   10.0
-                  calendar-longitude)))
+                  (calendar-longitude))))
          ; store the spring/summer information,
          ; compute sunrise and sunset (two first components of rise-set).
          ; length of day is the third component (it is only the difference
@@ -409,8 +439,8 @@ Corresponding value is nil if there is no sunrise/sunset."
                   (if (> (car (cdr (cdr equator-rise-set))) 12) 1 0))
             (solar-sunrise-and-sunset 
              (list t0 (car (cdr exact-local-noon)))
-             calendar-latitude
-             calendar-longitude)))
+             (calendar-latitude)
+             (calendar-longitude))))
          (rise (car rise-set))
          (adj-rise (if rise (dst-adjust-time date rise) nil))
          (set (car (cdr rise-set)))
@@ -447,7 +477,7 @@ Corresponding value is nil if there is no 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
+\(-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."
@@ -501,7 +531,7 @@ calendar-time-zone are used to interpret local 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.
 
 The azimuth is given in degrees as well as the height (between -180 and 180)."
@@ -509,14 +539,14 @@ The azimuth is given in degrees as well as the height (between -180 and 180)."
          (ec (solar-equatorial-coordinates time for-sunrise-sunset))
          (st (+ solar-sidereal-time-greenwich-midnight
                 (* ut 1.00273790935)))
-         (ah (- (* st 15) (* 15 (car ec)) (* -1 calendar-longitude)))
+         (ah (- (* st 15) (* 15 (car ec)) (* -1 (calendar-longitude))))
                        ; hour angle (in degrees)
          (de (car (cdr ec)))
-         (azimuth (solar-atn2 (solar-sin-degrees ah)
-                             (- (* (solar-cosine-degrees ah)
+         (azimuth (solar-atn2 (- (* (solar-cosine-degrees ah)
                                    (solar-sin-degrees latitude))
                                 (* (solar-tangent-degrees de)
-                                   (solar-cosine-degrees latitude)))))
+                                   (solar-cosine-degrees latitude)))
+                              (solar-sin-degrees ah)))
          (height (solar-arcsin 
                   (+ (* (solar-sin-degrees latitude) (solar-sin-degrees de))
                      (* (solar-cosine-degrees latitude)
@@ -531,7 +561,7 @@ The azimuth is given in degrees as well as the height (between -180 and 180)."
 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."
    (let* ((tm (solar-ephemeris-time time)) 
           (ec (solar-ecliptic-coordinates tm for-sunrise-sunset)))
@@ -845,10 +875,11 @@ This function is suitable for execution in a .emacs file."
         (msg (format "%s: %s" date-string time-string))
         (one-window (one-window-p t)))
    (if (<= (length msg) (frame-width))
-       (message msg)
+       (message "%s" msg)
      (with-output-to-temp-buffer "*temp*"
        (princ (concat date-string "\n" time-string)))
-     (message (substitute-command-keys
+     (message "%s"
+             (substitute-command-keys
                (if one-window
                    (if pop-up-windows
                        "Type \\[delete-other-windows] to remove temp window."
@@ -880,13 +911,11 @@ No diary entry if there is no sunset on that date."
       (solar-setup))
   (if (= (% (calendar-absolute-from-gregorian date) 7) 5);;  Friday
       (let* ((sunset (car (cdr (solar-sunrise-sunset date))))
-            (light (if sunset
-                        (dst-adjust-time
-                         date
-                         (- (car sunset) (/ 18.0 60.0))))))
-        (if (and light (calendar-date-equal date (car light)))
+                  (light (if sunset
+                        (cons (- (car sunset) (/ 18.0 60.0)) (cdr sunset)))))
+        (if sunset
             (format "%s Sabbath candle lighting"
-                    (apply 'solar-time-string (cdr light)))))))
+                    (apply 'solar-time-string light))))))
 
 (defun solar-equinoxes/solstices (k year)
   "Date of equinox/solstice K for YEAR.