astronoby 0.2.0 → 0.4.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.
@@ -3,11 +3,13 @@
3
3
  module Astronoby
4
4
  class GreenwichSiderealTime
5
5
  JULIAN_CENTURIES_EXPONENTS = [
6
- BigDecimal("6.697374558"),
7
- BigDecimal("2400.051336"),
8
- BigDecimal("0.000025862")
6
+ 6.697374558,
7
+ 2400.051336,
8
+ 0.000025862
9
9
  ].freeze
10
10
 
11
+ SIDEREAL_MINUTE_IN_UT_MINUTE = 0.9972695663
12
+
11
13
  attr_reader :date, :time
12
14
 
13
15
  # Source:
@@ -18,20 +20,20 @@ module Astronoby
18
20
  def self.from_utc(utc)
19
21
  date = utc.to_date
20
22
  julian_day = utc.to_date.ajd
21
- t = (julian_day - Epoch::J2000) / Epoch::DAYS_PER_JULIAN_CENTURY
23
+ t = (julian_day - Epoch::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
22
24
  t0 = (
23
25
  (JULIAN_CENTURIES_EXPONENTS[0] +
24
26
  (JULIAN_CENTURIES_EXPONENTS[1] * t) +
25
- (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % 24
27
+ (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
26
28
  ).abs
27
29
 
28
30
  ut_in_hours = utc.hour +
29
- utc.min / 60.0 +
30
- (utc.sec + utc.subsec) / 3600.0
31
+ utc.min / Constants::MINUTES_PER_HOUR +
32
+ (utc.sec + utc.subsec) / Constants::SECONDS_PER_HOUR
31
33
 
32
- gmst = BigDecimal("1.002737909") * ut_in_hours + t0
33
- gmst += 24 if gmst.negative?
34
- gmst -= 24 if gmst > 24
34
+ gmst = 1.002737909 * ut_in_hours + t0
35
+ gmst += Constants::HOURS_PER_DAY if gmst.negative?
36
+ gmst -= Constants::HOURS_PER_DAY if gmst > Constants::HOURS_PER_DAY
35
37
 
36
38
  new(date: date, time: gmst)
37
39
  end
@@ -49,38 +51,25 @@ module Astronoby
49
51
  def to_utc
50
52
  date = @date
51
53
  julian_day = @date.ajd
52
- t = (julian_day - Epoch::J2000) / Epoch::DAYS_PER_JULIAN_CENTURY
54
+ t = (julian_day - Epoch::J2000) / Constants::DAYS_PER_JULIAN_CENTURY
53
55
 
54
56
  t0 = (
55
57
  (JULIAN_CENTURIES_EXPONENTS[0] +
56
58
  (JULIAN_CENTURIES_EXPONENTS[1] * t) +
57
- (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % 24
59
+ (JULIAN_CENTURIES_EXPONENTS[2] * t * t)) % Constants::HOURS_PER_DAY
58
60
  ).abs
59
61
 
60
62
  a = @time - t0
61
- a += 24 if a.negative?
62
- a -= 24 if a > 24
63
+ a += Constants::HOURS_PER_DAY if a.negative?
64
+ a -= Constants::HOURS_PER_DAY if a > Constants::HOURS_PER_DAY
63
65
 
64
- utc = BigDecimal("0.9972695663") * a
66
+ utc = SIDEREAL_MINUTE_IN_UT_MINUTE * a
65
67
 
66
- decimal_hour_to_time(date, utc)
68
+ Util::Time.decimal_hour_to_time(date, utc)
67
69
  end
68
70
 
69
71
  def to_lst(longitude:)
70
72
  LocalSiderealTime.from_gst(gst: self, longitude: longitude)
71
73
  end
72
-
73
- private
74
-
75
- def decimal_hour_to_time(date, decimal)
76
- absolute_hour = decimal.abs
77
- hour = absolute_hour.floor
78
- decimal_minute = 60 * (absolute_hour - hour)
79
- absolute_decimal_minute = (60 * (absolute_hour - hour)).abs
80
- minute = decimal_minute.floor
81
- second = 60 * (absolute_decimal_minute - absolute_decimal_minute.floor)
82
-
83
- ::Time.utc(date.year, date.month, date.day, hour, minute, second).round
84
- end
85
74
  end
86
75
  end
@@ -12,8 +12,8 @@ module Astronoby
12
12
  def self.from_gst(gst:, longitude:)
13
13
  date = gst.date
14
14
  time = gst.time + longitude.hours
15
- time += 24 if time.negative?
16
- time -= 24 if time > 24
15
+ time += Constants::HOURS_PER_DAY if time.negative?
16
+ time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
17
17
 
18
18
  new(date: date, time: time, longitude: longitude)
19
19
  end
@@ -32,8 +32,8 @@ module Astronoby
32
32
  def to_gst
33
33
  date = @date
34
34
  time = @time - @longitude.hours
35
- time += 24 if time.negative?
36
- time -= 24 if time > 24
35
+ time += Constants::HOURS_PER_DAY if time.negative?
36
+ time -= Constants::HOURS_PER_DAY if time > Constants::HOURS_PER_DAY
37
37
 
38
38
  GreenwichSiderealTime.new(date: date, time: time)
39
39
  end
@@ -42,7 +42,7 @@ module Astronoby
42
42
  previous_solution &&
43
43
  (solution - previous_solution).abs <= precision
44
44
  )
45
- return Angle.as_radians(solution)
45
+ return Angle.from_radians(solution)
46
46
  end
47
47
 
48
48
  eccentric_anomaly_newton_raphson(
@@ -51,7 +51,7 @@ module Astronoby
51
51
  precision,
52
52
  maximum_iteration_count,
53
53
  current_iteration + 1,
54
- Angle.as_radians(solution)
54
+ Angle.from_radians(solution)
55
55
  )
56
56
  end
57
57
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ module Util
5
+ module Maths
6
+ class << self
7
+ # Source:
8
+ # Title: Astronomical Algorithms
9
+ # Author: Jean Meeus
10
+ # Edition: 2nd edition
11
+ # Chapter: 3 - Interpolation
12
+
13
+ # @param values [Array<Numeric>] First term
14
+ # @param factor [Numeric] Interpolation factor
15
+ # @return [Float] Interpolated value
16
+ def interpolate(values, factor)
17
+ unless factor.between?(0, 1)
18
+ raise IncompatibleArgumentsError,
19
+ "Interpolation factor must be between 0 and 1"
20
+ end
21
+
22
+ if values.length == 3
23
+ return interpolate_3_terms(values, factor)
24
+ elsif values.length == 5
25
+ return interpolate_5_terms(values, factor)
26
+ end
27
+
28
+ raise IncompatibleArgumentsError,
29
+ "Only 3 or 5 terms are supported for interpolation"
30
+ end
31
+
32
+ private
33
+
34
+ # @return [Float] Interpolated value
35
+ def interpolate_3_terms(terms, factor)
36
+ y1, y2, y3 = terms
37
+
38
+ a = y2 - y1
39
+ b = y3 - y2
40
+ c = b - a
41
+
42
+ y2 + (factor / 2.0) * (a + b + factor * c)
43
+ end
44
+
45
+ # @return [Float] Interpolated value
46
+ def interpolate_5_terms(terms, factor)
47
+ y1, y2, y3, y4, y5 = terms
48
+
49
+ a = y2 - y1
50
+ b = y3 - y2
51
+ c = y4 - y3
52
+ d = y5 - y4
53
+
54
+ e = b - a
55
+ f = c - b
56
+ g = d - c
57
+
58
+ h = f - e
59
+ j = g - f
60
+
61
+ k = j - h
62
+
63
+ y3 +
64
+ factor * ((b + c) / 2.0 - (h + j) / 12.0) +
65
+ factor**2 * (f / 2.0 - k / 24.0) +
66
+ factor**3 * (h + j) / 12.0 +
67
+ factor**4 * k / 24.0
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ module Util
5
+ module Time
6
+ # @param date [Date]
7
+ # @param decimal [Numeric] Hour of the day, in decimal hours
8
+ # @return [::Time] Date and time
9
+ def self.decimal_hour_to_time(date, decimal)
10
+ absolute_hour = decimal.abs
11
+ hour = absolute_hour.floor
12
+
13
+ unless hour.between?(0, Constants::HOURS_PER_DAY)
14
+ raise(
15
+ IncompatibleArgumentsError,
16
+ "Hour must be between 0 and #{Constants::HOURS_PER_DAY.to_i}, got #{hour}"
17
+ )
18
+ end
19
+
20
+ decimal_minute = Constants::MINUTES_PER_HOUR * (absolute_hour - hour)
21
+ absolute_decimal_minute = (
22
+ Constants::MINUTES_PER_HOUR * (absolute_hour - hour)
23
+ ).abs
24
+ minute = decimal_minute.floor
25
+ second = Constants::SECONDS_PER_MINUTE *
26
+ (absolute_decimal_minute - absolute_decimal_minute.floor)
27
+
28
+ ::Time.utc(date.year, date.month, date.day, hour, minute, second).round
29
+ end
30
+
31
+ # @param instant [Numeric, Time, Date, DateTime]
32
+ # @return [Integer, Float] Number of leap seconds for the given instant
33
+ def self.terrestrial_universal_time_delta(instant)
34
+ # Source:
35
+ # Title: Astronomical Algorithms
36
+ # Author: Jean Meeus
37
+ # Edition: 2nd edition
38
+ # Chapter: 10 - Dynamical Time and Universal Time
39
+
40
+ jd = case instant
41
+ when Numeric
42
+ instant
43
+ when ::Time, ::Date, ::DateTime
44
+ Epoch.from_time(instant)
45
+ else
46
+ raise IncompatibleArgumentsError,
47
+ "Expected a Numeric, Time, Date or DateTime object, got #{instant.class}"
48
+ end
49
+
50
+ return 69 if jd >= 2457754.5
51
+ return 68 if jd >= 2457204.5
52
+ return 67 if jd >= 2456109.5
53
+ return 66 if jd >= 2454832.5
54
+ return 65 if jd >= 2453736.5
55
+ return 64 if jd >= 2451179.5
56
+ return 63 if jd >= 2450814.5
57
+
58
+ theta = ((jd - Epoch::J1900) / 365.25) / 100.0
59
+ if (2415020.5...2450814.5).cover?(jd) # 1900 - 1997
60
+ return -2.44 +
61
+ 87.24 * theta +
62
+ 815.20 * theta**2 -
63
+ 2_637.80 * theta**3 -
64
+ 18_756.33 * theta**4 +
65
+ 124_906.15 * theta**5 -
66
+ 303_191.19 * theta**6 +
67
+ 372_919.88 * theta**7 -
68
+ 232_424.66 * theta**8 +
69
+ 58_353.42 * theta**9
70
+ elsif (2378496.5...2415020.5).cover?(jd) # 1800 - 1899
71
+ return -2.5 +
72
+ 228.95 * theta +
73
+ 5_218.61 * theta**2 +
74
+ 56_282.84 * theta**3 +
75
+ 324_011.78 * theta**4 +
76
+ 1_061_660.75 * theta**5 +
77
+ 2_087_298.89 * theta**6 +
78
+ 2_513_807.78 * theta**7 +
79
+ 1_818_961.41 * theta**8 +
80
+ 727_058.63 * theta**9 +
81
+ 123_563.95 * theta**10
82
+ end
83
+
84
+ 0.0
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bigdecimal/math"
4
-
5
3
  module Astronoby
6
4
  module Util
7
5
  module Trigonometry
@@ -15,10 +13,12 @@ module Astronoby
15
13
  return angle if y.positive? && x.positive?
16
14
 
17
15
  if y.negative? && x.positive?
18
- return Angle.as_degrees(angle.degrees + 360)
16
+ return Angle.from_degrees(
17
+ angle.degrees + Constants::DEGREES_PER_CIRCLE
18
+ )
19
19
  end
20
20
 
21
- Angle.as_degrees(angle.degrees + 180)
21
+ Angle.from_degrees(angle.degrees + Constants::DEGREES_PER_CIRCLE / 2)
22
22
  end
23
23
  end
24
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Astronoby
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/astronoby.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "astronoby/constants"
3
4
  require "astronoby/angle"
4
5
  require "astronoby/angles/dms"
5
6
  require "astronoby/angles/hms"
6
7
  require "astronoby/epoch"
7
- require "astronoby/body"
8
8
  require "astronoby/bodies/sun"
9
9
  require "astronoby/coordinates/ecliptic"
10
10
  require "astronoby/coordinates/equatorial"
@@ -12,6 +12,8 @@ require "astronoby/coordinates/horizontal"
12
12
  require "astronoby/aberration"
13
13
  require "astronoby/equinox_solstice"
14
14
  require "astronoby/errors"
15
+ require "astronoby/events/observation_events"
16
+ require "astronoby/events/twilight_events"
15
17
  require "astronoby/geocentric_parallax"
16
18
  require "astronoby/mean_obliquity"
17
19
  require "astronoby/nutation"
@@ -21,6 +23,8 @@ require "astronoby/refraction"
21
23
  require "astronoby/time/greenwich_sidereal_time"
22
24
  require "astronoby/time/local_sidereal_time"
23
25
  require "astronoby/util/astrodynamics"
26
+ require "astronoby/util/maths"
27
+ require "astronoby/util/time"
24
28
  require "astronoby/util/trigonometry"
25
29
  require "astronoby/true_obliquity"
26
30
  require "astronoby/version"
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.2.0
4
+ version: 0.4.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-03-24 00:00:00.000000000 Z
11
+ date: 2024-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: matrix
@@ -92,13 +92,15 @@ files:
92
92
  - lib/astronoby/angles/dms.rb
93
93
  - lib/astronoby/angles/hms.rb
94
94
  - lib/astronoby/bodies/sun.rb
95
- - lib/astronoby/body.rb
95
+ - lib/astronoby/constants.rb
96
96
  - lib/astronoby/coordinates/ecliptic.rb
97
97
  - lib/astronoby/coordinates/equatorial.rb
98
98
  - lib/astronoby/coordinates/horizontal.rb
99
99
  - lib/astronoby/epoch.rb
100
100
  - lib/astronoby/equinox_solstice.rb
101
101
  - lib/astronoby/errors.rb
102
+ - lib/astronoby/events/observation_events.rb
103
+ - lib/astronoby/events/twilight_events.rb
102
104
  - lib/astronoby/geocentric_parallax.rb
103
105
  - lib/astronoby/mean_obliquity.rb
104
106
  - lib/astronoby/nutation.rb
@@ -109,6 +111,8 @@ files:
109
111
  - lib/astronoby/time/local_sidereal_time.rb
110
112
  - lib/astronoby/true_obliquity.rb
111
113
  - lib/astronoby/util/astrodynamics.rb
114
+ - lib/astronoby/util/maths.rb
115
+ - lib/astronoby/util/time.rb
112
116
  - lib/astronoby/util/trigonometry.rb
113
117
  - lib/astronoby/version.rb
114
118
  homepage: https://github.com/rhannequin/astronoby
@@ -126,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
130
  requirements:
127
131
  - - ">="
128
132
  - !ruby/object:Gem::Version
129
- version: 3.2.0
133
+ version: 3.0.0
130
134
  required_rubygems_version: !ruby/object:Gem::Requirement
131
135
  requirements:
132
136
  - - ">="
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Astronoby
4
- class Body
5
- DEFAULT_REFRACTION_VERTICAL_SHIFT = Angle.as_dms(0, 34, 0)
6
- RISING_SETTING_HOUR_ANGLE_RATIO_RANGE = (-1..1)
7
-
8
- def initialize(equatorial_coordinates)
9
- @equatorial_coordinates = equatorial_coordinates
10
- end
11
-
12
- # Source:
13
- # Title: Practical Astronomy with your Calculator or Spreadsheet
14
- # Authors: Peter Duffett-Smith and Jonathan Zwart
15
- # Edition: Cambridge University Press
16
- # Chapter: 33 - Rising and setting
17
-
18
- # @param latitude [Astronoby::Angle] Latitude of the observer
19
- # @param longitude [Astronoby::Angle] Longitude of the observer
20
- # @param date [Date] Date of the event
21
- # @param apparent [Boolean] Compute apparent or true data
22
- # @param vertical_shift [Astronoby::Angle] Vertical shift correction angle
23
- # @return [Time, nil] Sunrise time
24
- def rising_time(
25
- latitude:,
26
- longitude:,
27
- date:,
28
- apparent: true,
29
- vertical_shift: nil
30
- )
31
- time_ratio = time_ratio(latitude, apparent, vertical_shift)
32
- return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio)
33
-
34
- hour_angle = Angle.acos(time_ratio)
35
- local_sidereal_time = LocalSiderealTime.new(
36
- date: date,
37
- time: right_ascension.hours - hour_angle.hours,
38
- longitude: longitude
39
- )
40
-
41
- local_sidereal_time.to_gst.to_utc
42
- end
43
-
44
- # Source:
45
- # Title: Practical Astronomy with your Calculator or Spreadsheet
46
- # Authors: Peter Duffett-Smith and Jonathan Zwart
47
- # Edition: Cambridge University Press
48
- # Chapter: 33 - Rising and setting
49
-
50
- # @param latitude [Astronoby::Angle] Latitude of the observer
51
- # @param apparent [Boolean] Compute apparent or true data
52
- # @param vertical_shift [Astronoby::Angle] Vertical shift correction angle
53
- # @return [Astronoby::Angle, nil] Sunrise azimuth
54
- def rising_azimuth(latitude:, apparent: true, vertical_shift: nil)
55
- time_ratio = time_ratio(latitude, apparent, vertical_shift)
56
- return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio)
57
-
58
- azimuth_ratio = azimuth_ratio(latitude, apparent, vertical_shift)
59
-
60
- Angle.acos(azimuth_ratio)
61
- end
62
-
63
- # Source:
64
- # Title: Practical Astronomy with your Calculator or Spreadsheet
65
- # Authors: Peter Duffett-Smith and Jonathan Zwart
66
- # Edition: Cambridge University Press
67
- # Chapter: 33 - Rising and setting
68
-
69
- # @param latitude [Astronoby::Angle] Latitude of the observer
70
- # @param longitude [Astronoby::Angle] Longitude of the observer
71
- # @param date [Date] Date of the event
72
- # @param apparent [Boolean] Compute apparent or true data
73
- # @param vertical_shift [Astronoby::Angle] Vertical shift correction angle
74
- # @return [Time, nil] Sunset time
75
- def setting_time(
76
- latitude:,
77
- longitude:,
78
- date:,
79
- apparent: true,
80
- vertical_shift: nil
81
- )
82
- time_ratio = time_ratio(latitude, apparent, vertical_shift)
83
- return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio)
84
-
85
- hour_angle = Angle.acos(time_ratio)
86
- local_sidereal_time = LocalSiderealTime.new(
87
- date: date,
88
- time: right_ascension.hours + hour_angle.hours,
89
- longitude: longitude
90
- )
91
-
92
- local_sidereal_time.to_gst.to_utc
93
- end
94
-
95
- # Source:
96
- # Title: Practical Astronomy with your Calculator or Spreadsheet
97
- # Authors: Peter Duffett-Smith and Jonathan Zwart
98
- # Edition: Cambridge University Press
99
- # Chapter: 33 - Rising and setting
100
-
101
- # @param latitude [Astronoby::Angle] Latitude of the observer
102
- # @param apparent [Boolean] Compute apparent or true data
103
- # @param vertical_shift [Astronoby::Angle] Vertical shift correction angle
104
- # @return [Astronoby::Angle, nil] Sunset azimuth
105
- def setting_azimuth(latitude:, apparent: true, vertical_shift: nil)
106
- time_ratio = time_ratio(latitude, apparent, vertical_shift)
107
- return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio)
108
-
109
- azimuth_ratio = azimuth_ratio(latitude, apparent, vertical_shift)
110
-
111
- Angle.as_degrees(360 - Angle.acos(azimuth_ratio).degrees)
112
- end
113
-
114
- private
115
-
116
- def time_ratio(latitude, apparent, vertical_shift)
117
- shift = if vertical_shift
118
- vertical_shift
119
- elsif apparent
120
- DEFAULT_REFRACTION_VERTICAL_SHIFT
121
- else
122
- Angle.zero
123
- end
124
-
125
- term1 = shift.sin + latitude.sin * declination.sin
126
- term2 = latitude.cos * declination.cos
127
-
128
- -term1 / term2
129
- end
130
-
131
- def azimuth_ratio(latitude, apparent, vertical_shift)
132
- shift = if vertical_shift
133
- vertical_shift
134
- elsif apparent
135
- DEFAULT_REFRACTION_VERTICAL_SHIFT
136
- else
137
- Angle.zero
138
- end
139
-
140
- (declination.sin + shift.sin * latitude.cos) / (shift.cos * latitude.cos)
141
- end
142
-
143
- def azimuth_component(latitude)
144
- declination.sin / latitude.cos
145
- end
146
-
147
- def right_ascension
148
- @equatorial_coordinates.right_ascension
149
- end
150
-
151
- def declination
152
- @equatorial_coordinates.declination
153
- end
154
- end
155
- end