दिन, अक्षांश और देशांतर को देखते हुए सूर्य की स्थिति


83

यह सवाल तीन साल पहले के एक छोटे से पहले पूछा गया है। वहाँ एक जवाब दिया गया था, हालांकि मैंने समाधान में एक गड़बड़ पाया है।

नीचे दिया गया कोड 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))
}

जो समस्या मैं मार रहा हूं, वह यह है कि अजिमुथ यह रिटर्न गलत लगता है। उदाहरण के लिए, अगर मैं 12:00 बजे (दक्षिणी) गर्मियों में संक्रांति पर ifºE और 41ºS, 3ºS, 3ºN और 41ºN स्थानों पर फ़ंक्शन चलाता हूं:

> 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

ये संख्या सिर्फ सही नहीं लगती है। मैं जिस ऊंचाई से खुश हूं - पहले दो मोटे तौर पर एक ही होना चाहिए, तीसरा एक स्पर्श कम, और चौथा बहुत कम। हालाँकि उत्तर की ओर पहला अज़ीमुथ लगभग होना चाहिए, जबकि यह जो संख्या देता है वह पूर्ण विपरीत है। शेष तीनों को मोटे तौर पर दक्षिण की ओर इशारा करना चाहिए, हालांकि केवल अंतिम एक ही करता है। बीच के दो बिंदु उत्तर की ओर, फिर 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

मैंने चारों ओर गुगली की और सी में कोड का एक समान हिस्सा पाया, आर की गणना करने के लिए आर का उपयोग करने वाली रेखा में परिवर्तित हो गया, जो कुछ इस तरह होगा

az <- atan(sin(ha) / (cos(ha) * sin(lat) - tan(dec) * cos(lat)))

यहाँ आउटपुट सही दिशा में जा रहा है, लेकिन मैं इसे हर समय सही जवाब देने के लिए नहीं पा सकता हूँ जब इसे डिग्री में परिवर्तित किया जाता है।

कोड का एक सुधार (संदेह है कि यह ऊपर कुछ पंक्तियाँ हैं) यह सही azimuth की गणना करने के लिए शानदार होगा।



1
Maptools पैकेज में ऐसा करने के लिए कोड है, देखें? Solarpos
mdsumner

धन्यवाद @ulvund - आगे वहां कोशिश कर सकते हैं।
स्पूनएनजेड

4
ठीक है, तो मुझे लगता है कि आपको सिर्फ NOAA साइट से जावास्क्रिप्ट कॉपी करना चाहिए, यह बहुत सारे संस्करणों का स्रोत है। हमने जो कोड लिखा था, वह दो छोटे-छोटे कामों में हमारी जरूरत के मुताबिक गिर गया, लेकिन वह केवल ऊंचाई के लिए था और एक विशेष ऐप के लिए तैयार था। बस srrb.noaa.gov/highlights/sunrise/azel.html
mdsumner

1
क्या आपने पिछले प्रश्न से मेरा उत्तर देने की कोशिश की है ? ephemयहां तक ​​कि वायुमंडल के अपवर्तन (तापमान, दबाव से प्रभावित) और एक पर्यवेक्षक के उत्थान को भी ध्यान में रखा जा सकता है।
बजे

