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