이 질문은 요청 된 전 이상 3 년 전에 조금. 주어진 대답이 있었지만 솔루션에서 결함을 발견했습니다.
아래 코드는 R에 있습니다. 다른 언어로 이식했지만 R에서 직접 원본 코드를 테스트하여 문제가 내 이식과 관련이 없는지 확인했습니다.
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))
}
내가 치는 문제는 그것이 반환하는 방위각이 잘못 보인다는 것입니다. 예를 들어, 0ºE 및 41ºS, 3ºS, 3ºN 및 41ºN 위치에 대해 12:00에 (남부)하지에서 함수를 실행하는 경우 :
> 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
이 숫자는 옳지 않은 것 같습니다. 내가 만족하는 고도-처음 두 개는 거의 같고 세 번째는 터치가 더 낮아야하며 네 번째는 훨씬 더 낮아야합니다. 그러나 첫 번째 방위각은 대략 정북이어야하며, 제공하는 숫자는 완전히 반대입니다. 나머지 3 개는 대략 남쪽을 가리켜 야하지만 마지막 하나만 그렇습니다. 중간에있는 두 개는 북쪽에서 약간 떨어져 다시 180º 밖으로 나옵니다.
보시다시피 낮은 위도 (적도를 닫음)에서 트리거 된 몇 가지 오류도 있습니다.
이 섹션에 오류가 있다고 생각하며 세 번째 줄에서 오류가 발생합니다 (으로 시작 elc
).
# 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
나는 주변을 훑어 보았고 C에서 비슷한 코드 덩어리를 발견했으며 방위각을 계산하는 데 사용하는 라인은 R로 변환되었습니다.
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]
이제 지구상의 모든 위치에서 작동합니다.
토론
이 예제의 코드는 JJ Michalsky (Solar Energy. 40 : 227-235)의 1988 년 기사에서 거의 그대로 적용되었습니다. 이 기사는 R. Walraven (Solar Energy. 20 : 393-397)의 1978 년 기사에 제시된 알고리즘을 차례로 개선했습니다. Walraven은이 방법이 캘리포니아 데이비스 (38 ° 33 ’14 “N, 121 ° 44’17″W)에 편광 복사계를 정확하게 배치하는 데 몇 년 동안 성공적으로 사용되었다고보고했습니다.
Michalsky와 Walraven의 코드에는 모두 중요 / 치명적인 오류가 포함되어 있습니다. 특히, Michalsky의 알고리즘은 대부분의 미국에서 잘 작동하지만 적도 근처 또는 남반구 지역에서는 실패합니다. 1989 년에 호주 빅토리아에있는 JW Spencer는 똑같은 사실을 언급했습니다 (Solar Energy. 42 (4) : 353) :
근계:
계산 된 방위각을 Walraven에서 파생 된 올바른 사분면에 할당하는 Michalsky의 방법은 남부 (음수) 위도에 적용될 때 올바른 값을 제공하지 않습니다. 또한 0으로 나누기 때문에 위도가 0 인 경우 임계 고도 (elc) 계산이 실패합니다. cos (azimuth)의 부호를 고려하여 방위각을 올바른 사분면에 할당함으로써이 두 가지 반대를 피할 수 있습니다.
코드에 대한 편집 내용은 게시 된 주석에서 Spencer가 제안한 수정 사항을 기반으로합니다. R 함수 sunPosition()
가 ‘벡터화’상태를 유지 하도록 약간 변경했습니다 (즉, 한 번에 한 지점을 전달하는 대신 지점 위치의 벡터에서 제대로 작동 함).
기능의 정확성 sunPosition()
sunPosition()
제대로 작동하는지 테스트하기 위해 그 결과를 국립 해양 대기 청의 태양열 계산기 에서 계산 한 결과와 비교했습니다 . 두 경우 모두 2012 년 12 월 22 일 남하의 정오 (오후 12시)에 태양 위치를 계산했습니다. 모든 결과는 0.02도 이내로 일치했습니다.
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
코드의 기타 오류
게시 된 코드에 적어도 두 개의 다른 (매우 사소한) 오류가 있습니다. 첫 번째는 윤년의 2 월 29 일과 3 월 1 일이 모두 해당 연도의 61 일로 집계되도록합니다. 두 번째 오류는 원래 기사의 오타에서 비롯된 것으로 Michalsky가 1989 년 메모에서 수정했습니다 (Solar Energy. 43 (5) : 323).
이 코드 블록은 문제가되는 줄을 표시하고 주석 처리 한 후 바로 수정 된 버전을 표시합니다.
# 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()
위에서 확인한 수정 된 코드는 다음과 같습니다.
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))
}
참조 :
Michalsky, JJ 1988. 대략적인 태양 위치에 대한 천문 연감의 알고리즘 (1950-2050). 태양 에너지. 40 (3) : 227-235.
Michalsky, JJ 1989. 정오표. 태양 에너지. 43 (5) : 323.
Spencer, JW 1989. “근사적인 태양 위치에 대한 천문 연감의 알고리즘 (1950-2050)”에 대한 주석. 태양 에너지. 42 (4) : 353.
Walraven, R. 1978 년. 태양의 위치 계산. 태양 에너지. 20 : 393-397.
답변
위의 링크 중 하나에서 “NOAA Solar Calculations”를 사용하여 오류없이 번역 된 약간 다른 알고리즘을 사용하여 함수의 마지막 부분을 약간 변경했습니다. 지금 쓸모없는 코드를 주석 처리하고 위도를 라디안으로 변환 한 직후에 새 알고리즘을 추가했습니다.
# -----------------------------------------------
# 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)
첨부 된 이미지 :
답변
다음은 R에 더 관용적이고 디버그 및 유지 관리가 더 쉬운 재 작성입니다. 본질적으로 Josh의 대답이지만 비교를 위해 Josh와 Charlie의 알고리즘을 모두 사용하여 계산 된 방위각이 있습니다. 또한 다른 답변의 날짜 코드에 대한 단순화를 포함했습니다. 기본 원칙은 코드를 단위 테스트를 더 쉽게 작성할 수있는 여러 개의 작은 함수로 분할하는 것입니다.
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)
)
}
답변
이것은 Josh의 탁월한 답변에 대한 제안 업데이트입니다.
함수 시작의 대부분은 2000 년 1 월 1 일 정오 이후의 일수를 계산하기위한 상용구 코드입니다. 이것은 R의 기존 날짜 및 시간 함수를 사용하는 경우 훨씬 더 잘 처리됩니다.
또한 날짜와 시간을 지정하기 위해 6 개의 다른 변수를 사용하는 것보다 기존 날짜 개체 또는 날짜 문자열 + 형식 문자열을 지정하는 것이 더 쉽고 (다른 R 함수와 더 일관성이 있음) 생각합니다.
다음은 두 가지 도우미 기능입니다.
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
이후 mnlong
했다 %%
그것의 값이라는 라인이 불필요하므로, 그들은 모두, 이미 음수가되어야한다.
답변
파이썬 프로젝트에서 태양 위치가 필요했습니다. 저는 Josh O’Brien의 알고리즘을 적용했습니다.
조쉬 감사합니다.
누구에게나 유용 할 수 있다면 여기에 내 적응이 있습니다.
내 프로젝트에는 즉각적인 태양 위치 만 필요하므로 시간은 매개 변수가 아닙니다.
def sunPosition(lat=46.5, long=6.5):
# Latitude [rad]
lat_rad = math.radians(lat)
# Get Julian date - 2400000
day = time.gmtime().tm_yday
hour = time.gmtime().tm_hour + \
time.gmtime().tm_min/60.0 + \
time.gmtime().tm_sec/3600.0
delta = time.gmtime().tm_year - 1949
leap = delta / 4
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)
t = jd - 51545
# Ecliptic coordinates
# Mean longitude
mnlong_deg = (280.460 + .9856474 * t) % 360
# Mean anomaly
mnanom_rad = math.radians((357.528 + .9856003 * t) % 360)
# Ecliptic longitude and obliquity of ecliptic
eclong = math.radians((mnlong_deg +
1.915 * math.sin(mnanom_rad) +
0.020 * math.sin(2 * mnanom_rad)
) % 360)
oblqec_rad = math.radians(23.439 - 0.0000004 * t)
# Celestial coordinates
# Right ascension and declination
num = math.cos(oblqec_rad) * math.sin(eclong)
den = math.cos(eclong)
ra_rad = math.atan(num / den)
if den < 0:
ra_rad = ra_rad + math.pi
elif num < 0:
ra_rad = ra_rad + 2 * math.pi
dec_rad = math.asin(math.sin(oblqec_rad) * math.sin(eclong))
# Local coordinates
# Greenwich mean sidereal time
gmst = (6.697375 + .0657098242 * t + hour) % 24
# Local mean sidereal time
lmst = (gmst + long / 15) % 24
lmst_rad = math.radians(15 * lmst)
# Hour angle (rad)
ha_rad = (lmst_rad - ra_rad) % (2 * math.pi)
# Elevation
el_rad = math.asin(
math.sin(dec_rad) * math.sin(lat_rad) + \
math.cos(dec_rad) * math.cos(lat_rad) * math.cos(ha_rad))
# Azimuth
az_rad = math.asin(
- math.cos(dec_rad) * math.sin(ha_rad) / math.cos(el_rad))
if (math.sin(dec_rad) - math.sin(el_rad) * math.sin(lat_rad) < 0):
az_rad = math.pi - az_rad
elif (math.sin(az_rad) < 0):
az_rad += 2 * math.pi
return el_rad, az_rad
답변
위의 데이터 포인트 및 Richie Cotton의 기능에 약간의 문제가 발생했습니다 (Charlie의 코드 구현에서).
longitude= 176.0433687000000020361767383292317390441894531250
latitude= -39.173830619999996827118593500927090644836425781250
event_time = as.POSIXct("2013-10-24 12:00:00", format="%Y-%m-%d %H:%M:%S", tz = "UTC")
sunPosition(when=event_time, lat = latitude, long = longitude)
elevation azimuthJ azimuthC
1 -38.92275 180 NaN
Warning message:
In acos((sin(lat) * cos(zenithAngle) - sin(dec))/(cos(lat) * sin(zenithAngle))) : NaNs produced
solarAzimuthRadiansCharlie 함수에는 180도 주변의 부동 소수점 흥분이 있었기 때문에 (sin(lat) * cos(zenithAngle) - sin(dec)) / (cos(lat) * sin(zenithAngle))
1, 1.0000000000000004440892098보다 작은 양이어서 acos에 대한 입력이 1보다 크거나 -1보다 작아서는 안되므로 NaN을 생성합니다.
Josh의 계산과 유사한 경우가있을 수 있습니다. 부동 소수점 반올림 효과로 인해 asin 단계에 대한 입력이 -1 : 1을 벗어나게되지만 특정 데이터 세트에서 해당 항목에 도달하지 않았습니다.
6 개 정도의 경우에이 문제가 발생했을 때 “참”(낮이나 밤의 중간)이 발생하므로 경험적으로 실제 값은 1 / -1이어야합니다. 따라서 solarAzimuthRadiansJosh
및 내에서 반올림 단계를 적용하여 문제를 해결할 수 solarAzimuthRadiansCharlie
있습니다. NOAA 알고리즘의 이론적 정확성이 무엇인지 (수치 적 정확성이 문제가되지 않는 지점) 확실하지 않지만 소수점 이하 12 자리로 반올림하면 데이터 세트의 데이터가 수정되었습니다.