जवाबों:


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 (सौर ऊर्जा 40: 227-235) द्वारा 1988 के लेख से लगभग शब्दशः अनुकूलित किया गया है। बदले में उस लेख ने 1978 में आर वालरवेन (सौर ऊर्जा। 20: 393-397) लेख में प्रस्तुत एक एल्गोरिथ्म को परिष्कृत किया। वाल्रवेन ने बताया कि डेविस, सीए (38 ° 33 '14 "एन, 121 ° 44' 17" डब्ल्यू) में एक ध्रुवीकरण रेडियोमीटर को ठीक करने के लिए कई वर्षों से विधि का सफलतापूर्वक उपयोग किया गया था।

मिशाल्स्की और वाल्रवेन के कोड दोनों में महत्वपूर्ण / घातक त्रुटियां हैं। विशेष रूप से, जबकि मिशाल्स्की का एल्गोरिथ्म संयुक्त राज्य के अधिकांश हिस्सों में ठीक काम करता है, यह भूमध्य रेखा के पास के क्षेत्रों में या दक्षिणी गोलार्ध में विफल रहता है (जैसा कि आपने पाया है)। 1989 में, विक्टोरिया, ऑस्ट्रेलिया के जेडब्ल्यू स्पेंसर ने एक ही बात पर ध्यान दिया (सौर ऊर्जा। 42 (4): 353)

श्रीमान:

वालरवेन से प्राप्त सही चतुर्थांश में गणना की हुई अज़ीमथ को असाइन करने के लिए मीकाल्स्की की विधि, दक्षिणी (नकारात्मक) अक्षांशों के लिए लागू होने पर सही मान नहीं देती है। इसके अलावा महत्वपूर्ण ऊंचाई (elc) की गणना शून्य के विभाजन के कारण शून्य के अक्षांश के लिए विफल हो जाएगी। इन दोनों आपत्तियों को कॉस (अज़ीमथ) के संकेत पर विचार करके अज़िमुथ को सही चतुर्थांश में असाइन करने से बचा जा सकता है।

आपके कोड में मेरा संपादन उस प्रकाशित टिप्पणी में स्पेंसर द्वारा सुझाए गए सुधारों पर आधारित है। मैंने यह सुनिश्चित करने के लिए बस उन्हें कुछ हद तक बदल दिया है कि आर फ़ंक्शन sunPosition()'वेक्टराइज्ड' रहता है (यानी एक समय में एक बिंदु को पारित करने की आवश्यकता के बजाय बिंदु स्थानों के वैक्टर पर ठीक से काम करना)।

कार्य की सटीकता sunPosition()

यह परीक्षण करने के लिए कि यह sunPosition()सही ढंग से काम करता है, मैंने इसके परिणामों की तुलना राष्ट्रीय समुद्रीय और वायुमंडलीय प्रशासन के सौर कैलकुलेटर द्वारा गणना की है । दोनों ही मामलों में, दक्षिणी ग्रीष्मकालीन संक्रांति (22 दिसंबर), 2012 को दोपहर (दोपहर 12:00 बजे) के लिए सूर्य की स्थिति की गणना की गई। सभी परिणाम 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

कोड में अन्य त्रुटियां

पोस्ट किए गए कोड में कम से कम दो अन्य (काफी मामूली) त्रुटियां हैं। पहला साल 29 फरवरी और 1 मार्च को लीप वर्ष के दौरान दोनों को वर्ष के 61 वें दिन के रूप में माना जाता है। दूसरी त्रुटि एक मूल लेख में टाइपो से ली गई है, जिसे 1989 के नोट (सोलर एनर्जी। 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))
}

संदर्भ:

मिशाल्स्की, जे जे 1988. द एस्ट्रोनॉमिकल पंचांग की एल्गोरिथ्म लगभग अनुमानित सौर स्थिति (1950-2050) के लिए। सौर ऊर्जा। 40 (3): 227-235।

मिशाल्स्की, जे जे 1989. इरेटा। सौर ऊर्जा। 43 (5): 323।

स्पेंसर, JW 1989। "द एस्ट्रोनॉमिकल पंचांग के एल्गोरिथ्म के लिए अनुमानित सौर स्थिति (1950-2050)" पर टिप्पणियाँ। सौर ऊर्जा। 42 (4): 353।

वाल्रवेन, आर। 1978. सूर्य की स्थिति की गणना। सौर ऊर्जा। 20: 393-397।


शानदार जवाब के लिए धन्यवाद! मैं सप्ताहांत पर यहाँ नहीं गया है इसलिए यह खेद है। कम से कम आज रात तक यह कोशिश करने का मौका नहीं मिलेगा, लेकिन ऐसा लगता है कि यह चाल चलेगा। चीयर्स!
स्पूनएनजेड

1
@SpoonNZ - मेरी खुशी। अगर आपको उन उद्धृत संदर्भों में से किसी की पीडीएफ प्रतियां चाहिए, तो मुझे अपने ईमेल पते पर बताएं, और मैं उन्हें आपके पास भेज सकता हूं।
जोश ओ'ब्रायन

1
@ जोश'ब्रायन: बस एक अलग जवाब में कुछ सुझाव जोड़े। आप एक नज़र रखना चाहते हैं और उन्हें अपने में शामिल कर सकते हैं।
रिची कॉटन

@ रीचीकॉन - आपके सुझाव पोस्ट करने के लिए धन्यवाद। मैं उन्हें यहां नहीं जोड़ने जा रहा हूं, लेकिन केवल इसलिए कि वे बहुत ही Rविशिष्ट हैं और ओपी आर-कोड का उपयोग करके इसे किसी अन्य भाषा में पोर्ट करने से पहले डीबग करने का प्रयास कर रहा है। (वास्तव में, मैंने अभी अपनी पोस्ट को मूल कोड में एक तारीख-प्रसंस्करण त्रुटि को ठीक करने के लिए संपादित किया है, और यह बिल्कुल उसी तरह की त्रुटि है जो आपके द्वारा प्रस्तावित उच्च-स्तरीय कोड का उपयोग करने के लिए तर्क देता है।) चीयर्स!
जोश ओ'ब्रायन

एक भी जूलियन तिथियों को जोड़ सकता है: समय = 365 * (वर्ष - 2000) + मंजिल (वर्ष - 1949) / 4) + दिन + घंटा - 13.5
हॉक

