From af8ff3a5efbe1173d1a188e6d8d884c2bc809e9c Mon Sep 17 00:00:00 2001 From: hrbrmstr Date: Tue, 31 Dec 2019 16:23:30 -0500 Subject: [PATCH] plain C version --- .Rbuildignore | 1 + DESCRIPTION | 21 +- LICENSE | 2 + LICENSE.md | 21 ++ NAMESPACE | 11 +- R/daybreak-package.R | 12 +- R/daybreak-wrappers.R | 132 ++++++++++ R/validators.R | 15 ++ README.Rmd | 39 +++ README.md | 188 +++++++++++++++ inst/tinytest/test_daybreak.R | 119 +++++++++ man/astronomical_twilight.Rd | 23 ++ man/civil_twilight.Rd | 23 ++ man/day_astronomical_twilight_length.Rd | 23 ++ man/day_civil_twilight_length.Rd | 23 ++ man/day_length.Rd | 23 ++ man/day_nautical_twilight_length.Rd | 23 ++ man/daybreak.Rd | 7 +- man/nautical_twilight.Rd | 23 ++ man/sun_rise_set.Rd | 23 ++ src/daybreak-main.c | 176 ++++++++++++++ src/init.c | 32 +++ src/sunriset.c | 414 ++++++++++++++++++++++++++++++++ src/sunriset.h | 118 +++++++++ tests/tinytest.R | 5 + 25 files changed, 1479 insertions(+), 18 deletions(-) create mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 R/daybreak-wrappers.R create mode 100644 R/validators.R create mode 100644 README.md create mode 100644 inst/tinytest/test_daybreak.R create mode 100644 man/astronomical_twilight.Rd create mode 100644 man/civil_twilight.Rd create mode 100644 man/day_astronomical_twilight_length.Rd create mode 100644 man/day_civil_twilight_length.Rd create mode 100644 man/day_length.Rd create mode 100644 man/day_nautical_twilight_length.Rd create mode 100644 man/nautical_twilight.Rd create mode 100644 man/sun_rise_set.Rd create mode 100644 src/daybreak-main.c create mode 100644 src/init.c create mode 100644 src/sunriset.c create mode 100644 src/sunriset.h create mode 100644 tests/tinytest.R diff --git a/.Rbuildignore b/.Rbuildignore index c9a5c92..19cdbd8 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -19,3 +19,4 @@ ^CRAN-RELEASE$ ^appveyor\.yml$ ^tools$ +^LICENSE\.md$ diff --git a/DESCRIPTION b/DESCRIPTION index e285591..4b0cfd0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,24 +1,25 @@ Package: daybreak Type: Package -Title: daybreak title goes here otherwise CRAN checks fail +Title: Compute Sun Rise/Set Times, Start/End of Twilight, and the + Length of the Day at Any Date and Latitude Version: 0.1.0 Date: 2019-12-31 Authors@R: c( person("Bob", "Rudis", email = "bob@rud.is", role = c("aut", "cre"), - comment = c(ORCID = "0000-0001-5670-2640")) + comment = c(ORCID = "0000-0001-5670-2640")), + person("Paul", "Schlyter", role = "aut", comment = "sunriset lib") ) Maintainer: Bob Rudis -Description: A good description goes here otherwise CRAN checks fail. +Description: A wrapper for Paul Schlyter's C-based library for computing + sunrise, sunset, twilight start and end, plus the length of day for + a given data and coordinates. URL: https://gitlab.com/hrbrmstr/daybreak BugReports: https://gitlab.com/hrbrmstr/daybreak/issues Encoding: UTF-8 -License: AGPL -Suggests: - covr -Depends: +NeedsCompilation: yes +License: MIT + file LICENSE +Depends: R (>= 3.2.0) -Imports: - httr, - jsonlite Roxygen: list(markdown = TRUE) RoxygenNote: 7.0.2 +Suggests: tinytest diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b6a10f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2019 +COPYRIGHT HOLDER: Bob Rudis diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c36552c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2019 Bob Rudis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NAMESPACE b/NAMESPACE index 5b4b9ae..4b80233 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,11 @@ # Generated by roxygen2: do not edit by hand -import(httr) -importFrom(jsonlite,fromJSON) +export(astronomical_twilight) +export(civil_twilight) +export(day_astronomical_twilight_length) +export(day_civil_twilight_length) +export(day_length) +export(day_nautical_twilight_length) +export(nautical_twilight) +export(sun_rise_set) +useDynLib(daybreak, .registration = TRUE) diff --git a/R/daybreak-package.R b/R/daybreak-package.R index 7a94845..2d02832 100644 --- a/R/daybreak-package.R +++ b/R/daybreak-package.R @@ -1,9 +1,13 @@ -#' ... -#' +#' Compute Sun Rise/Set Times, Start/End of Twilight, and the +#' Length of the Day at Any Date and Latitude +#' +#' A wrapper for Paul Schlyter's C-based library for computing +#' sunrise, sunset, twilight start and end, plus the length of day for +#' a given data and coordinates. +#' #' @md #' @name daybreak #' @keywords internal #' @author Bob Rudis (bob@@rud.is) -#' @import httr -#' @importFrom jsonlite fromJSON +#' @useDynLib daybreak, .registration = TRUE "_PACKAGE" diff --git a/R/daybreak-wrappers.R b/R/daybreak-wrappers.R new file mode 100644 index 0000000..a94aa80 --- /dev/null +++ b/R/daybreak-wrappers.R @@ -0,0 +1,132 @@ +#' Length of day +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) length of day +#' @export +#' @examples +#' day_length("2019-12-31", -70.8636, 43.2683) +day_length <- function(date = Sys.Date(), lon, lat) { + + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + + .Call("r_day_length", date$year+1900L, date$mon+1L, date$mday, lon, lat) + +} + + +#' Length of civil twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) day civil twilight length +#' @export +#' @examples +#' day_length("2019-12-31", -70.8636, 43.2683) +day_civil_twilight_length <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_day_civil_twilight_length", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Length of nautical twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) day nautical twilight length +#' @export +#' @examples +#' day_nautical_twilight_length("2019-12-31", -70.8636, 43.2683) +day_nautical_twilight_length <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_day_nautical_twilight_length", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Length of astronomical twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) astronomical twilight length +#' @export +#' @examples +#' day_astronomical_twilight_length("2019-12-31", -70.8636, 43.2683) +day_astronomical_twilight_length <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_day_astronomical_twilight_length", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Sun rise/set times +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) sunrise/sunset +#' @export +#' @examples +#' sun_rise_set("2019-12-31", -70.8636, 43.2683) +sun_rise_set <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_sun_rise_set", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Civil twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) civil twilight +#' @export +#' @examples +#' civil_twilight("2019-12-31", -70.8636, 43.2683) +civil_twilight <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_civil_twilight", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Nautical twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) nautical twilight +#' @export +#' @examples +#' nautical_twilight("2019-12-31", -70.8636, 43.2683) +nautical_twilight <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_nautical_twilight", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + +#' Astronomical twilight +#' +#' @param date The date to compute the length for. An R [DateTimeClasses] object +#' or something that can be coerced into one by [as.POSIXlt()]. +#' @param lon,lat longitude & latitude +#' @return (dbl) astronomical twilight +#' @export +#' @examples +#' astronomical_twilight("2019-12-31", -70.8636, 43.2683) +astronomical_twilight <- function(date, lon, lat) { + date <- valid_date(date) + lon <- valid_lon(lon) + lat <- valid_lat(lat) + .Call("r_astronomical_twilight", date$year+1900L, date$mon+1L, date$mday, lon, lat) +} + diff --git a/R/validators.R b/R/validators.R new file mode 100644 index 0000000..b13c598 --- /dev/null +++ b/R/validators.R @@ -0,0 +1,15 @@ +valid_date <- function(date) { + as.POSIXlt(date[1]) +} + +valid_lon <- function(lon) { + lon <- lon[1] + stopifnot(lon >= -180 & lon <= 180) + lon +} + +valid_lat <- function(lat) { + lat <- lat[1] + stopifnot(lat >= -90 & lat <= 90) + lat +} diff --git a/README.Rmd b/README.Rmd index 2ffe897..85ceb4e 100644 --- a/README.Rmd +++ b/README.Rmd @@ -39,6 +39,45 @@ packageVersion("daybreak") ``` +Near me: + +```{r ex1} +day_length("2019-12-31", -70.8636, 43.2683) +day_civil_twilight_length("2019-12-31", -70.8636, 43.2683) +day_nautical_twilight_length("2019-12-31", -70.8636, 43.2683) +day_astronomical_twilight_length("2019-12-31", -70.8636, 43.2683) +sun_rise_set("2019-12-31", -70.8636, 43.2683) +civil_twilight("2019-12-31", -70.8636, 43.2683) +nautical_twilight("2019-12-31", -70.8636, 43.2683) +astronomical_twilight("2019-12-31", -70.8636, 43.2683) +``` + +Tromsø, Norway (Winter) + +```{r ex2} +day_length("2019-12-31", 18.9553, 69.6492) +day_civil_twilight_length("2019-12-31", 18.9553, 69.6492) +day_nautical_twilight_length("2019-12-31", 18.9553, 69.6492) +day_astronomical_twilight_length("2019-12-31", 18.9553, 69.6492) +sun_rise_set("2019-12-31", 18.9553, 69.6492) +civil_twilight("2019-12-31", 18.9553, 69.6492) +nautical_twilight("2019-12-31", 18.9553, 69.6492) +astronomical_twilight("2019-12-31", 18.9553, 69.6492) +``` + +Tromsø, Norway (Summer) + +```{r ex3} +day_length("2019-06-01", 18.9553, 69.6492) +day_civil_twilight_length("2019-06-01", 18.9553, 69.6492) +day_nautical_twilight_length("2019-06-01", 18.9553, 69.6492) +day_astronomical_twilight_length("2019-06-01", 18.9553, 69.6492) +sun_rise_set("2019-06-01", 18.9553, 69.6492) +civil_twilight("2019-06-01", 18.9553, 69.6492) +nautical_twilight("2019-06-01", 18.9553, 69.6492) +astronomical_twilight("2019-06-01", 18.9553, 69.6492) +``` + ## daybreak Metrics ```{r cloc, echo=FALSE} diff --git a/README.md b/README.md new file mode 100644 index 0000000..cd48c31 --- /dev/null +++ b/README.md @@ -0,0 +1,188 @@ + +[![Project Status: Active – The project has reached a stable, usable +state and is being actively +developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +[![Signed +by](https://img.shields.io/badge/Keybase-Verified-brightgreen.svg)](https://keybase.io/hrbrmstr) +![Signed commit +%](https://img.shields.io/badge/Signed_Commits-100%25-lightgrey.svg) +[![Linux build +Status](https://travis-ci.org/hrbrmstr/daybreak.svg?branch=master)](https://travis-ci.org/hrbrmstr/daybreak) +![Minimal R +Version](https://img.shields.io/badge/R%3E%3D-3.2.0-blue.svg) +![License](https://img.shields.io/badge/License-MIT-blue.svg) + +# daybreak + +Compute Sun Rise/Set Times, Start/End of Twilight, and the Length of the +Day at Any Date and Latitude + +## Description + +A wrapper for Paul Schlyter’s C-based library for computing sunrise, +sunset, twilight start and end, plus the length of day for a given data +and coordinates. + +## What’s Inside The Tin + +The following functions are implemented: + + - `astronomical_twilight`: Astronomical twilight + - `civil_twilight`: Civil twilight + - `day_astronomical_twilight_length`: Length of astronomical twilight + - `day_civil_twilight_length`: Length of civil twilight + - `day_length`: Length of day + - `day_nautical_twilight_length`: Length of nautical twilight + - `nautical_twilight`: Nautical twilight + - `sun_rise_set`: Sun rise/set times + +## Installation + +``` r +remotes::install_git("https://git.rud.is/hrbrmstr/daybreak.git") +# or +remotes::install_git("https://git.sr.ht/~hrbrmstr/daybreak") +# or +remotes::install_gitlab("hrbrmstr/daybreak") +# or +remotes::install_bitbucket("hrbrmstr/daybreak") +``` + +NOTE: To use the ‘remotes’ install options you will need to have the +[{remotes} package](https://github.com/r-lib/remotes) installed. + +## Usage + +``` r +library(daybreak) + +# current version +packageVersion("daybreak") +## [1] '0.1.0' +``` + +Near me: + +``` r +day_length("2019-12-31", -70.8636, 43.2683) +## [1] 9.031444 +day_civil_twilight_length("2019-12-31", -70.8636, 43.2683) +## [1] 10.10834 +day_nautical_twilight_length("2019-12-31", -70.8636, 43.2683) +## [1] 11.30569 +day_astronomical_twilight_length("2019-12-31", -70.8636, 43.2683) +## [1] 12.45998 +sun_rise_set("2019-12-31", -70.8636, 43.2683) +## $rise +## [1] 12.25761 +## +## $set +## [1] 21.28906 +civil_twilight("2019-12-31", -70.8636, 43.2683) +## $start +## [1] 11.71917 +## +## $end +## [1] 21.82751 +nautical_twilight("2019-12-31", -70.8636, 43.2683) +## $start +## [1] 11.12049 +## +## $end +## [1] 22.42618 +astronomical_twilight("2019-12-31", -70.8636, 43.2683) +## $start +## [1] 10.54335 +## +## $end +## [1] 23.00332 +``` + +Tromsø, Norway (Winter) + +``` r +day_length("2019-12-31", 18.9553, 69.6492) +## [1] 0 +day_civil_twilight_length("2019-12-31", 18.9553, 69.6492) +## [1] 4.613116 +day_nautical_twilight_length("2019-12-31", 18.9553, 69.6492) +## [1] 8.000323 +day_astronomical_twilight_length("2019-12-31", 18.9553, 69.6492) +## [1] 10.5871 +sun_rise_set("2019-12-31", 18.9553, 69.6492) +## $rise +## [1] NA +## +## $set +## [1] NA +civil_twilight("2019-12-31", 18.9553, 69.6492) +## $start +## [1] 8.476866 +## +## $end +## [1] 13.08998 +nautical_twilight("2019-12-31", 18.9553, 69.6492) +## $start +## [1] 6.783262 +## +## $end +## [1] 14.78359 +astronomical_twilight("2019-12-31", 18.9553, 69.6492) +## $start +## [1] 5.489872 +## +## $end +## [1] 16.07698 +``` + +Tromsø, Norway (Summer) + +``` r +day_length("2019-06-01", 18.9553, 69.6492) +## [1] 24 +day_civil_twilight_length("2019-06-01", 18.9553, 69.6492) +## [1] 24 +day_nautical_twilight_length("2019-06-01", 18.9553, 69.6492) +## [1] 24 +day_astronomical_twilight_length("2019-06-01", 18.9553, 69.6492) +## [1] 24 +sun_rise_set("2019-06-01", 18.9553, 69.6492) +## $rise +## [1] NA +## +## $set +## [1] NA +civil_twilight("2019-06-01", 18.9553, 69.6492) +## $start +## [1] NA +## +## $end +## [1] NA +nautical_twilight("2019-06-01", 18.9553, 69.6492) +## $start +## [1] NA +## +## $end +## [1] NA +astronomical_twilight("2019-06-01", 18.9553, 69.6492) +## $start +## [1] NA +## +## $end +## [1] NA +``` + +## daybreak Metrics + +| Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) | +| :----------- | -------: | ---: | --: | ---: | ----------: | ---: | -------: | ---: | +| C | 3 | 0.33 | 267 | 0.66 | 110 | 0.60 | 245 | 0.60 | +| R | 4 | 0.44 | 65 | 0.16 | 16 | 0.09 | 84 | 0.20 | +| C/C++ Header | 1 | 0.11 | 39 | 0.10 | 35 | 0.19 | 44 | 0.11 | +| Rmd | 1 | 0.11 | 32 | 0.08 | 21 | 0.12 | 37 | 0.09 | + +## Code of Conduct + +Please note that this project is released with a Contributor Code of +Conduct. By participating in this project you agree to abide by its +terms. diff --git a/inst/tinytest/test_daybreak.R b/inst/tinytest/test_daybreak.R new file mode 100644 index 0000000..78298ce --- /dev/null +++ b/inst/tinytest/test_daybreak.R @@ -0,0 +1,119 @@ +expect_equivalent( + day_length("2019-12-31", -70.8636, 43.2683), + 9.03144433072786 +) + +expect_equivalent( + day_civil_twilight_length("2019-12-31", -70.8636, 43.2683), + 10.1083411766176 +) + +expect_equivalent( + day_nautical_twilight_length("2019-12-31", -70.8636, 43.2683), + 11.3056891617088 +) + +expect_equivalent( + day_astronomical_twilight_length("2019-12-31", -70.8636, 43.2683), + 12.459975565969 +) + +expect_equivalent( + sun_rise_set("2019-12-31", -70.8636, 43.2683), + list(rise = 12.2576146717076, set = 21.2890590024355) +) + +expect_equivalent( + civil_twilight("2019-12-31", -70.8636, 43.2683), + list(start = 11.7191662487627, end = 21.8275074253803) +) + +expect_equivalent( + nautical_twilight("2019-12-31", -70.8636, 43.2683), + list(start = 11.1204922562171, end = 22.4261814179259) +) + +expect_equivalent( + astronomical_twilight("2019-12-31", -70.8636, 43.2683), + list(start = 10.543349054087, end = 23.003324620056) +) + +expect_equivalent( + day_length("2019-12-31", 18.9553, 69.6492), + 0 +) + +expect_equivalent( + day_civil_twilight_length("2019-12-31", 18.9553, 69.6492), + 4.61311561714866 +) + +expect_equivalent( + day_nautical_twilight_length("2019-12-31", 18.9553, 69.6492), + 8.00032348106164 +) + +expect_equivalent( + day_astronomical_twilight_length("2019-12-31", 18.9553, 69.6492), + 10.5871031550164 +) + +expect_equivalent( + sun_rise_set("2019-12-31", 18.9553, 69.6492), + list(rise = NA_real_, set = NA_real_) +) + +expect_equivalent( + civil_twilight("2019-12-31", 18.9553, 69.6492), + list(start = 8.47686578254252, end = 13.0899813996912) +) + +expect_equivalent( + nautical_twilight("2019-12-31", 18.9553, 69.6492), + list(start = 6.78326185058603, end = 14.7835853316477) +) + +expect_equivalent( + astronomical_twilight("2019-12-31", 18.9553, 69.6492), + list(start = 5.48987201360863, end = 16.0769751686251) +) + +expect_equivalent( + day_length("2019-06-01", 18.9553, 69.6492), + 24 +) + +expect_equivalent( + day_civil_twilight_length("2019-06-01", 18.9553, 69.6492), + 24 +) + +expect_equivalent( + day_nautical_twilight_length("2019-06-01", 18.9553, 69.6492), + 24 +) + +expect_equivalent( + day_astronomical_twilight_length("2019-06-01", 18.9553, 69.6492), + 24 +) + +expect_equivalent( + sun_rise_set("2019-06-01", 18.9553, 69.6492), + list(rise = NA_real_, set = NA_real_) +) + +expect_equivalent( + civil_twilight("2019-06-01", 18.9553, 69.6492), + list(start = NA_real_, end = NA_real_) +) + +expect_equivalent( + nautical_twilight("2019-06-01", 18.9553, 69.6492), + list(start = NA_real_, end = NA_real_) +) + +expect_equivalent( + astronomical_twilight("2019-06-01", 18.9553, 69.6492), + list(start = NA_real_, end = NA_real_) +) diff --git a/man/astronomical_twilight.Rd b/man/astronomical_twilight.Rd new file mode 100644 index 0000000..cba2b55 --- /dev/null +++ b/man/astronomical_twilight.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{astronomical_twilight} +\alias{astronomical_twilight} +\title{Astronomical twilight} +\usage{ +astronomical_twilight(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) astronomical twilight +} +\description{ +Astronomical twilight +} +\examples{ +astronomical_twilight("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/civil_twilight.Rd b/man/civil_twilight.Rd new file mode 100644 index 0000000..83e85b1 --- /dev/null +++ b/man/civil_twilight.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{civil_twilight} +\alias{civil_twilight} +\title{Civil twilight} +\usage{ +civil_twilight(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) civil twilight +} +\description{ +Civil twilight +} +\examples{ +civil_twilight("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/day_astronomical_twilight_length.Rd b/man/day_astronomical_twilight_length.Rd new file mode 100644 index 0000000..3389ee8 --- /dev/null +++ b/man/day_astronomical_twilight_length.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{day_astronomical_twilight_length} +\alias{day_astronomical_twilight_length} +\title{Length of astronomical twilight} +\usage{ +day_astronomical_twilight_length(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) astronomical twilight length +} +\description{ +Length of astronomical twilight +} +\examples{ +day_astronomical_twilight_length("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/day_civil_twilight_length.Rd b/man/day_civil_twilight_length.Rd new file mode 100644 index 0000000..a33a390 --- /dev/null +++ b/man/day_civil_twilight_length.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{day_civil_twilight_length} +\alias{day_civil_twilight_length} +\title{Length of civil twilight} +\usage{ +day_civil_twilight_length(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) day civil twilight length +} +\description{ +Length of civil twilight +} +\examples{ +day_length("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/day_length.Rd b/man/day_length.Rd new file mode 100644 index 0000000..274d780 --- /dev/null +++ b/man/day_length.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{day_length} +\alias{day_length} +\title{Length of day} +\usage{ +day_length(date = Sys.Date(), lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) length of day +} +\description{ +Length of day +} +\examples{ +day_length("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/day_nautical_twilight_length.Rd b/man/day_nautical_twilight_length.Rd new file mode 100644 index 0000000..7ad5481 --- /dev/null +++ b/man/day_nautical_twilight_length.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{day_nautical_twilight_length} +\alias{day_nautical_twilight_length} +\title{Length of nautical twilight} +\usage{ +day_nautical_twilight_length(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) day nautical twilight length +} +\description{ +Length of nautical twilight +} +\examples{ +day_nautical_twilight_length("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/daybreak.Rd b/man/daybreak.Rd index 25cc543..344083f 100644 --- a/man/daybreak.Rd +++ b/man/daybreak.Rd @@ -4,9 +4,12 @@ \name{daybreak} \alias{daybreak} \alias{daybreak-package} -\title{...} +\title{Compute Sun Rise/Set Times, Start/End of Twilight, and the +Length of the Day at Any Date and Latitude} \description{ -A good description goes here otherwise CRAN checks fail. +A wrapper for Paul Schlyter's C-based library for computing +sunrise, sunset, twilight start and end, plus the length of day for +a given data and coordinates. } \seealso{ Useful links: diff --git a/man/nautical_twilight.Rd b/man/nautical_twilight.Rd new file mode 100644 index 0000000..f071485 --- /dev/null +++ b/man/nautical_twilight.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{nautical_twilight} +\alias{nautical_twilight} +\title{Nautical twilight} +\usage{ +nautical_twilight(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) nautical twilight +} +\description{ +Nautical twilight +} +\examples{ +nautical_twilight("2019-12-31", -70.8636, 43.2683) +} diff --git a/man/sun_rise_set.Rd b/man/sun_rise_set.Rd new file mode 100644 index 0000000..9c102d0 --- /dev/null +++ b/man/sun_rise_set.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/daybreak-wrappers.R +\name{sun_rise_set} +\alias{sun_rise_set} +\title{Sun rise/set times} +\usage{ +sun_rise_set(date, lon, lat) +} +\arguments{ +\item{date}{The date to compute the length for. An R \link{DateTimeClasses} object +or something that can be coerced into one by \code{\link[=as.POSIXlt]{as.POSIXlt()}}.} + +\item{lon, lat}{longitude & latitude} +} +\value{ +(dbl) sunrise/sunset +} +\description{ +Sun rise/set times +} +\examples{ +sun_rise_set("2019-12-31", -70.8636, 43.2683) +} diff --git a/src/daybreak-main.c b/src/daybreak-main.c new file mode 100644 index 0000000..8d84ebf --- /dev/null +++ b/src/daybreak-main.c @@ -0,0 +1,176 @@ +#include +#include + +#include "sunriset.h" + +SEXP r_day_length(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + SEXP out = PROTECT(allocVector(REALSXP, 1)); + REAL(out)[0] = day_length( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat) + ); + UNPROTECT(1); + + return(out); + +} + +SEXP r_day_civil_twilight_length(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + SEXP out = PROTECT(allocVector(REALSXP, 1)); + REAL(out)[0] = day_civil_twilight_length( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat) + ); + UNPROTECT(1); + + return(out); + +} + +SEXP r_day_nautical_twilight_length(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + SEXP out = PROTECT(allocVector(REALSXP, 1)); + REAL(out)[0] = day_nautical_twilight_length( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat) + ); + UNPROTECT(1); + + return(out); + +} + +SEXP r_day_astronomical_twilight_length(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + SEXP out = PROTECT(allocVector(REALSXP, 1)); + REAL(out)[0] = day_astronomical_twilight_length( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat) + ); + UNPROTECT(1); + + return(out); + +} + +SEXP r_sun_rise_set(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + double rise, set; + + int res = sun_rise_set( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat), + &rise, &set + ); + + const char *names[] = { + "rise", + "set", + "" + }; + + SEXP out = PROTECT(mkNamed(VECSXP, names)); + + if (res == 0) { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(rise))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(set))); + } else { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(NA_REAL))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(NA_REAL))); + } + + UNPROTECT(3); + + return(out); + +} + +SEXP r_civil_twilight(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + double start, end; + + int res = civil_twilight( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat), + &start, &end + ); + + const char *names[] = { + "start", + "end", + "" + }; + + SEXP out = PROTECT(mkNamed(VECSXP, names)); + + if (res == 0) { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(start))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(end))); + } else { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(NA_REAL))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(NA_REAL))); + } + + UNPROTECT(3); + + return(out); + +} + +SEXP r_nautical_twilight(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + double start, end; + + int res = nautical_twilight( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat), + &start, &end + ); + + const char *names[] = { + "start", + "end", + "" + }; + + SEXP out = PROTECT(mkNamed(VECSXP, names)); + + if (res == 0) { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(start))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(end))); + } else { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(NA_REAL))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(NA_REAL))); + } + + UNPROTECT(3); + + return(out); + +} + +SEXP r_astronomical_twilight(SEXP year, SEXP month, SEXP day, SEXP lon, SEXP lat) { + + double start, end; + + int res = astronomical_twilight( + asInteger(year), asInteger(month), asInteger(day), asReal(lon), asReal(lat), + &start, &end + ); + + const char *names[] = { + "start", + "end", + "" + }; + + SEXP out = PROTECT(mkNamed(VECSXP, names)); + + if (res == 0) { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(start))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(end))); + } else { + SET_VECTOR_ELT(out, 0, PROTECT(ScalarReal(NA_REAL))); + SET_VECTOR_ELT(out, 1, PROTECT(ScalarReal(NA_REAL))); + } + + UNPROTECT(3); + + return(out); + +} diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..d68840d --- /dev/null +++ b/src/init.c @@ -0,0 +1,32 @@ +#include +#include +#include // for NULL +#include + +/* .Call calls */ +extern SEXP r_astronomical_twilight(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_civil_twilight(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_day_astronomical_twilight_length(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_day_civil_twilight_length(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_day_length(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_day_nautical_twilight_length(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_nautical_twilight(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_sun_rise_set(SEXP, SEXP, SEXP, SEXP, SEXP); + +static const R_CallMethodDef CallEntries[] = { + {"r_astronomical_twilight", (DL_FUNC) &r_astronomical_twilight, 5}, + {"r_civil_twilight", (DL_FUNC) &r_civil_twilight, 5}, + {"r_day_astronomical_twilight_length", (DL_FUNC) &r_day_astronomical_twilight_length, 5}, + {"r_day_civil_twilight_length", (DL_FUNC) &r_day_civil_twilight_length, 5}, + {"r_day_length", (DL_FUNC) &r_day_length, 5}, + {"r_day_nautical_twilight_length", (DL_FUNC) &r_day_nautical_twilight_length, 5}, + {"r_nautical_twilight", (DL_FUNC) &r_nautical_twilight, 5}, + {"r_sun_rise_set", (DL_FUNC) &r_sun_rise_set, 5}, + {NULL, NULL, 0} +}; + +void R_init_daybreak(DllInfo *dll) +{ + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/sunriset.c b/src/sunriset.c new file mode 100644 index 0000000..c209659 --- /dev/null +++ b/src/sunriset.c @@ -0,0 +1,414 @@ +/* +++Date last modified: 05-Jul-1997 */ +/* Updated comments, 05-Aug-2013 */ + +/* + +SUNRISET.C - computes Sun rise/set times, start/end of twilight, and + the length of the day at any date and latitude + +Written as DAYLEN.C, 1989-08-16 + +Modified to SUNRISET.C, 1992-12-01 +Split to a header file, 2017-12-10, by Joachim Nilsson + +(c) Paul Schlyter, 1989, 1992 + +Released to the public domain by Paul Schlyter, December 1992 + +*/ + +#include +#include + +#include "sunriset.h" + +/* A small test program */ +// #ifndef SUNRISET_LIB +// int main(void) +// { +// int year,month,day; +// double lon, lat; +// double daylen, civlen, nautlen, astrlen; +// double rise, set, civ_start, civ_end, naut_start, naut_end, +// astr_start, astr_end; +// int rs‘, civ, naut, astr; +// char buf[80]; +// +// printf( "Latitude (+ is north) and longitude (+ is east) : " ); +// fgets(buf, 80, stdin); +// sscanf(buf, "%lf %lf", &lat, &lon ); +// +// for(;;) +// { +// printf( "Input date ( yyyy mm dd ) (ctrl-C exits): " ); +// fgets(buf, 80, stdin); +// sscanf(buf, "%d %d %d", &year, &month, &day ); +// +// daylen = day_length(year,month,day,lon,lat); +// civlen = day_civil_twilight_length(year,month,day,lon,lat); +// nautlen = day_nautical_twilight_length(year,month,day,lon,lat); +// astrlen = day_astronomical_twilight_length(year,month,day, +// lon,lat); +// +// printf( "Day length: %5.2f hours\n", daylen ); +// printf( "With civil twilight %5.2f hours\n", civlen ); +// printf( "With nautical twilight %5.2f hours\n", nautlen ); +// printf( "With astronomical twilight %5.2f hours\n", astrlen ); +// printf( "Length of twilight: civil %5.2f hours\n", +// (civlen-daylen)/2.0); +// printf( " nautical %5.2f hours\n", +// (nautlen-daylen)/2.0); +// printf( " astronomical %5.2f hours\n", +// (astrlen-daylen)/2.0); +// +// rs = sun_rise_set ( year, month, day, lon, lat, +// &rise, &set ); +// civ = civil_twilight ( year, month, day, lon, lat, +// &civ_start, &civ_end ); +// naut = nautical_twilight ( year, month, day, lon, lat, +// &naut_start, &naut_end ); +// astr = astronomical_twilight( year, month, day, lon, lat, +// &astr_start, &astr_end ); +// +// printf( "Sun at south %5.2fh UT\n", (rise+set)/2.0 ); +// +// switch( rs ) +// { +// case 0: +// printf( "Sun rises %5.2fh UT, sets %5.2fh UT\n", +// rise, set ); +// break; +// case +1: +// printf( "Sun above horizon\n" ); +// break; +// case -1: +// printf( "Sun below horizon\n" ); +// break; +// } +// +// switch( civ ) +// { +// case 0: +// printf( "Civil twilight starts %5.2fh, " +// "ends %5.2fh UT\n", civ_start, civ_end ); +// break; +// case +1: +// printf( "Never darker than civil twilight\n" ); +// break; +// case -1: +// printf( "Never as bright as civil twilight\n" ); +// break; +// } +// +// switch( naut ) +// { +// case 0: +// printf( "Nautical twilight starts %5.2fh, " +// "ends %5.2fh UT\n", naut_start, naut_end ); +// break; +// case +1: +// printf( "Never darker than nautical twilight\n" ); +// break; +// case -1: +// printf( "Never as bright as nautical twilight\n" ); +// break; +// } +// +// switch( astr ) +// { +// case 0: +// printf( "Astronomical twilight starts %5.2fh, " +// "ends %5.2fh UT\n", astr_start, astr_end ); +// break; +// case +1: +// printf( "Never darker than astronomical twilight\n" ); +// break; +// case -1: +// printf( "Never as bright as astronomical twilight\n" ); +// break; +// } +// return 0; +// } +// } +// #endif /* SUNRISET_LIB */ + +/* The "workhorse" function for sun rise/set times */ + +int __sunriset__( int year, int month, int day, double lon, double lat, + double altit, int upper_limb, double *trise, double *tset ) +/***************************************************************************/ +/* Note: year,month,date = calendar date, 1801-2099 only. */ +/* Eastern longitude positive, Western longitude negative */ +/* Northern latitude positive, Southern latitude negative */ +/* The longitude value IS critical in this function! */ +/* altit = the altitude which the Sun should cross */ +/* Set to -35/60 degrees for rise/set, -6 degrees */ +/* for civil, -12 degrees for nautical and -18 */ +/* degrees for astronomical twilight. */ +/* upper_limb: non-zero -> upper limb, zero -> center */ +/* Set to non-zero (e.g. 1) when computing rise/set */ +/* times, and to zero when computing start/end of */ +/* twilight. */ +/* *rise = where to store the rise time */ +/* *set = where to store the set time */ +/* Both times are relative to the specified altitude, */ +/* and thus this function can be used to compute */ +/* various twilight times, as well as rise/set times */ +/* Return value: 0 = sun rises/sets this day, times stored at */ +/* *trise and *tset. */ +/* +1 = sun above the specified "horizon" 24 hours. */ +/* *trise set to time when the sun is at south, */ +/* minus 12 hours while *tset is set to the south */ +/* time plus 12 hours. "Day" length = 24 hours */ +/* -1 = sun is below the specified "horizon" 24 hours */ +/* "Day" length = 0 hours, *trise and *tset are */ +/* both set to the time when the sun is at south. */ +/* */ +/**********************************************************************/ +{ + double d, /* Days since 2000 Jan 0.0 (negative before) */ + sr, /* Solar distance, astronomical units */ + sRA, /* Sun's Right Ascension */ + sdec, /* Sun's declination */ + sradius, /* Sun's apparent radius */ + t, /* Diurnal arc */ + tsouth, /* Time when Sun is at south */ + sidtime; /* Local sidereal time */ + + int rc = 0; /* Return cde from function - usually 0 */ + + /* Compute d of 12h local mean solar time */ + d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0; + + /* Compute the local sidereal time of this moment */ + sidtime = revolution( GMST0(d) + 180.0 + lon ); + + /* Compute Sun's RA, Decl and distance at this moment */ + sun_RA_dec( d, &sRA, &sdec, &sr ); + + /* Compute time when Sun is at south - in hours UT */ + tsouth = 12.0 - rev180(sidtime - sRA)/15.0; + + /* Compute the Sun's apparent radius in degrees */ + sradius = 0.2666 / sr; + + /* Do correction to upper limb, if necessary */ + if ( upper_limb ) + altit -= sradius; + + /* Compute the diurnal arc that the Sun traverses to reach */ + /* the specified altitude altit: */ + { + double cost; + cost = ( sind(altit) - sind(lat) * sind(sdec) ) / + ( cosd(lat) * cosd(sdec) ); + if ( cost >= 1.0 ) + rc = -1, t = 0.0; /* Sun always below altit */ + else if ( cost <= -1.0 ) + rc = +1, t = 12.0; /* Sun always above altit */ + else + t = acosd(cost)/15.0; /* The diurnal arc, hours */ + } + + /* Store rise and set times - in hours UT */ + *trise = tsouth - t; + *tset = tsouth + t; + + return rc; +} /* __sunriset__ */ + + + +/* The "workhorse" function */ + + +double __daylen__( int year, int month, int day, double lon, double lat, + double altit, int upper_limb ) +/**********************************************************************/ +/* Note: year,month,date = calendar date, 1801-2099 only. */ +/* Eastern longitude positive, Western longitude negative */ +/* Northern latitude positive, Southern latitude negative */ +/* The longitude value is not critical. Set it to the correct */ +/* longitude if you're picky, otherwise set to to, say, 0.0 */ +/* The latitude however IS critical - be sure to get it correct */ +/* altit = the altitude which the Sun should cross */ +/* Set to -35/60 degrees for rise/set, -6 degrees */ +/* for civil, -12 degrees for nautical and -18 */ +/* degrees for astronomical twilight. */ +/* upper_limb: non-zero -> upper limb, zero -> center */ +/* Set to non-zero (e.g. 1) when computing day length */ +/* and to zero when computing day+twilight length. */ +/**********************************************************************/ +{ + double d, /* Days since 2000 Jan 0.0 (negative before) */ + obl_ecl, /* Obliquity (inclination) of Earth's axis */ + sr, /* Solar distance, astronomical units */ + slon, /* True solar longitude */ + sin_sdecl, /* Sine of Sun's declination */ + cos_sdecl, /* Cosine of Sun's declination */ + sradius, /* Sun's apparent radius */ + t; /* Diurnal arc */ + + /* Compute d of 12h local mean solar time */ + d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0; + + /* Compute obliquity of ecliptic (inclination of Earth's axis) */ + obl_ecl = 23.4393 - 3.563E-7 * d; + + /* Compute Sun's ecliptic longitude and distance */ + sunpos( d, &slon, &sr ); + + /* Compute sine and cosine of Sun's declination */ + sin_sdecl = sind(obl_ecl) * sind(slon); + cos_sdecl = sqrt( 1.0 - sin_sdecl * sin_sdecl ); + + /* Compute the Sun's apparent radius, degrees */ + sradius = 0.2666 / sr; + + /* Do correction to upper limb, if necessary */ + if ( upper_limb ) + altit -= sradius; + + /* Compute the diurnal arc that the Sun traverses to reach */ + /* the specified altitude altit: */ + { + double cost; + cost = ( sind(altit) - sind(lat) * sin_sdecl ) / + ( cosd(lat) * cos_sdecl ); + if ( cost >= 1.0 ) + t = 0.0; /* Sun always below altit */ + else if ( cost <= -1.0 ) + t = 24.0; /* Sun always above altit */ + else t = (2.0/15.0) * acosd(cost); /* The diurnal arc, hours */ + } + return t; +} /* __daylen__ */ + + +/* This function computes the Sun's position at any instant */ + +void sunpos( double d, double *lon, double *r ) +/******************************************************/ +/* Computes the Sun's ecliptic longitude and distance */ +/* at an instant given in d, number of days since */ +/* 2000 Jan 0.0. The Sun's ecliptic latitude is not */ +/* computed, since it's always very near 0. */ +/******************************************************/ +{ + double M, /* Mean anomaly of the Sun */ + w, /* Mean longitude of perihelion */ + /* Note: Sun's mean longitude = M + w */ + e, /* Eccentricity of Earth's orbit */ + E, /* Eccentric anomaly */ + x, y, /* x, y coordinates in orbit */ + v; /* True anomaly */ + + /* Compute mean elements */ + M = revolution( 356.0470 + 0.9856002585 * d ); + w = 282.9404 + 4.70935E-5 * d; + e = 0.016709 - 1.151E-9 * d; + + /* Compute true longitude and radius vector */ + E = M + e * RADEG * sind(M) * ( 1.0 + e * cosd(M) ); + x = cosd(E) - e; + y = sqrt( 1.0 - e*e ) * sind(E); + *r = sqrt( x*x + y*y ); /* Solar distance */ + v = atan2d( y, x ); /* True anomaly */ + *lon = v + w; /* True solar longitude */ + if ( *lon >= 360.0 ) + *lon -= 360.0; /* Make it 0..360 degrees */ +} + +void sun_RA_dec( double d, double *RA, double *dec, double *r ) +/******************************************************/ +/* Computes the Sun's equatorial coordinates RA, Decl */ +/* and also its distance, at an instant given in d, */ +/* the number of days since 2000 Jan 0.0. */ +/******************************************************/ +{ + double lon, obl_ecl, x, y, z; + + /* Compute Sun's ecliptical coordinates */ + sunpos( d, &lon, r ); + + /* Compute ecliptic rectangular coordinates (z=0) */ + x = *r * cosd(lon); + y = *r * sind(lon); + + /* Compute obliquity of ecliptic (inclination of Earth's axis) */ + obl_ecl = 23.4393 - 3.563E-7 * d; + + /* Convert to equatorial rectangular coordinates - x is unchanged */ + z = y * sind(obl_ecl); + y = y * cosd(obl_ecl); + + /* Convert to spherical coordinates */ + *RA = atan2d( y, x ); + *dec = atan2d( z, sqrt(x*x + y*y) ); + +} /* sun_RA_dec */ + + +/******************************************************************/ +/* This function reduces any angle to within the first revolution */ +/* by subtracting or adding even multiples of 360.0 until the */ +/* result is >= 0.0 and < 360.0 */ +/******************************************************************/ + +#define INV360 ( 1.0 / 360.0 ) + +double revolution( double x ) +/*****************************************/ +/* Reduce angle to within 0..360 degrees */ +/*****************************************/ +{ + return( x - 360.0 * floor( x * INV360 ) ); +} /* revolution */ + +double rev180( double x ) +/*********************************************/ +/* Reduce angle to within +180..+180 degrees */ +/*********************************************/ +{ + return( x - 360.0 * floor( x * INV360 + 0.5 ) ); +} /* revolution */ + + +/*******************************************************************/ +/* This function computes GMST0, the Greenwich Mean Sidereal Time */ +/* at 0h UT (i.e. the sidereal time at the Greenwich meridian at */ +/* 0h UT). GMST is then the sidereal time at Greenwich at any */ +/* time of the day. I've generalized GMST0 as well, and define it */ +/* as: GMST0 = GMST - UT -- this allows GMST0 to be computed at */ +/* other times than 0h UT as well. While this sounds somewhat */ +/* contradictory, it is very practical: instead of computing */ +/* GMST like: */ +/* */ +/* GMST = (GMST0) + UT * (366.2422/365.2422) */ +/* */ +/* where (GMST0) is the GMST last time UT was 0 hours, one simply */ +/* computes: */ +/* */ +/* GMST = GMST0 + UT */ +/* */ +/* where GMST0 is the GMST "at 0h UT" but at the current moment! */ +/* Defined in this way, GMST0 will increase with about 4 min a */ +/* day. It also happens that GMST0 (in degrees, 1 hr = 15 degr) */ +/* is equal to the Sun's mean longitude plus/minus 180 degrees! */ +/* (if we neglect aberration, which amounts to 20 seconds of arc */ +/* or 1.33 seconds of time) */ +/* */ +/*******************************************************************/ + +double GMST0( double d ) +{ + double sidtim0; + /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr */ + /* L = M + w, as defined in sunpos(). Since I'm too lazy to */ + /* add these numbers, I'll let the C compiler do it for me. */ + /* Any decent C compiler will add the constants at compile */ + /* time, imposing no runtime or code overhead. */ + sidtim0 = revolution( ( 180.0 + 356.0470 + 282.9404 ) + + ( 0.9856002585 + 4.70935E-5 ) * d ); + return sidtim0; +} /* GMST0 */ \ No newline at end of file diff --git a/src/sunriset.h b/src/sunriset.h new file mode 100644 index 0000000..c479e1b --- /dev/null +++ b/src/sunriset.h @@ -0,0 +1,118 @@ +/* + +SUNRISET.H - computes Sun rise/set times, start/end of twilight, and + the length of the day at any date and latitude + +Written as DAYLEN.C, 1989-08-16 + +Modified to SUNRISET.C, 1992-12-01 +Split to a header file, 2017-12-10, by Joachim Nilsson + +(c) Paul Schlyter, 1989, 1992 + +Released to the public domain by Paul Schlyter, December 1992 + +*/ + +/* A macro to compute the number of days elapsed since 2000 Jan 0.0 */ +/* (which is equal to 1999 Dec 31, 0h UT) */ + +#define days_since_2000_Jan_0(y,m,d) \ + (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L) + +/* Some conversion factors between radians and degrees */ + +#ifndef PI + #define PI 3.1415926535897932384 +#endif + +#define RADEG ( 180.0 / PI ) +#define DEGRAD ( PI / 180.0 ) + +/* The trigonometric functions in degrees */ + +#define sind(x) sin((x)*DEGRAD) +#define cosd(x) cos((x)*DEGRAD) +#define tand(x) tan((x)*DEGRAD) + +#define atand(x) (RADEG*atan(x)) +#define asind(x) (RADEG*asin(x)) +#define acosd(x) (RADEG*acos(x)) +#define atan2d(y,x) (RADEG*atan2(y,x)) + + +/* Following are some macros around the "workhorse" function __daylen__ */ +/* They mainly fill in the desired values for the reference altitude */ +/* below the horizon, and also selects whether this altitude should */ +/* refer to the Sun's center or its upper limb. */ + + +/* This macro computes the length of the day, from sunrise to sunset. */ +/* Sunrise/set is considered to occur when the Sun's upper limb is */ +/* 35 arc minutes below the horizon (this accounts for the refraction */ +/* of the Earth's atmosphere). */ +#define day_length(year,month,day,lon,lat) \ + __daylen__( year, month, day, lon, lat, -35.0/60.0, 1 ) + +/* This macro computes the length of the day, including civil twilight. */ +/* Civil twilight starts/ends when the Sun's center is 6 degrees below */ +/* the horizon. */ +#define day_civil_twilight_length(year,month,day,lon,lat) \ + __daylen__( year, month, day, lon, lat, -6.0, 0 ) + +/* This macro computes the length of the day, incl. nautical twilight. */ +/* Nautical twilight starts/ends when the Sun's center is 12 degrees */ +/* below the horizon. */ +#define day_nautical_twilight_length(year,month,day,lon,lat) \ + __daylen__( year, month, day, lon, lat, -12.0, 0 ) + +/* This macro computes the length of the day, incl. astronomical twilight. */ +/* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ +/* below the horizon. */ +#define day_astronomical_twilight_length(year,month,day,lon,lat) \ + __daylen__( year, month, day, lon, lat, -18.0, 0 ) + + +/* This macro computes times for sunrise/sunset. */ +/* Sunrise/set is considered to occur when the Sun's upper limb is */ +/* 35 arc minutes below the horizon (this accounts for the refraction */ +/* of the Earth's atmosphere). */ +#define sun_rise_set(year,month,day,lon,lat,rise,set) \ + __sunriset__( year, month, day, lon, lat, -35.0/60.0, 1, rise, set ) + +/* This macro computes the start and end times of civil twilight. */ +/* Civil twilight starts/ends when the Sun's center is 6 degrees below */ +/* the horizon. */ +#define civil_twilight(year,month,day,lon,lat,start,end) \ + __sunriset__( year, month, day, lon, lat, -6.0, 0, start, end ) + +/* This macro computes the start and end times of nautical twilight. */ +/* Nautical twilight starts/ends when the Sun's center is 12 degrees */ +/* below the horizon. */ +#define nautical_twilight(year,month,day,lon,lat,start,end) \ + __sunriset__( year, month, day, lon, lat, -12.0, 0, start, end ) + +/* This macro computes the start and end times of astronomical twilight. */ +/* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ +/* below the horizon. */ +#define astronomical_twilight(year,month,day,lon,lat,start,end) \ + __sunriset__( year, month, day, lon, lat, -18.0, 0, start, end ) + + +/* Function prototypes */ + +double __daylen__( int year, int month, int day, double lon, double lat, + double altit, int upper_limb ); + +int __sunriset__( int year, int month, int day, double lon, double lat, + double altit, int upper_limb, double *rise, double *set ); + +void sunpos( double d, double *lon, double *r ); + +void sun_RA_dec( double d, double *RA, double *dec, double *r ); + +double revolution( double x ); + +double rev180( double x ); + +double GMST0( double d ); diff --git a/tests/tinytest.R b/tests/tinytest.R new file mode 100644 index 0000000..37f581b --- /dev/null +++ b/tests/tinytest.R @@ -0,0 +1,5 @@ + +if ( requireNamespace("tinytest", quietly=TRUE) ){ + tinytest::test_package("daybreak") +} +