astronoby 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d5ac807296bab40ecf868557f3a6a7a1022360f78860b46332e87e5a2fd2697
4
- data.tar.gz: 26682e9092af30150eff0b47edb5a1476341f0d133c39ced8754148e6a4b0927
3
+ metadata.gz: 1c5bcfe7077b3772243f90a9c8a9ac8b0b0d90fdce2c777d6f05ab88062bc94c
4
+ data.tar.gz: ecd3bbbbcf699c1bde1b2fd6a92ccd7dc45ed1839cb3e6e6549ddb2d52002e48
5
5
  SHA512:
6
- metadata.gz: 5ae6f829d9c23476edd3c24411426a2c8c1aa6e432c19ee999889b01d9b7843476ea14c163bd083659e768a12a6b772cf46488dfebfa5c5c8f7449a2580bc420
7
- data.tar.gz: 54e3f4ce4090c120b3156d4429fb3fbc6c2830ec816355931d8b416b734210a4d82af3ef5c058a4795b27e753b748f524a0419c2f3696603c5b05ee2b60a71cd
6
+ metadata.gz: a97c4ee597dbcd5b3adbb9da8402dd95d413d120e51c263675a8019e839496168d4a893c473b6dbf1687463eb8e2f4749b13a9acab1205488944ae49f160de83
7
+ data.tar.gz: 70b0f12a52a668e1f3948a20fb5fe38b59e42ed0531381b8a5a03d130d1e64b2e7ba2c56806982d1f0e667dc47776a54609d7a35a5b067594b78295a440cf020
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.2
data/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
- ## [0.0.1] - 2022-04-20
3
+ ## 0.1.0 - 2024-02-28
4
4
 
