時刻、緯度、経度を指定した太陽の位置


83

この質問は、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

これらの数字は正しくないようです。私が満足している標高-最初の2つはほぼ同じで、3つ目は少し低く、4つ目ははるかに低くなっているはずです。ただし、最初の方位角はほぼ北にあるはずですが、それが与える数値は完全に反対です。残りの3つはほぼ真南を指しているはずですが、最後の1つだけがそうしています。北のすぐそばの真ん中の2つ、再び180度外れています。

ご覧のとおり、低緯度でトリガーされるエラーもいくつかあります(赤道を閉じる)

障害はこのセクションにあり、エラーは3行目(で始まる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)))

ここでの出力は正しい方向に向かっているように見えますが、度に変換して戻すと、常に正しい答えを返すことができません。

正しい方位角を計算するためのコードの修正(上記の数行だけだと思われます)は素晴らしいでしょう。


2
あなたは数学のstackexchangeでより良い運があるかもしれません
abcde123483

1
maptoolsパッケージにこれを行うためのコードがあります。?solarpos– mdsumner 2012
1

ありがとう@ ulvund-次に試してみるかもしれません。
SpoonNZ 2012年

4
では、NOAAサイトからJavascriptをコピーするだけでよいと思います。これは、多くのバージョンのソースです。私たちが書いたコードは、これらすべてを2つの小さな関数で必要なものにまとめましたが、それは昇格専用であり、特定のアプリに合わせて調整されていました。srrb.noaa.gov/highlights/sunrise/azel.html
mdsumner

1
前の質問から私の答えを試しましたか?ephem大気の屈折(温度、圧力の影響を受ける)や観測者の高度も考慮に入れる場合があります。
jfs 2012

回答:


110

これは重要なトピックのように思われるので、通常よりも長い回答を投稿しました。このアルゴリズムが将来他の人に使用される場合は、それが派生した文献への参照を伴うことが重要だと思います。 。

短い答え

お気づきのとおり、投稿されたコードは、赤道付近や南半球では正しく機能しません。

これを修正するには、元のコードで次の行を置き換えるだけです。

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(SolarEnergy。40:227-235)による1988年の記事からほぼ逐語的に適合されています。その記事は、R。Walravenによる1978年の記事(SolarEnergy。20:393-397)で提示されたアルゴリズムを改良しました。Walravenは、この方法がカリフォルニア州デイビス(38°33 '14 "N、121°44 '17" W)に偏光放射計を正確に配置するために数年間成功裏に使用されたと報告しました。

MichalskyとWalravenの両方のコードには、重要/致命的なエラーが含まれています。特に、ミヒャルスキーのアルゴリズムは米国のほとんどで問題なく機能しますが、赤道付近または南半球では失敗します(ご存知のとおり)。1989年、オーストラリアのビクトリア州のJWスペンサーは、同じことを指摘しました(SolarEnergy。42(4):353)。

拝啓:

計算された方位角をWalravenから導出された正しい象限に割り当てるためのミヒャルスキーの方法は、南(負)の緯度に適用されたときに正しい値を与えません。さらに、ゼロによる除算のため、緯度がゼロの場合、臨界標高(elc)の計算は失敗します。cos(azimuth)の符号を考慮して、方位角を正しい象限に割り当てるだけで、これらの両方の異議を回避できます。

あなたのコードに対する私の編集は、その公開されたコメントでスペンサーによって提案された修正に基づいています。R関数sunPosition()が「ベクトル化」されたままになるように(つまり、一度に1つのポイントを渡す必要はなく、ポイント位置のベクトルで適切に機能するように)、それらを少し変更しただけです。

関数の精度 sunPosition()

それがsunPosition()正しく機能することをテストするために、私はその結果を米国海洋大気庁の太陽計算機によって計算された結果と比較しました。どちらの場合も、太陽の位置は、2012年の南夏至(12月22日)の正午(12:00 PM)に計算されました。すべての結果は、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つの(非常にマイナーな)エラーがあります。1つ目は、うるう年の2月29日と3月1日の両方が、その年の61日として集計される原因です。2番目のエラーは、1989年のノート(SolarEnergy。43(5):323)でMichalskyによって修正された元の記事のタイプミスに由来します。

このコードブロックは、問題のある行を示し、コメントアウトされ、すぐに修正されたバージョンが続きます。

# 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))
}

参照:

ミヒャルスキー、JJ1988。おおよその太陽の位置(1950-2050)のための天文学的なアルマナックのアルゴリズム。太陽光エネルギー。40(3):227-235。

ミヒャルスキー、JJ 1989年。エラッタ。太陽光エネルギー。43(5):323。

スペンサー、JW 1989年。「おおよその太陽の位置のための天文学的なアルマナックのアルゴリズム(1950-2050)」へのコメント。太陽光エネルギー。42(4):353。

Walraven、R.1978。太陽の位置を計算する。太陽光エネルギー。20:393-397。


素晴らしい答えをありがとう!私は週末にここにいなかったので、申し訳ありませんでした。少なくとも今夜までこれを試す機会はありませんが、うまくいくようです。乾杯!
SpoonNZ 2012年

1
@ SpoonNZ-よろしくお願いします。これらの引用文献のPDFコピーが必要な場合は、私の電子メールアドレスでお知らせください。お送りします。
Josh O'Brien

1
@ JoshO'Brien:別の回答にいくつかの提案を追加しました。あなたは見て、あなた自身にそれらを取り入れたいと思うかもしれません。
リッチーコットン

@ RichieCotton-提案を投稿していただきありがとうございます。私はここでそれらを追加するつもりはないが、唯一の彼らがあるため、R固有とOPが別の言語への移植前に、それをデバッグしようとするR-コードを使用していました。(実際に、私はちょうど今、元のコードでの日付処理のエラーを修正するために私のポストを編集した、そしてそれはだまさにあなたが提案したような、より高いレベルのコードを使用するために主張しているエラーの一種。)乾杯!
Josh O'Brien

