sun-times 0.1.2

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.
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