5
- - Add `Astronoby::Angle`
6
- - Support angles in degrees and radians
5
+ ### Features
6
+
7
+ * Support angles in hours
8
+ * Support coordinates
9
+ * `Astronoby::Coordinates::Horizontal`
10
+ * `Astronoby::Coordinates::Equatorial`
11
+ * `Astronoby::Coordinates::Ecliptic`
12
+ * Add new body `Astronoby::Sun`
13
+ * Add `Astronoby::Aberration`
14
+ * Add `Astronoby::Epoch`
15
+ * Add `Astronoby::MeanObliquity`
16
+ * Add `Astronoby::TrueObliquity`
17
+ * Add `Astronoby::Nutation`
18
+ * Add `Astronoby::Precession`
19
+ * Add `Astronoby::Refraction`
20
+ * Add utils
21
+ * `Astronoby::Util::Astrodynamics`
22
+ * `Astronoby::Util::Time`
23
+ * `Astronoby::Util::Trigonometry`
24
+
25
+ ## 0.0.1 - 2022-04-20
26
+
27
+ * Add `Astronoby::Angle`
28
+ * Support angles in degrees and radians
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,86 @@
1
+ # Contributing to Astronoby
2
+
3
+ By participating in this project, you agree to follow the [code of conduct].
4
+
5
+ [code of conduct]: https://github.com/rhannequin/astronoby/blob/main/CODE_OF_CONDUCT.md
6
+
7
+ Here are some ways *you* can contribute:
8
+
9
+ * by reporting bugs
10
+ * by suggesting new features
11
+ * by writing or editing documentation
12
+ * by writing specifications
13
+ * by writing code (**no patch is too small** : fix typos, add comments, etc.)
14
+ * by refactoring code
15
+ * by closing [issues]
16
+ * by reviewing patches
17
+
18
+ [issues]: https://github.com/rhannequin/astronoby/issues
19
+
20
+ ## Submitting an Issue
21
+
22
+ * We use the [GitHub issue tracker][issues] to track bugs and features.
23
+ * Before submitting a bug report or feature request, check to make sure it hasn't
24
+ already been submitted.
25
+ * When submitting a bug report, please include any other details that may be
26
+ necessary to reproduce the bug, including your gem version, Ruby version, and
27
+ operating system.
28
+
29
+ ## Cleaning up issues
30
+
31
+ * Issues that have no response from the submitter will be closed after 30 days.
32
+ * Issues will be closed once they're assumed to be fixed or answered. If the
33
+ maintainer is wrong, it can be opened again.
34
+ * If your issue is closed by mistake, please understand and explain the issue.
35
+ We will happily reopen the issue.
36
+
37
+ ## Submitting a Pull Request
38
+
39
+ 1. [Fork][fork] the [official repository][repo].
40
+ 1. [Create a topic branch.][branch]
41
+ 1. Implement your feature or bug fix.
42
+ 1. Add, commit, and push your changes.
43
+ 1. [Submit a pull request.][pr]
44
+
45
+ ### Notes
46
+
47
+ * Please add tests if you changed code. Contributions without tests won't be accepted.
48
+ * If you don't know how to add tests, please put in a PR and leave a comment
49
+ asking for help. We love helping!
50
+ * Please don't update the Gem version.
51
+
52
+ ## Setting up
53
+
54
+ ```sh
55
+ bin/setup
56
+ ```
57
+
58
+ ## Running the test suite
59
+
60
+ The default rake task will run the full test suite:
61
+
62
+ ```sh
63
+ bundle exec rake
64
+ ```
65
+
66
+ To run an individual rspec test, you can provide a path and line number:
67
+
68
+ ```sh
69
+ bundle exec rspec spec/path/to/spec.rb:123
70
+ ```
71
+
72
+ ## Formatting
73
+
74
+ Use [standard] to automatically format your code:
75
+
76
+ ```sh
77
+ bundle exec standardrb --fix
78
+ ```
79
+
80
+ [repo]: https://github.com/rhannequin/astronoby/tree/main
81
+ [fork]: https://help.github.com/articles/fork-a-repo/
82
+ [branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/
83
+ [pr]: https://help.github.com/articles/using-pull-requests/
84
+ [standard]: https://github.com/testdouble/standard
85
+
86
+ Inspired by https://github.com/thoughtbot/factory_bot/blob/main/CONTRIBUTING.md
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- astronoby (0.0.1)
4
+ astronoby (0.1.0)
5
+ matrix (~> 0.4.2)
5
6
  rake (~> 13.0)
6
7
  rspec (~> 3.0)
7
8
 
@@ -10,53 +11,67 @@ GEM
10
11
  specs:
11
12
  ast (2.4.2)
12
13
  diff-lcs (1.5.0)
13
- parallel (1.22.1)
14
- parser (3.1.2.0)
14
+ json (2.6.3)
15
+ language_server-protocol (3.17.0.3)
16
+ lint_roller (1.0.0)
17
+ matrix (0.4.2)
18
+ parallel (1.23.0)
19
+ parser (3.2.2.3)
15
20
  ast (~> 2.4.1)
16
- prettier (2.1.0)
21
+ racc
22
+ racc (1.7.0)
17
23
  rainbow (3.1.1)
18
- rake (13.0.6)
19
- regexp_parser (2.3.0)
24
+ rake (13.1.0)
25
+ regexp_parser (2.8.1)
20
26
  rexml (3.2.5)
21
- rspec (3.11.0)
22
- rspec-core (~> 3.11.0)
23
- rspec-expectations (~> 3.11.0)
24
- rspec-mocks (~> 3.11.0)
25
- rspec-core (3.11.0)
26
- rspec-support (~> 3.11.0)
27
- rspec-expectations (3.11.0)
27
+ rspec (3.12.0)
28
+ rspec-core (~> 3.12.0)
29
+ rspec-expectations (~> 3.12.0)
30
+ rspec-mocks (~> 3.12.0)
31
+ rspec-core (3.12.2)
32
+ rspec-support (~> 3.12.0)
33
+ rspec-expectations (3.12.3)
28
34
  diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.11.0)
30
- rspec-mocks (3.11.1)
35
+ rspec-support (~> 3.12.0)
36
+ rspec-mocks (3.12.6)
31
37
  diff-lcs (>= 1.2.0, < 2.0)
