Module: Sun

Defined in:
lib/sun.rb,
lib/sun/version.rb

Defined Under Namespace

Classes: Error, InvalidCoordinates, InvalidTime

Constant Summary collapse

SOLAR_ZENITH =

The approximate correction for atmospheric refraction at sunrise and sunset

90.833
JULIAN_CONSTANT =

2000-01-01 12:00:00 UTC in Julian days

2451545.to_r
VERSION =
"1.0.1"

Class Method Summary collapse

Class Method Details

.apparent_longitude(julian_century) ⇒ Object



151
152
153
# File 'lib/sun.rb', line 151

def self.apparent_longitude(julian_century)
  true_longitude(julian_century) - 0.00569 - 0.00478 * Math.sin(radians(125.04 - 1934.136 * julian_century))
end

.date(time) ⇒ Object

Helpers



50
51
52
53
54
55
56
57
# File 'lib/sun.rb', line 50

def self.date(time)
  case time
  when Date then time
  when Time then time.to_date
  else
    raise InvalidTime, "must pass a Date or Time object"
  end
end

.date_at_time(date, minutes) ⇒ Object



71
72
73
# File 'lib/sun.rb', line 71

def self.date_at_time(date, minutes)
  Time.at(date_to_unix_time(date) + minutes * 60)
end

.date_to_unix_time(date) ⇒ Object



67
68
69
# File 'lib/sun.rb', line 67

def self.date_to_unix_time(date)
  Time.utc(date.year, date.month, date.day).to_i
end

.declination(oblique_correction, julian_century) ⇒ Object



155
156
157
# File 'lib/sun.rb', line 155

def self.declination(oblique_correction, julian_century)
  degrees(Math.asin(Math.sin(radians(oblique_correction)) * Math.sin(radians(apparent_longitude(julian_century)))))
end

.degrees(radians) ⇒ Object



59
60
61
# File 'lib/sun.rb', line 59

def self.degrees(radians)
  Rational(180 * radians, Math::PI)
end

.eccentricity_of_earth_orbit(julian_century) ⇒ Object



138
139
140
# File 'lib/sun.rb', line 138

def self.eccentricity_of_earth_orbit(julian_century)
  0.016708634 - julian_century * (0.000042037 + 0.0000001267 * julian_century)
end

.equation_of_center(julian_century) ⇒ Object



142
143
144
145
# File 'lib/sun.rb', line 142

def self.equation_of_center(julian_century)
  geometric_mean_anomoly = geometric_mean_anomoly(julian_century)
  Math.sin(radians(geometric_mean_anomoly)) * (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) + Math.sin(radians(2 * geometric_mean_anomoly)) * (0.019993 - 0.000101 * julian_century) + Math.sin(radians(3 * geometric_mean_anomoly)) * 0.00028
end

.equation_of_time(date, longitude) ⇒ Object



159
160
161
162
163
164
165
166
167
# File 'lib/sun.rb', line 159

def self.equation_of_time(date, longitude)
  julian_century = julian_century(date)
  oblique_correction = oblique_correction(julian_century)
  geometric_mean_anomoly = geometric_mean_anomoly(julian_century)
  geometric_mean_longitude = geometric_mean_longitude(julian_century)
  eccentricity_of_earth_orbit = eccentricity_of_earth_orbit(julian_century)
  y = y(oblique_correction)
  4 * degrees(y * Math.sin(2 * radians(geometric_mean_longitude)) - 2 * eccentricity_of_earth_orbit * Math.sin(radians(geometric_mean_anomoly)) + 4 * eccentricity_of_earth_orbit * y * Math.sin(radians(geometric_mean_anomoly)) * Math.cos(2 * radians(geometric_mean_longitude)) - 0.5 * y * y * Math.sin(4 * radians(geometric_mean_longitude)) - 1.25 * eccentricity_of_earth_orbit * eccentricity_of_earth_orbit * Math.sin(2 * radians(geometric_mean_anomoly)))
end

.geometric_mean_anomoly(julian_century) ⇒ Object



126
127
128
# File 'lib/sun.rb', line 126

def self.geometric_mean_anomoly(julian_century)
  357.52911 + julian_century * (35999.05029 - 0.0001537 * julian_century)
end

.geometric_mean_longitude(julian_century) ⇒ Object



130
131
132
# File 'lib/sun.rb', line 130

def self.geometric_mean_longitude(julian_century)
  (280.46646 + julian_century * (36000.76983 + julian_century * 0.0003032)) % 360
end

.hour_angle(date, latitude) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/sun.rb', line 169

