Using the Ruby Date class for Astronomical data

早过忘川 提交于 2019-12-08 02:54:20

问题


~ Approximate Solar Noon

lw = 88.743  # my longitude

jdate = Date.ordinal_to_jd(Time.now.year, Time.now.yday)
n = (jdate - 2451545 - 0.0009 - lw / 360).round  # lw is users longitude west of 0.
j_noon = 2451545 + 0.0009 + lw / 360 + n 
puts j_noon

=> 2455616.24740833

As an update, part of the confusion would be that solar noon is where all calculations started since January 1, 4713 BC Greenwich noon.

The correct use of Date.ordinal_to_jd has not compensated for this fact. So by adding or subtracting 12 hours like this:

jdn = Date.ordinal_to_jd(Time.now.year, Time.now.yday) - 0.5

we should get less errors. Just which do we use though since our calculations start with yesterdays noon?

The code is derived from the two equations from this page Sunrise_equation.

The first answer I got from a user here was that we don't understand the use of 0.0009 and lw / 360. lw / 360 would appear to be a fractional day of arc from the prime meridian. As for the 0.0009, it must be a small amount of variance in seconds since January 1, 4713 BC Greenwich noon. see IAU standards for more info

I calculate it to be 0.007776 seconds according to this page.

I have a little bit of info from Date class not including method details.

        =begin
--------------------------------------------------------------------- Class: Date
Class representing a date.

See the documentation to the file date.rb for an overview.

Internally, the date is represented as an Astronomical Julian Day Number, ajd. 
The Day of Calendar Reform, sg, is also stored, for conversions to other date formats. 
(There is also an of field for a time zone offset, 
but this is only for the use of the DateTime subclass.)

A new Date object is created using one of the object creation class methods named  
after the corresponding date format, and the arguments appropriate to that date
format; for instance, Date::civil() 
(aliased to Date::new()) with year, month, and day-of-month, or Date::ordinal() with
year and day-of-year.

All of these object creation class methods also take the Day of Calendar Reform as an
optional argument.

Date objects are immutable once created.

Once a Date has been created, date values can be retrieved for the different date
formats supported using instance methods. For instance, #mon() gives the Civil month,
#cwday() gives the Commercial day of the week, and #yday() gives the Ordinal day of
the year. Date values can be retrieved in any format, regardless of what format was
used to create the Date instance.

The Date class includes the Comparable module, allowing date objects to be compared
and sorted, ranges of dates to be created, and so forth.

---------------------------------------------------------------------------------

Includes:
Comparable(<, <=, ==, >, >=, between?)

Constants:
MONTHNAMES:      [nil] + %w(January February March April May June July August
                            September October November December)
DAYNAMES:        %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
ABBR_MONTHNAMES: [nil] + %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
ABBR_DAYNAMES:   %w(Sun Mon Tue Wed Thu Fri Sat)
ITALY:           2299161
ENGLAND:         2361222
JULIAN:          Infinity.new
GREGORIAN:       -Infinity.new

Class methods:
_load, _parse, _strptime, ajd_to_amjd, ajd_to_jd, amjd_to_ajd, civil, civil_to_jd,
commercial, commercial_to_jd, day_fraction_to_time, gregorian?, gregorian_leap?, jd,
jd_to_ajd, jd_to_civil, jd_to_commercial, jd_to_ld, jd_to_mjd, jd_to_ordinal,
jd_to_wday, julian?, julian_leap?, ld_to_jd, mjd_to_jd, new, now, ordinal,
ordinal_to_jd, parse, s3e, strptime, time_to_day_fraction, today, valid_civil?,
valid_commercial?, valid_jd?, valid_ordinal?, valid_time?

Instance methods:
+, -, <<, <=>, ===, >>, _dump, ajd, amjd, asctime, civil, commercial, ctime, cwday,
cweek, cwyear, day, day_fraction, downto, england, eql?, gregorian, gregorian?, hash,
hour, inspect, italy, jd, julian, julian?, ld, leap?, mday, min, mjd, mon, month,
new_offset, new_start, next, next_day, offset, ordinal, sec, sec_fraction, start,
step, strftime, succ, time, to_s, to_yaml, upto, wday, weeknum0, weeknum1, wnum0, 
wnum1, yday, year, zone

=end

As a side note, it's great that Ruby has a way to calculate the julian-date. I'm looking into the Javascript code from NOAA.

Here is a class that I was inspired to write by the link.

class JulianDayNumber

  def initialize(year = 2000, month = 1, day = 1) #defaults to Jan. 01, 2000
    @year = year
    @month = month
    @day = day
  end

  def calcJDN

    if (@month <= 2) then 
      @year -= 1
      @month += 12
    end

    varA = (@year/100).floor
    varB = 2 - varA + (varA/4).floor

    jdn = (365.25*(@year + 4716)).floor \
           + (30.6001*(@month+1)).floor \
           + @day + varB - 1524.5

    return jdn
  end