32
- rspec-support (~> 3.11.0)
33
- rspec-support (3.11.0)
34
- rubocop (1.27.0)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-support (3.12.1)
40
+ rubocop (1.52.0)
41
+ json (~> 2.3)
35
42
  parallel (~> 1.10)
36
- parser (>= 3.1.0.0)
43
+ parser (>= 3.2.0.0)
37
44
  rainbow (>= 2.2.2, < 4.0)
38
45
  regexp_parser (>= 1.8, < 3.0)
39
- rexml
40
- rubocop-ast (>= 1.16.0, < 2.0)
46
+ rexml (>= 3.2.5, < 4.0)
47
+ rubocop-ast (>= 1.28.0, < 2.0)
41
48
  ruby-progressbar (~> 1.7)
42
- unicode-display_width (>= 1.4.0, < 3.0)
43
- rubocop-ast (1.17.0)
44
- parser (>= 3.1.1.0)
45
- rubocop-performance (1.13.3)
49
+ unicode-display_width (>= 2.4.0, < 3.0)
50
+ rubocop-ast (1.29.0)
51
+ parser (>= 3.2.1.0)
52
+ rubocop-performance (1.18.0)
46
53
  rubocop (>= 1.7.0, < 2.0)
47
54
  rubocop-ast (>= 0.4.0)
48
- ruby-progressbar (1.11.0)
49
- standard (1.10.0)
50
- rubocop (= 1.27.0)
51
- rubocop-performance (= 1.13.3)
52
- unicode-display_width (2.1.0)
55
+ ruby-progressbar (1.13.0)
56
+ standard (1.29.0)
57
+ language_server-protocol (~> 3.17.0.2)
58
+ lint_roller (~> 1.0)
59
+ rubocop (~> 1.52.0)
60
+ standard-custom (~> 1.0.0)
61
+ standard-performance (~> 1.1.0)
62
+ standard-custom (1.0.1)
63
+ lint_roller (~> 1.0)
64
+ standard-performance (1.1.0)
65
+ lint_roller (~> 1.0)
66
+ rubocop-performance (~> 1.18.0)
67
+ unicode-display_width (2.4.2)
53
68
 
54
69
  PLATFORMS
70
+ ruby
55
71
  x86_64-linux
56
72
 
57
73
  DEPENDENCIES
58
74
  astronoby!
59
- prettier (~> 2.0)
60
75
  standard (~> 1.3)
61
76
 
62
77
  BUNDLED WITH
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
- # Astronoby ![specs](https://github.com/rhannequin/astronoby/workflows/Ruby/badge.svg)
1
+ # Astronoby
2
2
 
