astronoby 0.5.0 → 0.7.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/.ruby-version +1 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +162 -0
- data/Gemfile.lock +54 -34
- data/README.md +42 -272
- data/UPGRADING.md +238 -0
- 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/aberration.rb +56 -31
- data/lib/astronoby/angle.rb +20 -16
- data/lib/astronoby/angles/dms.rb +2 -2
- data/lib/astronoby/angles/hms.rb +2 -2
- data/lib/astronoby/bodies/earth.rb +56 -0
- data/lib/astronoby/bodies/jupiter.rb +11 -0
- data/lib/astronoby/bodies/mars.rb +11 -0
- data/lib/astronoby/bodies/mercury.rb +11 -0
- data/lib/astronoby/bodies/moon.rb +50 -285
- data/lib/astronoby/bodies/neptune.rb +11 -0
- data/lib/astronoby/bodies/saturn.rb +11 -0
- data/lib/astronoby/bodies/solar_system_body.rb +122 -0
- data/lib/astronoby/bodies/sun.rb +16 -220
- data/lib/astronoby/bodies/uranus.rb +11 -0
- data/lib/astronoby/bodies/venus.rb +11 -0
- data/lib/astronoby/constants.rb +13 -1
- data/lib/astronoby/coordinates/ecliptic.rb +2 -37
- data/lib/astronoby/coordinates/equatorial.rb +25 -7
- data/lib/astronoby/coordinates/horizontal.rb +0 -46
- data/lib/astronoby/corrections/light_time_delay.rb +90 -0
- data/lib/astronoby/deflection.rb +187 -0
- data/lib/astronoby/distance.rb +9 -0
- data/lib/astronoby/ephem.rb +39 -0
- data/lib/astronoby/equinox_solstice.rb +21 -18
- data/lib/astronoby/errors.rb +4 -0
- data/lib/astronoby/events/moon_phases.rb +2 -1
- data/lib/astronoby/events/rise_transit_set_calculator.rb +352 -0
- data/lib/astronoby/events/rise_transit_set_event.rb +13 -0
- data/lib/astronoby/events/rise_transit_set_events.rb +13 -0
- data/lib/astronoby/events/twilight_calculator.rb +166 -0
- data/lib/astronoby/events/twilight_event.rb +28 -0
- data/lib/astronoby/instant.rb +171 -0
- data/lib/astronoby/mean_obliquity.rb +23 -10
- data/lib/astronoby/nutation.rb +227 -42
- data/lib/astronoby/observer.rb +66 -1
- data/lib/astronoby/precession.rb +91 -17
- data/lib/astronoby/reference_frame.rb +49 -0
- data/lib/astronoby/reference_frames/apparent.rb +60 -0
- data/lib/astronoby/reference_frames/astrometric.rb +21 -0
- data/lib/astronoby/reference_frames/geometric.rb +20 -0
- data/lib/astronoby/reference_frames/mean_of_date.rb +38 -0
- data/lib/astronoby/reference_frames/topocentric.rb +82 -0
- data/lib/astronoby/time/greenwich_sidereal_time.rb +1 -1
- data/lib/astronoby/true_obliquity.rb +2 -1
- data/lib/astronoby/util/maths.rb +68 -49
- data/lib/astronoby/util/time.rb +466 -32
- data/lib/astronoby/vector.rb +36 -0
- data/lib/astronoby/velocity.rb +116 -0
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +26 -5
- metadata +81 -18
- data/.tool-versions +0 -1
- data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +0 -143
- data/lib/astronoby/events/observation_events.rb +0 -259
- data/lib/astronoby/events/rise_transit_set_iteration.rb +0 -215
- data/lib/astronoby/events/twilight_events.rb +0 -121
- data/lib/astronoby/util/astrodynamics.rb +0 -60
data/UPGRADING.md
CHANGED
@@ -7,6 +7,244 @@ changes to it as long as a major version has not been released.
|
|
7
7
|
If you are already using Astronoby and wish to follow the changes to its
|
8
8
|
public API, please read the upgrading notes for each release.
|
9
9
|
|
10
|
+
## Upgrading from 0.6.0 to 0.7.0
|
11
|
+
|
12
|
+
## Signature change for `Sun` and `Moon`
|
13
|
+
|
14
|
+
Both classes are now initialized with the key arguments `ephem` and `instant`.
|
15
|
+
`ephem` comes from `Ephem.load` which provides the raw geometric data for Solar
|
16
|
+
System bodies. `instant` is an instance of `Instant`, the new concept for
|
17
|
+
representing an instant in time.
|
18
|
+
|
19
|
+
```rb
|
20
|
+
Ephem::IO::Download.call(name: "de421.bsp", target: "tmp/de421.bsp")
|
21
|
+
ephem = Astronoby::Ephem.load("tmp/de421.bsp")
|
22
|
+
|
23
|
+
time = Time.utc(2025, 6, 19, 12, 0, 0)
|
24
|
+
instant = Astronoby::Instant.from_time(time)
|
25
|
+
|
26
|
+
sun = Astronoby::Sun.new(instant: instant, ephem: ephem)
|
27
|
+
```
|
28
|
+
|
29
|
+
Learn more on [ephem].
|
30
|
+
|
31
|
+
[ephem]: https://github.com/rhannequin/astronoby/wiki/Ephem
|
32
|
+
|
33
|
+
## Drop of methods for `Sun`
|
34
|
+
|
35
|
+
`Sun` doesn't expose the following class and instance methods anymore:
|
36
|
+
* `::equation_of_time` (replaced with `#equation_of_time`)
|
37
|
+
* `#epoch` (replaced with `#instant`)
|
38
|
+
* `#true_ecliptic_coordinates` (replaced with `#ecliptic` on reference frames)
|
39
|
+
* `#apparent_ecliptic_coordinates` (replaced with `#ecliptic` on reference frames)
|
40
|
+
* `#horizontal_coordinates` (replaced with `#horizontal` on the Topocentric reference frame)
|
41
|
+
* `#observation_events`
|
42
|
+
* `#twilight_events`
|
43
|
+
* `#earth_distance` (replaced with `#distance` on referential frames)
|
44
|
+
* `#angular_size` (replaced with `#angular_diameter` on referential frames)
|
45
|
+
* `#true_anomaly`
|
46
|
+
* `#mean_anomaly`
|
47
|
+
* `#longitude_at_perigee`
|
48
|
+
* `#orbital_eccentricity`
|
49
|
+
|
50
|
+
Learn more on [reference frames].
|
51
|
+
|
52
|
+
[reference frames]: https://github.com/rhannequin/astronoby/wiki/Reference-Frames
|
53
|
+
|
54
|
+
## Drop of instance methods for `Moon`
|
55
|
+
|
56
|
+
`Moon` doesn't expose the following nstance methods anymore:
|
57
|
+
* `#apparent_ecliptic_coordinates` (replaced with `#ecliptic` on reference frames)
|
58
|
+
* `#apparent_equatorial_coordinates` (replaced with `#equatorial` on reference frames)
|
59
|
+
* `#horizontal_coordinates` (replaced with the Apparent and Topocentric reference frames)
|
60
|
+
* `#distance` (replaced with `#distance` on referential frames)
|
61
|
+
* `#mean_longitude`
|
62
|
+
* `#mean_elongation`
|
63
|
+
* `#mean_anomaly`
|
64
|
+
* `#argument_of_latitude`
|
65
|
+
* `#phase_angle`
|
66
|
+
* `#observation_events`
|
67
|
+
|
68
|
+
## Signature change for `Aberration`
|
69
|
+
|
70
|
+
`Aberration` is now initialized with the key arguments `astrometric_position`
|
71
|
+
and `observer_velocity`. `astrometric_position` is a position vector
|
72
|
+
(`Astronoby::Vector<Astronoby::Distance>`) available from any referential frame
|
73
|
+
with the `#position` method.`observer_velocity` is a velocity vector
|
74
|
+
(`Astronoby::Vector<Astronoby::Distance>`) available from any referential frame
|
75
|
+
with the `#velocity` method.
|
76
|
+
|
77
|
+
`observer_velocity` is meant to be a geometric velocity, while
|
78
|
+
`astrometric_position` is meant to be an astrometric position.
|
79
|
+
|
80
|
+
```rb
|
81
|
+
time = Time.utc(2025, 4, 1)
|
82
|
+
instant = Astronoby::Instant.from_time(time)
|
83
|
+
ephem = Astronoby::Ephem.load("de421.bsp")
|
84
|
+
earth = Astronoby::Earth.new(instant: instant, ephem: ephem)
|
85
|
+
mars = Astronoby::Mars.new(instant: instant, ephem: ephem)
|
86
|
+
earth_geometric_velocity = earth.geometric.velocity
|
87
|
+
mars_astrometric_position = mars.astrometric.position
|
88
|
+
|
89
|
+
aberration = Astronoby::Aberration.new(
|
90
|
+
astrometric_position: mars_astrometric_position,
|
91
|
+
observer_velocity: earth_geometric_velocity
|
92
|
+
)
|
93
|
+
```
|
94
|
+
|
95
|
+
## Signature change for `Angle#to_dms` and `Angle#to_hms`
|
96
|
+
|
97
|
+
`Angle#to_dms` and `Angle#to_hms` don't have arguments anymore. The angle value
|
98
|
+
is now taken from the object itself. This was a misbehavior in the previous
|
99
|
+
implementation.
|
100
|
+
|
101
|
+
```rb
|
102
|
+
angle = Astronoby::Angle.from_degrees(45.0)
|
103
|
+
dms = angle.to_dms
|
104
|
+
|
105
|
+
dms.format
|
106
|
+
# => "+45° 0′ 0.0″"
|
107
|
+
```
|
108
|
+
|
109
|
+
## Signature change for `EquinoxSolstice`
|
110
|
+
|
111
|
+
`EquinoxSolstice.new` now takes an additional argument expected to be an ephem
|
112
|
+
(`Astronoby::Ephem`).
|
113
|
+
|
114
|
+
## Signature change for `Nutation`
|
115
|
+
|
116
|
+
The expected `epoch` (`Astronoby::Epoch`) argument has been replaced by an
|
117
|
+
`instant` (`Astronoby::Instant`) key argument.
|
118
|
+
|
119
|
+
## Drop of `Nutation::for_ecliptic_longitude` and `Nutation::for_obliquity_of_the_ecliptic`
|
120
|
+
|
121
|
+
`Nutation::for_ecliptic_longitude` and `Nutation::for_obliquity_of_the_ecliptic`
|
122
|
+
methods have been removed. The `Nutation` class now exposes the
|
123
|
+
`#nutation_in_longitude` and `#nutation_in_obliquity` instance methods which
|
124
|
+
both return an angle (`Astronoby::Angle`).
|
125
|
+
|
126
|
+
## Signature change for `Precession`
|
127
|
+
|
128
|
+
The expected `coordinates` and `epoch` (`Astronoby::Epoch`) key arguments have
|
129
|
+
been replaced by an `instant` (`Astronoby::Instant`) key argument.
|
130
|
+
|
131
|
+
## Drop of `Precession#for_equatorial_coordinates` and `Precession#precess`
|
132
|
+
|
133
|
+
`Precession#for_equatorial_coordinates` and `Precession#precess` methods have
|
134
|
+
been refactoed into class methods.
|
135
|
+
|
136
|
+
## Drop of `Coordinates::Horizontal#to_equatorial`
|
137
|
+
|
138
|
+
`Coordinates::Horizontal#to_equatorial` has been removed as equatorial
|
139
|
+
coordinates are now available from the reference frames.
|
140
|
+
|
141
|
+
## Drop of instance methods for `Coordinates::Ecliptic`
|
142
|
+
|
143
|
+
`Coordinates::Ecliptic#to_true_equatorial` and
|
144
|
+
`Coordinates::Ecliptic#to_apparent_equatorial` have been removed as
|
145
|
+
equatorial coordinates are now available from the reference frames.
|
146
|
+
|
147
|
+
## Drop of `Coordinates::Equatorial#to_epoch`
|
148
|
+
|
149
|
+
`Coordinates::Equatorial#to_epoch` has been removed.
|
150
|
+
|
151
|
+
## Drop of `Events::ObservationEvents`
|
152
|
+
|
153
|
+
`Events::ObservationEvents` has been removed. The rising, transit and setting
|
154
|
+
times can now be calculated using `RiseTransitSetCalculator`.
|
155
|
+
|
156
|
+
```rb
|
157
|
+
ephem = Astronoby::Ephem.load("inpop19a.bsp")
|
158
|
+
observer = Astronoby::Observer.new(
|
159
|
+
latitude: Astronoby::Angle.from_degrees(48),
|
160
|
+
longitude: Astronoby::Angle.from_degrees(2)
|
161
|
+
)
|
162
|
+
date = Date.new(2025, 4, 24)
|
163
|
+
|
164
|
+
calculator = Astronoby::RiseTransitSetCalculator.new(
|
165
|
+
body: Astronoby::Sun,
|
166
|
+
observer: observer,
|
167
|
+
ephem: ephem
|
168
|
+
)
|
169
|
+
|
170
|
+
event = calculator.event_on(date)
|
171
|
+
|
172
|
+
event.rising_time
|
173
|
+
# => 2025-04-24 04:45:42 UTC
|
174
|
+
|
175
|
+
event.transit_time
|
176
|
+
# => 2025-04-24 11:50:04 UTC
|
177
|
+
|
178
|
+
event.setting_time
|
179
|
+
# => 2025-04-24 18:55:24 UTC
|
180
|
+
```
|
181
|
+
|
182
|
+
## Drop of `RiseTransitSetIteration`
|
183
|
+
|
184
|
+
`RiseTransitSetIteration` has been removed as it was only used by
|
185
|
+
`Events::ObservationEvents`.
|
186
|
+
|
187
|
+
## Drop of `Events::TwilightEvents`
|
188
|
+
|
189
|
+
`Events::TwilightEvents` has been removed. The twilight times can now be
|
190
|
+
calculated using `TwilightCalculator`.
|
191
|
+
|
192
|
+
```rb
|
193
|
+
ephem = Astronoby::Ephem.load("inpop19a.bsp")
|
194
|
+
observer = Astronoby::Observer.new(
|
195
|
+
latitude: Astronoby::Angle.from_degrees(48),
|
196
|
+
longitude: Astronoby::Angle.from_degrees(2)
|
197
|
+
)
|
198
|
+
date = Date.new(2025, 4, 24)
|
199
|
+
|
200
|
+
calculator = Astronoby::TwilightCalculator.new(
|
201
|
+
observer: observer,
|
202
|
+
ephem: ephem
|
203
|
+
)
|
204
|
+
|
205
|
+
event = calculator.event_on(date)
|
206
|
+
# Returns a Astronoby::TwilightEvent object
|
207
|
+
|
208
|
+
event.morning_astronomical_twilight_time
|
209
|
+
# => 2025-04-24 02:43:38 UTC
|
210
|
+
|
211
|
+
event.morning_nautical_twilight_time
|
212
|
+
# => 2025-04-24 03:30:45 UTC
|
213
|
+
|
214
|
+
event.morning_civil_twilight_time
|
215
|
+
# => 2025-04-24 04:12:38 UTC
|
216
|
+
|
217
|
+
event.evening_civil_twilight_time
|
218
|
+
# => 2025-04-24 19:27:34 UTC
|
219
|
+
|
220
|
+
event.evening_nautical_twilight_time
|
221
|
+
# => 2025-04-24 20:09:27 UTC
|
222
|
+
|
223
|
+
event.evening_astronomical_twilight_time
|
224
|
+
# => 2025-04-24 20:56:34 UTC
|
225
|
+
```
|
226
|
+
|
227
|
+
## Drop of `EphemerideLunaireParisienne`
|
228
|
+
|
229
|
+
`EphemerideLunaireParisienne` has been removed.
|
230
|
+
|
231
|
+
## Drop of `Util::Astrodynamics`
|
232
|
+
|
233
|
+
`Util::Astrodynamics` has been removed.
|
234
|
+
|
235
|
+
## Drop of `Util::Maths.interpolate` and `Util::Maths.normalize_angles_for_interpolation`
|
236
|
+
|
237
|
+
`Util::Maths.interpolate` and `Util::Maths.normalize_angles_for_interpolation`
|
238
|
+
have been removed.
|
239
|
+
|
240
|
+
## Drop of `Constants::SECONDS_PER_DEGREE`
|
241
|
+
|
242
|
+
`Constants::SECONDS_PER_DEGREE` has been removed.
|
243
|
+
|
244
|
+
## Upgrading from 0.5.0 to 0.6.0
|
245
|
+
|
246
|
+
No breaking changes.
|
247
|
+
|
10
248
|
## Upgrading from 0.4.0 to 0.5.0
|
11
249
|
|
12
250
|
### `Sun#horizontal_coordinates` method signature changed ([#69])
|
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
|