end

jd = JulianDayNumber.new(2011, 3, 2)
julianday = jd.calcJDN
puts julianday

=> 2455622.5

Now this gets me there but I'm still researching for the way back for a number such as the one calculated by the top most equation. Trying this we can see that we do get a 0.5 in the JDN. Who is right? Ruby or NOAA?


NOAA uses the January 1st 2000 value of 2451545.0 that is subtracted from the jd to get time in fractional century like this

    def calcTimeJulianCent(j)
      t = (j - 2451545.0)/36525.0
      return t
    end 

回答1:


Ruby has a number of ways of calculating Julian Day and you need to pick the right one. NOAA is calculating the JD since January 1, 4713 BC Greenwich noon as you know. It always ends in .5 because they are leaving out the fractional days.

Ruby's Julian Day is weird:

For scientific purposes, it is convenient to refer to a date simply as a day count, counting from an arbitrary initial day. The date first chosen for this was January 1, 4713 BCE. A count of days from this date is the Julian Day Number or Julian Date, which is abbreviated as jd in the Date class. This is in local time, and counts from midnight on the initial day.

Which makes no sense for astronomical use. but wait..

The stricter usage is in UTC, and counts from midday on the initial day. This is referred to in the Date class as the Astronomical Julian Day Number, and abbreviated as ajd. In the Date class, the Astronomical Julian Day Number includes fractional days.

(rubydoc)

This is what you are looking for, ajd. Just get it without the fractional days:

julianday = Date.civil(@year, @month, @day).ajd
puts julianday

=> 2455622.5

No need to port 9 lines of JavaScript from NOAA. Ruby's got your back! ;)




回答2:


The method ordinal_to_jd converts the day with index 0 of the year 2011 (Gregorian calendar) to the corresponding day in the Julian calendar, then you are using the magical value of 0.0009 for which i dont know any reason, then you are adding the ratio of your longitude (east or west?) of the whole 360* circle and then adding todays day-of-year (54 if you evaluated it today). The combination of Julian calendar and longitudinal ratio makes not much sense, but hey its a nice number since you mixed a 0.0009 in.




回答3:


Well thanks everybody, I guess that I can answer my own question now. I overlooked a simple method in the Date class. It is Date.day_fraction_to_time(day fractional). As I have a working program now I would like to share it with eveyone.

include Math
to_r = PI / 180.0
to_d = 180.0 / PI

latitude = 41.9478 # my latitude
longitude = 88.74277  # my longitude
lw = longitude / 360

jdate = Date.civil(Time.now.year, Time.now.month, Time.now.day).ajd
jdate = (jdate * 2).to_i/2 + 1

n = (jdate - 2451545 - 0.0009 - lw).round
j_noon = 2451545  + 0.0009   + lw  + n
mean_anomaly = (357.52911 + 0.98560028 * (jdate - 2451545)) % 360
center = 1.9148 * sin(mean_anomaly * to_r) + 0.0200 * sin(2 * mean_anomaly * to_r) + \
         0.0003 * sin(3 *  mean_anomaly * to_r)
lambda = (mean_anomaly + 102.9372 + center + 180) % 360
j_transit = j_noon + (0.0053 * sin(mean_anomaly * to_r)) - (0.0069 * sin(2 * lambda * \
            to_r))
delta = asin(0.397753054 * sin(lambda * to_r)) * to_d
omega = acos(sin(-0.83 * to_r)/cos(latitude * to_r) * cos(delta * to_r) \
        - tan(latitude * to_r) * tan(delta * to_r)) * to_d
j_set = 2451545 + 0.0009 + ((omega + longitude)/360 + n + 0.0053 * sin(mean_anomaly * \
        to_r)) - 0.0069 * sin(2 * lambda * to_r)

j_rise = j_transit - (j_set - j_transit)

rise = Date.day_fraction_to_time(j_rise - jdate)# + 0.25 for + 6 hours
risehour = rise[0].to_s
risemin = rise[1].to_s
risetime = "#{risehour}:#{risemin}"
puts "Sun rise = #{risetime} UTC"

transit = Date.day_fraction_to_time(j_transit - jdate)# + 0.25
transithour = transit[0].to_s
transitmin = transit[1].to_s
transittime = "#{transithour}:#{transitmin}"
puts "Solar noon = #{transittime} UTC"

set = Date.day_fraction_to_time(j_set - jdate)# + 0.25
sethour = set[0].to_s
setmin = set[1].to_s
settime = "#{sethour}:#{setmin} UTC"
puts "Sun set = #{settime}"


来源:https://stackoverflow.com/questions/5095456/using-the-ruby-date-class-for-astronomical-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!