sun_calc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8a3f04d3ad8fb0b03c8aa0944f880e38aef08d21
4
+ data.tar.gz: fbfa83fc6d4e7b9a6d1507cb5d6d5e39c34b0ab1
5
+ SHA512:
6
+ metadata.gz: 647287232b4fa0ba2548789ca692811bb222a71ee883910e23207f8cd249519870134508e5c62c2c57fcaf3b9b05418409f28fc2fdbd595b0f9811b4923862ac
7
+ data.tar.gz: b9acc4bc213f343686ec17a265a52239cec0b4a656767fd63cd949e338c8758282a85fe1a578dca697532621c24b98747528a194ca40206584a19ed8cc8d8717
data/LICENSE ADDED
@@ -0,0 +1,49 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018, Fishbrain AB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+
23
+ License for original art (https://github.com/mourner/suncalc):
24
+
25
+ BSD 2-Clause "Simplified" License
26
+
27
+ Copyright (c) 2014, Vladimir Agafonkin
28
+ All rights reserved.
29
+
30
+ Redistribution and use in source and binary forms, with or without modification,
31
+ are permitted provided that the following conditions are met:
32
+
33
+ 1. Redistributions of source code must retain the above copyright notice,
34
+ this list of conditions and the following disclaimer.
35
+
36
+ 2. Redistributions in binary form must reproduce the above copyright notice,
37
+ this list of conditions and the following disclaimer in the documentation
38
+ and/or other materials provided with the distribution.
39
+
40
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
41
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
42
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
44
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
45
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
47
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
48
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
49
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.markdown ADDED
@@ -0,0 +1,36 @@
1
+ # sun\_calc
2
+
3
+ [![Build Status](https://travis-ci.com/fishbrain/sun_calc.svg?token=AahQSHRoAYGzTHE366Lz&branch=master)](https://travis-ci.com/fishbrain/sun_calc)
4
+
5
+ A Ruby library, translated from the
6
+ [SunCalc](https://github.com/mourner/suncalc) JavaScript library, for
7
+ calculating sun positions, sunlight phases, moon positions, and lunar phase for
8
+ a given time and location.
9
+
10
+ ## Installing
11
+
12
+ ```
13
+ gem install sun_calc
14
+ ```
15
+
16
+ ## Using
17
+
18
+ ```ruby
19
+ require 'sun_calc'
20
+ SunCalc.sun_position(Time.now, 59.3345, 18.0662)
21
+ #=> {:azimuth=>0.5724083892517442, :altitude=>0.6713233729313038}
22
+ ```
23
+
24
+ The library shares a similar API as the JavaScript library.
25
+
26
+ | JavaScript | Ruby |
27
+ |-------------------|-----------------|
28
+ | `getPosition` | `sun_position` |
29
+ | `getTimes` | `sun_times` |
30
+ | `addTime` | `add_sun_time` |
31
+ | `getMoonPosition` | `moon_position` |
32
+ | `getMoonTimes` | `moon_times` |
33
+
34
+ ## Maintainer
35
+
36
+ - Alexander Cederblad (<mailto:alexander@fishbrain.com>)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SunCalc
4
+ ONE_RADIAN = Math::PI / 180
5
+ ONE_DAY_IN_SECONDS = 60 * 60 * 24
6
+ J0 = 0.0009
7
+ J1970 = 2_440_588
8
+ J2000 = 2_451_545
9
+ OBLIQUITY_OF_THE_EARTH = ONE_RADIAN * 23.4397
10
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SunCalc
4
+ # :nodoc:
5
+ module Helpers
6
+ def to_julian(date)
7
+ date.to_f / ONE_DAY_IN_SECONDS - 0.5 + J1970
8
+ end
9
+
10
+ def from_julian(j)
11
+ Time.at((j + 0.5 - J1970) * ONE_DAY_IN_SECONDS).utc
12
+ end
13
+
14
+ def to_days(date)
15
+ to_julian(date) - J2000
16
+ end
17
+
18
+ def right_ascension(l, b)
19
+ Math.atan2(
20
+ Math.sin(l) * Math.cos(OBLIQUITY_OF_THE_EARTH) -
21
+ Math.tan(b) * Math.sin(OBLIQUITY_OF_THE_EARTH),
22
+ Math.cos(l)
23
+ )
24
+ end
25
+
26
+ def declination(l, b)
27
+ Math.asin(Math.sin(b) * Math.cos(OBLIQUITY_OF_THE_EARTH) +
28
+ Math.cos(b) * Math.sin(OBLIQUITY_OF_THE_EARTH) * Math.sin(l))
29
+ end
30
+
31
+ def azimuth(h, phi, dec)
32
+ Math.atan2(Math.sin(h),
33
+ Math.cos(h) * Math.sin(phi) - Math.tan(dec) * Math.cos(phi))
34
+ end
35
+
36
+ def altitude(h, phi, dec)
37
+ Math.asin(Math.sin(phi) * Math.sin(dec) +
38
+ Math.cos(phi) * Math.cos(dec) * Math.cos(h))
39
+ end
40
+
41
+ def sidereal_time(d, lw)
42
+ ONE_RADIAN * (280.16 + 360.9856235 * d) - lw
43
+ end
44
+
45
+ def astro_refraction(h)
46
+ # The following formula works for positive altitudes only.
47
+ h = 0 if h < 0
48
+ # Based on forumla 16.4 of "Astronomical Algorithms" 2nd edition by Jean
49
+ # Meeus (Willmann-Bell, Richmond) 1998.
50
+ 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179))
51
+ end
52
+
53
+ def solar_mean_anomaly(d)
54
+ ONE_RADIAN * (357.5291 + 0.98560028 * d)
55
+ end
56
+
57
+ def ecliptic_longitude(m)
58
+ # Equation of center.
59
+ c = ONE_RADIAN * (1.9148 * Math.sin(m) +
60
+ 0.02 * Math.sin(2 * m) + 0.0003 * Math.sin(3 * m))
61
+ # Perihelion of Earth.
62
+ p = ONE_RADIAN * 102.9372
63
+ m + c + p + Math::PI
64
+ end
65
+
66
+ def sun_coords(d)
67
+ m = solar_mean_anomaly(d)
68
+ l = ecliptic_longitude(m)
69
+ { dec: declination(l, 0),
70
+ ra: right_ascension(l, 0) }
71
+ end
72
+
73
+ def julian_cycle(d, lw)
74
+ (d - J0 - lw / (2 * Math::PI)).round
75
+ end
76
+
77
+ def approx_transit(ht, lw, n)
78
+ J0 + (ht + lw) / (2 * Math::PI) + n
79
+ end
80
+
81
+ def solar_transit_j(ds, m, l)
82
+ J2000 + ds + 0.0053 * Math.sin(m) - 0.0069 * Math.sin(2 * l)
83
+ end
84
+
85
+ def hour_angle(h0, phi, dec)
86
+ Math.acos(
87
+ (Math.sin(h0) - Math.sin(phi) * Math.sin(dec)) /
88
+ (Math.cos(phi) * Math.cos(dec))
89
+ )
90
+ end
91
+
92
+ def get_set_j(h0, lw, phi, dec, n, m, l)
93
+ w = hour_angle(h0, phi, dec)
94
+ a = approx_transit(w, lw, n)
95
+ solar_transit_j(a, m, l)
96
+ end
97
+
98
+ def moon_coords(d)
99
+ # Geocentric ecliptic coordinates of the moon
100
+ l = ONE_RADIAN * (218.316 + 13.176396 * d) # ecliptic longitude
101
+ m = ONE_RADIAN * (134.963 + 13.064993 * d) # mean anomaly
102
+ f = ONE_RADIAN * (93.272 + 13.229350 * d) # mean distance
103
+ l += ONE_RADIAN * 6.289 * Math.sin(m) # longitude
104
+ b = ONE_RADIAN * 5.128 * Math.sin(f) # latitude
105
+ dt = 385_001 - 20_905 * Math.cos(m) # distance to the moon in km
106
+ { ra: right_ascension(l, b),
107
+ dec: declination(l, b),
108
+ dist: dt }
109
+ end
110
+
111
+ def hours_later(date, h)
112
+ Time.at(date.to_f + h * ONE_DAY_IN_SECONDS / 24).utc
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ class SunCalc
5
+ VERSION = '0.0.1'.freeze
6
+ end
data/lib/sun_calc.rb ADDED
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ require 'sun_calc/constants'
6
+ require 'sun_calc/helpers'
7
+
8
+ # SunCalc provides methods for calculating sun/moon positions and phases.
9
+ #
10
+ # Most of the formulas are based on:
11
+ # - http://aa.quae.nl/en/reken/zonpositie.html
12
+ # - http://aa.quae.nl/en/reken/hemelpositie.html
13
+ class SunCalc
14
+ extend Helpers
15
+
16
+ # Sun times configuration (angle, morning name, evening name).
17
+ DEFAULT_SUN_TIMES = [
18
+ [-0.833, 'sunrise', 'sunset'],
19
+ [-0.3, 'sunrise_end', 'sunset_start'],
20
+ [-6, 'dawn', 'dusk'],
21
+ [-12, 'nautical_dawn', 'nautical_dusk'],
22
+ [-18, 'night_end', 'night_start'],
23
+ [6, 'golden_hour_end', 'golden_hour_start']
24
+ ].freeze
25
+
26
+ @__sun_times__ = DEFAULT_SUN_TIMES.dup
27
+
28
+ # Calculates sun position for a given date, latitude, and longitude.
29
+ def self.sun_position(date, lat, lng)
30
+ lw = ONE_RADIAN * -lng
31
+ phi = ONE_RADIAN * lat
32
+ d = to_days(date)
33
+ c = sun_coords(d)
34
+ h = sidereal_time(d, lw) - c[:ra]
35
+ { azimuth: azimuth(h, phi, c[:dec]),
36
+ altitude: altitude(h, phi, c[:dec]) }
37
+ end
38
+
39
+ # Calculates sun times for a given date, latitude, and longitude.
40
+ def self.sun_times(date, lat, lng)
41
+ lw = ONE_RADIAN * -lng
42
+ phi = ONE_RADIAN * lat
43
+ d = to_days(date)
44
+ n = julian_cycle(d, lw)
45
+ ds = approx_transit(0, lw, n)
46
+ m = solar_mean_anomaly(ds)
47
+ l = ecliptic_longitude(m)
48
+ dec = declination(l, 0)
49
+ j_noon = solar_transit_j(ds, m, l)
50
+ { solar_noon: from_julian(j_noon),
51
+ nadir: from_julian(j_noon - 0.5) }.tap do |result|
52
+ @__sun_times__.each do |time|
53
+ begin
54
+ j_set = get_set_j(time[0] * ONE_RADIAN, lw, phi, dec, n, m, l)
55
+ j_rise = j_noon - (j_set - j_noon)
56
+ result[time[1].to_sym] = from_julian(j_rise)
57
+ result[time[2].to_sym] = from_julian(j_set)
58
+ rescue Math::DomainError
59
+ result[time[1].to_sym] = nil
60
+ result[time[2].to_sym] = nil
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ # Adds a custom time to the times configuration.
67
+ def self.add_sun_time(angle, rise_name, set_name)
68
+ @__sun_times__.push([angle, rise_name, set_name])
69
+ end
70
+
71
+ # Calculates moon position for a gived date, latitude, and longitude.
72
+ def self.moon_position(date, lat, lng)
73
+ lw = ONE_RADIAN * -lng
74
+ phi = ONE_RADIAN * lat
75
+ d = to_days(date)
76
+ c = moon_coords(d)
77
+ h_ = sidereal_time(d, lw) - c[:ra]
78
+ h = altitude(h_, phi, c[:dec])
79
+ # Formula 14.1 from "Astronomical Algorithms" 2nd edition by Jean Meeus
80
+ # (Willmann-Bell, Richmond) 1998.
81
+ pa = Math.atan2(Math.sin(h_),
82
+ Math.tan(phi) * Math.cos(c[:dec]) -
83
+ Math.sin(c[:dec]) * Math.cos(h_))
84
+ h += astro_refraction(h) # altitude correction for refraction
85
+
86
+ { azimuth: azimuth(h_, phi, c[:dec]),
87
+ altitude: h,
88
+ distance: c[:dist],
89
+ parallacticAngle: pa }
90
+ end
91
+
92
+ # Calculates moon times for a given date, latitude, and longitude.
93
+ #
94
+ # Calculations for moon rise and set times are based on:
95
+ # - http://www.stargazing.net/kepler/moonrise.html
96
+ def self.moon_times(date, lat, lng)
97
+ t = Time.utc(date.year, date.month, date.day)
98
+ hc = 0.133 * ONE_RADIAN
99
+ h0 = SunCalc.moon_position(t, lat, lng)[:altitude] - hc
100
+ ye = 0
101
+ rise = nil
102
+ set = nil
103
+ # Iterate in 2-hour steps checking if a 3-point quadratic curve crosses zero
104
+ # (which means rise or set).
105
+ (1...24).step(2).each do |i|
106
+ h1 = SunCalc.moon_position(hours_later(t, i), lat, lng)[:altitude] - hc
107
+ h2 = SunCalc.moon_position(
108
+ hours_later(t, i + 1), lat, lng
109
+ )[:altitude] - hc
110
+ a = (h0 + h2) / 2 - h1
111
+ b = (h2 - h0) / 2
112
+ xe = -b / (2 * a)
113
+ ye = (a * xe + b) * xe + h1
114
+ d = b * b - 4 * a * h1
115
+ roots = 0
116
+ x1 = 0
117
+ x2 = 0
118
+ if d >= 0
119
+ dx = Math.sqrt(d) / (a.abs * 2)
120
+ x1 = xe - dx
121
+ x2 = xe + dx
122
+ roots += 1 if x1.abs <= 1
123
+ roots += 1 if x2.abs <= 1
124
+ x1 = x2 if x1 < -1
125
+ end
126
+ if roots == 1
127
+ if h0 < 0
128
+ rise = i + x1
129
+ else
130
+ set = i + x1
131
+ end
132
+ elsif roots == 2
133
+ rise = i + (ye < 0 ? x2 : x1)
134
+ set = i + (ye < 0 ? x1 : x2)
135
+ end
136
+ break if rise && set
137
+ h0 = h2
138
+ end
139
+ {}.tap do |result|
140
+ result[:moonrise] = hours_later(t, rise) if rise
141
+ result[:moonset] = hours_later(t, set) if set
142
+ result[ye > 0 ? :always_up : :always_down] = true if !rise && !set
143
+ end
144
+ end
145
+
146
+ # Calculates illumination parameters for the moon for a given date.
147
+ #
148
+ # Formulas are based on:
149
+ # - http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro
150
+ # - Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus
151
+ # (Willmann-Bell, Richmond) 1998.
152
+ def self.moon_illumination(date = Time.now)
153
+ d = to_days(date)
154
+ s = sun_coords(d)
155
+ m = moon_coords(d)
156
+ sdist = 149_598_000 # Distance from Earth to Sun in kilometers
157
+ phi = Math.acos(Math.sin(s[:dec]) * Math.sin(m[:dec]) +
158
+ Math.cos(s[:dec]) * Math.cos(m[:dec]) * Math.cos(s[:ra] -
159
+ m[:ra]))
160
+ inc = Math.atan2(sdist * Math.sin(phi), m[:dist] - sdist * Math.cos(phi))
161
+ angle = Math.atan2(Math.cos(s[:dec]) * Math.sin(s[:ra] - m[:ra]),
162
+ Math.sin(s[:dec]) * Math.cos(m[:dec]) -
163
+ Math.cos(s[:dec]) * Math.sin(m[:dec]) *
164
+ Math.cos(s[:ra] - m[:ra]))
165
+ { fraction: (1 + Math.cos(inc)) / 2,
166
+ phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math::PI,
167
+ angle: angle }
168
+ end
169
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sun_calc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Cederblad
8
+ - Fishbrain AB
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-05-04 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email:
16
+ - alexander@fishbrain.com
17
+ - developer@fishbrain.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - LICENSE
23
+ - README.markdown
24
+ - lib/sun_calc.rb
25
+ - lib/sun_calc/constants.rb
26
+ - lib/sun_calc/helpers.rb
27
+ - lib/sun_calc/version.rb
28
+ homepage: https://www.github.com/fishbrain/sun_calc
29
+ licenses:
30
+ - MIT
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 2.5.1
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Library for calculating sun/moon positions and phases.
52
+ test_files: []