def self.hour_angle(date, latitude)
  julian_century = julian_century(date)
  oblique_correction = oblique_correction(julian_century)
  declination = declination(oblique_correction, julian_century)
  res = Math.cos(radians(SOLAR_ZENITH)) / (Math.cos(radians(latitude)) * Math.cos(radians(declination))) - Math.tan(radians(latitude)) * Math.tan(radians(declination))
  degrees(Math.acos(res))
end

.julian_century(time) ⇒ Object



114
115
116
# File 'lib/sun.rb', line 114

def self.julian_century(time)
  (julian_days(time) - JULIAN_CONSTANT) / 36525
end

.julian_days(time) ⇒ Object

Calculations



106
107
108
109
110
111
112
# File 'lib/sun.rb', line 106

def self.julian_days(time)
  if time.is_a?(Time)
    time.to_datetime.ajd
  else
    date(time).ajd
  end
end

.mean_obliquity_of_ecliptic(julian_century) ⇒ Object



118
119
120
# File 'lib/sun.rb', line 118

def self.mean_obliquity_of_ecliptic(julian_century)
  23 + (26 + ((21.448 - julian_century * (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60
end

.oblique_correction(julian_century) ⇒ Object



122
123
124
# File 'lib/sun.rb', line 122

def self.oblique_correction(julian_century)
  mean_obliquity_of_ecliptic(julian_century) + 0.00256 * Math.cos(radians(125.04 - 1934.136 * julian_century))
end

.offset_multiplier(type) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/sun.rb', line 96

def self.offset_multiplier(type)
  case type
  when :sunrise then -1
  when :solar_noon then 0
  when :sunset then 1
  end
end

.radians(degrees) ⇒ Object



63
64
65
# File 'lib/sun.rb', line 63

def self.radians(degrees)
  Rational(Math::PI * degrees, 180)
end

.solar_noon(time, latitude, longitude) ⇒ Object



26
27
28
# File 'lib/sun.rb', line 26

def self.solar_noon(time, latitude, longitude)
  sun_time(:solar_noon, time, latitude, longitude)
end

.solar_noon_minutes(time, latitude, longitude) ⇒ Object



40
41
42
# File 'lib/sun.rb', line 40

def self.solar_noon_minutes(time, latitude, longitude)
  sun_time_minutes(:solar_noon, time, latitude, longitude)
end

.sun_time(type, time, latitude, longitude) ⇒ Object

Base our calculations off the astronomical julian date for our input time. Our formula is sensitive to time of day, so we ignore it in order to give consistent results for any time on the same date.



78
79
80
81
82
# File 'lib/sun.rb', line 78

def self.sun_time(type, time, latitude, longitude)
  date = date(time)
  minutes = sun_time_minutes(type, date, latitude, longitude)
  date_at_time(date, minutes)
end

.sun_time_minutes(type, time, latitude, longitude) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/sun.rb', line 84

def self.sun_time_minutes(type, time, latitude, longitude)
  date = date(time)
  if offset_multiplier(type) == 0
    offset = 0
  else
    offset = offset_multiplier(type) * 4 * hour_angle(date, latitude)
  end
  720 - (4 * longitude) - equation_of_time(date, longitude) + offset
rescue Math::DomainError
  raise InvalidCoordinates, "Could not determine sun times for coordinates: #{latitude}, #{longitude}"
end

.sunrise(time, latitude, longitude) ⇒ Object

Sun times (UTC)



22
23
24
# File 'lib/sun.rb', line 22

def self.sunrise(time, latitude, longitude)
  sun_time(:sunrise, time, latitude, longitude)
end

.sunrise_minutes(time, latitude, longitude) ⇒ Object

Sun times in minutes after midnight (UTC)



36
37
38
# File 'lib/sun.rb', line 36

def self.sunrise_minutes(time, latitude, longitude)
  sun_time_minutes(:sunrise, time, latitude, longitude)
end

.sunset(time, latitude, longitude) ⇒ Object



30
31
32
# File 'lib/sun.rb', line 30

def self.sunset(time, latitude, longitude)
  sun_time(:sunset, time, latitude, longitude)
end

.sunset_minutes(time, latitude, longitude) ⇒ Object



44
45
46
# File 'lib/sun.rb', line 44

def self.sunset_minutes(time, latitude, longitude)
  sun_time_minutes(:sunset, time, latitude, longitude)
end

.true_longitude(julian_century) ⇒ Object



147
148
149
# File 'lib/sun.rb', line 147

def self.true_longitude(julian_century)
  geometric_mean_longitude(julian_century) + equation_of_center(julian_century)
end

.y(oblique_correction) ⇒ Object



134
135
136
# File 'lib/sun.rb', line 134

def self.y(oblique_correction)
  Math.tan(radians(Rational(oblique_correction, 2))) * Math.tan(radians(Rational(oblique_correction, 2)))
end