solar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +33 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/solar.rb +443 -0
- data/solar.gemspec +59 -0
- data/test/helper.rb +18 -0
- data/test/test_solar.rb +3 -0
- metadata +126 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1"
|
12
|
+
gem "jeweler", "~> 1.8.4"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.8)
|
5
|
+
i18n (~> 0.6)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
git (1.2.5)
|
8
|
+
i18n (0.6.1)
|
9
|
+
jeweler (1.8.4)
|
10
|
+
bundler (~> 1.0)
|
11
|
+
git (>= 1.2.5)
|
12
|
+
rake
|
13
|
+
rdoc
|
14
|
+
json (1.7.5)
|
15
|
+
multi_json (1.3.6)
|
16
|
+
rake (0.9.2.2)
|
17
|
+
rdoc (3.12)
|
18
|
+
json (~> 1.4)
|
19
|
+
shoulda (3.1.1)
|
20
|
+
shoulda-context (~> 1.0)
|
21
|
+
shoulda-matchers (~> 1.2)
|
22
|
+
shoulda-context (1.0.0)
|
23
|
+
shoulda-matchers (1.3.0)
|
24
|
+
activesupport (>= 3.0.0)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
bundler (~> 1)
|
31
|
+
jeweler (~> 1.8.4)
|
32
|
+
rdoc (~> 3.12)
|
33
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Javier Goizueta
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= solar
|
2
|
+
|
3
|
+
Calculation of solar position, rise & set times for a given position & time.
|
4
|
+
|
5
|
+
== Contributing to solar
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2012 Javier Goizueta. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "solar"
|
18
|
+
gem.homepage = "http://github.com/jgoizueta/solar"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{# Calculation of solar position, rise & set times}
|
21
|
+
gem.description = %Q{# Calculation of solar position, rise & set times for a given position & time.}
|
22
|
+
gem.email = "jgoizueta@gmail.com"
|
23
|
+
gem.authors = ["Javier Goizueta"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'rdoc/task'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "solar #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/solar.rb
ADDED
@@ -0,0 +1,443 @@
|
|
1
|
+
# Calculation of solar position, rise & set times for a given position & time.
|
2
|
+
# Algorithms are taken from Jean Meeus, Astronomical Algorithms
|
3
|
+
# Some code & ideas taken from John P. Power's astro-algo: http://astro-algo.rubyforge.org/astro-algo/
|
4
|
+
module Solar
|
5
|
+
|
6
|
+
ALTITUDES = {
|
7
|
+
:official => -50/60.0,
|
8
|
+
:civil => -6.0,
|
9
|
+
:nautical => -12.0,
|
10
|
+
:astronomical => -18.0
|
11
|
+
}
|
12
|
+
|
13
|
+
class <<self
|
14
|
+
|
15
|
+
# Day-night (or twilight) status at a given position and time
|
16
|
+
# returns :night, :day or :twilight
|
17
|
+
# options:
|
18
|
+
# * :twilight_zenith zenith for the sun at dawn (beginning of twilight)
|
19
|
+
# and at dusk (end of twilight). Default: :civil
|
20
|
+
# * :day_zenith zenith for the san at sunrise and sun set.
|
21
|
+
# Default: :official (sun aparently under the horizon, tangent to it)
|
22
|
+
# These parameters can be assigned zenith values in degrees of the symbols:
|
23
|
+
# :official, :civil, :nautical or :astronomical.
|
24
|
+
def day_or_night(t, longitude, latitude, options={})
|
25
|
+
if options[:zenith]
|
26
|
+
twilight_altitude = day_altitude = altitude_from_options(options)
|
27
|
+
else
|
28
|
+
twilight_altitude = altitude_from_options(:zenith => options[:twilight_zenith] || :civil)
|
29
|
+
day_altitude = altitude_from_options(:zenith => options[:day_zenith] || :official)
|
30
|
+
end
|
31
|
+
al,az = position(t, longitude, latitude)
|
32
|
+
(al > day_altitude) ? :day : (al <= twilight_altitude) ? :night : :twilight
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sun horizontal coordinates (relative position) in degrees:
|
36
|
+
# * elevation (altitude over horizon) in degrees; positive upwards
|
37
|
+
# * azimuth in degrees measured clockwise (towards East) from North direction
|
38
|
+
def position(t, longitude, latitude)
|
39
|
+
|
40
|
+
delta_rad, alpha_rad = equatorial_position_rad(t)
|
41
|
+
alpha_deg = to_deg(alpha_rad)
|
42
|
+
# alpha_h += 360 if alpha_h < 0
|
43
|
+
|
44
|
+
|
45
|
+
# t as Julian centuries of 36525 ephemeris days form the epoch J2000.0
|
46
|
+
if false
|
47
|
+
# Float
|
48
|
+
jd = jd_f(t)
|
49
|
+
else
|
50
|
+
# Rational
|
51
|
+
jd = jd_r(t)
|
52
|
+
end
|
53
|
+
t = to_jc(jd)
|
54
|
+
|
55
|
+
# Sidereal time at Greenwich
|
56
|
+
theta = 280.46061837 + 360.98564736629*(jd-2451545) + (0.000387933 - t/38710000)*t*t
|
57
|
+
|
58
|
+
# Reduce magnitude to minimize errors
|
59
|
+
theta %= 360
|
60
|
+
|
61
|
+
# Local hour angle
|
62
|
+
h = theta + longitude - alpha_deg
|
63
|
+
h %= 360
|
64
|
+
|
65
|
+
latitude_rad = to_rad(latitude)
|
66
|
+
h_rad = to_rad(h)
|
67
|
+
|
68
|
+
# Local horizontal coordinates : Meeus pg 89
|
69
|
+
altitude_rad = Math.asin(Math.sin(latitude_rad)*Math.sin(delta_rad) + Math.cos(latitude_rad)*Math.cos(delta_rad)*Math.cos(h_rad))
|
70
|
+
azimuth_rad = Math.atan2((Math.sin(h_rad)),((Math.cos(h_rad) * Math.sin(latitude_rad)) - Math.tan(delta_rad) * Math.cos(latitude_rad)))
|
71
|
+
|
72
|
+
[to_deg(altitude_rad), (180+to_deg(azimuth_rad))%360 ]
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sun rise time for a given date (UTC) and position.
|
77
|
+
# The :zenith or :altitude of the sun can be passed as an argument,
|
78
|
+
# which can be numeric (in degrees) or symbolic:
|
79
|
+
# :official, :civil, :nautical or :astronomical.
|
80
|
+
# nil is returned if the sun doesn't rise at the date and position.
|
81
|
+
def rise(date, longitude, latitude, options={})
|
82
|
+
rising, transit, setting = passages(date, longitude, latitude, options)
|
83
|
+
if rising==setting || (setting-rising)==1
|
84
|
+
nil # rising==setting => no rise; setting-rising == 1 => no set
|
85
|
+
else
|
86
|
+
rising
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sun set time for a given date (UTC) and position.
|
91
|
+
# The :zenith or :altitude of the sun can be passed as an argument,
|
92
|
+
# which can be numeric (in degrees) or symbolic:
|
93
|
+
# :official, :civil, :nautical or :astronomical.
|
94
|
+
# nil is returned if the sun doesn't set at the date and position.
|
95
|
+
def set(date, longitude, latitude, options={})
|
96
|
+
rising, transit, setting = passages(date, longitude, latitude, options)
|
97
|
+
if rising==setting || (setting-rising)==1
|
98
|
+
nil # rising==setting => no rise; setting-rising == 1 => no set
|
99
|
+
else
|
100
|
+
setting
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Rise and set times as given by rise() and set()
|
105
|
+
def rise_and_set(date, longitude, latitude, options={})
|
106
|
+
rising, transit, setting = passages(date, longitude, latitude, options)
|
107
|
+
if rising==setting || (setting-rising)==1
|
108
|
+
nil # rising==setting => no rise; setting-rising == 1 => no set
|
109
|
+
else
|
110
|
+
[rising, setting]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Solar passages [rising, transit, setting] for a given date (UTC) and position.
|
115
|
+
# The :zenith or :altitude of the sun can be passed as an argument,
|
116
|
+
# which can be numeric (in degrees) or symbolic:
|
117
|
+
# :official, :civil, :nautical or :astronomical.
|
118
|
+
# In circumpolar case:
|
119
|
+
# If Sun never rises, returns 00:00:00 on Date for all passages.
|
120
|
+
# If Sun never sets, returns 00:00:00 (rising), 12:00:00 (transit), 24:00:00 (setting)
|
121
|
+
# on Date for all passages.
|
122
|
+
def passages(date, longitude, latitude, options={})
|
123
|
+
|
124
|
+
ho = altitude_from_options(options)
|
125
|
+
t = to_jc(jd_r(date.to_datetime))
|
126
|
+
theta0 = (100.46061837 + (36000.770053608 + (0.000387933 - t/38710000)*t)*t) % 360
|
127
|
+
# Calculate apparent right ascention and declination for 0 hr Dynamical time for three days (degrees)
|
128
|
+
ra = []
|
129
|
+
decl = []
|
130
|
+
-1.upto(1) do |i|
|
131
|
+
declination, right_ascention = equatorial_position_rad((date+i).to_datetime)
|
132
|
+
ra << to_deg(right_ascention)
|
133
|
+
decl << to_deg(declination)
|
134
|
+
end
|
135
|
+
# tweak right ascention around 180 degrees (autumnal equinox)
|
136
|
+
if ra[0] > ra[1]
|
137
|
+
ra[0] -= 360
|
138
|
+
end
|
139
|
+
if ra[2] < ra[1]
|
140
|
+
ra[2] += 360
|
141
|
+
end
|
142
|
+
|
143
|
+
ho_rad, latitude_rad = [ho, latitude].map{|x| to_rad(x)}
|
144
|
+
decl_rad = decl.map{|x| to_rad(x)}
|
145
|
+
|
146
|
+
# approximate Hour Angle (degrees)
|
147
|
+
ha = Math.sin(ho_rad) / (Math.cos(latitude_rad) * Math.cos(decl_rad[1])) - Math.tan(latitude_rad) * Math.tan(decl_rad[1])
|
148
|
+
# handle circumpolar. see note 2 at end of chapter
|
149
|
+
if ha.abs <= 1
|
150
|
+
ha = to_deg(Math.acos(ha))
|
151
|
+
elsif ha > 1 # circumpolar - sun never rises
|
152
|
+
# format sunrise, sunset & solar noon as DateTime
|
153
|
+
sunrise = date.to_datetime
|
154
|
+
transit = date.to_datetime
|
155
|
+
sunset = date.to_datetime
|
156
|
+
return [sunrise, transit, sunset]
|
157
|
+
else # cirumpolar - sun never sets
|
158
|
+
# format sunrise, sunset & solar noon as DateTime
|
159
|
+
sunrise = date.to_datetime
|
160
|
+
transit = date.to_datetime + 0.5
|
161
|
+
sunset = date.to_datetime + 1
|
162
|
+
return [sunrise, transit, sunset]
|
163
|
+
end
|
164
|
+
# approximate m (fraction of 1 day)
|
165
|
+
# store days added or subtracted to add in later
|
166
|
+
m = []
|
167
|
+
days = [0]*3
|
168
|
+
for i in 0..2
|
169
|
+
case i
|
170
|
+
when 0
|
171
|
+
m[i] = (ra[1] - longitude - theta0) / 360 # transit
|
172
|
+
day_offset = +1
|
173
|
+
when 1
|
174
|
+
m[i] = m[0] - ha / 360 # rising
|
175
|
+
day_offset = -1
|
176
|
+
when 2
|
177
|
+
m[i] = m[0] + ha / 360 # setting
|
178
|
+
day_offset = -1
|
179
|
+
end
|
180
|
+
|
181
|
+
until m[i] >= 0 do
|
182
|
+
m[i] += 1
|
183
|
+
days[i] += day_offset
|
184
|
+
end
|
185
|
+
until m[i] <= 1 do
|
186
|
+
m[i] -= 1
|
187
|
+
days[i] -= day_offset
|
188
|
+
end
|
189
|
+
end
|
190
|
+
theta = [] # apparent sidereal time (degrees)
|
191
|
+
ra2 = [] # apparent right ascension (degrees)
|
192
|
+
decl2 = [] # apparent declination (degrees)
|
193
|
+
h = [] # local hour angle (degrees)
|
194
|
+
alt = [] # altitude (degrees)
|
195
|
+
delta_m = [1]*3
|
196
|
+
while ( delta_m[0] >= 0.01 || delta_m[1] >= 0.01 || delta_m[2] >= 0.01 ) do
|
197
|
+
0.upto(2) do |i|
|
198
|
+
theta[i] = theta0 + 360.985647 * m[i]
|
199
|
+
n = m[i] + delta_t(date.to_datetime).to_r / 86400
|
200
|
+
a = ra[1] - ra[0]
|
201
|
+
b = ra[2] - ra[1]
|
202
|
+
c = b - a
|
203
|
+
ra2[i] = ra[1] + n / 2 * ( a + b + n * c )
|
204
|
+
|
205
|
+
n = m[i] + delta_t(date.to_datetime).to_r / 86400
|
206
|
+
a = decl[1] - decl[0]
|
207
|
+
b = decl[2] - decl[1]
|
208
|
+
c = b - a
|
209
|
+
decl2[i] = decl[1] + n / 2 * ( a + b + n * c )
|
210
|
+
|
211
|
+
h[i] = theta[i] + longitude - ra2[i]
|
212
|
+
|
213
|
+
alt[i] = to_deg Math.asin(Math.sin(latitude_rad) * Math.sin(to_rad(decl2[i])) +
|
214
|
+
Math.cos(latitude_rad) * Math.cos(to_rad(decl2[i])) * Math.cos(to_rad(h[i])))
|
215
|
+
end
|
216
|
+
# adjust m
|
217
|
+
delta_m[0] = -h[0] / 360
|
218
|
+
1.upto(2) do |i|
|
219
|
+
delta_m[i] = (alt[i] - ho) / (360 * Math.cos(to_rad(decl2[i])) * Math.cos(latitude_rad) * Math.sin(to_rad(h[i])))
|
220
|
+
end
|
221
|
+
0.upto(2) do |i|
|
222
|
+
m[i] += delta_m[i]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
# format sunrise, sunset & solar noon as DateTime
|
226
|
+
sunrise = date.to_datetime + m[1] + days[1]
|
227
|
+
transit = date.to_datetime + m[0] + days[0]
|
228
|
+
sunset = date.to_datetime + m[2] + days[2]
|
229
|
+
[sunrise, transit, sunset]
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
# Julian Day as Rational
|
236
|
+
def jd_r(t)
|
237
|
+
if false
|
238
|
+
# This computes JD with precision of seconds and yields smaller denominators
|
239
|
+
t = t.utc
|
240
|
+
t.to_date.ajd + Rational(t.hour,24) + Rational(t.min,1440) + Rational(t.sec,86400)
|
241
|
+
else
|
242
|
+
# This preserves the internal precision of t (which we probably don't need)
|
243
|
+
# and produces larger denominators in general
|
244
|
+
t.to_datetime.utc.ajd
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Julian Day as Float
|
249
|
+
def jd_f(t)
|
250
|
+
# t.to_date.ajd.to_f + t.hour/24.0 + t.min/1440.0 + t.sec/86400.0
|
251
|
+
t.to_datetime.utc.ajd.to_f
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_rad(deg)
|
255
|
+
deg*Math::PI/180.0
|
256
|
+
end
|
257
|
+
|
258
|
+
def to_deg(rad)
|
259
|
+
rad*180.0/Math::PI
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_h(deg)
|
263
|
+
deg/15.0
|
264
|
+
end
|
265
|
+
|
266
|
+
# Julian day to Julian Centuries since J2000.0
|
267
|
+
def to_jc(jd)
|
268
|
+
(jd - 2451545)/36525
|
269
|
+
end
|
270
|
+
|
271
|
+
def polynomial(coefficients, x)
|
272
|
+
coefficients.inject(0.0){|p, a| p*x + a}
|
273
|
+
end
|
274
|
+
|
275
|
+
# Conversion of Float to Rational preserving the exact value of the number
|
276
|
+
def to_r(x)
|
277
|
+
x = x.to_f
|
278
|
+
return Rational(x.to_i,1) if x.modulo(1)==0
|
279
|
+
if !x.finite?
|
280
|
+
return Rational(0,0) if x.nan?
|
281
|
+
return x<0 ? Rational(-1,0) : Rational(1,0)
|
282
|
+
end
|
283
|
+
|
284
|
+
f,e = Math.frexp(x)
|
285
|
+
|
286
|
+
if e < Float::MIN_EXP
|
287
|
+
bits = e+Float::MANT_DIG-Float::MIN_EXP
|
288
|
+
else
|
289
|
+
bits = [Float::MANT_DIG,e].max
|
290
|
+
#return Rational(x.to_i,1) if bits<e
|
291
|
+
end
|
292
|
+
p = Math.ldexp(f,bits)
|
293
|
+
e = bits - e
|
294
|
+
if e<Float::MAX_EXP
|
295
|
+
q = Math.ldexp(1,e)
|
296
|
+
else
|
297
|
+
q = Float::RADIX**e
|
298
|
+
end
|
299
|
+
return Rational(p.to_i,q.to_i)
|
300
|
+
end
|
301
|
+
|
302
|
+
# time to dynamical time
|
303
|
+
def to_td(t)
|
304
|
+
t = t.utc
|
305
|
+
t + to_r(delta_t(t))/86400
|
306
|
+
end
|
307
|
+
|
308
|
+
# dynamical_time to utc
|
309
|
+
def to_utc(td)
|
310
|
+
raise "Invalid dynamical time (should be utc)" unless td.utc?
|
311
|
+
td - to_r(delta_t(td))/86400
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
# Compute difference between dynamical time and UTC in seconds.
|
316
|
+
# See http://sunearth.gsfc.nasa.gov/eclipse/SEcat5/deltatpoly.html.
|
317
|
+
# Good from -1999 to +3000.
|
318
|
+
def delta_t(date)
|
319
|
+
|
320
|
+
year = date.year.to_f
|
321
|
+
y = year + (date.month.to_f - 0.5) / 12.0
|
322
|
+
|
323
|
+
case
|
324
|
+
when year < -500.0
|
325
|
+
u = (year - 1820.0) / 100.0
|
326
|
+
-20.0 + 32.0*u*u
|
327
|
+
when year < 500.0
|
328
|
+
u = y / 100.0
|
329
|
+
polynomial [0.0090316521, 0.022174192, -0.1798452, -5.952053, 33.78311, -1014.41, 10583.6], u
|
330
|
+
when year < 1600.0
|
331
|
+
u = (y - 1000.0) / 100.0
|
332
|
+
polynomial [0.0083572073, -0.005050998, -0.8503463, 0.319781, 71.23472, -556.01, 1574.2], u
|
333
|
+
when year < 1700.0
|
334
|
+
t = y - 1600.0
|
335
|
+
polynomial [1.0/7129.0, -0.01532, -0.9808, 120.0], t
|
336
|
+
when year < 1800.0
|
337
|
+
t = y - 1700.0
|
338
|
+
polynomial [-1.0/1174000.0, 0.00013336, -0.0059285, 0.1603, 8.83], t
|
339
|
+
when year < 1860.0
|
340
|
+
t = y - 1800.0
|
341
|
+
polynomial [0.000000000875, -0.0000001699, 0.0000121272, -0.00037436, 0.0041116, 0.0068612, -0.332447, 13.72], t
|
342
|
+
when year < 1900.0
|
343
|
+
t = y - 1860.0
|
344
|
+
polynomial [1.0/233174.0, -0.0004473624, 0.01680668, -0.251754, 0.5737, 7.62], t
|
345
|
+
when year < 1920.0
|
346
|
+
t = y - 1900.0
|
347
|
+
polynomial [-0.000197, 0.0061966, -0.0598939, 1.494119, -2.79], t
|
348
|
+
when year < 1941.0
|
349
|
+
t = y - 1920.0
|
350
|
+
polynomial [0.0020936, -0.076100, 0.84493, 21.20], t
|
351
|
+
when year < 1961.0
|
352
|
+
t = y - 1950.0
|
353
|
+
polynomial [1.0/2547.0, -1.0/233.0, 0.407, 29.07], t
|
354
|
+
when year < 1986.0
|
355
|
+
t = y - 1975.0
|
356
|
+
polynomial [-1.0/718.0, -1.0/260.0, 1.067, 45.45], t
|
357
|
+
when year < 2005.0
|
358
|
+
t = y - 2000.0
|
359
|
+
polynomial [0.00002373599, 0.000651814, 0.0017275, -0.060374, 0.3345, 63.86], t
|
360
|
+
when year < 2050.0
|
361
|
+
t = y - 2000.0
|
362
|
+
polynomial [0.005589, 0.32217, 62.92], t
|
363
|
+
when year < 2150.0
|
364
|
+
-20.0 + 32.0*((y - 1820.0)/100.0)**2 - 0.5628*(2150.0 - y)
|
365
|
+
else
|
366
|
+
u = (year - 1820.0) / 100.0
|
367
|
+
-20.0 + 32*u*u
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# Solar equatorial coordinates / Low accuracy : Meeus pg 151
|
372
|
+
# returns [declination in radians, right ascension in radians]
|
373
|
+
def equatorial_position_rad(t)
|
374
|
+
# t as Julian centuries of 36525 ephemeris days form the epoch J2000.0
|
375
|
+
if false
|
376
|
+
# Float
|
377
|
+
jd = jd_f(to_td(t))
|
378
|
+
else
|
379
|
+
# Rational
|
380
|
+
jd = jd_r(to_td(t))
|
381
|
+
end
|
382
|
+
t = to_jc(jd)
|
383
|
+
|
384
|
+
# Geometric mean longitude of the Sun, referred to the mean equinox of the date
|
385
|
+
l = 280.46645 + (36000.76983 + 0.0003032*t)*t
|
386
|
+
|
387
|
+
# Mean anomaly of the Sun
|
388
|
+
m_deg = 357.52910 + (35999.05030 - (0.0001559 + 0.00000048*t)*t)*t
|
389
|
+
m_rad = to_rad(m_deg)
|
390
|
+
|
391
|
+
# Eccentricity of the Earth's orbit
|
392
|
+
e = 0.016708617 - (0.000042037 + 0.0000001236*t)*t
|
393
|
+
|
394
|
+
# Sun's Equation of the center
|
395
|
+
c = (1.914600 - (0.004817 + 0.000014*t)*t)*Math.sin(m_rad) + (0.019993 - 0.000101*t)*Math.sin(2*m_rad) + 0.000290*Math.sin(3*m_rad)
|
396
|
+
|
397
|
+
# Sun's true longitude
|
398
|
+
o = l + c
|
399
|
+
|
400
|
+
# Reduce magnitude to minimize errors
|
401
|
+
o %= 360
|
402
|
+
|
403
|
+
# Sun's apparent Longitude
|
404
|
+
omega_deg = 125.04 - 1934.136*t
|
405
|
+
omega_rad = to_rad(omega_deg)
|
406
|
+
lambda_deg = o - 0.00569 - 0.00478 * Math.sin(omega_rad)
|
407
|
+
|
408
|
+
# Reduce magnitude to minimize errors
|
409
|
+
lambda_deg %= 360
|
410
|
+
|
411
|
+
lambda_rad = to_rad(lambda_deg)
|
412
|
+
|
413
|
+
# Obliquity of the ecliptic
|
414
|
+
epsilon_deg = 23.4392966666667 - (0.012777777777777778 + (0.00059/60 - 0.00059/60*t)*t)*t + 0.00256*Math.cos(omega_rad)
|
415
|
+
epsilon_rad = to_rad(epsilon_deg)
|
416
|
+
|
417
|
+
# Sun's declination
|
418
|
+
delta_rad = Math.asin(Math.sin(epsilon_rad)*Math.sin(lambda_rad))
|
419
|
+
|
420
|
+
# Sun's right ascension
|
421
|
+
alpha_rad = Math.atan2(((Math.cos(epsilon_rad) * Math.sin(lambda_rad))),(Math.cos(lambda_rad)))
|
422
|
+
|
423
|
+
[delta_rad, alpha_rad]
|
424
|
+
end
|
425
|
+
|
426
|
+
def altitude_from_options(options)
|
427
|
+
if options.has_key?(:zenith)
|
428
|
+
zenith = options[:zenith]
|
429
|
+
if Symbol===zenith
|
430
|
+
altitude = ALTITUDES[zenith]
|
431
|
+
else
|
432
|
+
altitude = 90.0 - zenith
|
433
|
+
end
|
434
|
+
else
|
435
|
+
altitude = options[:altitude] || :official
|
436
|
+
altitude = ALTITUDES[altitude] if Symbol===altitude
|
437
|
+
end
|
438
|
+
altitude
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
|
443
|
+
end
|
data/solar.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "solar"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Javier Goizueta"]
|
12
|
+
s.date = "2012-10-07"
|
13
|
+
s.description = "# Calculation of solar position, rise & set times for a given position & time."
|
14
|
+
s.email = "jgoizueta@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/solar.rb",
|
28
|
+
"solar.gemspec",
|
29
|
+
"test/helper.rb",
|
30
|
+
"test/test_solar.rb"
|
31
|
+
]
|
32
|
+
s.homepage = "http://github.com/jgoizueta/solar"
|
33
|
+
s.licenses = ["MIT"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = "1.8.23"
|
36
|
+
s.summary = "# Calculation of solar position, rise & set times"
|
37
|
+
|
38
|
+
if s.respond_to? :specification_version then
|
39
|
+
s.specification_version = 3
|
40
|
+
|
41
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
42
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
43
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
44
|
+
s.add_development_dependency(%q<bundler>, ["~> 1"])
|
45
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
46
|
+
else
|
47
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
48
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
49
|
+
s.add_dependency(%q<bundler>, ["~> 1"])
|
50
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
54
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
55
|
+
s.add_dependency(%q<bundler>, ["~> 1"])
|
56
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'solar'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
data/test/test_solar.rb
ADDED
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: solar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Javier Goizueta
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: shoulda
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.12'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.12'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: jeweler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.8.4
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.8.4
|
78
|
+
description: ! '# Calculation of solar position, rise & set times for a given position
|
79
|
+
& time.'
|
80
|
+
email: jgoizueta@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files:
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.rdoc
|
86
|
+
files:
|
87
|
+
- .document
|
88
|
+
- Gemfile
|
89
|
+
- Gemfile.lock
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.rdoc
|
92
|
+
- Rakefile
|
93
|
+
- VERSION
|
94
|
+
- lib/solar.rb
|
95
|
+
- solar.gemspec
|
96
|
+
- test/helper.rb
|
97
|
+
- test/test_solar.rb
|
98
|
+
homepage: http://github.com/jgoizueta/solar
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
hash: -1434105629411668525
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.8.23
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: ! '# Calculation of solar position, rise & set times'
|
126
|
+
test_files: []
|