ユリウス暦の日付を次のように組み合わせることができます:時間= 365 *(年-2000)+フロア((年-1949)/ 4)+日+時間-13.5
ホーク

19

上記のリンクの1つから「NOAASolarCalculations」を使用して、わずかに異なるアルゴリズムを使用して関数の最後の部分を少し変更しました。これは、エラーなしで変換されたと思います。今では役に立たないコードをコメントアウトし、緯度からラジアンへの変換の直後に新しいアルゴリズムを追加しました。

# -----------------------------------------------
# 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))

あなたが言及した4つのケースで方位角の傾向を確認するために、時刻に対してそれをプロットしましょう:

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)

添付画像:

ここに画像の説明を入力してください


@Josh O'Brien:あなたの非常に詳細な答えは素晴らしい読み物です。関連する注記として、SunPosition関数はまったく同じ結果を生成します。
mbask 2012年

必要に応じて画像ファイルを添付しました。
mdsumner 2012年

1
@ Charlie-すばらしい答えです。プロットは特に素晴らしい追加です。それらを見る前に、私は太陽の夜間の方位角座標が「赤道」とより「温帯」の場所でどれほど異なるかを理解していませんでした。本当にかっこいい。
Josh O'Brien

12

これは、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)
  )
}

NOAAのWebサイトに対してテストする場合は、次の点に注意してください。esrl.noaa.gov / gmd / grad / solcalc / azel.htmlNOAAはLongitudeWestを+ veとして使用します。このアルゴリズムは、経度西を-veとして使用します。
neon22 2014年

「sunPosition(lat = 43、long = -89)」を実行すると、標高52、方位角175になります。ただし、NOAAのWebアプリesrl.noaa.gov/gmd/grad/solcalcを使用すると、標高は次のようになります。およそ5と272の方位角。私は何かが足りないのですか?NOAAは正しいですが、正確な結果を出すためにsunPositionを取得できません。
Tedward 2016

@Tedwardは、sunPositionデフォルトで現在の時刻と日付を使用します。それはあなたが望んでいたことですか?
リッチーコットン

はい。また、いくつかの異なる時間でテストしました。これはその日の遅い時間でした。今日は新たなスタートを切ってもう一度やり直します。私は何か間違ったことをしているとかなり確信していますが、何が起こっているのかわかりません。私はそれで働き続けます。
Tedward 2016

正確な結果を得るには、「いつ」をUTCに変換する必要がありました。stackoverflow.com/questions/39393514/…を参照してください。@aichaoは変換用のコードを提案します。
Tedward 2016

10

これは、Joshの優れた回答に対する推奨される更新です。

関数の開始の多くは、2000年1月1日の正午からの日数を計算するための定型コードです。これは、Rの既存の日付と時刻の関数を使用して処理する方がはるかに適切です。

また、日付と時刻を指定するために6つの異なる変数を使用するよりも、既存の日付オブジェクトまたは日付文字列+形式文字列を指定する方が簡単(および他のR関数との整合性が高い)だと思います。

ここに2つのヘルパー関数があります

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%%その値を要求していたので、それらはすべてすでに負ではないはずなので、この行は不要です。


まことにありがとうございます!前述のように、私はこれをPHPに移植しました(おそらくJavascriptに移行します-どの関数を処理するかを決定する必要があります)ので、コードはあまり役に立ちませんが、移植できるはずです(わずかですが)元のコードよりも多くの思考が必要です!)。タイムゾーンを処理するコードを少し調整する必要があるので、この変更を同時に統合できる可能性があります。
SpoonNZ 2012年

2
気の利いた変更@Richieコットン。割り当て時間<-hour_of_dayは実際には時間<-hour_of_day(when)である必要があり、可変時間はクラス「difftime」のオブジェクトではなく、日数を保持する必要があることに注意してください。関数astronomers_almanac_timeの2行目は、as.numeric(difftime(x、origin、units = "days")、units = "days")のように変更する必要があります。
mbask 2012年

1
素晴らしい提案をありがとう。(興味があれば)sunPosition()関数全体の編集バージョンを投稿に含めると、その構造がよりRっぽくなるので便利かもしれません。
Josh O'Brien

@ JoshO'Brien:完了しました。それは私たちのすべての答えの組み合わせなので、私は答えコミュニティウィキを作りました。現在の時刻とデフォルト(スイス?)の座標についてはあなたと同じ答えが得られますが、さらに多くのテストが必要です。
リッチーコットン

@ RichieCotton-なんて素晴らしいアイデアでしょう。機会があり次第、あなたが何をしたかを詳しく見ていきます。
Josh O'Brien

4

Pythonプロジェクトで太陽の位置が必要でした。JoshO'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

これは私にとって本当に役に立ちました。ありがとう。私がしたことの1つは、夏時間の調整を追加することです。役に立つ場合は、次のようになります。if(time.localtime()。tm_isdst == 1):hour + = 1
Mark Ireland

1

上記のデータポイントとRichieCottonの関数(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の外側になりますが、特定のデータセットではヒットしていません。

私がこれにぶつかった半ダースほどの場合、「真」(昼夜の真ん中)は問題が発生したときであるため、経験的に真の値は1 / -1である必要があります。そのため、私はにより内丸めステップを適用することを快適に固定だろうsolarAzimuthRadiansJoshsolarAzimuthRadiansCharlie。NOAAアルゴリズムの理論上の精度(とにかく数値の精度が重要でなくなるポイント)が何であるかはわかりませんが、小数点以下12桁に丸めると、データセット内のデータが修正されました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.