solar 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8f9579b11f6e74d46611e8558786d36fd18a6f3
4
+ data.tar.gz: 73152c453267480d1d4b9db82fdf4fbad9b8e82b
5
+ SHA512:
6
+ metadata.gz: 40c66e8eca3c9457b31e6863d44057fcd758622133e6b11b2de734f5a06ec00812551ed09ff2a28fa51d574255366c41c2d9d439cffc0e8ebe98b5626554621c
7
+ data.tar.gz: d5d1537301f3f070966a250c874525dbb9be2f6e50c68a6f8d3e39e49617adf0a136d55b8a7c226798c3650f06e73efe2254758d60db8a30eb9b30a572177dd4
data/Gemfile CHANGED
@@ -8,5 +8,5 @@ group :development do
8
8
  gem "shoulda", ">= 0"
9
9
  gem "rdoc", "~> 3.12"
10
10
  gem "bundler", "~> 1"
11
- gem "jeweler", "~> 1.8.4"
11
+ gem "jeweler", "~> 2.0.1"
12
12
  end
@@ -4,16 +4,41 @@ GEM
4
4
  activesupport (3.2.8)
5
5
  i18n (~> 0.6)
6
6
  multi_json (~> 1.0)
7
- git (1.2.5)
7
+ addressable (2.3.8)
8
+ builder (3.2.2)
9
+ faraday (0.7.6)
10
+ addressable (~> 2.2)
11
+ multipart-post (~> 1.1)
12
+ rack (~> 1.1)
13
+ git (1.2.9.1)
14
+ github_api (0.4.10)
15
+ faraday (~> 0.7.6)
16
+ hashie (~> 1.2.0)
17
+ multi_json (~> 1.0)
18
+ oauth2 (~> 0.5.2)
19
+ hashie (1.2.0)
20
+ highline (1.7.2)
8
21
  i18n (0.6.1)
9
- jeweler (1.8.4)
10
- bundler (~> 1.0)
22
+ jeweler (2.0.1)
23
+ builder
24
+ bundler (>= 1.0)
11
25
  git (>= 1.2.5)
26
+ github_api
27
+ highline (>= 1.6.15)
28
+ nokogiri (>= 1.5.10)
12
29
  rake
13
30
  rdoc
14
31
  json (1.7.5)
32
+ mini_portile (0.6.2)
15
33
  multi_json (1.3.6)
16
- rake (0.9.2.2)
34
+ multipart-post (1.2.0)
35
+ nokogiri (1.6.6.2)
36
+ mini_portile (~> 0.6.0)
37
+ oauth2 (0.5.2)
38
+ faraday (~> 0.7)
39
+ multi_json (~> 1.0)
40
+ rack (1.6.4)
41
+ rake (10.4.2)
17
42
  rdoc (3.12)
18
43
  json (~> 1.4)
19
44
  shoulda (3.1.1)
@@ -29,6 +54,6 @@ PLATFORMS
29
54
  DEPENDENCIES
30
55
  activesupport
31
56
  bundler (~> 1)
32
- jeweler (~> 1.8.4)
57
+ jeweler (~> 2.0.1)
33
58
  rdoc (~> 3.12)
34
59
  shoulda
@@ -1,6 +1,7 @@
1
1
  = solar
2
2
 
3
3
  Calculation of solar position, rise & set times for a given position & time.
4
+ Also calculation of solar radiation and radiation on a sloped surface.
4
5
 
5
6
  == Contributing to solar
6
7
 
@@ -14,6 +15,5 @@ Calculation of solar position, rise & set times for a given position & time.
14
15
 
15
16
  == Copyright
16
17
 
17
- Copyright (c) 2012 Javier Goizueta. See LICENSE.txt for
18
+ Copyright (c) 2012-2015 Javier Goizueta. See LICENSE.txt for
18
19
  further details.
19
-
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.1
@@ -5,466 +5,11 @@ require 'active_support/time'
5
5
  # Algorithms are taken from Jean Meeus, Astronomical Algorithms
