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 +4 -4
- data/.tool-versions +1 -0
- data/CHANGELOG.md +26 -4
- data/CONTRIBUTING.md +86 -0
- data/Gemfile.lock +46 -31
- data/README.md +66 -9
- data/lib/astronoby/aberration.rb +42 -0
- data/lib/astronoby/angle.rb +155 -21
- data/lib/astronoby/angles/dms.rb +18 -0
- data/lib/astronoby/angles/hms.rb +17 -0
- data/lib/astronoby/bodies/sun.rb +81 -0
- data/lib/astronoby/body.rb +80 -0
- data/lib/astronoby/coordinates/ecliptic.rb +42 -0
- data/lib/astronoby/coordinates/equatorial.rb +89 -0
- data/lib/astronoby/coordinates/horizontal.rb +53 -0
- data/lib/astronoby/epoch.rb +24 -0
- data/lib/astronoby/errors.rb +5 -0
- data/lib/astronoby/mean_obliquity.rb +32 -0
- data/lib/astronoby/nutation.rb +71 -0
- data/lib/astronoby/precession.rb +86 -0
- data/lib/astronoby/refraction.rb +61 -0
- data/lib/astronoby/true_obliquity.rb +12 -0
- data/lib/astronoby/util/astrodynamics.rb +60 -0
- data/lib/astronoby/util/time.rb +93 -0
- data/lib/astronoby/util/trigonometry.rb +26 -0
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +19 -8
- metadata +40 -25
- data/.prettierrc +0 -11
- data/.standard.yml +0 -3
- data/astronoby.gemspec +0 -40
- data/lib/astronoby/angles/degree.rb +0 -17
- data/lib/astronoby/angles/radian.rb +0 -17
- data/sig/astronoby.rbs +0 -4
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Sun
|
5
|
+
# Source:
|
6
|
+
# Title: Celestial Calculations
|
7
|
+
# Author: J. L. Lawrence
|
8
|
+
# Edition: MIT Press
|
9
|
+
# Chapter: 6 - The Sun
|
10
|
+
|
11
|
+
def initialize(epoch:)
|
12
|
+
@epoch = epoch
|
13
|
+
end
|
14
|
+
|
15
|
+
def ecliptic_coordinates
|
16
|
+
Coordinates::Ecliptic.new(
|
17
|
+
latitude: Angle.zero,
|
18
|
+
longitude: Angle.as_degrees(
|
19
|
+
(true_anomaly + longitude_at_perigee).degrees % 360
|
20
|
+
)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def horizontal_coordinates(latitude:, longitude:)
|
25
|
+
time = Epoch.to_utc(@epoch)
|
26
|
+
|
27
|
+
ecliptic_coordinates
|
28
|
+
.to_equatorial(epoch: @epoch)
|
29
|
+
.to_horizontal(time: time, latitude: latitude, longitude: longitude)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def mean_anomaly
|
35
|
+
Angle.as_degrees(
|
36
|
+
(longitude_at_base_epoch - longitude_at_perigee).degrees % 360
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def true_anomaly
|
41
|
+
eccentric_anomaly = Util::Astrodynamics.eccentric_anomaly_newton_raphson(
|
42
|
+
mean_anomaly,
|
43
|
+
orbital_eccentricity.degrees,
|
44
|
+
2e-06,
|
45
|
+
10
|
46
|
+
)
|
47
|
+
|
48
|
+
tan = Math.sqrt(
|
49
|
+
(1 + orbital_eccentricity.degrees) / (1 - orbital_eccentricity.degrees)
|
50
|
+
) * Math.tan(eccentric_anomaly.radians / 2)
|
51
|
+
|
52
|
+
Angle.as_degrees((Angle.atan(tan).degrees * 2) % 360)
|
53
|
+
end
|
54
|
+
|
55
|
+
def days_since_epoch
|
56
|
+
Epoch::DEFAULT_EPOCH - @epoch
|
57
|
+
end
|
58
|
+
|
59
|
+
def centuries
|
60
|
+
@centuries ||= (@epoch - Epoch::J1900) / Epoch::DAYS_PER_JULIAN_CENTURY
|
61
|
+
end
|
62
|
+
|
63
|
+
def longitude_at_base_epoch
|
64
|
+
Angle.as_degrees(
|
65
|
+
(279.6966778 + 36000.76892 * centuries + 0.0003025 * centuries**2) % 360
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def longitude_at_perigee
|
70
|
+
Angle.as_degrees(
|
71
|
+
(281.2208444 + 1.719175 * centuries + 0.000452778 * centuries**2) % 360
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def orbital_eccentricity
|
76
|
+
Angle.as_degrees(
|
77
|
+
(0.01675104 - 0.0000418 * centuries - 0.000000126 * centuries**2) % 360
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Body
|
5
|
+
def initialize(equatorial_coordinates)
|
6
|
+
@equatorial_coordinates = equatorial_coordinates
|
7
|
+
end
|
8
|
+
|
9
|
+
# Source:
|
10
|
+
# Title: Celestial Calculations
|
11
|
+
# Author: J. L. Lawrence
|
12
|
+
# Edition: MIT Press
|
13
|
+
# Chapter: 5 - Stars in the Nighttime Sky
|
14
|
+
def rising_time(latitude:, longitude:, date:)
|
15
|
+
h2_component = h2(latitude: latitude)
|
16
|
+
return nil if h2_component.nil?
|
17
|
+
|
18
|
+
rising_lst = 24 +
|
19
|
+
@equatorial_coordinates.right_ascension.hours - h2_component.degrees
|
20
|
+
rising_lst -= 24 if rising_lst > 24
|
21
|
+
|
22
|
+
Util::Time.lst_to_ut(date: date, longitude: longitude, lst: rising_lst)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Source:
|
26
|
+
# Title: Celestial Calculations
|
27
|
+
# Author: J. L. Lawrence
|
28
|
+
# Edition: MIT Press
|
29
|
+
# Chapter: 5 - Stars in the Nighttime Sky
|
30
|
+
def rising_azimuth(latitude:)
|
31
|
+
ar = azimuth_component(latitude: latitude)
|
32
|
+
return nil if ar >= 1
|
33
|
+
|
34
|
+
Angle.acos(ar)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Source:
|
38
|
+
# Title: Celestial Calculations
|
39
|
+
# Author: J. L. Lawrence
|
40
|
+
# Edition: MIT Press
|
41
|
+
# Chapter: 5 - Stars in the Nighttime Sky
|
42
|
+
def setting_time(latitude:, longitude:, date:)
|
43
|
+
h2_component = h2(latitude: latitude)
|
44
|
+
return nil if h2_component.nil?
|
45
|
+
|
46
|
+
setting_lst = @equatorial_coordinates.right_ascension.hours + h2_component.degrees
|
47
|
+
setting_lst -= 24 if setting_lst > 24
|
48
|
+
|
49
|
+
Util::Time.lst_to_ut(date: date, longitude: longitude, lst: setting_lst)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Source:
|
53
|
+
# Title: Celestial Calculations
|
54
|
+
# Author: J. L. Lawrence
|
55
|
+
# Edition: MIT Press
|
56
|
+
# Chapter: 5 - Stars in the Nighttime Sky
|
57
|
+
def setting_azimuth(latitude:)
|
58
|
+
rising_az = rising_azimuth(latitude: latitude)
|
59
|
+
return nil if rising_az.nil?
|
60
|
+
|
61
|
+
Angle.as_degrees(360 - rising_az.degrees)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def azimuth_component(latitude:)
|
67
|
+
@equatorial_coordinates.declination.sin / latitude.cos
|
68
|
+
end
|
69
|
+
|
70
|
+
def h2(latitude:)
|
71
|
+
ar = azimuth_component(latitude: latitude)
|
72
|
+
return nil if ar >= 1
|
73
|
+
|
74
|
+
h1 = latitude.tan * @equatorial_coordinates.declination.tan
|
75
|
+
return nil if h1.abs > 1
|
76
|
+
|
77
|
+
Angle.as_radians(Math.acos(-h1) / 15.0)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Coordinates
|
5
|
+
class Ecliptic
|
6
|
+
attr_reader :latitude, :longitude
|
7
|
+
|
8
|
+
def initialize(latitude:, longitude:)
|
9
|
+
@latitude = latitude
|
10
|
+
@longitude = longitude
|
11
|
+
end
|
12
|
+
|
13
|
+
# Source:
|
14
|
+
# Title: Celestial Calculations
|
15
|
+
# Author: J. L. Lawrence
|
16
|
+
# Edition: MIT Press
|
17
|
+
# Chapter: 4 - Orbits and Coordinate Systems
|
18
|
+
def to_equatorial(epoch:)
|
19
|
+
mean_obliquity = MeanObliquity.for_epoch(epoch)
|
20
|
+
|
21
|
+
y = Angle.as_radians(
|
22
|
+
@longitude.sin * mean_obliquity.cos -
|
23
|
+
@latitude.tan * mean_obliquity.sin
|
24
|
+
)
|
25
|
+
x = Angle.as_radians(@longitude.cos)
|
26
|
+
r = Angle.atan(y.radians / x.radians)
|
27
|
+
right_ascension = Util::Trigonometry.adjustement_for_arctangent(y, x, r)
|
28
|
+
|
29
|
+
declination = Angle.asin(
|
30
|
+
@latitude.sin * mean_obliquity.cos +
|
31
|
+
@latitude.cos * mean_obliquity.sin * @longitude.sin
|
32
|
+
)
|
33
|
+
|
34
|
+
Equatorial.new(
|
35
|
+
right_ascension: right_ascension,
|
36
|
+
declination: declination,
|
37
|
+
epoch: epoch
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Coordinates
|
5
|
+
class Equatorial
|
6
|
+
attr_reader :declination, :right_ascension, :hour_angle, :epoch
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
declination:,
|
10
|
+
right_ascension:,
|
11
|
+
hour_angle: nil,
|
12
|
+
epoch: Epoch::DEFAULT_EPOCH
|
13
|
+
)
|
14
|
+
@right_ascension = right_ascension
|
15
|
+
@declination = declination
|
16
|
+
@hour_angle = hour_angle
|
17
|
+
@epoch = epoch
|
18
|
+
end
|
19
|
+
|
20
|
+
def compute_hour_angle(time:, longitude:)
|
21
|
+
lst = Util::Time.local_sidereal_time(
|
22
|
+
time: time,
|
23
|
+
longitude: longitude
|
24
|
+
)
|
25
|
+
|
26
|
+
ha = (lst - @right_ascension.hours)
|
27
|
+
ha += 24 if ha.negative?
|
28
|
+
|
29
|
+
Angle.as_hours(ha)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_horizontal(time:, latitude:, longitude:)
|
33
|
+
ha = @hour_angle || compute_hour_angle(time: time, longitude: longitude)
|
34
|
+
t0 = @declination.sin * latitude.sin +
|
35
|
+
@declination.cos * latitude.cos * ha.cos
|
36
|
+
altitude = Angle.asin(t0)
|
37
|
+
|
38
|
+
t1 = @declination.sin - latitude.sin * altitude.sin
|
39
|
+
t2 = t1 / (latitude.cos * altitude.cos)
|
40
|
+
azimuth = Angle.acos(t2)
|
41
|
+
|
42
|
+
if ha.sin.positive?
|
43
|
+
azimuth = Angle.as_degrees(BigDecimal("360") - azimuth.degrees)
|
44
|
+
end
|
45
|
+
|
46
|
+
Horizontal.new(
|
47
|
+
azimuth: azimuth,
|
48
|
+
altitude: altitude,
|
49
|
+
latitude: latitude,
|
50
|
+
longitude: longitude
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Source:
|
55
|
+
# Title: Celestial Calculations
|
56
|
+
# Author: J. L. Lawrence
|
57
|
+
# Edition: MIT Press
|
58
|
+
# Chapter: 4 - Orbits and Coordinate Systems
|
59
|
+
def to_ecliptic(epoch:)
|
60
|
+
mean_obliquity = MeanObliquity.for_epoch(epoch)
|
61
|
+
|
62
|
+
y = Angle.as_radians(
|
63
|
+
@right_ascension.sin * mean_obliquity.cos +
|
64
|
+
@declination.tan * mean_obliquity.sin
|
65
|
+
)
|
66
|
+
x = Angle.as_radians(@right_ascension.cos)
|
67
|
+
r = Angle.atan(y.radians / x.radians)
|
68
|
+
longitude = Util::Trigonometry.adjustement_for_arctangent(y, x, r)
|
69
|
+
|
70
|
+
latitude = Angle.asin(
|
71
|
+
@declination.sin * mean_obliquity.cos -
|
72
|
+
@declination.cos * mean_obliquity.sin * @right_ascension.sin
|
73
|
+
)
|
74
|
+
|
75
|
+
Ecliptic.new(
|
76
|
+
latitude: latitude,
|
77
|
+
longitude: longitude
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_epoch(epoch)
|
82
|
+
Precession.for_equatorial_coordinates(
|
83
|
+
coordinates: self,
|
84
|
+
epoch: epoch
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Coordinates
|
5
|
+
class Horizontal
|
6
|
+
attr_reader :azimuth, :altitude, :latitude, :longitude
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
azimuth:,
|
10
|
+
altitude:,
|
11
|
+
latitude:,
|
12
|
+
longitude:
|
13
|
+
)
|
14
|
+
@azimuth = azimuth
|
15
|
+
@altitude = altitude
|
16
|
+
@latitude = latitude
|
17
|
+
@longitude = longitude
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_equatorial(time:)
|
21
|
+
t0 = @altitude.sin * @latitude.sin +
|
22
|
+
@altitude.cos * @latitude.cos * @azimuth.cos
|
23
|
+
|
24
|
+
declination = Angle.asin(t0)
|
25
|
+
|
26
|
+
t1 = @altitude.sin - @latitude.sin * declination.sin
|
27
|
+
|
28
|
+
hour_angle_degrees = Angle
|
29
|
+
.acos(t1 / (@latitude.cos * declination.cos))
|
30
|
+
.degrees
|
31
|
+
|
32
|
+
if @azimuth.sin.positive?
|
33
|
+
hour_angle_degrees = Angle
|
34
|
+
.as_degrees(BigDecimal("360") - hour_angle_degrees)
|
35
|
+
.degrees
|
36
|
+
end
|
37
|
+
|
38
|
+
hour_angle_hours = Angle.as_degrees(hour_angle_degrees).hours
|
39
|
+
right_ascension_decimal = Util::Time.local_sidereal_time(
|
40
|
+
time: time,
|
41
|
+
longitude: @longitude
|
42
|
+
) - hour_angle_hours
|
43
|
+
right_ascension_decimal += 24 if right_ascension_decimal.negative?
|
44
|
+
right_ascension = Angle.as_hours(right_ascension_decimal)
|
45
|
+
|
46
|
+
Equatorial.new(
|
47
|
+
right_ascension: right_ascension,
|
48
|
+
declination: declination
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Epoch
|
5
|
+
B1900 = 2415020.3135
|
6
|
+
J1900 = 2415020.0
|
7
|
+
B1950 = 2433282.4235
|
8
|
+
J1950 = 2433282.5
|
9
|
+
J2000 = 2451545.0
|
10
|
+
|
11
|
+
DEFAULT_EPOCH = J2000
|
12
|
+
DAYS_PER_JULIAN_CENTURY = 36525.0
|
13
|
+
|
14
|
+
JULIAN_DAY_NUMBER_OFFSET = 0.5
|
15
|
+
|
16
|
+
def self.from_time(time)
|
17
|
+
time.to_datetime.ajd
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.to_utc(epoch)
|
21
|
+
DateTime.jd(epoch + JULIAN_DAY_NUMBER_OFFSET).to_time.utc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Astronoby
|
6
|
+
class MeanObliquity
|
7
|
+
# Source:
|
8
|
+
# IAU resolution in 2006 in favor of the P03 astronomical model
|
9
|
+
# The Astronomical Almanac for 2010
|
10
|
+
|
11
|
+
EPOCH_OF_REFERENCE = Epoch::DEFAULT_EPOCH
|
12
|
+
OBLIQUITY_OF_REFERENCE = 23.4392794
|
13
|
+
|
14
|
+
def self.for_epoch(epoch)
|
15
|
+
return obliquity_of_reference if epoch == EPOCH_OF_REFERENCE
|
16
|
+
|
17
|
+
t = (epoch - EPOCH_OF_REFERENCE) / Epoch::DAYS_PER_JULIAN_CENTURY
|
18
|
+
|
19
|
+
Angle.as_degrees(
|
20
|
+
obliquity_of_reference.degrees - (
|
21
|
+
46.815 * t -
|
22
|
+
0.0006 * t * t +
|
23
|
+
0.00181 * t * t * t
|
24
|
+
) / 3600
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.obliquity_of_reference
|
29
|
+
Angle.as_dms(23, 26, 21.45)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Nutation
|
5
|
+
# Source:
|
6
|
+
# Title: Practical Astronomy with your Calculator or Spreadsheet
|
7
|
+
# Authors: Peter Duffett-Smith and Jonathan Zwart
|
8
|
+
# Edition: Cambridge University Press
|
9
|
+
# Chapter: 35 - Nutation
|
10
|
+
|
11
|
+
def initialize(epoch)
|
12
|
+
@epoch = epoch
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.for_ecliptic_longitude(epoch:)
|
16
|
+
new(epoch).for_ecliptic_longitude
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.for_obliquity_of_the_ecliptic(epoch:)
|
20
|
+
new(epoch).for_obliquity_of_the_ecliptic
|
21
|
+
end
|
22
|
+
|
23
|
+
def for_ecliptic_longitude
|
24
|
+
Angle.as_dms(
|
25
|
+
0,
|
26
|
+
0,
|
27
|
+
(
|
28
|
+
-17.2 * moon_ascending_node_longitude.sin -
|
29
|
+
1.3 * Math.sin(2 * sun_mean_longitude.radians)
|
30
|
+
)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def for_obliquity_of_the_ecliptic
|
35
|
+
Angle.as_dms(
|
36
|
+
0,
|
37
|
+
0,
|
38
|
+
(
|
39
|
+
9.2 * moon_ascending_node_longitude.cos +
|
40
|
+
0.5 * Math.cos(2 * sun_mean_longitude.radians)
|
41
|
+
)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def julian_centuries
|
48
|
+
(@epoch - Epoch::J1900) / Epoch::DAYS_PER_JULIAN_CENTURY
|
49
|
+
end
|
50
|
+
|
51
|
+
def sun_mean_longitude
|
52
|
+
Angle.as_degrees(
|
53
|
+
(279.6967 + 360.0 * (centuries_a - centuries_a.to_i)) % 360
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def moon_ascending_node_longitude
|
58
|
+
Angle.as_degrees(
|
59
|
+
(259.1833 - 360.0 * (centuries_b - centuries_b.to_i)) % 360
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def centuries_a
|
64
|
+
100.002136 * julian_centuries
|
65
|
+
end
|
66
|
+
|
67
|
+
def centuries_b
|
68
|
+
5.372617 * julian_centuries
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "matrix"
|
4
|
+
|
5
|
+
module Astronoby
|
6
|
+
class Precession
|
7
|
+
def self.for_equatorial_coordinates(coordinates:, epoch:)
|
8
|
+
new(coordinates, epoch).precess
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(coordinates, epoch)
|
12
|
+
@coordinates = coordinates
|
13
|
+
@epoch = epoch
|
14
|
+
end
|
15
|
+
|
16
|
+
# Source:
|
17
|
+
# Title: Practical Astronomy with your Calculator or Spreadsheet
|
18
|
+
# Authors: Peter Duffett-Smith and Jonathan Zwart
|
19
|
+
# Edition: Cambridge University Press
|
20
|
+
# Chapter: 34 - Precession
|
21
|
+
def precess
|
22
|
+
matrix_a = matrix_for_epoch(@coordinates.epoch)
|
23
|
+
matrix_b = matrix_for_epoch(@epoch).transpose
|
24
|
+
|
25
|
+
vector = Vector[
|
26
|
+
@coordinates.right_ascension.cos * @coordinates.declination.cos,
|
27
|
+
@coordinates.right_ascension.sin * @coordinates.declination.cos,
|
28
|
+
@coordinates.declination.sin
|
29
|
+
]
|
30
|
+
|
31
|
+
s = matrix_a * vector
|
32
|
+
w = matrix_b * s
|
33
|
+
|
34
|
+
Coordinates::Equatorial.new(
|
35
|
+
right_ascension: Util::Trigonometry.adjustement_for_arctangent(
|
36
|
+
Angle.as_radians(w[1]),
|
37
|
+
Angle.as_radians(w[0]),
|
38
|
+
Angle.atan(w[1] / w[0])
|
39
|
+
),
|
40
|
+
declination: Angle.asin(w[2]),
|
41
|
+
epoch: @epoch
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def matrix_for_epoch(epoch)
|
48
|
+
t = (epoch - Epoch::DEFAULT_EPOCH) / Epoch::DAYS_PER_JULIAN_CENTURY
|
49
|
+
|
50
|
+
zeta = Angle.as_degrees(
|
51
|
+
0.6406161 * t + 0.0000839 * t * t + 0.000005 * t * t * t
|
52
|
+
)
|
53
|
+
z = Angle.as_degrees(
|
54
|
+
0.6406161 * t + 0.0003041 * t * t + 0.0000051 * t * t * t
|
55
|
+
)
|
56
|
+
theta = Angle.as_degrees(
|
57
|
+
0.5567530 * t - 0.0001185 * t * t - 0.0000116 * t * t * t
|
58
|
+
)
|
59
|
+
|
60
|
+
cx = zeta.cos
|
61
|
+
sx = zeta.sin
|
62
|
+
cz = z.cos
|
63
|
+
sz = z.sin
|
64
|
+
ct = theta.cos
|
65
|
+
st = theta.sin
|
66
|
+
|
67
|
+
Matrix[
|
68
|
+
[
|
69
|
+
cx * ct * cz - sx * sz,
|
70
|
+
cx * ct * sz + sx * cz,
|
71
|
+
cx * st
|
72
|
+
],
|
73
|
+
[
|
74
|
+
-sx * ct * cz - cx * sz,
|
75
|
+
-sx * ct * sz + cx * cz,
|
76
|
+
-sx * st
|
77
|
+
],
|
78
|
+
[
|
79
|
+
-st * cz,
|
80
|
+
-st * sz,
|
81
|
+
ct
|
82
|
+
]
|
83
|
+
]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Refraction
|
5
|
+
DEFAULT_PRESSURE = 1000
|
6
|
+
DEFAULT_TEMPERATURE = 25
|
7
|
+
|
8
|
+
def self.for_horizontal_coordinates(
|
9
|
+
coordinates:,
|
10
|
+
pressure: DEFAULT_PRESSURE,
|
11
|
+
temperature: DEFAULT_TEMPERATURE
|
12
|
+
)
|
13
|
+
new(coordinates, pressure, temperature).refract
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(coordinates, pressure, temperature)
|
17
|
+
@coordinates = coordinates
|
18
|
+
@pressure = pressure
|
19
|
+
@temperature = temperature
|
20
|
+
end
|
21
|
+
|
22
|
+
# Source:
|
23
|
+
# Title: Practical Astronomy with your Calculator or Spreadsheet
|
24
|
+
# Authors: Peter Duffett-Smith and Jonathan Zwart
|
25
|
+
# Edition: Cambridge University Press
|
26
|
+
# Chapter: 37 - Refraction
|
27
|
+
def refract
|
28
|
+
altitude_in_degrees = @coordinates.altitude.degrees
|
29
|
+
|
30
|
+
refraction_angle = Angle.as_degrees(
|
31
|
+
if altitude_in_degrees > 15
|
32
|
+
zenith_angle = Angle.as_degrees(90 - @coordinates.altitude.degrees)
|
33
|
+
0.00452 * @pressure * zenith_angle.tan / (273 + @temperature)
|
34
|
+
else
|
35
|
+
(
|
36
|
+
@pressure *
|
37
|
+
(
|
38
|
+
0.1594 +
|
39
|
+
0.0196 * altitude_in_degrees +
|
40
|
+
0.00002 * altitude_in_degrees * altitude_in_degrees
|
41
|
+
)
|
42
|
+
)./(
|
43
|
+
(273 + @temperature) *
|
44
|
+
(
|
45
|
+
1 +
|
46
|
+
0.505 * altitude_in_degrees +
|
47
|
+
0.0845 * altitude_in_degrees * altitude_in_degrees
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
)
|
52
|
+
|
53
|
+
Coordinates::Horizontal.new(
|
54
|
+
azimuth: @coordinates.azimuth,
|
55
|
+
altitude: @coordinates.altitude + refraction_angle,
|
56
|
+
latitude: @coordinates.latitude,
|
57
|
+
longitude: @coordinates.longitude
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class TrueObliquity
|
5
|
+
def self.for_epoch(epoch)
|
6
|
+
mean_obliquity = MeanObliquity.for_epoch(epoch)
|
7
|
+
nutation = Nutation.for_obliquity_of_the_ecliptic(epoch: epoch)
|
8
|
+
|
9
|
+
mean_obliquity + nutation
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
module Util
|
5
|
+
module Astrodynamics
|
6
|
+
class << self
|
7
|
+
# Source:
|
8
|
+
# Title: Celestial Calculations
|
9
|
+
# Author: J. L. Lawrence
|
10
|
+
# Edition: MIT Press
|
11
|
+
# Chapter: 4 - Orbits and Coordinate Systems
|
12
|
+
def eccentric_anomaly_newton_raphson(
|
13
|
+
mean_anomaly,
|
14
|
+
orbital_eccentricity,
|
15
|
+
precision,
|
16
|
+
maximum_iteration_count,
|
17
|
+
current_iteration = 0,
|
18
|
+
solution_on_previous_interation = nil
|
19
|
+
)
|
20
|
+
previous_solution = solution_on_previous_interation&.radians
|
21
|
+
|
22
|
+
solution = if previous_solution.nil?
|
23
|
+
if orbital_eccentricity <= 0.75
|
24
|
+
mean_anomaly.radians
|
25
|
+
else
|
26
|
+
Math::PI
|
27
|
+
end
|
28
|
+
else
|
29
|
+
previous_solution - (
|
30
|
+
(
|
31
|
+
previous_solution -
|
32
|
+
orbital_eccentricity * Math.sin(previous_solution) -
|
33
|
+
mean_anomaly.radians
|
34
|
+
) / (
|
35
|
+
1 - orbital_eccentricity * Math.cos(previous_solution)
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
if current_iteration >= maximum_iteration_count ||
|
41
|
+
(
|
42
|
+
previous_solution &&
|
43
|
+
(solution - previous_solution).abs <= precision
|
44
|
+
)
|
45
|
+
return Angle.as_radians(solution)
|
46
|
+
end
|
47
|
+
|
48
|
+
eccentric_anomaly_newton_raphson(
|
49
|
+
mean_anomaly,
|
50
|
+
orbital_eccentricity,
|
51
|
+
precision,
|
52
|
+
maximum_iteration_count,
|
53
|
+
current_iteration + 1,
|
54
|
+
Angle.as_radians(solution)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|