astronoby 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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