solar 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'