sun_calc 0.0.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.
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: []