astronoby 0.5.0 → 0.6.0
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 +4 -4
- data/CHANGELOG.md +46 -0
- data/Gemfile.lock +25 -27
- data/README.md +36 -23
- data/benchmark/README.md +131 -0
- data/benchmark/benchmark.rb +259 -0
- data/benchmark/data/imcce.csv.zip +0 -0
- data/benchmark/data/sun_calc.csv.zip +0 -0
- data/lib/astronoby/bodies/moon.rb +5 -0
- data/lib/astronoby/events/observation_events.rb +62 -36
- data/lib/astronoby/events/rise_transit_set_iteration.rb +40 -37
- data/lib/astronoby/observer.rb +11 -1
- data/lib/astronoby/time/greenwich_sidereal_time.rb +1 -1
- data/lib/astronoby/util/maths.rb +22 -0
- data/lib/astronoby/util/time.rb +12 -1
- data/lib/astronoby/version.rb +1 -1
- metadata +22 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15679e58775b5058f6a154502a3e94731effe12a9c90d0a28c642b96d07aa6b2
|
|
4
|
+
data.tar.gz: 00f9bcc5a4c0fa4111e33911ba3f1e3da4829edad54e4d1419c0d716f660ea69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b524b994952bfec402e42e69976c5f74d69d51e1d6fbf3b2f21327c9ed0e02011c32d897d09c216f7784c3c286ed69c11beee466207a64051aef536fbc8f5a64
|
|
7
|
+
data.tar.gz: 5ebbd92e2bb360d2d354800b7f05d35e4e74bce01cae06c8cf8176dae286e8ae70baa39c509c2c3466b05c676d624d4942a4a7cd08546ec895255785645e235b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 - 2024-12-10
|
|
4
|
+
|
|
5
|
+
_If you are upgrading: please see [UPGRADING.md]._
|
|
6
|
+
|
|
7
|
+
[UPGRADING.md]: https://github.com/rhannequin/astronoby/blob/main/UPGRADING.md
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
* Fix `ObservationEvents` infinite loop in ([#110])
|
|
12
|
+
* Fix observation events times with local time dates in ([#105])
|
|
13
|
+
* Fix `IncompatibleArgumentsError` on Moon's observation events in ([#111])
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* Add `Astronoby::Moon#current_phase_fraction` in ([#115])
|
|
18
|
+
* Add sources and results for comparison in ([#114])
|
|
19
|
+
|
|
20
|
+
### Improvements
|
|
21
|
+
|
|
22
|
+
* Bump standard from 1.36.0 to 1.39.2 by @dependabot in ([#95])
|
|
23
|
+
* Bump standard from 1.39.2 to 1.40.0 by @dependabot in ([#96])
|
|
24
|
+
* Bump dependencies in ([#100])
|
|
25
|
+
* Move dependencies to development ones in ([#99])
|
|
26
|
+
* Bump standard from 1.40.0 to 1.41.1 by @dependabot in ([#104])
|
|
27
|
+
* Bump standard from 1.41.1 to 1.42.0 by @dependabot in ([#107])
|
|
28
|
+
* Bump standard from 1.42.0 to 1.42.1 by @dependabot in ([#108])
|
|
29
|
+
* Bump dependencies in ([#116])
|
|
30
|
+
* Add supported Ruby versions in ([#117])
|
|
31
|
+
|
|
32
|
+
**Full Changelog**: https://github.com/rhannequin/astronoby/compare/v0.5.0...v0.6.0
|
|
33
|
+
|
|
34
|
+
[#95]: https://github\.com/rhannequin/astronoby/pull/95
|
|
35
|
+
[#96]: https://github\.com/rhannequin/astronoby/pull/96
|
|
36
|
+
[#99]: https://github\.com/rhannequin/astronoby/pull/99
|
|
37
|
+
[#100]: https://github\.com/rhannequin/astronoby/pull/100
|
|
38
|
+
[#104]: https://github\.com/rhannequin/astronoby/pull/104
|
|
39
|
+
[#105]: https://github\.com/rhannequin/astronoby/pull/105
|
|
40
|
+
[#107]: https://github\.com/rhannequin/astronoby/pull/107
|
|
41
|
+
[#108]: https://github\.com/rhannequin/astronoby/pull/108
|
|
42
|
+
[#110]: https://github\.com/rhannequin/astronoby/pull/110
|
|
43
|
+
[#111]: https://github\.com/rhannequin/astronoby/pull/111
|
|
44
|
+
[#114]: https://github\.com/rhannequin/astronoby/pull/114
|
|
45
|
+
[#115]: https://github\.com/rhannequin/astronoby/pull/115
|
|
46
|
+
[#116]: https://github\.com/rhannequin/astronoby/pull/116
|
|
47
|
+
[#117]: https://github\.com/rhannequin/astronoby/pull/117
|
|
48
|
+
|
|
3
49
|
## 0.5.0 - 2024-06-11
|
|
4
50
|
|
|
5
51
|
_If you are upgrading: please see [UPGRADING.md]._
|
data/Gemfile.lock
CHANGED
|
@@ -1,74 +1,69 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
astronoby (0.
|
|
4
|
+
astronoby (0.6.0)
|
|
5
5
|
matrix (~> 0.4.2)
|
|
6
|
-
rake (~> 13.0)
|
|
7
|
-
rspec (~> 3.0)
|
|
8
6
|
|
|
9
7
|
GEM
|
|
10
8
|
remote: https://rubygems.org/
|
|
11
9
|
specs:
|
|
12
10
|
ast (2.4.2)
|
|
13
11
|
diff-lcs (1.5.1)
|
|
14
|
-
json (2.
|
|
12
|
+
json (2.9.0)
|
|
15
13
|
language_server-protocol (3.17.0.3)
|
|
16
14
|
lint_roller (1.1.0)
|
|
17
15
|
matrix (0.4.2)
|
|
18
|
-
parallel (1.
|
|
19
|
-
parser (3.3.
|
|
16
|
+
parallel (1.26.3)
|
|
17
|
+
parser (3.3.6.0)
|
|
20
18
|
ast (~> 2.4.1)
|
|
21
19
|
racc
|
|
22
|
-
racc (1.
|
|
20
|
+
racc (1.8.1)
|
|
23
21
|
rainbow (3.1.1)
|
|
24
22
|
rake (13.2.1)
|
|
25
|
-
regexp_parser (2.9.
|
|
26
|
-
rexml (3.2.8)
|
|
27
|
-
strscan (>= 3.0.9)
|
|
23
|
+
regexp_parser (2.9.3)
|
|
28
24
|
rspec (3.13.0)
|
|
29
25
|
rspec-core (~> 3.13.0)
|
|
30
26
|
rspec-expectations (~> 3.13.0)
|
|
31
27
|
rspec-mocks (~> 3.13.0)
|
|
32
|
-
rspec-core (3.13.
|
|
28
|
+
rspec-core (3.13.2)
|
|
33
29
|
rspec-support (~> 3.13.0)
|
|
34
|
-
rspec-expectations (3.13.
|
|
30
|
+
rspec-expectations (3.13.3)
|
|
35
31
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
36
32
|
rspec-support (~> 3.13.0)
|
|
37
|
-
rspec-mocks (3.13.
|
|
33
|
+
rspec-mocks (3.13.2)
|
|
38
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
39
35
|
rspec-support (~> 3.13.0)
|
|
40
|
-
rspec-support (3.13.
|
|
41
|
-
rubocop (1.
|
|
36
|
+
rspec-support (3.13.2)
|
|
37
|
+
rubocop (1.68.0)
|
|
42
38
|
json (~> 2.3)
|
|
43
39
|
language_server-protocol (>= 3.17.0)
|
|
44
40
|
parallel (~> 1.10)
|
|
45
41
|
parser (>= 3.3.0.2)
|
|
46
42
|
rainbow (>= 2.2.2, < 4.0)
|
|
47
|
-
regexp_parser (>=
|
|
48
|
-
|
|
49
|
-
rubocop-ast (>= 1.31.1, < 2.0)
|
|
43
|
+
regexp_parser (>= 2.4, < 3.0)
|
|
44
|
+
rubocop-ast (>= 1.32.2, < 2.0)
|
|
50
45
|
ruby-progressbar (~> 1.7)
|
|
51
46
|
unicode-display_width (>= 2.4.0, < 3.0)
|
|
52
|
-
rubocop-ast (1.
|
|
47
|
+
rubocop-ast (1.36.2)
|
|
53
48
|
parser (>= 3.3.1.0)
|
|
54
|
-
rubocop-performance (1.
|
|
49
|
+
rubocop-performance (1.22.1)
|
|
55
50
|
rubocop (>= 1.48.1, < 2.0)
|
|
56
51
|
rubocop-ast (>= 1.31.1, < 2.0)
|
|
57
52
|
ruby-progressbar (1.13.0)
|
|
58
|
-
|
|
53
|
+
rubyzip (2.3.2)
|
|
54
|
+
standard (1.42.1)
|
|
59
55
|
language_server-protocol (~> 3.17.0.2)
|
|
60
56
|
lint_roller (~> 1.0)
|
|
61
|
-
rubocop (~> 1.
|
|
57
|
+
rubocop (~> 1.68.0)
|
|
62
58
|
standard-custom (~> 1.0.0)
|
|
63
|
-
standard-performance (~> 1.
|
|
59
|
+
standard-performance (~> 1.5)
|
|
64
60
|
standard-custom (1.0.2)
|
|
65
61
|
lint_roller (~> 1.0)
|
|
66
62
|
rubocop (~> 1.50)
|
|
67
|
-
standard-performance (1.
|
|
63
|
+
standard-performance (1.5.0)
|
|
68
64
|
lint_roller (~> 1.1)
|
|
69
|
-
rubocop-performance (~> 1.
|
|
70
|
-
|
|
71
|
-
unicode-display_width (2.5.0)
|
|
65
|
+
rubocop-performance (~> 1.22.0)
|
|
66
|
+
unicode-display_width (2.6.0)
|
|
72
67
|
|
|
73
68
|
PLATFORMS
|
|
74
69
|
ruby
|
|
@@ -76,6 +71,9 @@ PLATFORMS
|
|
|
76
71
|
|
|
77
72
|
DEPENDENCIES
|
|
78
73
|
astronoby!
|
|
74
|
+
rake (~> 13.0)
|
|
75
|
+
rspec (~> 3.0)
|
|
76
|
+
rubyzip (~> 2.3)
|
|
79
77
|
standard (~> 1.3)
|
|
80
78
|
|
|
81
79
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -140,32 +140,37 @@ horizontal_coordinates.altitude.str(:dms)
|
|
|
140
140
|
|
|
141
141
|
#### Sunrise and sunset times and azimuths
|
|
142
142
|
|
|
143
|
+
Only date part of the time is relevant for the calculation. The offset must
|
|
144
|
+
be provided to the observer.
|
|
145
|
+
|
|
143
146
|
```rb
|
|
144
|
-
|
|
147
|
+
utc_offset = "-05:00"
|
|
148
|
+
time = Time.new(2015, 2, 5, 0, 0, 0, utc_offset)
|
|
145
149
|
observer = Astronoby::Observer.new(
|
|
146
150
|
latitude: Astronoby::Angle.from_degrees(38),
|
|
147
|
-
longitude: Astronoby::Angle.from_degrees(-78)
|
|
151
|
+
longitude: Astronoby::Angle.from_degrees(-78),
|
|
152
|
+
utc_offset: utc_offset
|
|
148
153
|
)
|
|
149
154
|
sun = Astronoby::Sun.new(time: time)
|
|
150
155
|
observation_events = sun.observation_events(observer: observer)
|
|
151
156
|
|
|
152
|
-
observation_events.rising_time
|
|
153
|
-
# => 2015-02-05
|
|
157
|
+
observation_events.rising_time.getlocal(utc_offset)
|
|
158
|
+
# => 2015-02-05 07:12:59 -0500
|
|
154
159
|
|
|
155
160
|
observation_events.rising_azimuth.str(:dms)
|
|
156
|
-
# => "+109° 29′
|
|
161
|
+
# => "+109° 29′ 35.5069″"
|
|
157
162
|
|
|
158
|
-
observation_events.transit_time
|
|
159
|
-
# => 2015-02-05
|
|
163
|
+
observation_events.transit_time.getlocal(utc_offset)
|
|
164
|
+
# => 2015-02-05 12:25:59 -0500
|
|
160
165
|
|
|
161
166
|
observation_events.transit_altitude.str(:dms)
|
|
162
|
-
# => "+36° 8′
|
|
167
|
+
# => "+36° 8′ 14.9673″"
|
|
163
168
|
|
|
164
|
-
observation_events.setting_time
|
|
165
|
-
# => 2015-02-05
|
|
169
|
+
observation_events.setting_time.getlocal(utc_offset)
|
|
170
|
+
# => 2015-02-05 17:39:27 -0500
|
|
166
171
|
|
|
167
172
|
observation_events.setting_azimuth.str(:dms)
|
|
168
|
-
# => "+250° 40′
|
|
173
|
+
# => "+250° 40′ 41.7129″"
|
|
169
174
|
```
|
|
170
175
|
|
|
171
176
|
#### Twilight times
|
|
@@ -244,6 +249,9 @@ moon = Astronoby::Moon.new(time: time)
|
|
|
244
249
|
moon.illuminated_fraction.round(2)
|
|
245
250
|
# => 0.31
|
|
246
251
|
|
|
252
|
+
moon.current_phase_fraction.round(2)
|
|
253
|
+
# => 0.82
|
|
254
|
+
|
|
247
255
|
moon.distance.km.round
|
|
248
256
|
# => 368409
|
|
249
257
|
|
|
@@ -268,32 +276,37 @@ june_phases.each { puts "#{_1.phase}: #{_1.time}" }
|
|
|
268
276
|
|
|
269
277
|
#### Moonrise and moonset times and azimuths
|
|
270
278
|
|
|
279
|
+
Only date part of the time is relevant for the calculation. The offset must
|
|
280
|
+
be provided to the observer.
|
|
281
|
+
|
|
271
282
|
```rb
|
|
272
|
-
|
|
283
|
+
utc_offset = "-10:00"
|
|
284
|
+
time = Time.new(2024, 9, 1, 0, 0, 0, utc_offset)
|
|
273
285
|
observer = Astronoby::Observer.new(
|
|
274
|
-
latitude: Astronoby::Angle.from_degrees(
|
|
275
|
-
longitude: Astronoby::Angle.from_degrees(
|
|
286
|
+
latitude: Astronoby::Angle.from_degrees(-17.5325),
|
|
287
|
+
longitude: Astronoby::Angle.from_degrees(-149.5677),
|
|
288
|
+
utc_offset: utc_offset
|
|
276
289
|
)
|
|
277
290
|
moon = Astronoby::Moon.new(time: time)
|
|
278
291
|
observation_events = moon.observation_events(observer: observer)
|
|
279
292
|
|
|
280
|
-
observation_events.rising_time
|
|
281
|
-
# => 2024-
|
|
293
|
+
observation_events.rising_time.getlocal(utc_offset)
|
|
294
|
+
# => 2024-09-01 05:24:55 -1000
|
|
282
295
|
|
|
283
296
|
observation_events.rising_azimuth.str(:dms)
|
|
284
|
-
# => "+
|
|
297
|
+
# => "+72° 15′ 19.1814″"
|
|
285
298
|
|
|
286
|
-
observation_events.transit_time
|
|
287
|
-
# => 2024-
|
|
299
|
+
observation_events.transit_time.getlocal(utc_offset)
|
|
300
|
+
# => 2024-09-01 11:12:32 -1000
|
|
288
301
|
|
|
289
302
|
observation_events.transit_altitude.str(:dms)
|
|
290
|
-
# => "+
|
|
303
|
+
# => "+56° 39′ 59.132″"
|
|
291
304
|
|
|
292
|
-
observation_events.setting_time
|
|
293
|
-
# => 2024-
|
|
305
|
+
observation_events.setting_time.getlocal(utc_offset)
|
|
306
|
+
# => 2024-09-01 16:12:08 -1000
|
|
294
307
|
|
|
295
308
|
observation_events.setting_azimuth.str(:dms)
|
|
296
|
-
# => "+
|
|
309
|
+
# => "+290° 25′ 42.5421″"
|
|
297
310
|
```
|
|
298
311
|
|
|
299
312
|
## Precision
|
data/benchmark/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Benchmark
|
|
2
|
+
|
|
3
|
+
This is a first attempt to benchmark the accuracy of the library. It is not
|
|
4
|
+
very scientific, but it gives a rough idea.
|
|
5
|
+
|
|
6
|
+
## Method
|
|
7
|
+
|
|
8
|
+
The goal is to answer these two questions:
|
|
9
|
+
- Is the library accurate enough compared to a source of truth?
|
|
10
|
+
- Is the library accurate enough compared with other Ruby libraries?
|
|
11
|
+
|
|
12
|
+
The source of truth is the <abbr title="Institut de Mécanique
|
|
13
|
+
Céleste et de Calcul des Éphémérides">IMCCE</abbr>, a French public
|
|
14
|
+
institude attached to the Paris Observatory. Their ephemerides are used by
|
|
15
|
+
governements and public institutions in multiple European countries, their
|
|
16
|
+
precesion is among the highest in the world.
|
|
17
|
+
They also provide web services to easily access all their data. Many thanks
|
|
18
|
+
for providing such high accuracy data for free.
|
|
19
|
+
|
|
20
|
+
The other Ruby library is [sun_calc](https://github.com/fishbrain/sun_calc).
|
|
21
|
+
|
|
22
|
+
474,336 combinations of dates, latitudes and longitudes have been used to
|
|
23
|
+
produce time predictions for the following events:
|
|
24
|
+
- sunrise
|
|
25
|
+
- sun's highest point
|
|
26
|
+
- sunset
|
|
27
|
+
- moonrise
|
|
28
|
+
- moon's highest point
|
|
29
|
+
- moonset
|
|
30
|
+
|
|
31
|
+
For each combination, we first find out which of SunCalc or Astronoby is the
|
|
32
|
+
closest to the IMCCE. Then we calculate the difference between Astronoby and
|
|
33
|
+
the IMCCE, to discover if the difference is larger than the defined
|
|
34
|
+
threshold of 5 minutes.
|
|
35
|
+
|
|
36
|
+
## Results
|
|
37
|
+
|
|
38
|
+
The following output has been generated using what will be part of version 0.6.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Unarchiving sun_calc.csv.zip...
|
|
42
|
+
Done unarchiving sun_calc.csv.zip.
|
|
43
|
+
Parsing sun_calc.csv...
|
|
44
|
+
Done parsing sun_calc.csv.
|
|
45
|
+
Unarchiving imcce.csv.zip...
|
|
46
|
+
Done unarchiving imcce.csv.zip.
|
|
47
|
+
Parsing imcce.csv...
|
|
48
|
+
Done parsing imcce.csv.
|
|
49
|
+
Comparing data...
|
|
50
|
+
2024-01-01: Done.
|
|
51
|
+
2024-01-02: Done.
|
|
52
|
+
...
|
|
53
|
+
2024-12-30: Done.
|
|
54
|
+
2024-12-31: Done.
|
|
55
|
+
Done comparing data.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Sun rising time:
|
|
59
|
+
astronoby: 295395 (62.28%)
|
|
60
|
+
sun_calc: 99710 (21.02%)
|
|
61
|
+
n/a: 79231 (16.7%)
|
|
62
|
+
|
|
63
|
+
Sun transit time:
|
|
64
|
+
astronoby: 434495 (91.6%)
|
|
65
|
+
sun_calc: 39769 (8.38%)
|
|
66
|
+
n/a: 72 (0.02%)
|
|
67
|
+
|
|
68
|
+
Sun setting time:
|
|
69
|
+
astronoby: 358428 (75.56%)
|
|
70
|
+
n/a: 79231 (16.7%)
|
|
71
|
+
sun_calc: 36677 (7.73%)
|
|
72
|
+
|
|
73
|
+
Moon rising time:
|
|
74
|
+
astronoby: 290866 (61.32%)
|
|
75
|
+
n/a: 113815 (23.99%)
|
|
76
|
+
sun_calc: 69655 (14.68%)
|
|
77
|
+
|
|
78
|
+
Moon transit time:
|
|
79
|
+
astronoby: 341902 (72.08%)
|
|
80
|
+
n/a: 101916 (21.49%)
|
|
81
|
+
sun_calc: 30518 (6.43%)
|
|
82
|
+
|
|
83
|
+
Moon setting time:
|
|
84
|
+
astronoby: 327099 (68.96%)
|
|
85
|
+
n/a: 114308 (24.1%)
|
|
86
|
+
sun_calc: 32929 (6.94%)
|
|
87
|
+
|
|
88
|
+
Moon illuminated fraction:
|
|
89
|
+
astronoby: 474336 (100.0%)
|
|
90
|
+
|
|
91
|
+
Sun rising time too far:
|
|
92
|
+
false: 452887 (95.48%)
|
|
93
|
+
true: 21449 (4.52%)
|
|
94
|
+
|
|
95
|
+
Sun transit time too far:
|
|
96
|
+
false: 396617 (83.62%)
|
|
97
|
+
true: 77719 (16.38%)
|
|
98
|
+
|
|
99
|
+
Sun setting time too far:
|
|
100
|
+
false: 453208 (95.55%)
|
|
101
|
+
true: 21128 (4.45%)
|
|
102
|
+
|
|
103
|
+
Moon rising time too far:
|
|
104
|
+
false: 459044 (96.78%)
|
|
105
|
+
true: 15292 (3.22%)
|
|
106
|
+
|
|
107
|
+
Moon transit time too far:
|
|
108
|
+
false: 384516 (81.06%)
|
|
109
|
+
true: 89820 (18.94%)
|
|
110
|
+
|
|
111
|
+
Moon setting time too far:
|
|
112
|
+
false: 459222 (96.81%)
|
|
113
|
+
true: 15114 (3.19%)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Conclusion
|
|
117
|
+
|
|
118
|
+
As we can see, Astronoby is more accurate than SunCalc in a vast majority of
|
|
119
|
+
cases. When it comes to the Moon's illuminated fraction, Astronoby is always
|
|
120
|
+
more accurate than SunCalc.
|
|
121
|
+
|
|
122
|
+
`n/a` values means that at least one of the three sources don't have a value
|
|
123
|
+
for the combination of date, latitude and longitude. This happens because
|
|
124
|
+
the Moon and the Sun cannot always rise, transit and set everywhere on Earth
|
|
125
|
+
every day of the year. Latitudes close to the poles are more likely to miss
|
|
126
|
+
data.
|
|
127
|
+
|
|
128
|
+
Astronoby can be considered "good enough" for more around 90% of the cases,
|
|
129
|
+
which means there is still work to do if we want to always be less than 5
|
|
130
|
+
minutes away from the what the IMCCE provides. We can notice that transit
|
|
131
|
+
times are those that experience the most significant differences.
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
require "astronoby"
|
|
2
|
+
require "csv"
|
|
3
|
+
require "zip"
|
|
4
|
+
|
|
5
|
+
class Source
|
|
6
|
+
NAMES = [
|
|
7
|
+
ASTRONOBY = "astronoby",
|
|
8
|
+
IMCCE = "imcce",
|
|
9
|
+
SUN_CALC = "sun_calc"
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
attr_accessor :name,
|
|
13
|
+
:sun_rising_time,
|
|
14
|
+
:sun_transit_time,
|
|
15
|
+
:sun_setting_time,
|
|
16
|
+
:moon_rising_time,
|
|
17
|
+
:moon_transit_time,
|
|
18
|
+
:moon_setting_time,
|
|
19
|
+
:moon_illuminated_fraction
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Comparison
|
|
23
|
+
SUN_CALC = "sun_calc"
|
|
24
|
+
ASTRONOBY = "astronoby"
|
|
25
|
+
NON_APPLICABLE = "n/a"
|
|
26
|
+
|
|
27
|
+
TOO_FAR_THRESHOLD = 60 * 5 # 5 minutes
|
|
28
|
+
|
|
29
|
+
attr_accessor :sources, :truth
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
@sources = []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
%i[
|
|
36
|
+
sun_rising_time
|
|
37
|
+
sun_transit_time
|
|
38
|
+
sun_setting_time
|
|
39
|
+
moon_rising_time
|
|
40
|
+
moon_transit_time
|
|
41
|
+
moon_setting_time
|
|
42
|
+
].each do |attribute|
|
|
43
|
+
define_method(:"closest_#{attribute}") do
|
|
44
|
+
compare(attribute)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
define_method(:"#{attribute}_too_far?") do
|
|
48
|
+
too_far?(attribute)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def closest_moon_illuminated_fraction
|
|
53
|
+
compare(:moon_illuminated_fraction)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def compare(attribute)
|
|
59
|
+
unless truth.public_send(attribute) && sources.all? { |source| source.public_send(attribute) }
|
|
60
|
+
return NON_APPLICABLE
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
closest_source = sources.min_by do |source|
|
|
64
|
+
(truth.public_send(attribute) - source.public_send(attribute)).abs
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
closest_source.name
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def too_far?(attribute)
|
|
71
|
+
truth_attribute = truth.public_send(attribute)
|
|
72
|
+
astronoby_attribute = sources
|
|
73
|
+
.find { _1.name == Source::ASTRONOBY }
|
|
74
|
+
.public_send(attribute)
|
|
75
|
+
|
|
76
|
+
return false unless truth_attribute && astronoby_attribute
|
|
77
|
+
|
|
78
|
+
(truth_attribute - astronoby_attribute).abs > TOO_FAR_THRESHOLD
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Result
|
|
83
|
+
def initialize
|
|
84
|
+
@sun_rising_time = []
|
|
85
|
+
@sun_transit_time = []
|
|
86
|
+
@sun_setting_time = []
|
|
87
|
+
@moon_rising_time = []
|
|
88
|
+
@moon_transit_time = []
|
|
89
|
+
@moon_setting_time = []
|
|
90
|
+
@illuminated_fraction = []
|
|
91
|
+
@sun_rising_time_too_far = []
|
|
92
|
+
@sun_transit_time_too_far = []
|
|
93
|
+
@sun_setting_time_too_far = []
|
|
94
|
+
@moon_rising_time_too_far = []
|
|
95
|
+
@moon_transit_time_too_far = []
|
|
96
|
+
@moon_setting_time_too_far = []
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def add_comparison(comparison)
|
|
100
|
+
@sun_rising_time << comparison.closest_sun_rising_time
|
|
101
|
+
@sun_transit_time << comparison.closest_sun_transit_time
|
|
102
|
+
@sun_setting_time << comparison.closest_sun_setting_time
|
|
103
|
+
@moon_rising_time << comparison.closest_moon_rising_time
|
|
104
|
+
@moon_transit_time << comparison.closest_moon_transit_time
|
|
105
|
+
@moon_setting_time << comparison.closest_moon_setting_time
|
|
106
|
+
@illuminated_fraction << comparison.closest_moon_illuminated_fraction
|
|
107
|
+
@sun_rising_time_too_far << comparison.sun_rising_time_too_far?
|
|
108
|
+
@sun_transit_time_too_far << comparison.sun_transit_time_too_far?
|
|
109
|
+
@sun_setting_time_too_far << comparison.sun_setting_time_too_far?
|
|
110
|
+
@moon_rising_time_too_far << comparison.moon_rising_time_too_far?
|
|
111
|
+
@moon_transit_time_too_far << comparison.moon_transit_time_too_far?
|
|
112
|
+
@moon_setting_time_too_far << comparison.moon_setting_time_too_far?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def display
|
|
116
|
+
puts "Sun rising time:"
|
|
117
|
+
tally(@sun_rising_time)
|
|
118
|
+
puts "Sun transit time:"
|
|
119
|
+
tally(@sun_transit_time)
|
|
120
|
+
puts "Sun setting time:"
|
|
121
|
+
tally(@sun_setting_time)
|
|
122
|
+
puts "Moon rising time:"
|
|
123
|
+
tally(@moon_rising_time)
|
|
124
|
+
puts "Moon transit time:"
|
|
125
|
+
tally(@moon_transit_time)
|
|
126
|
+
puts "Moon setting time:"
|
|
127
|
+
tally(@moon_setting_time)
|
|
128
|
+
puts "Moon illuminated fraction:"
|
|
129
|
+
tally(@illuminated_fraction)
|
|
130
|
+
puts "Sun rising time too far:"
|
|
131
|
+
tally(@sun_rising_time_too_far)
|
|
132
|
+
puts "Sun transit time too far:"
|
|
133
|
+
tally(@sun_transit_time_too_far)
|
|
134
|
+
puts "Sun setting time too far:"
|
|
135
|
+
tally(@sun_setting_time_too_far)
|
|
136
|
+
puts "Moon rising time too far:"
|
|
137
|
+
tally(@moon_rising_time_too_far)
|
|
138
|
+
puts "Moon transit time too far:"
|
|
139
|
+
tally(@moon_transit_time_too_far)
|
|
140
|
+
puts "Moon setting time too far:"
|
|
141
|
+
tally(@moon_setting_time_too_far)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
def tally(data)
|
|
147
|
+
t = data.tally
|
|
148
|
+
t.sort_by { |_key, value| -value }.each do |key, value|
|
|
149
|
+
puts "#{key}: #{value} (#{(value.to_f / t.values.sum * 100).round(2)}%)"
|
|
150
|
+
end
|
|
151
|
+
puts "\n"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
data = {}
|
|
156
|
+
result = Result.new
|
|
157
|
+
|
|
158
|
+
sun_calc_zip_file = File.join(File.dirname(__FILE__), "data/sun_calc.csv.zip")
|
|
159
|
+
imcce_zip_file = File.join(File.dirname(__FILE__), "data/imcce.csv.zip")
|
|
160
|
+
|
|
161
|
+
puts "Unarchiving sun_calc.csv.zip..."
|
|
162
|
+
|
|
163
|
+
Zip::File.open(sun_calc_zip_file) do |zip_file|
|
|
164
|
+
puts "Done unarchiving sun_calc.csv.zip."
|
|
165
|
+
|
|
166
|
+
csv_file = zip_file.find { |entry| entry.name.end_with?(".csv") }
|
|
167
|
+
break unless csv_file
|
|
168
|
+
|
|
169
|
+
puts "Parsing sun_calc.csv..."
|
|
170
|
+
|
|
171
|
+
csv_content = csv_file.get_input_stream.read
|
|
172
|
+
CSV.parse(csv_content, headers: true) do |row|
|
|
173
|
+
data[row["date"]] ||= {}
|
|
174
|
+
data[row["date"]][row["latitude"]] ||= {}
|
|
175
|
+
data[row["date"]][row["latitude"]][row["longitude"]] = Comparison.new.tap do |comparison|
|
|
176
|
+
source = Source.new.tap do |source|
|
|
177
|
+
source.name = Source::SUN_CALC
|
|
178
|
+
source.sun_rising_time = Time.new(row["sun_rising_time"]) if row["sun_rising_time"]
|
|
179
|
+
source.sun_transit_time = Time.new(row["sun_transit_time"]) if row["sun_transit_time"]
|
|
180
|
+
source.sun_setting_time = Time.new(row["sun_setting_time"]) if row["sun_setting_time"]
|
|
181
|
+
source.moon_rising_time = Time.new(row["moon_rising_time"]) if row["moon_rising_time"]
|
|
182
|
+
source.moon_transit_time = Time.new(row["moon_transit_time"]) if row["moon_transit_time"]
|
|
183
|
+
source.moon_setting_time = Time.new(row["moon_setting_time"]) if row["moon_setting_time"]
|
|
184
|
+
source.moon_illuminated_fraction = row["illuminated_fraction"].to_f
|
|
185
|
+
end
|
|
186
|
+
comparison.sources << source
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
puts "Done parsing sun_calc.csv."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
puts "Unarchiving imcce.csv.zip..."
|
|
194
|
+
|
|
195
|
+
Zip::File.open(imcce_zip_file) do |zip_file|
|
|
196
|
+
puts "Done unarchiving imcce.csv.zip."
|
|
197
|
+
|
|
198
|
+
csv_file = zip_file.find { |entry| entry.name.end_with?(".csv") }
|
|
199
|
+
break unless csv_file
|
|
200
|
+
|
|
201
|
+
puts "Parsing imcce.csv..."
|
|
202
|
+
|
|
203
|
+
csv_content = csv_file.get_input_stream.read
|
|
204
|
+
CSV.parse(csv_content, headers: true) do |row|
|
|
205
|
+
comparison = data[row["date"]][row["latitude"]][row["longitude"]]
|
|
206
|
+
comparison.truth = Source.new.tap do |source|
|
|
207
|
+
source.name = Source::IMCCE
|
|
208
|
+
source.sun_rising_time = Time.new(row["sun_rising_time"] + " UTC") if row["sun_rising_time"]
|
|
209
|
+
source.sun_transit_time = Time.new(row["sun_transit_time"] + " UTC") if row["sun_transit_time"]
|
|
210
|
+
source.sun_setting_time = Time.new(row["sun_setting_time"] + " UTC") if row["sun_setting_time"]
|
|
211
|
+
source.moon_rising_time = Time.new(row["moon_rising_time"] + " UTC") if row["moon_rising_time"]
|
|
212
|
+
source.moon_transit_time = Time.new(row["moon_transit_time"] + " UTC") if row["moon_transit_time"]
|
|
213
|
+
source.moon_setting_time = Time.new(row["moon_setting_time"] + " UTC") if row["moon_setting_time"]
|
|
214
|
+
source.moon_illuminated_fraction = row["illuminated_fraction"].to_f
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
puts "Done parsing imcce.csv."
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
puts "Comparing data..."
|
|
222
|
+
|
|
223
|
+
data.each do |date, latitudes|
|
|
224
|
+
latitudes.each do |latitude, longitudes|
|
|
225
|
+
longitudes.each do |longitude, comparison|
|
|
226
|
+
noon = Time.new("#{date}T12:00:00Z")
|
|
227
|
+
observer = Astronoby::Observer.new(
|
|
228
|
+
latitude: Astronoby::Angle.from_degrees(latitude.to_i),
|
|
229
|
+
longitude: Astronoby::Angle.from_degrees(longitude.to_i)
|
|
230
|
+
)
|
|
231
|
+
sun = Astronoby::Sun.new(time: noon)
|
|
232
|
+
sun_observation_events = sun.observation_events(observer: observer)
|
|
233
|
+
moon = Astronoby::Moon.new(time: noon)
|
|
234
|
+
moon_observation_events = moon.observation_events(observer: observer)
|
|
235
|
+
|
|
236
|
+
source = Source.new.tap do |source|
|
|
237
|
+
source.name = Source::ASTRONOBY
|
|
238
|
+
source.sun_rising_time = sun_observation_events.rising_time
|
|
239
|
+
source.sun_transit_time = sun_observation_events.transit_time
|
|
240
|
+
source.sun_setting_time = sun_observation_events.setting_time
|
|
241
|
+
source.moon_rising_time = moon_observation_events.rising_time
|
|
242
|
+
source.moon_transit_time = moon_observation_events.transit_time
|
|
243
|
+
source.moon_setting_time = moon_observation_events.setting_time
|
|
244
|
+
source.moon_illuminated_fraction = moon.illuminated_fraction
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
comparison.sources << source
|
|
248
|
+
result.add_comparison(comparison)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
puts "#{date}: Done."
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
puts "Done comparing data."
|
|
256
|
+
puts
|
|
257
|
+
puts
|
|
258
|
+
|
|
259
|
+
puts result.display
|
|
Binary file
|
|
Binary file
|
|
@@ -167,6 +167,11 @@ module Astronoby
|
|
|
167
167
|
@illuminated_fraction ||= (1 + phase_angle.cos) / 2
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
+
# @return [Float] Phase fraction, from 0 to 1
|
|
171
|
+
def current_phase_fraction
|
|
172
|
+
mean_elongation.degrees / Constants::DEGREES_PER_CIRCLE
|
|
173
|
+
end
|
|
174
|
+
|
|
170
175
|
# @param observer [Astronoby::Observer] Observer of the event
|
|
171
176
|
# @return [Astronoby::Events::ObservationEvents] Moon's observation events
|
|
172
177
|
def observation_events(observer:)
|
|
@@ -7,6 +7,7 @@ module Astronoby
|
|
|
7
7
|
RISING_SETTING_HOUR_ANGLE_RATIO_RANGE = (-1..1)
|
|
8
8
|
EARTH_SIDEREAL_ROTATION_RATE = 360.98564736629
|
|
9
9
|
ITERATION_PRECISION = 0.0001
|
|
10
|
+
ITERATION_LIMIT = 5
|
|
10
11
|
|
|
11
12
|
attr_reader :rising_time,
|
|
12
13
|
:rising_azimuth,
|
|
@@ -53,7 +54,11 @@ module Astronoby
|
|
|
53
54
|
|
|
54
55
|
def compute
|
|
55
56
|
@initial_transit = initial_transit
|
|
56
|
-
@transit_time = Util::Time.decimal_hour_to_time(
|
|
57
|
+
@transit_time = Util::Time.decimal_hour_to_time(
|
|
58
|
+
@date,
|
|
59
|
+
@observer.utc_offset,
|
|
60
|
+
@initial_transit
|
|
61
|
+
)
|
|
57
62
|
@transit_altitude = local_horizontal_altitude_transit
|
|
58
63
|
|
|
59
64
|
return if h0.nil?
|
|
@@ -79,20 +84,33 @@ module Astronoby
|
|
|
79
84
|
Constants::HOURS_PER_DAY * @final_setting
|
|
80
85
|
)
|
|
81
86
|
|
|
82
|
-
@rising_time = Util::Time.decimal_hour_to_time(
|
|
87
|
+
@rising_time = Util::Time.decimal_hour_to_time(
|
|
88
|
+
@date,
|
|
89
|
+
@observer.utc_offset,
|
|
90
|
+
rationalized_corrected_rising
|
|
91
|
+
)
|
|
83
92
|
@rising_azimuth = local_horizontal_azimuth_rising
|
|
84
|
-
@transit_time = Util::Time.decimal_hour_to_time(
|
|
93
|
+
@transit_time = Util::Time.decimal_hour_to_time(
|
|
94
|
+
@date,
|
|
95
|
+
@observer.utc_offset,
|
|
96
|
+
rationalized_corrected_transit
|
|
97
|
+
)
|
|
85
98
|
@transit_altitude = local_horizontal_altitude_transit
|
|
86
|
-
@setting_time = Util::Time.decimal_hour_to_time(
|
|
99
|
+
@setting_time = Util::Time.decimal_hour_to_time(
|
|
100
|
+
@date,
|
|
101
|
+
@observer.utc_offset,
|
|
102
|
+
rationalized_corrected_setting
|
|
103
|
+
)
|
|
87
104
|
@setting_azimuth = local_horizontal_azimuth_setting
|
|
88
105
|
end
|
|
89
106
|
|
|
90
107
|
def iterate(initial_rising, initial_transit, initial_setting)
|
|
91
108
|
delta = 1
|
|
109
|
+
iteration = 1
|
|
92
110
|
corrected_rising = initial_rising
|
|
93
111
|
corrected_transit = initial_transit
|
|
94
112
|
corrected_setting = initial_setting
|
|
95
|
-
until delta < ITERATION_PRECISION
|
|
113
|
+
until delta < ITERATION_PRECISION || iteration > ITERATION_LIMIT
|
|
96
114
|
iterate = RiseTransitSetIteration.new(
|
|
97
115
|
observer: @observer,
|
|
98
116
|
date: @date,
|
|
@@ -108,6 +126,7 @@ module Astronoby
|
|
|
108
126
|
corrected_rising = rationalize_decimal_time corrected_rising + iterate[0]
|
|
109
127
|
corrected_transit = rationalize_decimal_time corrected_transit + iterate[1]
|
|
110
128
|
corrected_setting = rationalize_decimal_time corrected_setting + iterate[2]
|
|
129
|
+
iteration += 1
|
|
111
130
|
end
|
|
112
131
|
[corrected_rising, corrected_transit, corrected_setting]
|
|
113
132
|
end
|
|
@@ -155,11 +174,6 @@ module Astronoby
|
|
|
155
174
|
)
|
|
156
175
|
end
|
|
157
176
|
|
|
158
|
-
def leap_day_portion
|
|
159
|
-
leap_seconds = Util::Time.terrestrial_universal_time_delta(@date)
|
|
160
|
-
leap_seconds / Constants::SECONDS_PER_DAY
|
|
161
|
-
end
|
|
162
|
-
|
|
163
177
|
def local_hour_angle_transit
|
|
164
178
|
gst_transit - observer_longitude - right_ascension_transit
|
|
165
179
|
end
|
|
@@ -168,6 +182,8 @@ module Astronoby
|
|
|
168
182
|
term1 = declination_rising.sin + (-shift).sin * @observer.latitude.cos
|
|
169
183
|
term2 = (-shift).cos * @observer.latitude.cos
|
|
170
184
|
angle = term1 / term2
|
|
185
|
+
return nil if angle.abs > 1
|
|
186
|
+
|
|
171
187
|
Angle.acos(angle)
|
|
172
188
|
end
|
|
173
189
|
|
|
@@ -182,14 +198,16 @@ module Astronoby
|
|
|
182
198
|
term1 = declination_setting.sin + (-shift).sin * @observer.latitude.cos
|
|
183
199
|
term2 = (-shift).cos * @observer.latitude.cos
|
|
184
200
|
angle = term1 / term2
|
|
201
|
+
return nil if angle.abs > 1
|
|
202
|
+
|
|
185
203
|
Angle.from_degrees(
|
|
186
204
|
Constants::DEGREES_PER_CIRCLE - Angle.acos(angle).degrees
|
|
187
205
|
)
|
|
188
206
|
end
|
|
189
207
|
|
|
190
208
|
def rationalize_decimal_time(decimal_time)
|
|
191
|
-
decimal_time += 1
|
|
192
|
-
decimal_time -= 1
|
|
209
|
+
decimal_time += 1 while decimal_time.negative?
|
|
210
|
+
decimal_time -= 1 while decimal_time > 1
|
|
193
211
|
decimal_time
|
|
194
212
|
end
|
|
195
213
|
|
|
@@ -202,12 +220,14 @@ module Astronoby
|
|
|
202
220
|
def right_ascension_transit
|
|
203
221
|
Angle.from_degrees(
|
|
204
222
|
Util::Maths.interpolate(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
223
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
224
|
+
[
|
|
225
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
|
226
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
|
227
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
|
228
|
+
]
|
|
229
|
+
),
|
|
230
|
+
@final_transit || @initial_transit
|
|
211
231
|
)
|
|
212
232
|
)
|
|
213
233
|
end
|
|
@@ -215,12 +235,14 @@ module Astronoby
|
|
|
215
235
|
def declination_rising
|
|
216
236
|
Angle.from_degrees(
|
|
217
237
|
Util::Maths.interpolate(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
238
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
239
|
+
[
|
|
240
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
|
241
|
+
@coordinates_of_the_day.declination.degrees,
|
|
242
|
+
@coordinates_of_the_next_day.declination.degrees
|
|
243
|
+
]
|
|
244
|
+
),
|
|
245
|
+
@final_rising
|
|
224
246
|
)
|
|
225
247
|
)
|
|
226
248
|
end
|
|
@@ -228,12 +250,14 @@ module Astronoby
|
|
|
228
250
|
def declination_transit
|
|
229
251
|
Angle.from_degrees(
|
|
230
252
|
Util::Maths.interpolate(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
253
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
254
|
+
[
|
|
255
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
|
256
|
+
@coordinates_of_the_day.declination.degrees,
|
|
257
|
+
@coordinates_of_the_next_day.declination.degrees
|
|
258
|
+
]
|
|
259
|
+
),
|
|
260
|
+
@final_transit || @initial_transit
|
|
237
261
|
)
|
|
238
262
|
)
|
|
239
263
|
end
|
|
@@ -241,12 +265,14 @@ module Astronoby
|
|
|
241
265
|
def declination_setting
|
|
242
266
|
Angle.from_degrees(
|
|
243
267
|
Util::Maths.interpolate(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
268
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
269
|
+
[
|
|
270
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
|
271
|
+
@coordinates_of_the_day.declination.degrees,
|
|
272
|
+
@coordinates_of_the_next_day.declination.degrees
|
|
273
|
+
]
|
|
274
|
+
),
|
|
275
|
+
@final_setting
|
|
250
276
|
)
|
|
251
277
|
)
|
|
252
278
|
end
|
|
@@ -112,13 +112,6 @@ module Astronoby
|
|
|
112
112
|
)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
def leap_day_portion
|
|
116
|
-
@leap_day_portion ||= begin
|
|
117
|
-
leap_seconds = Util::Time.terrestrial_universal_time_delta(@date)
|
|
118
|
-
leap_seconds / Constants::SECONDS_PER_DAY
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
115
|
def local_hour_angle_rising
|
|
123
116
|
@local_hour_angle_rising ||=
|
|
124
117
|
gst_rising - observer_longitude - right_ascension_rising
|
|
@@ -150,12 +143,14 @@ module Astronoby
|
|
|
150
143
|
def right_ascension_rising
|
|
151
144
|
Angle.from_degrees(
|
|
152
145
|
Util::Maths.interpolate(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
147
|
+
[
|
|
148
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
|
149
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
|
150
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
|
151
|
+
]
|
|
152
|
+
),
|
|
153
|
+
@initial_rising
|
|
159
154
|
)
|
|
160
155
|
)
|
|
161
156
|
end
|
|
@@ -163,12 +158,14 @@ module Astronoby
|
|
|
163
158
|
def right_ascension_transit
|
|
164
159
|
Angle.from_degrees(
|
|
165
160
|
Util::Maths.interpolate(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
161
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
162
|
+
[
|
|
163
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
|
164
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
|
165
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
|
166
|
+
]
|
|
167
|
+
),
|
|
168
|
+
@initial_transit
|
|
172
169
|
)
|
|
173
170
|
)
|
|
174
171
|
end
|
|
@@ -176,12 +173,14 @@ module Astronoby
|
|
|
176
173
|
def right_ascension_setting
|
|
177
174
|
Angle.from_degrees(
|
|
178
175
|
Util::Maths.interpolate(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
176
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
177
|
+
[
|
|
178
|
+
@coordinates_of_the_previous_day.right_ascension.degrees,
|
|
179
|
+
@coordinates_of_the_day.right_ascension.degrees,
|
|
180
|
+
@coordinates_of_the_next_day.right_ascension.degrees
|
|
181
|
+
]
|
|
182
|
+
),
|
|
183
|
+
@initial_setting
|
|
185
184
|
)
|
|
186
185
|
)
|
|
187
186
|
end
|
|
@@ -189,12 +188,14 @@ module Astronoby
|
|
|
189
188
|
def declination_rising
|
|
190
189
|
Angle.from_degrees(
|
|
191
190
|
Util::Maths.interpolate(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
191
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
192
|
+
[
|
|
193
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
|
194
|
+
@coordinates_of_the_day.declination.degrees,
|
|
195
|
+
@coordinates_of_the_next_day.declination.degrees
|
|
196
|
+
]
|
|
197
|
+
),
|
|
198
|
+
@initial_rising
|
|
198
199
|
)
|
|
199
200
|
)
|
|
200
201
|
end
|
|
@@ -202,12 +203,14 @@ module Astronoby
|
|
|
202
203
|
def declination_setting
|
|
203
204
|
Angle.from_degrees(
|
|
204
205
|
Util::Maths.interpolate(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
Util::Maths.normalize_angles_for_interpolation(
|
|
207
|
+
[
|
|
208
|
+
@coordinates_of_the_previous_day.declination.degrees,
|
|
209
|
+
@coordinates_of_the_day.declination.degrees,
|
|
210
|
+
@coordinates_of_the_next_day.declination.degrees
|
|
211
|
+
]
|
|
212
|
+
),
|
|
213
|
+
@initial_setting
|
|
211
214
|
)
|
|
212
215
|
)
|
|
213
216
|
end
|
data/lib/astronoby/observer.rb
CHANGED
|
@@ -10,12 +10,18 @@ module Astronoby
|
|
|
10
10
|
MOLAR_MASS_OF_AIR = 0.0289644
|
|
11
11
|
UNIVERSAL_GAS_CONSTANT = 8.31432
|
|
12
12
|
|
|
13
|
-
attr_reader :latitude,
|
|
13
|
+
attr_reader :latitude,
|
|
14
|
+
:longitude,
|
|
15
|
+
:elevation,
|
|
16
|
+
:utc_offset,
|
|
17
|
+
:temperature,
|
|
18
|
+
:pressure
|
|
14
19
|
|
|
15
20
|
# @param latitude [Angle] geographic latitude of the observer
|
|
16
21
|
# @param longitude [Angle] geographic longitude of the observer
|
|
17
22
|
# @param elevation [Astronoby::Distance] geographic elevation (or altitude)
|
|
18
23
|
# of the observer above sea level
|
|
24
|
+
# @param utc_offset [Numeric, String] offset from Coordinated Universal Time
|
|
19
25
|
# @param temperature [Numeric] temperature at the observer's location in
|
|
20
26
|
# kelvins
|
|
21
27
|
# @param pressure [Numeric] atmospheric pressure at the observer's
|
|
@@ -24,12 +30,14 @@ module Astronoby
|
|
|
24
30
|
latitude:,
|
|
25
31
|
longitude:,
|
|
26
32
|
elevation: DEFAULT_ELEVATION,
|
|
33
|
+
utc_offset: 0,
|
|
27
34
|
temperature: DEFAULT_TEMPERATURE,
|
|
28
35
|
pressure: nil
|
|
29
36
|
)
|
|
30
37
|
@latitude = latitude
|
|
31
38
|
@longitude = longitude
|
|
32
39
|
@elevation = elevation
|
|
40
|
+
@utc_offset = utc_offset
|
|
33
41
|
@temperature = temperature
|
|
34
42
|
@pressure = pressure || compute_pressure
|
|
35
43
|
end
|
|
@@ -40,6 +48,7 @@ module Astronoby
|
|
|
40
48
|
@latitude == other.latitude &&
|
|
41
49
|
@longitude == other.longitude &&
|
|
42
50
|
@elevation == other.elevation &&
|
|
51
|
+
@utc_offset == other.utc_offset &&
|
|
43
52
|
@temperature == other.temperature &&
|
|
44
53
|
@pressure == other.pressure
|
|
45
54
|
end
|
|
@@ -51,6 +60,7 @@ module Astronoby
|
|
|
51
60
|
@latitude,
|
|
52
61
|
@longitude,
|
|
53
62
|
@elevation,
|
|
63
|
+
@utc_offset,
|
|
54
64
|
@temperature,
|
|
55
65
|
@pressure
|
|
56
66
|
].hash
|
data/lib/astronoby/util/maths.rb
CHANGED
|
@@ -29,6 +29,28 @@ module Astronoby
|
|
|
29
29
|
"Only 3 or 5 terms are supported for interpolation"
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Fixes angles forced to be in range [0, 360] or other angle range, for
|
|
33
|
+
# interpolation use
|
|
34
|
+
# @param angles [Array<Integer|Float>] Angles values
|
|
35
|
+
# @param full_circle [Integer] Full circle value
|
|
36
|
+
# @return [Array<Interger|Float>] Normalized values
|
|
37
|
+
def normalize_angles_for_interpolation(angles, full_circle: 360)
|
|
38
|
+
normalized = angles.dup
|
|
39
|
+
|
|
40
|
+
(1...normalized.size).each do |i|
|
|
41
|
+
prev_angle = normalized[i - 1]
|
|
42
|
+
|
|
43
|
+
while normalized[i] - prev_angle > full_circle / 2
|
|
44
|
+
normalized[i] -= full_circle
|
|
45
|
+
end
|
|
46
|
+
while normalized[i] - prev_angle < -full_circle / 2
|
|
47
|
+
normalized[i] += full_circle
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
normalized
|
|
52
|
+
end
|
|
53
|
+
|
|
32
54
|
private
|
|
33
55
|
|
|
34
56
|
# @return [Float] Interpolated value
|
data/lib/astronoby/util/time.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Astronoby
|
|
|
6
6
|
# @param date [Date]
|
|
7
7
|
# @param decimal [Numeric] Hour of the day, in decimal hours
|
|
8
8
|
# @return [::Time] Date and time
|
|
9
|
-
def self.decimal_hour_to_time(date, decimal)
|
|
9
|
+
def self.decimal_hour_to_time(date, utc_offset, decimal)
|
|
10
10
|
absolute_hour = decimal.abs
|
|
11
11
|
hour = absolute_hour.floor
|
|
12
12
|
|
|
@@ -25,6 +25,17 @@ module Astronoby
|
|
|
25
25
|
second = Constants::SECONDS_PER_MINUTE *
|
|
26
26
|
(absolute_decimal_minute - absolute_decimal_minute.floor)
|
|
27
27
|
|
|
28
|
+
date_in_local_time = ::Time
|
|
29
|
+
.utc(date.year, date.month, date.day, hour, minute, second)
|
|
30
|
+
.getlocal(utc_offset)
|
|
31
|
+
.to_date
|
|
32
|
+
|
|
33
|
+
if date_in_local_time < date
|
|
34
|
+
date = date.next_day
|
|
35
|
+
elsif date_in_local_time > date
|
|
36
|
+
date = date.prev_day
|
|
37
|
+
end
|
|
38
|
+
|
|
28
39
|
::Time.utc(date.year, date.month, date.day, hour, minute, second).round
|
|
29
40
|
end
|
|
30
41
|
|
data/lib/astronoby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: astronoby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rémy Hannequin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-12-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: matrix
|
|
@@ -31,7 +31,7 @@ dependencies:
|
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '13.0'
|
|
34
|
-
type: :
|
|
34
|
+
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
@@ -45,13 +45,27 @@ dependencies:
|
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: '3.0'
|
|
48
|
-
type: :
|
|
48
|
+
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubyzip
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.3'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.3'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: standard
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,6 +100,10 @@ files:
|
|
|
86
100
|
- README.md
|
|
87
101
|
- Rakefile
|
|
88
102
|
- UPGRADING.md
|
|
103
|
+
- benchmark/README.md
|
|
104
|
+
- benchmark/benchmark.rb
|
|
105
|
+
- benchmark/data/imcce.csv.zip
|
|
106
|
+
- benchmark/data/sun_calc.csv.zip
|
|
89
107
|
- lib/astronoby.rb
|
|
90
108
|
- lib/astronoby/aberration.rb
|
|
91
109
|
- lib/astronoby/angle.rb
|