19

उपरोक्त लिंक में से एक से "एनओएए सौर गणना" का उपयोग करके मैंने फ़ंक्शन के अंतिम भाग को थोड़ा अलग एल्गोरिथ्म का उपयोग करके बदल दिया है, जो मुझे आशा है, त्रुटियों के बिना अनुवाद किया है। मैंने अब बेकार कोड पर टिप्पणी की है और रेडियों रूपांतरण के लिए अक्षांश के ठीक बाद नया एल्गोरिथ्म जोड़ा है:

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

चित्र संलग्न:

यहाँ छवि विवरण दर्ज करें


@ जोश ओ ब्रायन: आपका बहुत विस्तृत जवाब एक उत्कृष्ट रीड है। एक संबंधित नोट के रूप में, हमारे SunPosition कार्य के समान परिणाम प्राप्त करते हैं।
mbask

मैंने छवि फ़ाइल संलग्न की है, यदि आप इसे चाहते हैं।
मन्दसुमर

1
@Charlie - महान जवाब, और भूखंड एक विशेष रूप से अच्छा जोड़ हैं। उन्हें देखने से पहले, मैंने इस बात की सराहना नहीं की थी कि सूर्य के रात्रि-काल के अज़ीमुथल निर्देशांक 'भूमध्यरेखीय' बनाम अधिक 'समशीतोष्ण' स्थानों पर कितने भिन्न होंगे। सचमुच मस्त।
जोश ओ'ब्रायन 18

12

यहाँ R के लिए अधिक मुहावरेदार और फिर से डीबग करना और बनाए रखना आसान है। यह अनिवार्य रूप से जोश का उत्तर है, लेकिन तुलना के लिए जोश और चार्ली के एल्गोरिदम का उपयोग करके अजीमथ की गणना की जाती है। मैंने अपने अन्य उत्तर से दिनांक कोड में सरलीकरण को भी शामिल किया है। मूल सिद्धांत कोड को कई छोटे कार्यों में विभाजित करना था जिन्हें आप अधिक आसानी से इकाई परीक्षण लिख सकते हैं।

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 वेबसाइट के खिलाफ परीक्षण यहाँ: esrl.noaa.gov/gmd/grad/solcalc/azel.html कि NOAA देशांतर पश्चिम + ve के रूप में उपयोग करता है। यह एल्गोरिथ्म लॉन्गिट्यूड वेस्ट को -ve का उपयोग करता है।
नियॉन 22

जब मैं "sunPosition (lat = 43, long = -89)" चलाता हूं, तो मुझे 52 की ऊंचाई और 175 का azimuth मिलता है। लेकिन NOAA के वेब ऐप esrl.noaa.gov/gmd/grad/ololcalc का उपयोग करने पर मुझे ऊंचाई मिलती है लगभग 5 और 272 का अजीमुथ। क्या मुझे कुछ याद आ रहा है? NOAA सही है, लेकिन मैं सटीक परिणाम देने के लिए sunPosition प्राप्त नहीं कर सकता।
टेडवर्ड

@ sunPositionवर्तमान समय और तारीख का उपयोग करने के लिए अतिरिक्त चूक। क्या वही है जो आप चाहते थे?
रिची कॉटन

हाँ। मैंने कुछ अलग समयों के साथ भी परीक्षण किया। इस दिन में देर हो चुकी थी, मैं आज फिर से नए सिरे से कोशिश करने जा रहा हूं। मुझे पूरा यकीन है कि मैं कुछ गलत कर रहा हूं, लेकिन मुझे नहीं पता। मैं इस पर काम करता रहूंगा।
टेडवर्ड

मुझे सटीक परिणाम प्राप्त करने के लिए "जब" को यूटीसी में बदलने की आवश्यकता थी। Stackoverflow.com/questions/39393514/… देखें । @aichao रूपांतरण के लिए कोड सुझाता है।
टेडवर्ड

10

यह जोश के उत्कृष्ट उत्तर का एक सुझाया गया अद्यतन है।

1 जनवरी 2000 को दोपहर के बाद से दिनों की संख्या की गणना के लिए फ़ंक्शन की शुरुआत का बहुत बॉयलर कोड है। यह आर की मौजूदा तिथि और समय फ़ंक्शन का उपयोग करने से बहुत बेहतर है।

मुझे यह भी लगता है कि तिथि और समय को निर्दिष्ट करने के लिए छह अलग-अलग चर होने के बजाय, किसी मौजूदा दिनांक ऑब्जेक्ट या दिनांक तार + प्रारूप स्ट्रिंग को निर्दिष्ट करना आसान (और अन्य आर कार्यों के साथ अधिक सुसंगत) है।