3
- Ruby library to provide a useful API to compute astronomical calculations, based on _Astronomical Algorithms_ by Jean Meeus.
3
+ [![Tests](https://github.com/rhannequin/astronoby/workflows/Ruby/badge.svg)](https://github.com/rhannequin/astronoby/actions?query=workflow%3ARuby)
4
+
5
+ Ruby library to provide a useful API to compute astronomical calculations, based
6
+ on astrometry books.
7
+
8
+ The main reference is:
9
+ - _Astronomical Algorithms_ by Jean Meeus
10
+ - _Celestial Calculations_ by J. L. Lawrence
11
+ - _Practical Astronomy with your Calculator or Spreadsheet_ by Peter
12
+ Duffet-Smith and Jonathan Zwart
4
13
 
5
14
  ## Installation
6
15
 
@@ -8,28 +17,76 @@ Install the gem and add to the application's Gemfile by executing:
8
17
 
9
18
  $ bundle add astronoby
10
19
 
11
- If bundler is not being used to manage dependencies, install the gem by executing:
20
+ If bundler is not being used to manage dependencies, install the gem by
21
+ executing:
12
22
 
13
23
  $ gem install astronoby
14
24
 
15
25
  ## Usage
16
26
 
17
- Library still heavily in development.
27
+ This library is still in heavy development. The following API is likely to
28
+ change any time.
29
+
30
+ ```rb
31
+ time = Time.utc(2023, 2, 17, 11, 0, 0)
32
+ epoch = Astronoby::Epoch.from_time(time)
33
+
34
+ latitude = Astronoby::Angle.as_degrees(48.8566)
35
+ longitude = Astronoby::Angle.as_degrees(2.3522)
36
+
37
+ sun = Astronoby::Sun.new(epoch: epoch)
38
+
39
+ horizontal_coordinates = sun.horizontal_coordinates(
40
+ latitude: latitude,
41
+ longitude: longitude
42
+ )
43
+
44
+ horizontal_coordinates.altitude.degrees.to_f
45
+ # => 27.502365130176567
46
+
47
+ horizontal_coordinates.altitude.str(:dms)
48
+ # => "+27° 30′ 8.5144″"
49
+ ```
50
+
51
+ ## Precision
52
+
53
+ The current precision for the Sun's apparent location in the sky, compared
54
+ to values computed by the [IMCCE] is approximately 1 arc minute. It corresponds
55
+ to twice the apparent size of Jupiter when it is the closest to Earth.
56
+
57
+ While the precision is not enough for very high accuracy computations, it is
58
+ equal to the Human naked eye's angular resolution.
59
+
60
+ [IMCCE]: https://www.imcce.fr
18
61
 
19
62
  ## Development
20
63
 
21
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
64
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
65
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
66
+ prompt that will allow you to experiment.
22
67
 
23
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
68
+ To install this gem onto your local machine, run `bundle exec rake install`. To
69
+ release a new version, update the version number in `version.rb`, and then run
70
+ `bundle exec rake release`, which will create a git tag for the version, push
71
+ git commits and the created tag, and push the `.gem` file to [rubygems.org].
72
+
73
+ [rubygems.org]: https://rubygems.org
24
74
 
25
75
  ## Contributing
26
76
 
27
- Bug reports and pull requests are welcome on GitHub at https://github.com/rhannequin/astronoby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rhannequin/astronoby/blob/main/CODE_OF_CONDUCT.md).
77
+ Please see [CONTRIBUTING.md](https://github.com/rhannequin/astronoby/blob/main/CONTRIBUTING.md).
78
+
79
+ [code of conduct]: https://github.com/rhannequin/astronoby/blob/main/CODE_OF_CONDUCT.md
28
80
 
29
81
  ## License
30
82
 
31
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
83
+ The gem is available as open source under the terms of the [MIT License].
84
+
85
+ [MIT License]: https://opensource.org/licenses/MIT
32
86
 
33
87
  ## Code of Conduct
34
88
 
35
- Everyone interacting in the Astronoby project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rhannequin/astronoby/blob/main/CODE_OF_CONDUCT.md).
89
+ Everyone interacting in the Astronoby project's codebases, issue trackers, chat
90
+ rooms and mailing lists is expected to follow the [code of conduct].
91
+
92
+ [code of conduct]: https://github.com/rhannequin/astronoby/blob/main/CODE_OF_CONDUCT.md
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Aberration
5
+ def self.for_ecliptic_coordinates(coordinates:, epoch:)
6
+ new(coordinates, epoch).apply
7
+ end
8
+
9
+ def initialize(coordinates, epoch)
10
+ @coordinates = coordinates
11
+ @epoch = epoch
12
+ end
13
+
14
+ # Source:
15
+ # Title: Practical Astronomy with your Calculator or Spreadsheet
16
+ # Authors: Peter Duffett-Smith and Jonathan Zwart
17
+ # Edition: Cambridge University Press
18
+ # Chapter: 36 - Aberration
19
+ def apply
20
+ delta_longitude = Angle.as_degrees(
21
+ -20.5 * (
22
+ sun_longitude - @coordinates.longitude
23
+ ).cos / @coordinates.latitude.cos / 3600
24
+ )
25
+
26
+ delta_latitude = Angle.as_degrees(
27
+ -20.5 *
28
+ (sun_longitude - @coordinates.longitude).sin *
29
+ @coordinates.latitude.sin / 3600
30
+ )
31
+
32
+ Coordinates::Ecliptic.new(
33
+ latitude: @coordinates.latitude + delta_latitude,
34
+ longitude: @coordinates.longitude + delta_longitude
35
+ )
36
+ end
37
+
38
+ def sun_longitude
39
+ @_sun_longitude ||= Sun.new(epoch: @epoch).ecliptic_coordinates.longitude
40
+ end
41
+ end
42
+ end
@@ -4,39 +4,173 @@ require "bigdecimal/math"
4
4
 
5
5
  module Astronoby
6
6
  class Angle
7
- UNITS = [
8
- DEGREES = :degrees,
9
- RADIANS = :radians
10
- ].freeze
7
+ PRECISION = 14
8
+ PI = BigMath.PI(PRECISION)
9
+ PI_IN_DEGREES = BigDecimal("180")
11
10
 
12
- UNIT_CLASS_NAMES = {
13
- DEGREES => "Astronoby::Degree",
14
- RADIANS => "Astronoby::Radian"
15
- }
11
+ FULL_CIRCLE_IN_RADIANS = (2 * PI)
16
12
 
17
- PI = BigMath.PI(10)
13
+ RADIAN_PER_HOUR = PI / BigDecimal("12")
14
+ MINUTES_PER_DEGREE = BigDecimal("60")
15
+ MINUTES_PER_HOUR = BigDecimal("60")
16
+ SECONDS_PER_MINUTE = BigDecimal("60")
17
+ SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE
18
+
19
+ FORMATS = %i[dms hms].freeze
18
20
 
19
21
  class << self
20
- UNIT_CLASS_NAMES.each do |unit, class_name|
21
- define_method("as_#{unit}") do |angle|
22
- Kernel.const_get(class_name).new(angle)
23
- end
22
+ def zero
23
+ new(0)
24
+ end
25
+
26
+ def as_radians(radians)
27
+ normalized_radians = radians.remainder(FULL_CIRCLE_IN_RADIANS)
28
+ new(normalized_radians)
29
+ end
30
+
31
+ def as_degrees(degrees)
32
+ radians = degrees / PI_IN_DEGREES * PI
33
+ as_radians(radians)
34
+ end
35
+
36
+ def as_hours(hours)
37
+ radians = hours * RADIAN_PER_HOUR
38
+ as_radians(radians)
39
+ end
40
+
41
+ def as_hms(hour, minute, second)
42
+ hours = hour + minute / MINUTES_PER_HOUR + second / SECONDS_PER_HOUR
43
+ as_hours(hours)
44
+ end
45
+
46
+ def as_dms(degree, minute, second)
47
+ sign = degree.negative? ? -1 : 1
48
+ degrees = degree.abs + minute / MINUTES_PER_HOUR + second / SECONDS_PER_HOUR
49
+ as_degrees(sign * degrees)
50
+ end
51
+
52
+ def asin(ratio)
53
+ radians = Math.asin(ratio)
54
+ as_radians(radians)
24
55
  end
56
+
57
+ def acos(ratio)
58
+ radians = Math.acos(ratio)
59
+ as_radians(radians)
60
+ end
61
+
62
+ def atan(ratio)
63
+ radians = Math.atan(ratio)
64
+ as_radians(radians)
65
+ end
66
+ end
67
+
68
+ attr_reader :radians
69
+
70
+ def initialize(radians)
71
+ @radians = if radians.is_a?(Integer) || radians.is_a?(BigDecimal)
72
+ BigDecimal(radians)
73
+ else
74
+ BigDecimal(radians, PRECISION)
75
+ end
76
+ freeze
77
+ end
78
+
79
+ def degrees
80
+ @radians * PI_IN_DEGREES / PI
81
+ end
82
+
83
+ def hours
84
+ @radians / RADIAN_PER_HOUR
25
85
  end
26
86
 
27
- UNITS.each do |unit|
28
- define_method("to_#{unit}") do
29
- raise NotImplementedError, "#{self.class} must implement #to_#{unit} method."
87
+ def +(other)
88
+ self.class.as_radians(radians + other.radians)
89
+ end
90
+
91
+ def -(other)
92
+ self.class.as_radians(@radians - other.radians)
93
+ end
94
+
95
+ def sin
96
+ Math.sin(radians)
97
+ end
98
+
99
+ def cos
100
+ Math.cos(radians)
101
+ end
102
+
103
+ def tan
104
+ Math.tan(radians)
105
+ end
106
+
107
+ def positive?
108
+ radians > 0
109
+ end
110
+
111
+ def negative?
112
+ radians < 0
113
+ end
114
+
115
+ def zero?
116
+ radians.zero?
117
+ end
118
+
119
+ def ==(other)
120
+ other.is_a?(self.class) && radians == other.radians
121
+ end
122
+ alias_method :eql?, :==
123
+
124
+ def hash
125
+ [radians, self.class].hash
126
+ end
127
+
128
+ def <=>(other)
129
+ return nil unless other.is_a?(self.class)
130
+
131
+ radians <=> other.radians
132
+ end
133
+
134
+ def str(format)
135
+ case format
136
+ when :dms then to_dms(degrees).format
137
+ when :hms then to_hms(hours).format
138
+ else
139
+ raise UnsupportedFormatError.new(
140
+ "Expected a format between #{FORMATS.join(", ")}, got #{format}"
141
+ )
30
142
  end
31
143
  end
32
144
 
33
- def initialize(angle, unit:)
34
- @angle = BigDecimal(angle)
35
- @unit = unit
145
+ def to_dms(deg)
146
+ sign = deg.negative? ? "-" : "+"
147
+ absolute_degrees = deg.abs
148
+ degrees = absolute_degrees.floor
149
+ decimal_minutes = MINUTES_PER_DEGREE * (absolute_degrees - degrees)
150
+ absolute_decimal_minutes = (
151
+ MINUTES_PER_DEGREE * (absolute_degrees - degrees)
152
+ ).abs
153
+ minutes = decimal_minutes.floor
154
+ seconds = SECONDS_PER_MINUTE * (
155
+ absolute_decimal_minutes - absolute_decimal_minutes.floor
156
+ )
157
+
158
+ Dms.new(sign, degrees, minutes, seconds.to_f.floor(4))
36
159
  end
37
160
 
38
- def value
39
- @angle
161
+ def to_hms(hrs)
162
+ absolute_hours = hrs.abs
163
+ hours = absolute_hours.floor
164
+ decimal_minutes = MINUTES_PER_HOUR * (absolute_hours - hours)
165
+ absolute_decimal_minutes = (
166
+ MINUTES_PER_HOUR * (absolute_hours - hours)
167
+ ).abs
168
+ minutes = decimal_minutes.floor
169
+ seconds = SECONDS_PER_MINUTE * (
170
+ absolute_decimal_minutes - absolute_decimal_minutes.floor
171
+ )
172
+
173
+ Hms.new(hours, minutes, seconds.to_f.floor(4))
40
174
  end
41
175
  end
42
176
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Dms
5
+ attr_reader :sign, :degrees, :minutes, :seconds
6
+
7
+ def initialize(sign, degrees, minutes, seconds)
8
+ @sign = sign
9
+ @degrees = degrees
10
+ @minutes = minutes
11
+ @seconds = seconds
12
+ end
13
+
14
+ def format
15
+ "#{sign}#{degrees}° #{minutes}′ #{seconds}″"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Astronoby
4
+ class Hms
5
+ attr_reader :hours, :minutes, :seconds
6
+
7
+ def initialize(hours, minutes, seconds)
8
+ @hours = hours
9
+ @minutes = minutes
10
+ @seconds = seconds
11
+ end
12
+
13
+ def format
14
+ "#{hours}h #{minutes}m #{seconds}s"
15
+ end
16
+ end
17
+ end