Position of the sun given time of day, latitude and longitude

前端 未结 6 1155
误落风尘
误落风尘 2020-11-28 02:24

This question has been asked before a little over three years ago. There was an answer given, however I\'ve found a glitch in the solution.

Code below is in R. I\'ve

6条回答
  •  没有蜡笔的小新
    2020-11-28 02:58

    Here's a rewrite in that's more idiomatic to R, and easier to debug and maintain. It is essentially Josh's answer, but with azimuth calculated using both Josh and Charlie's algorithms for comparison. I've also included the simplifications to the date code from my other answer. The basic principle was to split the code up into lots of smaller functions that you can more easily write unit tests for.

    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)
      when <- lubridate::with_tz(when, "UTC")
      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)
    
      data.frame(
          elevation = radiansToDegrees(el), 
          azimuthJ  = radiansToDegrees(azJ),
          azimuthC  = radiansToDegrees(azC)
      )
    }
    

提交回复
热议问题