astronoby 0.4.0 → 0.5.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.
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class RiseTransitSetIteration
5
+ EARTH_SIDEREAL_ROTATION_RATE = 360.98564736629
6
+
7
+ # Source:
8
+ # Title: Astronomical Algorithms
9
+ # Author: Jean Meeus
10
+ # Edition: 2nd edition
11
+ # Chapter: 15 - Rising, Transit, and Setting
12
+
13
+ # @param observer [Astronoby::Observer] Observer
14
+ # @param date [Date] Date of the event
15
+ # @param coordinates_of_the_previous_day [Astronoby::Coordinates::Equatorial]
16
+ # Coordinates of the body of the previous day
17
+ # @param coordinates_of_the_day [Astronoby::Coordinates::Equatorial]
18
+ # Coordinates of the body of the day
19
+ # @param coordinates_of_the_next_day [Astronoby::Coordinates::Equatorial]
20
+ # Coordinates of the body of the next day
21
+ # @param shift [Astronoby::Angle] Altitude shift
22
+ # @param initial_rising [Float] Initial rising
23
+ # @param initial_transit [Float] Initial transit
24
+ # @param initial_setting [Float] Initial setting
25
+ def initialize(
26
+ observer:,
27
+ date:,
28
+ coordinates_of_the_previous_day:,
29
+ coordinates_of_the_day:,
30
+ coordinates_of_the_next_day:,
31
+ shift:,
32
+ initial_rising:,
33
+ initial_transit:,
34
+ initial_setting:
35
+ )
36
+ @observer = observer
37
+ @date = date
38
+ @coordinates_of_the_previous_day = coordinates_of_the_previous_day
39
+ @coordinates_of_the_day = coordinates_of_the_day
40
+ @coordinates_of_the_next_day = coordinates_of_the_next_day
41
+ @shift = shift
42
+ @initial_rising = initial_rising
43
+ @initial_transit = initial_transit
44
+ @initial_setting = initial_setting
45
+ end
46
+
47
+ # @return [Array<Float>] Iteration results
48
+ def iterate
49
+ [
50
+ delta_m_rising,
51
+ delta_m_transit,
52
+ delta_m_setting
53
+ ]
54
+ end
55
+
56
+ private
57
+
58
+ def delta_m_rising
59
+ (local_horizontal_altitude_rising - @shift).degrees./(
60
+ Constants::DEGREES_PER_CIRCLE *
61
+ declination_rising.cos *
62
+ @observer.latitude.cos *
63
+ local_hour_angle_rising.sin
64
+ )
65
+ end
66
+
67
+ def delta_m_transit
68
+ -local_hour_angle_transit.degrees / Constants::DEGREES_PER_CIRCLE
69
+ end
70
+
71
+ def delta_m_setting
72
+ (local_horizontal_altitude_setting - @shift).degrees./(
73
+ Constants::DEGREES_PER_CIRCLE *
74
+ declination_setting.cos *
75
+ @observer.latitude.cos *
76
+ local_hour_angle_setting.sin
77
+ )
78
+ end
79
+
80
+ def observer_longitude
81
+ # Longitude must be treated positively westwards from the meridian of
82
+ # Greenwich, and negatively to the east
83
+ @observer_longitude ||= -@observer.longitude
84
+ end
85
+
86
+ def apparent_gst_at_midnight
87
+ @apparent_gst_at_midnight ||= Angle.from_hours(
88
+ GreenwichSiderealTime.from_utc(
89
+ Time.utc(@date.year, @date.month, @date.day)
90
+ ).time
91
+ )
92
+ end
93
+
94
+ def gst_rising
95
+ @gst_rising ||= Angle.from_degrees(
96
+ apparent_gst_at_midnight.degrees +
97
+ EARTH_SIDEREAL_ROTATION_RATE * @initial_rising
98
+ )
99
+ end
100
+
101
+ def gst_transit
102
+ @gst_transit ||= Angle.from_degrees(
103
+ apparent_gst_at_midnight.degrees +
104
+ EARTH_SIDEREAL_ROTATION_RATE * @initial_transit
105
+ )
106
+ end
107
+
108
+ def gst_setting
109
+ @gst_setting ||= Angle.from_degrees(
110
+ apparent_gst_at_midnight.degrees +
111
+ EARTH_SIDEREAL_ROTATION_RATE * @initial_setting
112
+ )
113
+ end
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
+ def local_hour_angle_rising
123
+ @local_hour_angle_rising ||=
124
+ gst_rising - observer_longitude - right_ascension_rising
125
+ end
126
+
127
+ def local_hour_angle_transit
128
+ gst_transit - observer_longitude - right_ascension_transit
129
+ end
130
+
131
+ def local_hour_angle_setting
132
+ @local_hour_angle_setting ||=
133
+ gst_setting - observer_longitude - right_ascension_setting
134
+ end
135
+
136
+ def local_horizontal_altitude_rising
137
+ Angle.asin(
138
+ @observer.latitude.sin * declination_rising.sin +
139
+ @observer.latitude.cos * declination_rising.cos * local_hour_angle_rising.cos
140
+ )
141
+ end
142
+
143
+ def local_horizontal_altitude_setting
144
+ Angle.asin(
145
+ @observer.latitude.sin * declination_setting.sin +
146
+ @observer.latitude.cos * declination_setting.cos * local_hour_angle_setting.cos
147
+ )
148
+ end
149
+
150
+ def right_ascension_rising
151
+ Angle.from_degrees(
152
+ Util::Maths.interpolate(
153
+ [
154
+ @coordinates_of_the_previous_day.right_ascension.degrees,
155
+ @coordinates_of_the_day.right_ascension.degrees,
156
+ @coordinates_of_the_next_day.right_ascension.degrees
157
+ ],
158
+ @initial_rising + leap_day_portion
159
+ )
160
+ )
161
+ end
162
+
163
+ def right_ascension_transit
164
+ Angle.from_degrees(
165
+ Util::Maths.interpolate(
166
+ [
167
+ @coordinates_of_the_previous_day.right_ascension.degrees,
168
+ @coordinates_of_the_day.right_ascension.degrees,
169
+ @coordinates_of_the_next_day.right_ascension.degrees
170
+ ],
171
+ @initial_transit + leap_day_portion
172
+ )
173
+ )
174
+ end
175
+
176
+ def right_ascension_setting
177
+ Angle.from_degrees(
178
+ Util::Maths.interpolate(
179
+ [
180
+ @coordinates_of_the_previous_day.right_ascension.degrees,
181
+ @coordinates_of_the_day.right_ascension.degrees,
182
+ @coordinates_of_the_next_day.right_ascension.degrees
183
+ ],
184
+ @initial_setting + leap_day_portion
185
+ )
186
+ )
187
+ end
188
+
189
+ def declination_rising
190
+ Angle.from_degrees(
191
+ Util::Maths.interpolate(
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
+ @initial_rising + leap_day_portion
198
+ )
199
+ )
200
+ end
201
+
202
+ def declination_setting
203
+ Angle.from_degrees(
204
+ Util::Maths.interpolate(
205
+ [
206
+ @coordinates_of_the_previous_day.declination.degrees,
207
+ @coordinates_of_the_day.declination.degrees,
208
+ @coordinates_of_the_next_day.declination.degrees
209
+ ],
210
+ @initial_setting + leap_day_portion
211
+ )
212
+ )
213
+ end
214
+ end
215
+ end
@@ -8,104 +8,82 @@ module Astronoby
8
8
  # Edition: Cambridge University Press
