sun-times 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Joe Yates
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 ADDED
@@ -0,0 +1,35 @@
1
+ == SunTimes
2
+
3
+ Calculates sunrise and sunset times.
4
+
5
+ An implementation of the algorithm descibed at http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
6
+
7
+ == References
8
+
9
+ * http://www.astro.uu.nl/~strous/AA/en/reken/zonpositie.html - Calculations
10
+ * http://williams.best.vwh.net/sunrise_sunset_algorithm.htm - Algorithm
11
+ * http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/264573 - Ken Bloom's implementation
12
+
13
+ == Licence
14
+
15
+ This code is free to use under the terms of the MIT licence:
16
+
17
+ Copyright (c) 2010 Joe Yates
18
+
19
+ Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ of this software and associated documentation files (the "Software"), to
21
+ deal in the Software without restriction, including without limitation the
22
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
23
+ sell copies of the Software, and to permit persons to whom the Software is
24
+ furnished to do so, subject to the following conditions:
25
+
26
+ The above copyright notice and this permission notice shall be included in
27
+ all copies or substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
35
+ IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ require 'rake/clean'
6
+
7
+ $:.unshift(File.dirname(__FILE__) + '/lib')
8
+ require 'sun_times'
9
+
10
+ RDOC_OPTS = ['--quiet', '--title', 'Sun Times Calculator', '--main', 'README', '--inline-source']
11
+ CLEAN.include 'doc'
12
+
13
+ task :default => :test
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = 'sun-times'
17
+ s.summary = 'Module which calculates sunrise and sunset times'
18
+ s.version = SunTimes::VERSION::STRING
19
+
20
+ s.homepage = 'http://github.com/timoschilling/sun-times'
21
+ s.author = ['Joe Yates', 'Timo Schilling']
22
+ s.email = ['joe.g.yates@gmail.com', 'timo@schilling.io']
23
+
24
+ s.files = ['README', 'COPYING', 'Rakefile'] + FileList['{lib,test}/**/*.rb']
25
+ s.require_paths = ['lib']
26
+
27
+ s.has_rdoc = true
28
+ s.rdoc_options += RDOC_OPTS
29
+ s.extra_rdoc_files = ['README', 'COPYING']
30
+
31
+ s.test_file = 'test/test_all.rb'
32
+ end
33
+
34
+ Rake::TestTask.new do |t|
35
+ t.libs << 'test'
36
+ t.test_files = FileList['test/*_test.rb']
37
+ t.verbose = true
38
+ end
39
+
40
+ Rake::RDocTask.new do |rdoc|
41
+ rdoc.rdoc_dir = 'doc/rdoc'
42
+ rdoc.options += RDOC_OPTS
43
+ rdoc.main = "README"
44
+ rdoc.rdoc_files.add ['README', 'COPYING', 'lib/**/*.rb']
45
+ end
46
+
47
+ Rake::GemPackageTask.new(spec) do |pkg|
48
+ end
data/lib/sun_times.rb ADDED
@@ -0,0 +1,179 @@
1
+ #--
2
+ # Copyright (c) 2010 Joe Yates
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ # Algorithm from http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
25
+
26
+ require 'date'
27
+
28
+ module SunTimes
29
+
30
+ module VERSION #:nodoc:
31
+ MAJOR = 0
32
+ MINOR = 1
33
+ TINY = 2
34
+
35
+ STRING = [MAJOR, MINOR, TINY].join('.')
36
+ end
37
+
38
+ DEFAULT_ZENITH = 90.83333
39
+ KNOWN_EVENTS = [:rise, :set]
40
+ DEGREES_PER_HOUR = 360.0 / 24.0
41
+
42
+ # Helper method: calculates sunrise, with the same parameters as calculate
43
+ def SunTimes.rise(date, latitude, longitude, options = {})
44
+ calculate(:rise, date, latitude, longitude, options)
45
+ end
46
+
47
+ # Helper method: calculates sunset, with the same parameters as calculate
48
+ def SunTimes.set(date, latitude, longitude, options = {})
49
+ calculate(:set, date, latitude, longitude, options)
50
+ end
51
+
52
+ # Calculates the sunrise or sunset time for a specific date and location
53
+ #
54
+ # ==== Parameters
55
+ # * +event+ - One of :rise, :set.
56
+ # * +date+ - An object that responds to yday.
57
+ # * +latitude+ - The latitude of the location in degrees.
58
+ # * +longitude+ - The longitude of the location in degrees.
59
+ # * +options+ - Additional option is <tt>:zenith</tt>.
60
+ #
61
+ # ==== Example
62
+ # SunTimes.calculate(:rise, Date.new(2010, 3, 8), 43.779, 11.432)
63
+ # > Mon Mar 08 05:39:53 UTC 2010
64
+ def SunTimes.calculate(event, date, latitude, longitude, options = {})
65
+ raise "Unknown event '#{ event }'" if KNOWN_EVENTS.find_index(event).nil?
66
+ zenith = options.delete(:zenith) || DEFAULT_ZENITH
67
+
68
+ # lngHour
69
+ longitude_hour = longitude / DEGREES_PER_HOUR
70
+
71
+ # t
72
+ base_time = event == :rise ? 6.0 : 18.0
73
+ approximate_time = date.yday + (base_time - longitude_hour) / 24.0
74
+
75
+ # M
76
+ mean_sun_anomaly = (0.9856 * approximate_time) - 3.289
77
+
78
+ # L
79
+ sun_true_longitude = mean_sun_anomaly +
80
+ (1.916 * Math.sin(degrees_to_radians(mean_sun_anomaly))) +
81
+ (0.020 * Math.sin(2 * degrees_to_radians(mean_sun_anomaly))) +
82
+ 282.634
83
+ sun_true_longitude = coerce_degrees(sun_true_longitude)
84
+
85
+ # RA
86
+ tan_right_ascension = 0.91764 * Math.tan(degrees_to_radians(sun_true_longitude))
87
+ sun_right_ascension = radians_to_degrees(Math.atan(tan_right_ascension))
88
+ sun_right_ascension = coerce_degrees(sun_right_ascension)
89
+
90
+ # right ascension value needs to be in the same quadrant as L
91
+ sun_true_longitude_quadrant = (sun_true_longitude / 90.0).floor * 90.0
92
+ sun_right_ascension_quadrant = (sun_right_ascension / 90.0).floor * 90.0
93
+ sun_right_ascension += (sun_true_longitude_quadrant - sun_right_ascension_quadrant)
94
+
95
+ # RA = RA / 15
96
+ sun_right_ascension_hours = sun_right_ascension / DEGREES_PER_HOUR
97
+
98
+ sin_declination = 0.39782 * Math.sin(degrees_to_radians(sun_true_longitude))
99
+ cos_declination = Math.cos(Math.asin(sin_declination))
100
+
101
+ cos_local_hour_angle =
102
+ (Math.cos(degrees_to_radians(zenith)) - (sin_declination * Math.sin(degrees_to_radians(latitude)))) /
103
+ (cos_declination * Math.cos(degrees_to_radians(latitude)))
104
+
105
+ # the sun never rises on this location (on the specified date)
106
+ return nil if cos_local_hour_angle > 1
107
+ # the sun never sets on this location (on the specified date)
108
+ return nil if cos_local_hour_angle < -1
109
+
110
+ # H
111
+ suns_local_hour =
112
+ if event == :rise
113
+ 360 - radians_to_degrees(Math.acos(cos_local_hour_angle))
114
+ else
115
+ radians_to_degrees(Math.acos(cos_local_hour_angle))
116
+ end
117
+
118
+ # H = H / 15
119
+ suns_local_hour_hours = suns_local_hour / DEGREES_PER_HOUR
120
+
121
+ # T = H + RA - (0.06571 * t) - 6.622
122
+ local_mean_time = suns_local_hour_hours + sun_right_ascension_hours - (0.06571 * approximate_time) - 6.622
123
+
124
+ # UT = T - lngHour
125
+ gmt_hours = local_mean_time - longitude_hour
126
+ gmt_hours -= 24.0 if gmt_hours > 24
127
+ gmt_hours += 24.0 if gmt_hours < 0
128
+
129
+ case
130
+ when date.respond_to?( :offset )
131
+ offset_hours = date.offset * 24
132
+ when date.respond_to?( :gmt_offset )
133
+ offset_hours = date.gmt_offset / 3600
134
+ else
135
+ offset_hours = nil
136
+ end
137
+
138
+ if ! offset_hours.nil?
139
+ if gmt_hours + offset_hours < 0
140
+ next_day = Date.new(date.year, date.month, date.day + 1)
141
+ return calculate(event, next_day, latitude, longitude, options = {})
142
+ end
143
+ if gmt_hours + offset_hours > 24
144
+ previous_day = Date.new(date.year, date.month, date.day + 1)
145
+ return calculate(event, previous_day, latitude, longitude, options = {})
146
+ end
147
+ end
148
+
149
+ hour = gmt_hours.floor
150
+ hour_remainder = (gmt_hours.to_f - hour.to_f) * 60.0
151
+ minute = hour_remainder.floor
152
+ seconds = (hour_remainder - minute) * 60.0
153
+
154
+ Time.gm(date.year, date.month, date.day, hour, minute, seconds)
155
+ end
156
+
157
+ private
158
+
159
+ def SunTimes.degrees_to_radians(d)
160
+ d.to_f / 360.0 * 2.0 * Math::PI
161
+ end
162
+
163
+ def SunTimes.radians_to_degrees(r)
164
+ r.to_f * 360.0 / (2.0 * Math::PI)
165
+ end
166
+
167
+ def SunTimes.coerce_degrees(d)
168
+ if d < 0
169
+ d += 360
170
+ return coerce_degrees(d)
171
+ end
172
+ if d >= 360
173
+ d -= 360
174
+ return coerce_degrees(d)
175
+ end
176
+ d
177
+ end
178
+
179
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+ require 'test/unit'
4
+ require 'sun_times'
5
+ require 'date'
6
+
7
+ class SunTimesTest < Test::Unit::TestCase
8
+
9
+ def test_rise_20100308_pontassieve
10
+ rise = SunTimes.calculate(:rise, Date.new(2010, 3, 8), 43.779, 11.432)
11
+ assert_equal(Time.gm(2010, 3, 8, 5, 39, 53), rise)
12
+ end
13
+
14
+ def test_set_20100308_pontassieve
15
+ set = SunTimes.calculate(:set, Date.new(2010, 3, 8), 43.779, 11.432)
16
+ assert_equal(Time.gm(2010, 3, 8, 17, 11, 16), set)
17
+ end
18
+
19
+ def test_rise_helper
20
+ rise = SunTimes.rise(Date.new(2010, 3, 8), 43.779, 11.432)
21
+ assert_equal(Time.gm(2010, 3, 8, 5, 39, 53), rise)
22
+ end
23
+
24
+ def test_set_helper
25
+ set = SunTimes.set(Date.new(2010, 3, 8), 43.779, 11.432)
26
+ assert_equal(Time.gm(2010, 3, 8, 17, 11, 16), set)
27
+ end
28
+
29
+ def test_midnight_sun_on_20100621_north_cape
30
+ rise = SunTimes.calculate(:rise, Date.new(2010, 6, 21), 71.170219, 25.785556)
31
+ assert_nil(rise)
32
+ set = SunTimes.calculate(:set, Date.new(2010, 6, 21), 71.170219, 25.785556)
33
+ assert_nil(set)
34
+ end
35
+
36
+ def test_unknown_event
37
+ assert_raise(RuntimeError) { SunTimes.calculate(:foo, Date.new(2010, 3, 8), 43.779, 11.432) }
38
+ end
39
+
40
+ def test_time
41
+ datetime = Time.gm(2010, 6, 13, 0, 0, 0)
42
+ set = SunTimes.calculate(:set, datetime, 43.779, 11.432)
43
+ assert_equal(Time.gm(2010, 6, 13, 18, 56, 55), set)
44
+ end
45
+
46
+ def test_datetime
47
+ datetime = DateTime.new(2010, 6, 13, 0, 0, 0)
48
+ set = SunTimes.calculate(:set, datetime, 43.779, 11.432)
49
+ assert_equal(Time.gm(2010, 6, 13, 18, 56, 55), set)
50
+ end
51
+
52
+ def test_respects_timezone_if_supplied
53
+ pst = DateTime.new(2011, 12, 13, 0, 0, 0, Rational(-8, 24))
54
+ set = SunTimes.calculate(:set, pst, 45.52, -122.681944)
55
+ result_datetime = DateTime.new(set.year, set.month, set.day, set.hour, set.min, set.sec, 0)
56
+ assert(pst < result_datetime)
57
+ assert(pst + 1 > result_datetime)
58
+ end
59
+
60
+ end
data/test/test_all.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__)+'/*_test.rb'].each do |test|
2
+ require test
3
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sun-times
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joe Yates
9
+ - Timo Schilling
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-05-20 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description:
16
+ email:
17
+ - joe.g.yates@gmail.com
18
+ - timo@schilling.io
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files:
22
+ - README
23
+ - COPYING
24
+ files:
25
+ - README
26
+ - COPYING
27
+ - Rakefile
28
+ - lib/sun_times.rb
29
+ - test/calculate_test.rb
30
+ - test/test_all.rb
31
+ homepage: http://github.com/timoschilling/sun-times
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --quiet
36
+ - --title
37
+ - Sun Times Calculator
38
+ - --main
39
+ - README
40
+ - --inline-source
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 1.8.24
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Module which calculates sunrise and sunset times
61
+ test_files:
62
+ - test/test_all.rb