6
6
  # Some code & ideas taken from John P. Power's astro-algo: http://astro-algo.rubyforge.org/astro-algo/
7
7
  module Solar
8
-
9
- ALTITUDES = {
10
- :official => -50/60.0,
11
- :civil => -6.0,
12
- :nautical => -12.0,
13
- :astronomical => -18.0
14
- }
15
-
16
- class <<self
17
-
18
- # Day-night (or twilight) status at a given position and time
19
- # returns :night, :day or :twilight
20
- # options:
21
- # * :twilight_zenith zenith for the sun at dawn (beginning of twilight)
22
- # and at dusk (end of twilight). Default: :civil
23
- # * :day_zenith zenith for the san at sunrise and sun set.
24
- # Default: :official (sun aparently under the horizon, tangent to it)
25
- # These parameters can be assigned zenith values in degrees of the symbols:
26
- # :official, :civil, :nautical or :astronomical.
27
- # Simple day night result (returning :day or :night) can be requested
28
- # by setting :simple=>true (which usses the official day definition)
29
- # or by setting a :zenith parameter to the fine the kind of day-night
30
- # distinction.
31
- # By passing :detailed=>true, the result will be one of:
32
- # :night, :astronomical_twilight, :nautical_twilight, :civil_twilight, :day
33
- def day_or_night(t, longitude, latitude, options={})
34
- h, az = position(t, longitude, latitude)
35
- options = {:zenith=>:official} if options[:simple]
36
- if options[:detailed]
37
- if h<Solar::ALTITUDES[:astronomical]
38
- :night
39
- elsif h<Solar::ALTITUDES[:nautical]
40
- :astronomical_twilight
41
- elsif h<Solar::ALTITUDES[:civil]
42
- :nautical_twilight
43
- elsif h<Solar::ALTITUDES[:official]
44
- :civil_twilight
45
- else
46
- :day
47
- end
48
- else
49
- # Determined :night / :twilight / :day state;
50
- # twilight/day definition can be changed with options :zenith or :twilight_zenith, :day_zenith
51
- if options[:zenith]
52
- # only :day / :night distinction as defined by :zenith
53
- twilight_altitude = day_altitude = altitude_from_options(options)
54
- else
55
- twilight_altitude = altitude_from_options(:zenith => options[:twilight_zenith] || :civil)
56
- day_altitude = altitude_from_options(:zenith => options[:day_zenith] || :official)
57
- end
58
- (h > day_altitude) ? :day : (h <= twilight_altitude) ? :night : :twilight
59
- end
60
- end
61
-
62
- # Sun horizontal coordinates (relative position) in degrees:
63
- # * elevation (altitude over horizon) in degrees; positive upwards
64
- # * azimuth in degrees measured clockwise (towards East) from North direction
65
- def position(t, longitude, latitude)
66
-
67
- delta_rad, alpha_rad = equatorial_position_rad(t)
68
- alpha_deg = to_deg(alpha_rad)
69
- # alpha_h += 360 if alpha_h < 0
70
-
71
-
72
- # t as Julian centuries of 36525 ephemeris days form the epoch J2000.0
73
- if false
74
- # Float
75
- jd = jd_f(t)
76
- else
77
- # Rational
78
- jd = jd_r(t)
79
- end
80
- t = to_jc(jd)
81
-
82
- # Sidereal time at Greenwich
83
- theta = 280.46061837 + 360.98564736629*(jd-2451545) + (0.000387933 - t/38710000)*t*t
84
-
85
- # Reduce magnitude to minimize errors
86
- theta %= 360
87
-
88
- # Local hour angle
89
- h = theta + longitude - alpha_deg
90
- h %= 360
91
-
92
- latitude_rad = to_rad(latitude)
93
- h_rad = to_rad(h)
94
-
95
- # Local horizontal coordinates : Meeus pg 89
96
- altitude_rad = Math.asin(Math.sin(latitude_rad)*Math.sin(delta_rad) + Math.cos(latitude_rad)*Math.cos(delta_rad)*Math.cos(h_rad))
97
- azimuth_rad = Math.atan2((Math.sin(h_rad)),((Math.cos(h_rad) * Math.sin(latitude_rad)) - Math.tan(delta_rad) * Math.cos(latitude_rad)))
98
-
99
- [to_deg(altitude_rad), (180+to_deg(azimuth_rad))%360 ]
100
-
101
- end
102
-
103
- # Sun rise time for a given date (UTC) and position.
104
- # The :zenith or :altitude of the sun can be passed as an argument,
105
- # which can be numeric (in degrees) or symbolic:
106
- # :official, :civil, :nautical or :astronomical.
107
- # nil is returned if the sun doesn't rise at the date and position.
108
- def rise(date, longitude, latitude, options={})
109
- rising, transit, setting = passages(date, longitude, latitude, options)
110
- if rising==setting || (setting-rising)==1
111
- nil # rising==setting => no rise; setting-rising == 1 => no set
112
- else
113
- rising
114
- end
115
- end
116
-
117
- # Sun set time for a given date (UTC) and position.
118
- # The :zenith or :altitude of the sun can be passed as an argument,
119
- # which can be numeric (in degrees) or symbolic:
120
- # :official, :civil, :nautical or :astronomical.
121
- # nil is returned if the sun doesn't set at the date and position.
122
- def set(date, longitude, latitude, options={})
123
- rising, transit, setting = passages(date, longitude, latitude, options)
124
- if rising==setting || (setting-rising)==1
125
- nil # rising==setting => no rise; setting-rising == 1 => no set
126
- else
127
- setting
128
- end
129
- end
130
-
131
- # Rise and set times as given by rise() and set()
132
- def rise_and_set(date, longitude, latitude, options={})
133
- rising, transit, setting = passages(date, longitude, latitude, options)
134
- if rising==setting || (setting-rising)==1
135
- nil # rising==setting => no rise; setting-rising == 1 => no set
136
- else
137
- [rising, setting]
138
- end
139
- end
140
-
141
- # Solar passages [rising, transit, setting] for a given date (UTC) and position.
142
- # The :zenith or :altitude of the sun can be passed as an argument,
143
- # which can be numeric (in degrees) or symbolic:
144
- # :official, :civil, :nautical or :astronomical.
145
- # In circumpolar case:
146
- # If Sun never rises, returns 00:00:00 on Date for all passages.
147
- # If Sun never sets, returns 00:00:00 (rising), 12:00:00 (transit), 24:00:00 (setting)
148
- # on Date for all passages.
149
- def passages(date, longitude, latitude, options={})
150
-
151
- ho = altitude_from_options(options)
152
- t = to_jc(jd_r(date.to_datetime))
153
- theta0 = (100.46061837 + (36000.770053608 + (0.000387933 - t/38710000)*t)*t) % 360
154
- # Calculate apparent right ascention and declination for 0 hr Dynamical time for three days (degrees)
155
- ra = []
156
- decl = []
157
- -1.upto(1) do |i|
158
- declination, right_ascention = equatorial_position_rad((date+i).to_datetime)
159
- ra << to_deg(right_ascention)
160
- decl << to_deg(declination)
161
- end
162
- # tweak right ascention around 180 degrees (autumnal equinox)
163
- if ra[0] > ra[1]
164
- ra[0] -= 360
165
- end
166
- if ra[2] < ra[1]
167
- ra[2] += 360
168
- end
169
-
170
- ho_rad, latitude_rad = [ho, latitude].map{|x| to_rad(x)}
171
- decl_rad = decl.map{|x| to_rad(x)}
172
-
173
- # approximate Hour Angle (degrees)
174
- ha = Math.sin(ho_rad) / (Math.cos(latitude_rad) * Math.cos(decl_rad[1])) - Math.tan(latitude_rad) * Math.tan(decl_rad[1])
175
- # handle circumpolar. see note 2 at end of chapter
176
- if ha.abs <= 1
177
- ha = to_deg(Math.acos(ha))
178
- elsif ha > 1 # circumpolar - sun never rises
179
- # format sunrise, sunset & solar noon as DateTime
180
- sunrise = date.to_datetime
181
- transit = date.to_datetime
182
- sunset = date.to_datetime
183
- return [sunrise, transit, sunset]
184
- else # cirumpolar - sun never sets
185
- # format sunrise, sunset & solar noon as DateTime
186
- sunrise = date.to_datetime
187
- transit = date.to_datetime + 0.5
188
- sunset = date.to_datetime + 1
189
- return [sunrise, transit, sunset]
190
- end
191
- # approximate m (fraction of 1 day)
192
- # store days added or subtracted to add in later
193
- m = []
194
- days = [0]*3
195
- for i in 0..2
196
- case i
197
- when 0
198
- m[i] = (ra[1] - longitude - theta0) / 360 # transit
199
- day_offset = +1
200
- when 1
201
- m[i] = m[0] - ha / 360 # rising
202
- day_offset = -1
203
- when 2
204
- m[i] = m[0] + ha / 360 # setting
205
- day_offset = -1
206
- end
207
-
208
- until m[i] >= 0 do
209
- m[i] += 1
210
- days[i] += day_offset
211
- end
212
- until m[i] <= 1 do
213
- m[i] -= 1
214
- days[i] -= day_offset
215
- end
216
- end
217
- theta = [] # apparent sidereal time (degrees)
218
- ra2 = [] # apparent right ascension (degrees)
219
- decl2 = [] # apparent declination (degrees)
220
- h = [] # local hour angle (degrees)
221
- alt = [] # altitude (degrees)
222
- delta_m = [1]*3
223
- while ( delta_m[0] >= 0.01 || delta_m[1] >= 0.01 || delta_m[2] >= 0.01 ) do
224
- 0.upto(2) do |i|
225
- theta[i] = theta0 + 360.985647 * m[i]
226
- n = m[i] + delta_t(date.to_datetime).to_r / 86400
227
- a = ra[1] - ra[0]
228
- b = ra[2] - ra[1]
229
- c = b - a
230
- ra2[i] = ra[1] + n / 2 * ( a + b + n * c )
231
-
232
- n = m[i] + delta_t(date.to_datetime).to_r / 86400
233
- a = decl[1] - decl[0]
234
- b = decl[2] - decl[1]
235
- c = b - a
236
- decl2[i] = decl[1] + n / 2 * ( a + b + n * c )
237
-
238
- h[i] = theta[i] + longitude - ra2[i]
239
-
240
- alt[i] = to_deg Math.asin(Math.sin(latitude_rad) * Math.sin(to_rad(decl2[i])) +
241
- Math.cos(latitude_rad) * Math.cos(to_rad(decl2[i])) * Math.cos(to_rad(h[i])))
242
- end
243
- # adjust m
244
- delta_m[0] = -h[0] / 360
245
- 1.upto(2) do |i|
246
- delta_m[i] = (alt[i] - ho) / (360 * Math.cos(to_rad(decl2[i])) * Math.cos(latitude_rad) * Math.sin(to_rad(h[i])))
247
- end
248
- 0.upto(2) do |i|
249
- m[i] += delta_m[i]
250
- end
251
- end
252
- # format sunrise, sunset & solar noon as DateTime
253
- sunrise = date.to_datetime + m[1] + days[1]
254
- transit = date.to_datetime + m[0] + days[0]
255
- sunset = date.to_datetime + m[2] + days[2]
256
- [sunrise, transit, sunset]
257
- end
258
-
259
-
260
- private
261
-
262
- # Julian Day as Rational
263
- def jd_r(t)
264
- if false
265
- # This computes JD with precision of seconds and yields smaller denominators
266
- t = t.utc
267
- t.to_date.ajd + Rational(t.hour,24) + Rational(t.min,1440) + Rational(t.sec,86400)
268
- else
269
- # This preserves the internal precision of t (which we probably don't need)
270
- # and produces larger denominators in general
271
- t.to_datetime.utc.ajd
272
- end
273
- end
274
-
275
- # Julian Day as Float
276
- def jd_f(t)
277
- # t.to_date.ajd.to_f + t.hour/24.0 + t.min/1440.0 + t.sec/86400.0
278
- t.to_datetime.utc.ajd.to_f
279
- end
280
-
281
- def to_rad(deg)
282
- deg*Math::PI/180.0
283
- end
284
-
285
- def to_deg(rad)
286
- rad*180.0/Math::PI
287
- end
288
-
289
- def to_h(deg)
290
- deg/15.0
291
- end
292
-
293
- # Julian day to Julian Centuries since J2000.0
294
- def to_jc(jd)
295
- (jd - 2451545)/36525
296
- end
297
-
298
- def polynomial(coefficients, x)
299
- coefficients.inject(0.0){|p, a| p*x + a}
300
- end
301
-
302
- # Conversion of Float to Rational preserving the exact value of the number
303
- def to_r(x)
304
- x = x.to_f
305
- return Rational(x.to_i,1) if x.modulo(1)==0
306
- if !x.finite?
307
- return Rational(0,0) if x.nan?
308
- return x<0 ? Rational(-1,0) : Rational(1,0)
309
- end
310
-
311
- f,e = Math.frexp(x)
312
-
313
- if e < Float::MIN_EXP
314
- bits = e+Float::MANT_DIG-Float::MIN_EXP
315
- else
316
- bits = [Float::MANT_DIG,e].max
317
- #return Rational(x.to_i,1) if bits<e
318
- end
319
- p = Math.ldexp(f,bits)
320
- e = bits - e
321
- if e<Float::MAX_EXP
322
- q = Math.ldexp(1,e)
323
- else
324
- q = Float::RADIX**e
325
- end
326
- return Rational(p.to_i,q.to_i)
327
- end
328
-
329
- # time to dynamical time
330
- def to_td(t)
331
- t = t.utc
332
- t + to_r(delta_t(t))/86400
333
- end
334
-
335
- # dynamical_time to utc
336
- def to_utc(td)
337
- raise "Invalid dynamical time (should be utc)" unless td.utc?
338
- td - to_r(delta_t(td))/86400
339
-
340
- end
341
-
342
- # Compute difference between dynamical time and UTC in seconds.
343
- # See http://sunearth.gsfc.nasa.gov/eclipse/SEcat5/deltatpoly.html.
344
- # Good from -1999 to +3000.
345
- def delta_t(date)
346
-
347
- year = date.year.to_f
348
- y = year + (date.month.to_f - 0.5) / 12.0
349
-
350
- case
351
- when year < -500.0
352
- u = (year - 1820.0) / 100.0
353
- -20.0 + 32.0*u*u
354
- when year < 500.0
355
- u = y / 100.0
356
- polynomial [0.0090316521, 0.022174192, -0.1798452, -5.952053, 33.78311, -1014.41, 10583.6], u
357
- when year < 1600.0
358
- u = (y - 1000.0) / 100.0
359
- polynomial [0.0083572073, -0.005050998, -0.8503463, 0.319781, 71.23472, -556.01, 1574.2], u
360
- when year < 1700.0
361
- t = y - 1600.0
362
- polynomial [1.0/7129.0, -0.01532, -0.9808, 120.0], t
363
- when year < 1800.0
364
- t = y - 1700.0
365
- polynomial [-1.0/1174000.0, 0.00013336, -0.0059285, 0.1603, 8.83], t
366
- when year < 1860.0
367
- t = y - 1800.0
368
- polynomial [0.000000000875, -0.0000001699, 0.0000121272, -0.00037436, 0.0041116, 0.0068612, -0.332447, 13.72], t
369
- when year < 1900.0
370
- t = y - 1860.0
371
- polynomial [1.0/233174.0, -0.0004473624, 0.01680668, -0.251754, 0.5737, 7.62], t
372
- when year < 1920.0
373
- t = y - 1900.0
374
- polynomial [-0.000197, 0.0061966, -0.0598939, 1.494119, -2.79], t
375
- when year < 1941.0
376
- t = y - 1920.0
377
- polynomial [0.0020936, -0.076100, 0.84493, 21.20], t
378
- when year < 1961.0
379
- t = y - 1950.0
380
- polynomial [1.0/2547.0, -1.0/233.0, 0.407, 29.07], t
381
- when year < 1986.0
382
- t = y - 1975.0
383
- polynomial [-1.0/718.0, -1.0/260.0, 1.067, 45.45], t
384
- when year < 2005.0
385
- t = y - 2000.0
386
- polynomial [0.00002373599, 0.000651814, 0.0017275, -0.060374, 0.3345, 63.86], t
387
- when year < 2050.0
388
- t = y - 2000.0
389
- polynomial [0.005589, 0.32217, 62.92], t
390
- when year < 2150.0
391
- -20.0 + 32.0*((y - 1820.0)/100.0)**2 - 0.5628*(2150.0 - y)
392
- else
393
- u = (year - 1820.0) / 100.0
394
- -20.0 + 32*u*u
395
- end
396
- end
397
-
398
- # Solar equatorial coordinates / Low accuracy : Meeus pg 151
399
- # returns [declination in radians, right ascension in radians]
400
- def equatorial_position_rad(t)
401
- # t as Julian centuries of 36525 ephemeris days form the epoch J2000.0
402
- if false
403
- # Float
404
- jd = jd_f(to_td(t))
405
- else
406
- # Rational
407
- jd = jd_r(to_td(t))
408
- end
409
- t = to_jc(jd)
410
-
411
- # Geometric mean longitude of the Sun, referred to the mean equinox of the date
412
- l = 280.46645 + (36000.76983 + 0.0003032*t)*t
413
-
414
- # Mean anomaly of the Sun
415
- m_deg = 357.52910 + (35999.05030 - (0.0001559 + 0.00000048*t)*t)*t
416
- m_rad = to_rad(m_deg)
417
-
418
- # Eccentricity of the Earth's orbit
419
- e = 0.016708617 - (0.000042037 + 0.0000001236*t)*t
420
-
421
- # Sun's Equation of the center
422
- 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)
423
-
424
- # Sun's true longitude
425
- o = l + c
426
-
427
- # Reduce magnitude to minimize errors
428
- o %= 360
429
-
430
- # Sun's apparent Longitude
431
- omega_deg = 125.04 - 1934.136*t
432
- omega_rad = to_rad(omega_deg)
433
- lambda_deg = o - 0.00569 - 0.00478 * Math.sin(omega_rad)
434
-
435
- # Reduce magnitude to minimize errors
436
- lambda_deg %= 360
437
-
438
- lambda_rad = to_rad(lambda_deg)
439
-
440
- # Obliquity of the ecliptic
441
- epsilon_deg = 23.4392966666667 - (0.012777777777777778 + (0.00059/60 - 0.00059/60*t)*t)*t + 0.00256*Math.cos(omega_rad)
442
- epsilon_rad = to_rad(epsilon_deg)
443
-
444
- # Sun's declination
445
- delta_rad = Math.asin(Math.sin(epsilon_rad)*Math.sin(lambda_rad))
446
-
447
- # Sun's right ascension
448
- alpha_rad = Math.atan2(((Math.cos(epsilon_rad) * Math.sin(lambda_rad))),(Math.cos(lambda_rad)))
449
-
450
- [delta_rad, alpha_rad]
451
- end
452
-
453
- def altitude_from_options(options)
454
- if options.has_key?(:zenith)
455
- zenith = options[:zenith]
456
- if Symbol===zenith
457
- altitude = ALTITUDES[zenith]
458
- else
459
- altitude = 90.0 - zenith
460
- end
461
- else
462
- altitude = options[:altitude] || :official
463
- altitude = ALTITUDES[altitude] if Symbol===altitude
464
- end
465
- altitude
466
- end
467
-
468
- end
469
-
470
8
  end
9
+
10
+ require 'solar/support.rb'
11
+ require 'solar/passages.rb'
12
+ require 'solar/position.rb'
13
+ require 'solar/day_night.rb'
14
+ require 'solar/radiation.rb'
15
+ require 'solar/lambert.rb'