यहाँ दो सहायक कार्य हैं

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 में पोर्ट किया है (और शायद जावास्क्रिप्ट में जाएगा - बस यह तय करेगा कि मैं कहाँ से क्या कार्य संभालना चाहता हूं) ताकि कोड मेरे लिए बहुत मददगार न हो, लेकिन इसे पोर्ट करने में सक्षम होना चाहिए (हालांकि थोड़ा साथ मूल कोड की तुलना में अधिक सोच शामिल है!)। मुझे उस कोड को ट्विक करना होगा जो टाइम ज़ोन को थोड़ा संभालता है, इसलिए उसी समय इस बदलाव को एकीकृत करने में सक्षम हो सकता है।
स्पूनएनजेड

2
निफ्टी में बदलाव होता है @ रीची कॉटन। ध्यान दें कि असाइनमेंट घंटे <- hour_of_day को वास्तव में घंटा होना चाहिए <- hour_of_day (जब) ​​और उस परिवर्तनीय समय को दिनों की संख्या को पकड़ना चाहिए, न कि क्लास "difftime" का ऑब्जेक्ट। फंक्शन की दूसरी पंक्ति खगोलविदों_लमानैक_टाइम को कुछ इस तरह बदला जाना चाहिए जैसे कि न्युमरिक (सुस्पष्टता (x, उत्पत्ति, इकाइयां = "दिन"), इकाइयों = "दिन")।
mbask 10:12

1
महान सुझावों के लिए धन्यवाद। यह अच्छा हो सकता है (यदि आप रुचि रखते हैं) अपने पोस्ट में संपूर्ण sunPosition()फ़ंक्शन का एक संपादित संस्करण शामिल करें जो इसके निर्माण में अधिक आर-ईश है।
जोश ओ'ब्रायन

@ जोश'ब्रायन: हो गया। मैंने उत्तर समुदाय विकि बनाया है, क्योंकि यह हमारे सभी उत्तरों का संयोजन है। यह आपके वर्तमान समय और डिफ़ॉल्ट (स्विस?) निर्देशांक के समान उत्तर देता है, लेकिन बहुत अधिक परीक्षण की आवश्यकता है।
रिची कॉटन

@ रीचीकॉटन - कितना अच्छा विचार है। मैंने मौका मिलते ही आपके बारे में गहराई से जानकारी ली।
जोश ओ'ब्रायन

4

मुझे पायथन प्रोजेक्ट में सूर्य की स्थिति की आवश्यकता थी। मैंने जोश ओ'ब्रायन के एल्गोरिथ्म को अनुकूलित किया।

शुक्रिया जोश।

मामले में यह किसी के लिए उपयोगी हो सकता है, यहाँ मेरा अनुकूलन है।

ध्यान दें कि मेरी परियोजना को केवल तत्काल सूर्य की स्थिति की आवश्यकता थी इसलिए समय एक पैरामीटर नहीं है।

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

यह वास्तव में मेरे लिए उपयोगी था। धन्यवाद। एक चीज जो मैंने की थी वह डेलाइट सेविंग्स के लिए एक समायोजन है। मामले में यह उपयोग की है, यह बस था: अगर (time.localtime ()। Tm_isdst == 1): घंटा + = 1
मार्क आयरलैंड

1

मुझे डेटा बिंदु और रिची कॉटन के कार्यों के साथ थोड़ी सी समस्या का सामना करना पड़ा (चार्ली के कोड के कार्यान्वयन में)

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 पर सबसे नगण्य राशि है, जो कि एक NaN उत्पन्न करता है क्योंकि acos के लिए इनपुट 1 या उससे कम -1 से ऊपर नहीं होना चाहिए।

मुझे संदेह है कि जोश की गणना के लिए समान किनारे के मामले हो सकते हैं, जहां फ़्लोटिंग पॉइंट राउंडिंग प्रभाव के कारण इनपुट चरण के लिए इनपुट -1: 1 के बाहर होता है, लेकिन मैंने उन्हें अपने विशेष डेटासेट में नहीं मारा है।

आधा दर्जन या इतने मामलों में मैंने इसे मारा है, "सच" (दिन या रात के मध्य) जब समस्या होती है तो अनुभवजन्य रूप से सही मूल्य 1 / -1 होना चाहिए। उस कारण से, मुझे यह तय करना आसान होगा कि भीतर solarAzimuthRadiansJoshऔर बाहर एक गोल कदम लागू करके solarAzimuthRadiansCharlie। मुझे यकीन नहीं है कि एनओएए एल्गोरिथ्म की सैद्धांतिक सटीकता क्या है (जिस बिंदु पर संख्यात्मक सटीकता वैसे भी मायने रखती है) लेकिन 12 दशमलव स्थानों पर चक्कर लगाने से मेरे डेटा सेट में डेटा तय हो गया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.