astronoby 0.3.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.
@@ -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.from_degrees(angle.degrees + 360)
16
+ return Angle.from_degrees(
17
+ angle.degrees + Constants::DEGREES_PER_CIRCLE
18
+ )
19
19
  end
20
20
 
21
- Angle.from_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.3.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.3.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-29 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.from_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 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.from_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