9
9
  # Chapter: 39 - Calculating correction for parallax
10
10
 
11
- ASTRONOMICAL_UNIT_IN_METERS = 149_597_870_700
12
- EARTH_FLATTENING_CORRECTION = 0.996647
13
- EARTH_EQUATORIAL_RADIUS = 6378140.0
14
-
15
11
  # Equatorial horizontal parallax
16
- # @param distance [Numeric] Distance of the body from the center of the
17
- # Earth, in meters
12
+ # @param distance [Astronoby::Distance] Distance of the body from the center
13
+ # of the Earth
18
14
  # @return [Astronoby::Angle] Equatorial horizontal parallax angle
19
15
  def self.angle(distance:)
20
- distance_in_earth_radius = distance / EARTH_EQUATORIAL_RADIUS
21
- Angle.asin(1 / distance_in_earth_radius)
16
+ Angle.from_radians(Angle.from_dms(0, 0, 8.794).sin / distance.au)
22
17
  end
23
18
 
24
19
  # Correct equatorial coordinates with the equatorial horizontal parallax
25
- # @param latitude [Astronoby::Angle] Observer's latitude
26
- # @param longitude [Astronoby::Angle] Observer's longitude
27
- # @param elevation [Numeric] Observer's elevation above sea level in meters
20
+ # @param observer [Astronoby::Observer] Observer
28
21
  # @param time [Time] Date-time of the observation
29
22
  # @param coordinates [Astronoby::Coordinates::Equatorial]
30
23
  # Equatorial coordinates of the observed body
