- Position of the sun given time of day, latitude and longitude
- sunPosition <- function(year, month, day, hour=12, min=0, sec=0,
- lat=46.5, long=6.5) {
- twopi <- 2 * pi
- deg2rad <- pi / 180
- # Get day of the year, e.g. Feb 1 = 32, Mar 1 = 61 on leap years
- month.days <- c(0,31,28,31,30,31,30,31,31,30,31,30)
- day <- day + cumsum(month.days)[month]
- leapdays <- year %% 4 == 0 & (year %% 400 == 0 | year %% 100 != 0) & day >= 60
- day[leapdays] <- day[leapdays] + 1
- # Get Julian date - 2400000
- hour <- hour + min / 60 + sec / 3600 # hour plus fraction
- delta <- year - 1949
- leap <- trunc(delta / 4) # former leapyears
- jd <- 32916.5 + delta * 365 + leap + day + hour / 24
- # The input to the Atronomer's almanach is the difference between
- # the Julian date and JD 2451545.0 (noon, 1 January 2000)
- time <- jd - 51545.
- # Ecliptic coordinates
- # Mean longitude
- mnlong <- 280.460 + .9856474 * time
- mnlong <- mnlong %% 360
- mnlong[mnlong < 0] <- mnlong[mnlong < 0] + 360
- # Mean anomaly
- mnanom <- 357.528 + .9856003 * time
- mnanom <- mnanom %% 360
- mnanom[mnanom < 0] <- mnanom[mnanom < 0] + 360
- mnanom <- mnanom * deg2rad
- # Ecliptic longitude and obliquity of ecliptic
- eclong <- mnlong + 1.915 * sin(mnanom) + 0.020 * sin(2 * mnanom)
- eclong <- eclong %% 360
- eclong[eclong < 0] <- eclong[eclong < 0] + 360
- oblqec <- 23.429 - 0.0000004 * time
- eclong <- eclong * deg2rad
- oblqec <- oblqec * deg2rad
- # Celestial coordinates
- # Right ascension and declination
- num <- cos(oblqec) * sin(eclong)
- den <- cos(eclong)
- ra <- atan(num / den)
- ra[den < 0] <- ra[den < 0] + pi
- ra[den >= 0 & num < 0] <- ra[den >= 0 & num < 0] + twopi
- dec <- asin(sin(oblqec) * sin(eclong))
- # Local coordinates
- # Greenwich mean sidereal time
- gmst <- 6.697375 + .0657098242 * time + hour
- gmst <- gmst %% 24
- gmst[gmst < 0] <- gmst[gmst < 0] + 24.
- # Local mean sidereal time
- lmst <- gmst + long / 15.
- lmst <- lmst %% 24.
- lmst[lmst < 0] <- lmst[lmst < 0] + 24.
- lmst <- lmst * 15. * deg2rad
- # Hour angle
- ha <- lmst - ra
- ha[ha < -pi] <- ha[ha < -pi] + twopi
- ha[ha > pi] <- ha[ha > pi] - twopi
- # Latitude to radians
- lat <- lat * deg2rad
- # Azimuth and elevation
- el <- asin(sin(dec) * sin(lat) + cos(dec) * cos(lat) * cos(ha))
- az <- asin(-cos(dec) * sin(ha) / cos(el))
- elc <- asin(sin(dec) / sin(lat))
- az[el >= elc] <- pi - az[el >= elc]
- az[el <= elc & ha > 0] <- az[el <= elc & ha > 0] + twopi
- el <- el / deg2rad
- az <- az / deg2rad
- lat <- lat / deg2rad
- return(list(elevation=el, azimuth=az))
- }
- > sunPosition(2012,12,22,12,0,0,-41,0)
- $elevation
- [1] 72.42113
- $azimuth
- [1] 180.9211
- > sunPosition(2012,12,22,12,0,0,-3,0)
- $elevation
- [1] 69.57493
- $azimuth
- [1] -0.79713
- Warning message:
- In asin(sin(dec)/sin(lat)) : NaNs produced
- > sunPosition(2012,12,22,12,0,0,3,0)
- $elevation
- [1] 63.57538
- $azimuth
- [1] -0.6250971
- Warning message:
- In asin(sin(dec)/sin(lat)) : NaNs produced
- > sunPosition(2012,12,22,12,0,0,41,0)
- $elevation
- [1] 25.57642
- $azimuth
- [1] 180.3084
- # Azimuth and elevation
- el <- asin(sin(dec) * sin(lat) + cos(dec) * cos(lat) * cos(ha))
- az <- asin(-cos(dec) * sin(ha) / cos(el))
- elc <- asin(sin(dec) / sin(lat))
- az[el >= elc] <- pi - az[el >= elc]
- az[el <= elc & ha > 0] <- az[el <= elc & ha > 0] + twopi
- az <- atan(sin(ha) / (cos(ha) * sin(lat) - tan(dec) * cos(lat)))
- elc <- asin(sin(dec) / sin(lat))
- az[el >= elc] <- pi - az[el >= elc]
- az[el <= elc & ha > 0] <- az[el <= elc & ha > 0] + twopi
- cosAzPos <- (0 <= sin(dec) - sin(el) * sin(lat))
- sinAzNeg <- (sin(az) < 0)
- az[cosAzPos & sinAzNeg] <- az[cosAzPos & sinAzNeg] + twopi
- az[!cosAzPos] <- pi - az[!cosAzPos]
- testPts <- data.frame(lat = c(-41,-3,3, 41),
- long = c(0, 0, 0, 0))
- # Sun's position as returned by the NOAA Solar Calculator,
- NOAA <- data.frame(elevNOAA = c(72.44, 69.57, 63.57, 25.6),
- azNOAA = c(359.09, 180.79, 180.62, 180.3))
- # Sun's position as returned by sunPosition()
- sunPos <- sunPosition(year = 2012,
- month = 12,
- day = 22,
- hour = 12,
- min = 0,
- sec = 0,
- lat = testPts$lat,
- long = testPts$long)
- cbind(testPts, NOAA, sunPos)
- # lat long elevNOAA azNOAA elevation azimuth
- # 1 -41 0 72.44 359.09 72.43112 359.0787
- # 2 -3 0 69.57 180.79 69.56493 180.7965
- # 3 3 0 63.57 180.62 63.56539 180.6247
- # 4 41 0 25.60 180.30 25.56642 180.3083
- # leapdays <- year %% 4 == 0 & (year %% 400 == 0 | year %% 100 != 0) & day >= 60
- leapdays <- year %% 4 == 0 & (year %% 400 == 0 | year %% 100 != 0) &
- day >= 60 & !(month==2 & day==60)
- # oblqec <- 23.429 - 0.0000004 * time
- oblqec <- 23.439 - 0.0000004 * time
- sunPosition <- function(year, month, day, hour=12, min=0, sec=0,
- lat=46.5, long=6.5) {
- twopi <- 2 * pi
- deg2rad <- pi / 180
- # Get day of the year, e.g. Feb 1 = 32, Mar 1 = 61 on leap years
- month.days <- c(0,31,28,31,30,31,30,31,31,30,31,30)
- day <- day + cumsum(month.days)[month]
- leapdays <- year %% 4 == 0 & (year %% 400 == 0 | year %% 100 != 0) &
- day >= 60 & !(month==2 & day==60)
- day[leapdays] <- day[leapdays] + 1
- # Get Julian date - 2400000
- hour <- hour + min / 60 + sec / 3600 # hour plus fraction
- delta <- year - 1949
- leap <- trunc(delta / 4) # former leapyears
- jd <- 32916.5 + delta * 365 + leap + day + hour / 24
- # The input to the Atronomer's almanach is the difference between
- # the Julian date and JD 2451545.0 (noon, 1 January 2000)
- time <- jd - 51545.
- # Ecliptic coordinates
- # Mean longitude
- mnlong <- 280.460 + .9856474 * time
- mnlong <- mnlong %% 360
- mnlong[mnlong < 0] <- mnlong[mnlong < 0] + 360
- # Mean anomaly
- mnanom <- 357.528 + .9856003 * time
- mnanom <- mnanom %% 360
- mnanom[mnanom < 0] <- mnanom[mnanom < 0] + 360
- mnanom <- mnanom * deg2rad
- # Ecliptic longitude and obliquity of ecliptic
- eclong <- mnlong + 1.915 * sin(mnanom) + 0.020 * sin(2 * mnanom)
- eclong <- eclong %% 360
- eclong[eclong < 0] <- eclong[eclong < 0] + 360
- oblqec <- 23.439 - 0.0000004 * time
- eclong <- eclong * deg2rad
- oblqec <- oblqec * deg2rad
- # Celestial coordinates
- # Right ascension and declination
- num <- cos(oblqec) * sin(eclong)
- den <- cos(eclong)
- ra <- atan(num / den)
- ra[den < 0] <- ra[den < 0] + pi
- ra[den >= 0 & num < 0] <- ra[den >= 0 & num < 0] + twopi
- dec <- asin(sin(oblqec) * sin(eclong))
- # Local coordinates
- # Greenwich mean sidereal time
- gmst <- 6.697375 + .0657098242 * time + hour
- gmst <- gmst %% 24
- gmst[gmst < 0] <- gmst[gmst < 0] + 24.
- # Local mean sidereal time
- lmst <- gmst + long / 15.
- lmst <- lmst %% 24.
- lmst[lmst < 0] <- lmst[lmst < 0] + 24.
- lmst <- lmst * 15. * deg2rad
- # Hour angle
- ha <- lmst - ra
- ha[ha < -pi] <- ha[ha < -pi] + twopi
- ha[ha > pi] <- ha[ha > pi] - twopi
- # Latitude to radians
- lat <- lat * deg2rad
- # Azimuth and elevation
- el <- asin(sin(dec) * sin(lat) + cos(dec) * cos(lat) * cos(ha))
- az <- asin(-cos(dec) * sin(ha) / cos(el))
- # For logic and names, see Spencer, J.W. 1989. Solar Energy. 42(4):353
- cosAzPos <- (0 <= sin(dec) - sin(el) * sin(lat))
- sinAzNeg <- (sin(az) < 0)
- az[cosAzPos & sinAzNeg] <- az[cosAzPos & sinAzNeg] + twopi
- az[!cosAzPos] <- pi - az[!cosAzPos]
- # if (0 < sin(dec) - sin(el) * sin(lat)) {
- # if(sin(az) < 0) az <- az + twopi
- # } else {
- # az <- pi - az
- # }
- el <- el / deg2rad
- az <- az / deg2rad
- lat <- lat / deg2rad
- return(list(elevation=el, azimuth=az))
- }
- # -----------------------------------------------
- # New code
- # Solar zenith angle
- zenithAngle <- acos(sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(ha))
- # Solar azimuth
- az <- acos(((sin(lat) * cos(zenithAngle)) - sin(dec)) / (cos(lat) * sin(zenithAngle)))
- rm(zenithAngle)
- # -----------------------------------------------
- # Azimuth and elevation
- el <- asin(sin(dec) * sin(lat) + cos(dec) * cos(lat) * cos(ha))
- #az <- asin(-cos(dec) * sin(ha) / cos(el))
- #elc <- asin(sin(dec) / sin(lat))
- #az[el >= elc] <- pi - az[el >= elc]
- #az[el <= elc & ha > 0] <- az[el <= elc & ha > 0] + twopi
- el <- el / deg2rad
- az <- az / deg2rad
- lat <- lat / deg2rad
- # -----------------------------------------------
- # New code
- if (ha > 0) az <- az + 180 else az <- 540 - az
- az <- az %% 360
- # -----------------------------------------------
- return(list(elevation=el, azimuth=az))
- hour <- seq(from = 0, to = 23, by = 0.5)
- azimuth <- data.frame(hour = hour)
- az41S <- apply(azimuth, 1, function(x) sunPosition(2012,12,22,x,0,0,-41,0)$azimuth)
- az03S <- apply(azimuth, 1, function(x) sunPosition(2012,12,22,x,0,0,-03,0)$azimuth)
- az03N <- apply(azimuth, 1, function(x) sunPosition(2012,12,22,x,0,0,03,0)$azimuth)
- az41N <- apply(azimuth, 1, function(x) sunPosition(2012,12,22,x,0,0,41,0)$azimuth)
- azimuth <- cbind(azimuth, az41S, az03S, az41N, az03N)
- rm(az41S, az03S, az41N, az03N)
- library(ggplot2)
- azimuth.plot <- melt(data = azimuth, id.vars = "hour")
- ggplot(aes(x = hour, y = value, color = variable), data = azimuth.plot) +
- geom_line(size = 2) +
- geom_vline(xintercept = 12) +
- facet_wrap(~ variable)
- astronomers_almanac_time <- function(x)
- {
- origin <- as.POSIXct("2000-01-01 12:00:00")
- as.numeric(difftime(x, origin, units = "days"))
- }
- hour_of_day <- function(x)
- {
- x <- as.POSIXlt(x)
- with(x, hour + min / 60 + sec / 3600)
- }
- sunPosition <- function(when = Sys.time(), format, lat=46.5, long=6.5) {
- twopi <- 2 * pi
- deg2rad <- pi / 180
- if(is.character(when)) when <- strptime(when, format)
- time <- astronomers_almanac_time(when)
- hour <- hour_of_day(when)
- #...
- mnlong[mnlong < 0] <- mnlong[mnlong < 0] + 360
- astronomersAlmanacTime <- function(x)
- {
- # Astronomer's almanach time is the number of
- # days since (noon, 1 January 2000)
- origin <- as.POSIXct("2000-01-01 12:00:00")
- as.numeric(difftime(x, origin, units = "days"))
- }
- hourOfDay <- function(x)
- {
- x <- as.POSIXlt(x)
- with(x, hour + min / 60 + sec / 3600)
- }
- degreesToRadians <- function(degrees)
- {
- degrees * pi / 180
- }
- radiansToDegrees <- function(radians)
- {
- radians * 180 / pi
- }
- meanLongitudeDegrees <- function(time)
- {
- (280.460 + 0.9856474 * time) %% 360
- }
- meanAnomalyRadians <- function(time)
- {
- degreesToRadians((357.528 + 0.9856003 * time) %% 360)
- }
- eclipticLongitudeRadians <- function(mnlong, mnanom)
- {
- degreesToRadians(
- (mnlong + 1.915 * sin(mnanom) + 0.020 * sin(2 * mnanom)) %% 360
- )
- }
- eclipticObliquityRadians <- function(time)
- {
- degreesToRadians(23.439 - 0.0000004 * time)
- }
- rightAscensionRadians <- function(oblqec, eclong)
- {
- num <- cos(oblqec) * sin(eclong)
- den <- cos(eclong)
- ra <- atan(num / den)
- ra[den < 0] <- ra[den < 0] + pi
- ra[den >= 0 & num < 0] <- ra[den >= 0 & num < 0] + 2 * pi
- ra
- }
- rightDeclinationRadians <- function(oblqec, eclong)
- {
- asin(sin(oblqec) * sin(eclong))
- }
- greenwichMeanSiderealTimeHours <- function(time, hour)
- {
- (6.697375 + 0.0657098242 * time + hour) %% 24
- }
- localMeanSiderealTimeRadians <- function(gmst, long)
- {
- degreesToRadians(15 * ((gmst + long / 15) %% 24))
- }
- hourAngleRadians <- function(lmst, ra)
- {
- ((lmst - ra + pi) %% (2 * pi)) - pi
- }
- elevationRadians <- function(lat, dec, ha)
- {
- asin(sin(dec) * sin(lat) + cos(dec) * cos(lat) * cos(ha))
- }
- solarAzimuthRadiansJosh <- function(lat, dec, ha, el)
- {
- az <- asin(-cos(dec) * sin(ha) / cos(el))
- cosAzPos <- (0 <= sin(dec) - sin(el) * sin(lat))
- sinAzNeg <- (sin(az) < 0)
- az[cosAzPos & sinAzNeg] <- az[cosAzPos & sinAzNeg] + 2 * pi
- az[!cosAzPos] <- pi - az[!cosAzPos]
- az
- }
- solarAzimuthRadiansCharlie <- function(lat, dec, ha)
- {
- zenithAngle <- acos(sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(ha))
- az <- acos((sin(lat) * cos(zenithAngle) - sin(dec)) / (cos(lat) * sin(zenithAngle)))
- ifelse(ha > 0, az + pi, 3 * pi - az) %% (2 * pi)
- }
- sunPosition <- function(when = Sys.time(), format, lat = 46.5, long = 6.5)
- {
- if(is.character(when)) when <- strptime(when, format)
- time <- astronomersAlmanacTime(when)
- hour <- hourOfDay(when)
- # Ecliptic coordinates
- mnlong <- meanLongitudeDegrees(time)
- mnanom <- meanAnomalyRadians(time)
- eclong <- eclipticLongitudeRadians(mnlong, mnanom)
- oblqec <- eclipticObliquityRadians(time)
- # Celestial coordinates
- ra <- rightAscensionRadians(oblqec, eclong)
- dec <- rightDeclinationRadians(oblqec, eclong)
- # Local coordinates
- gmst <- greenwichMeanSiderealTimeHours(time, hour)
- lmst <- localMeanSiderealTimeRadians(gmst, long)
- # Hour angle
- ha <- hourAngleRadians(lmst, ra)
- # Latitude to radians
- lat <- degreesToRadians(lat)
- # Azimuth and elevation
- el <- elevationRadians(lat, dec, ha)
- azJ <- solarAzimuthRadiansJosh(lat, dec, ha, el)
- azC <- solarAzimuthRadiansCharlie(lat, dec, ha)
- list(
- elevation = radiansToDegrees(el),
- azimuthJ = radiansToDegrees(azJ),
- azimuthC = radiansToDegrees(azC)
- )
- }