31
- # @param distance [Numeric] Distance of the observed body from the center of
32
- # the Earth, in meters
24
+ # @param distance [Astronoby::Distance] Distance of the observed body from
25
+ # the center of the Earth
33
26
  # @return [Astronoby::Coordinates::Equatorial] Apparent equatorial
34
27
  # coordinates with equatorial horizontal parallax
35
28
  def self.for_equatorial_coordinates(
36
- latitude:,
37
- longitude:,
38
- elevation:,
29
+ observer:,
39
30
  time:,
40
31
  coordinates:,
41
32
  distance:
42
33
  )
43
34
  new(
44
- latitude,
45
- longitude,
46
- elevation,
35
+ observer,
47
36
  time,
48
37
  coordinates,
49
38
  distance
50
39
  ).apply
51
40
  end
52
41
 
53
- # @param latitude [Astronoby::Angle] Observer's latitude
54
- # @param longitude [Astronoby::Angle] Observer's longitude
55
- # @param elevation [Numeric] Observer's elevation above sea level in meters
42
+ # @param observer [Astronoby::Observer] Observer
56
43
  # @param time [Time] Date-time of the observation
57
44
  # @param coordinates [Astronoby::Coordinates::Equatorial] Equatorial
58
45
  # coordinates of the observed body
59
- # @param distance [Numeric] Distance of the observed body from the center of
60
- # the Earth, in meters
46
+ # @param distance [Astronoby::Distance] Distance of the observed body from
47
+ # the center of the Earth
61
48
  def initialize(
62
- latitude,
63
- longitude,
64
- elevation,
49
+ observer,
65
50
  time,
66
51
  coordinates,
67
52
  distance
68
53
  )
69
- @latitude = latitude
70
- @longitude = longitude
71
- @elevation = elevation
54
+ @observer = observer
72
55
  @time = time
73
56
  @coordinates = coordinates
74
57
  @distance = distance
75
58
  end
76
59
 
77
60
  def apply
78
- term1 = Angle.atan(EARTH_FLATTENING_CORRECTION * @latitude.tan)
79
- quantity1 = term1.cos + elevation_ratio * @latitude.cos
80
- quantity2 = EARTH_FLATTENING_CORRECTION * term1.sin +
81
- elevation_ratio * @latitude.sin
82
-
83
- term2 = quantity1 * hour_angle.sin
84
- term3 = distance_in_earth_radius * declination.cos -
85
- quantity1 * hour_angle.cos
61
+ term1 = Angle.atan(Constants::EARTH_FLATTENING_CORRECTION * latitude.tan)
62
+ quantity1 = term1.cos + elevation_ratio * latitude.cos
63
+ quantity2 = Constants::EARTH_FLATTENING_CORRECTION * term1.sin +
64
+ elevation_ratio * latitude.sin
86
65
 
87
- delta = Angle.atan(term2 / term3)
66
+ term1 = -quantity1 * equatorial_horizontal_parallax.sin * hour_angle.sin
67
+ term2 = declination.cos - quantity1 * equatorial_horizontal_parallax.sin * hour_angle.cos
68
+ delta_right_ascension = Angle.atan(term1 / term2)
88
69
 
89
- apparent_hour_angle = hour_angle + delta
90
- apparent_right_ascension = right_ascension - delta
91
- apparent_declination = Angle.atan(
92
- (
93
- apparent_hour_angle.cos *
94
- (distance_in_earth_radius * declination.sin - quantity2)
95
- ) / (
96
- distance_in_earth_radius * declination.cos * hour_angle.cos - quantity1
97
- )
98
- )
70
+ term1 = (declination.sin - quantity2 * equatorial_horizontal_parallax.sin) * delta_right_ascension.cos
71
+ term2 = declination.cos - quantity1 * equatorial_horizontal_parallax.sin * hour_angle.cos
72
+ new_declination = Angle.atan(term1 / term2)
99
73
 
100
74
  Coordinates::Equatorial.new(
101
- right_ascension: apparent_right_ascension,
102
- declination: apparent_declination,
75
+ right_ascension: delta_right_ascension + right_ascension,
76
+ declination: new_declination,
103
77
  epoch: @coordinates.epoch
104
78
  )
105
79
  end
106
80
 
107
81
  private
108
82
 
83
+ def latitude
84
+ @observer.latitude
85
+ end
86
+
109
87
  def right_ascension
110
88
  @coordinates.right_ascension
111
89
  end
@@ -115,16 +93,18 @@ module Astronoby
115
93
  end
116
94
 
117
95
  def hour_angle
118
- @_hour_angle ||=
119
- @coordinates.compute_hour_angle(time: @time, longitude: @longitude)
96
+ @_hour_angle ||= @coordinates.compute_hour_angle(
97
+ time: @time,
98
+ longitude: @observer.longitude
99
+ )
120
100
  end
121
101
 
122
102
  def elevation_ratio
123
- @elevation / EARTH_EQUATORIAL_RADIUS
103
+ @observer.elevation.meters / Constants::EARTH_EQUATORIAL_RADIUS_IN_METERS.to_f
124
104
  end
125
105
 
126
- def distance_in_earth_radius
127
- @distance / EARTH_EQUATORIAL_RADIUS
106
+ def equatorial_horizontal_parallax
107
+ self.class.angle(distance: @distance)
128
108
  end
129
109
  end
130
110
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class MoonPhase
5
+ NEW_MOON = :new_moon
6
+ FIRST_QUARTER = :first_quarter
7
+ FULL_MOON = :full_moon
8
+ LAST_QUARTER = :last_quarter
9
+
10
+ attr_reader :time, :phase
11
+
12
+ # @param time [Time] Time of the Moon phase
13
+ # @return [Astronoby::MoonPhase] New Moon phase
14
+ def self.new_moon(time)
15
+ new(time: time, phase: NEW_MOON)
16
+ end
17
+
18
+ # @param time [Time] Time of the Moon phase
19
+ # @return [Astronoby::MoonPhase] First quarter Moon phase
20
+ def self.first_quarter(time)
21
+ new(time: time, phase: FIRST_QUARTER)
22
+ end
23
+
24
+ # @param time [Time] Time of the Moon phase
25
+ # @return [Astronoby::MoonPhase] Full Moon phase
26
+ def self.full_moon(time)
27
+ new(time: time, phase: FULL_MOON)
28
+ end
29
+
30
+ # @param time [Time] Time of the Moon phase
31
+ # @return [Astronoby::MoonPhase] Last quarter Moon phase
32
+ def self.last_quarter(time)
33
+ new(time: time, phase: LAST_QUARTER)
34
+ end
35
+
36
+ # @param time [Time] Time of the Moon phase
37
+ # @param phase [Symbol] Moon phase
38
+ def initialize(time:, phase:)
39
+ @time = time
40
+ @phase = phase
41
+ end
42
+ end
43
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Astronoby
4
4
  class Observer
5
- DEFAULT_ELEVATION = 0
5
+ DEFAULT_ELEVATION = Distance.zero
6
6
  DEFAULT_TEMPERATURE = 283.15
7
7
  PRESSURE_AT_SEA_LEVEL = 1013.25
8
8
  PASCAL_PER_MILLIBAR = 0.01
@@ -14,8 +14,8 @@ module Astronoby
14
14
 
15
15
  # @param latitude [Angle] geographic latitude of the observer
16
16
  # @param longitude [Angle] geographic longitude of the observer
17
- # @param elevation [Numeric] geographic elevation (or altitude) of the
18
- # observer above sea level in meters
17
+ # @param elevation [Astronoby::Distance] geographic elevation (or altitude)
18
+ # of the observer above sea level
19
19
  # @param temperature [Numeric] temperature at the observer's location in
20
20
  # kelvins
21
21
  # @param pressure [Numeric] atmospheric pressure at the observer's
@@ -67,7 +67,9 @@ module Astronoby
67
67
  # Barometric formula
68
68
  # https://en.wikipedia.org/wiki/Barometric_formula
69
69
  def pressure_ratio
70
- term1 = EARTH_GRAVITATIONAL_ACCELERATION * MOLAR_MASS_OF_AIR * @elevation
70
+ term1 = EARTH_GRAVITATIONAL_ACCELERATION *
71
+ MOLAR_MASS_OF_AIR *
72
+ @elevation.meters
71
73
  term2 = UNIVERSAL_GAS_CONSTANT * @temperature
72
74
 
73
75
  Math.exp(-term1 / term2)
@@ -5,17 +5,16 @@ module Astronoby
5
5
  LOW_ALTITUDE_BODY_ANGLE = Angle.from_degrees(15)
6
6
  ZENITH = Angle.from_degrees(90)
7
7
 
8
- def self.angle(coordinates:, observer:)
9
- new(coordinates, observer).refraction_angle
8
+ def self.angle(coordinates:)
9
+ new(coordinates).refraction_angle
10
10
  end
11
11
 
12
- def self.correct_horizontal_coordinates(coordinates:, observer:)
13
- new(coordinates, observer).refract
12
+ def self.correct_horizontal_coordinates(coordinates:)
13
+ new(coordinates).refract
14
14
  end
15
15
 
16
- def initialize(coordinates, observer)
16
+ def initialize(coordinates)
17
17
  @coordinates = coordinates
18
- @observer = observer
19
18
  end
20
19
 
21
20
  # Source:
@@ -27,8 +26,7 @@ module Astronoby
27
26
  Coordinates::Horizontal.new(
28
27
  azimuth: @coordinates.azimuth,
29
28
  altitude: @coordinates.altitude + refraction_angle,
30
- latitude: @coordinates.latitude,
31
- longitude: @coordinates.longitude
29
+ observer: @coordinates.observer
32
30
  )
33
31
  end
34
32
 
@@ -43,11 +41,11 @@ module Astronoby
43
41
  private
44
42
 
45
43
  def pressure
46
- @_pressure ||= @observer.pressure
44
+ @_pressure ||= @coordinates.observer.pressure
47
45
  end
48
46
 
49
47
  def temperature
50
- @_temperature ||= @observer.temperature
48
+ @_temperature ||= @coordinates.observer.temperature
51
49
  end
52
50
 
53
51
  def altitude_in_degrees
@@ -16,7 +16,7 @@ module Astronoby
16
16
  def interpolate(values, factor)
17
17
  unless factor.between?(0, 1)
18
18
  raise IncompatibleArgumentsError,
19
- "Interpolation factor must be between 0 and 1"
19
+ "Interpolation factor must be between 0 and 1, got #{factor}"
20
20
  end
21
21
 
22
22
  if values.length == 3
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/astronoby.rb CHANGED
@@ -4,7 +4,11 @@ require "astronoby/constants"
4
4
  require "astronoby/angle"
5
5
  require "astronoby/angles/dms"
6
6
  require "astronoby/angles/hms"
7
+ require "astronoby/distance"
7
8
  require "astronoby/epoch"
9
+ require "astronoby/astronomical_models/ephemeride_lunaire_parisienne"
10
+ require "astronoby/astronomical_models/moon_phases_periodic_terms"
11
+ require "astronoby/bodies/moon"
8
12
  require "astronoby/bodies/sun"
9
13
  require "astronoby/coordinates/ecliptic"
10
14
  require "astronoby/coordinates/equatorial"
@@ -12,10 +16,13 @@ require "astronoby/coordinates/horizontal"
12
16
  require "astronoby/aberration"
13
17
  require "astronoby/equinox_solstice"
14
18
  require "astronoby/errors"
19
+ require "astronoby/events/moon_phases"
15
20
  require "astronoby/events/observation_events"
21
+ require "astronoby/events/rise_transit_set_iteration"
16
22
  require "astronoby/events/twilight_events"
17
23
  require "astronoby/geocentric_parallax"
18
24
  require "astronoby/mean_obliquity"
25
+ require "astronoby/moon_phase"
19
26
  require "astronoby/nutation"
20
27
  require "astronoby/observer"
21
28
  require "astronoby/precession"
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.0
4
+ version: 0.5.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-04-29 00:00:00.000000000 Z
11
+ date: 2024-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: matrix
@@ -91,18 +91,25 @@ files:
91
91
  - lib/astronoby/angle.rb
92
92
  - lib/astronoby/angles/dms.rb
93
93
  - lib/astronoby/angles/hms.rb
94
+ - lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb
95
+ - lib/astronoby/astronomical_models/moon_phases_periodic_terms.rb
96
+ - lib/astronoby/bodies/moon.rb
94
97
  - lib/astronoby/bodies/sun.rb
95
98
  - lib/astronoby/constants.rb
96
99
  - lib/astronoby/coordinates/ecliptic.rb
97
100
  - lib/astronoby/coordinates/equatorial.rb
98
101
  - lib/astronoby/coordinates/horizontal.rb
102
+ - lib/astronoby/distance.rb
99
103
  - lib/astronoby/epoch.rb
100
104
  - lib/astronoby/equinox_solstice.rb
101
105
  - lib/astronoby/errors.rb
106
+ - lib/astronoby/events/moon_phases.rb
102
107
  - lib/astronoby/events/observation_events.rb
108
+ - lib/astronoby/events/rise_transit_set_iteration.rb
103
109
  - lib/astronoby/events/twilight_events.rb
104
110
  - lib/astronoby/geocentric_parallax.rb
105
111
  - lib/astronoby/mean_obliquity.rb
112
+ - lib/astronoby/moon_phase.rb
106
113
  - lib/astronoby/nutation.rb
107
114
  - lib/astronoby/observer.rb
108
115
  - lib/astronoby/precession.rb