astronoby 0.6.0 → 0.7.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/.ruby-version +1 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +116 -0
- data/Gemfile.lock +45 -23
- data/README.md +42 -285
- data/UPGRADING.md +238 -0
- data/lib/astronoby/aberration.rb +56 -31
- data/lib/astronoby/angle.rb +20 -16
- data/lib/astronoby/angles/dms.rb +2 -2
- data/lib/astronoby/angles/hms.rb +2 -2
- data/lib/astronoby/bodies/earth.rb +56 -0
- data/lib/astronoby/bodies/jupiter.rb +11 -0
- data/lib/astronoby/bodies/mars.rb +11 -0
- data/lib/astronoby/bodies/mercury.rb +11 -0
- data/lib/astronoby/bodies/moon.rb +50 -290
- data/lib/astronoby/bodies/neptune.rb +11 -0
- data/lib/astronoby/bodies/saturn.rb +11 -0
- data/lib/astronoby/bodies/solar_system_body.rb +122 -0
- data/lib/astronoby/bodies/sun.rb +16 -220
- data/lib/astronoby/bodies/uranus.rb +11 -0
- data/lib/astronoby/bodies/venus.rb +11 -0
- data/lib/astronoby/constants.rb +13 -1
- data/lib/astronoby/coordinates/ecliptic.rb +2 -37
- data/lib/astronoby/coordinates/equatorial.rb +25 -7
- data/lib/astronoby/coordinates/horizontal.rb +0 -46
- data/lib/astronoby/corrections/light_time_delay.rb +90 -0
- data/lib/astronoby/deflection.rb +187 -0
- data/lib/astronoby/distance.rb +9 -0
- data/lib/astronoby/ephem.rb +39 -0
- data/lib/astronoby/equinox_solstice.rb +21 -18
- data/lib/astronoby/errors.rb +4 -0
- data/lib/astronoby/events/moon_phases.rb +2 -1
- data/lib/astronoby/events/rise_transit_set_calculator.rb +352 -0
- data/lib/astronoby/events/rise_transit_set_event.rb +13 -0
- data/lib/astronoby/events/rise_transit_set_events.rb +13 -0
- data/lib/astronoby/events/twilight_calculator.rb +166 -0
- data/lib/astronoby/events/twilight_event.rb +28 -0
- data/lib/astronoby/instant.rb +171 -0
- data/lib/astronoby/mean_obliquity.rb +23 -10
- data/lib/astronoby/nutation.rb +227 -42
- data/lib/astronoby/observer.rb +55 -0
- data/lib/astronoby/precession.rb +91 -17
- data/lib/astronoby/reference_frame.rb +49 -0
- data/lib/astronoby/reference_frames/apparent.rb +60 -0
- data/lib/astronoby/reference_frames/astrometric.rb +21 -0
- data/lib/astronoby/reference_frames/geometric.rb +20 -0
- data/lib/astronoby/reference_frames/mean_of_date.rb +38 -0
- data/lib/astronoby/reference_frames/topocentric.rb +82 -0
- data/lib/astronoby/true_obliquity.rb +2 -1
- data/lib/astronoby/util/maths.rb +70 -73
- data/lib/astronoby/util/time.rb +454 -31
- data/lib/astronoby/vector.rb +36 -0
- data/lib/astronoby/velocity.rb +116 -0
- data/lib/astronoby/version.rb +1 -1
- data/lib/astronoby.rb +26 -5
- metadata +61 -16
- data/.tool-versions +0 -1
- data/lib/astronoby/astronomical_models/ephemeride_lunaire_parisienne.rb +0 -143
- data/lib/astronoby/events/observation_events.rb +0 -285
- data/lib/astronoby/events/rise_transit_set_iteration.rb +0 -218
- data/lib/astronoby/events/twilight_events.rb +0 -121
- data/lib/astronoby/util/astrodynamics.rb +0 -60
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astronoby
|
4
|
+
class Topocentric < ReferenceFrame
|
5
|
+
def self.build_from_apparent(
|
6
|
+
apparent:,
|
7
|
+
observer:,
|
8
|
+
instant:,
|
9
|
+
target_body:
|
10
|
+
)
|
11
|
+
matrix = observer.earth_fixed_rotation_matrix_for(instant)
|
12
|
+
|
13
|
+
observer_position = Distance.vector_from_meters(
|
14
|
+
matrix * observer.geocentric_position.map(&:m)
|
15
|
+
)
|
16
|
+
observer_velocity = Velocity.vector_from_mps(
|
17
|
+
matrix * observer.geocentric_velocity.map(&:kmps)
|
18
|
+
)
|
19
|
+
|
20
|
+
position = apparent.position - observer_position
|
21
|
+
velocity = apparent.velocity - observer_velocity
|
22
|
+
|
23
|
+
new(
|
24
|
+
position: position,
|
25
|
+
velocity: velocity,
|
26
|
+
instant: instant,
|
27
|
+
center_identifier: [observer.longitude, observer.latitude],
|
28
|
+
target_body: target_body,
|
29
|
+
observer: observer
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(
|
34
|
+
position:,
|
35
|
+
velocity:,
|
36
|
+
instant:,
|
37
|
+
center_identifier:,
|
38
|
+
target_body:,
|
39
|
+
observer:
|
40
|
+
)
|
41
|
+
super(
|
42
|
+
position: position,
|
43
|
+
velocity: velocity,
|
44
|
+
instant: instant,
|
45
|
+
center_identifier: center_identifier,
|
46
|
+
target_body: target_body
|
47
|
+
)
|
48
|
+
@observer = observer
|
49
|
+
end
|
50
|
+
|
51
|
+
def angular_diameter
|
52
|
+
@angular_radius ||= begin
|
53
|
+
return Angle.zero if @position.zero?
|
54
|
+
|
55
|
+
Angle.from_radians(
|
56
|
+
Math.atan(@target_body.class::EQUATORIAL_RADIUS.m / distance.m) * 2
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def ecliptic
|
62
|
+
@ecliptic ||= begin
|
63
|
+
return Coordinates::Ecliptic.zero if distance.zero?
|
64
|
+
|
65
|
+
equatorial.to_ecliptic(epoch: @instant.tdb)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def horizontal(refraction: false)
|
70
|
+
horizontal = equatorial.to_horizontal(
|
71
|
+
time: @instant.to_time,
|
72
|
+
observer: @observer
|
73
|
+
)
|
74
|
+
|
75
|
+
if refraction
|
76
|
+
Refraction.correct_horizontal_coordinates(coordinates: horizontal)
|
77
|
+
else
|
78
|
+
horizontal
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -3,8 +3,9 @@
|
|
3
3
|
module Astronoby
|
4
4
|
class TrueObliquity
|
5
5
|
def self.for_epoch(epoch)
|
6
|
+
instant = Instant.from_utc_julian_date(epoch)
|
6
7
|
mean_obliquity = MeanObliquity.for_epoch(epoch)
|
7
|
-
nutation = Nutation.
|
8
|
+
nutation = Nutation.new(instant: instant).nutation_in_obliquity
|
8
9
|
|
9
10
|
mean_obliquity + nutation
|
10
11
|
end
|
data/lib/astronoby/util/maths.rb
CHANGED
@@ -3,91 +3,88 @@
|
|
3
3
|
module Astronoby
|
4
4
|
module Util
|
5
5
|
module Maths
|
6
|
-
|
7
|
-
# Source:
|
8
|
-
# Title: Astronomical Algorithms
|
9
|
-
# Author: Jean Meeus
|
10
|
-
# Edition: 2nd edition
|
11
|
-
# Chapter: 3 - Interpolation
|
6
|
+
module_function
|
12
7
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def interpolate(values, factor)
|
17
|
-
unless factor.between?(0, 1)
|
18
|
-
raise IncompatibleArgumentsError,
|
19
|
-
"Interpolation factor must be between 0 and 1, got #{factor}"
|
20
|
-
end
|
8
|
+
def dot_product(a, b)
|
9
|
+
a.zip(b).sum { |x, y| x * y }
|
10
|
+
end
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
12
|
+
# Find maximum altitude using quadratic interpolation
|
13
|
+
# @param t1, t2, t3 [Time] Three consecutive times
|
14
|
+
# @param alt1, alt2, alt3 [Float] Corresponding altitudes
|
15
|
+
# @return [::Time] Time of maximum altitude
|
16
|
+
def quadratic_maximum(t1, t2, t3, alt1, alt2, alt3)
|
17
|
+
# Convert to float seconds for arithmetic
|
18
|
+
x1, x2, x3 = t1.to_f, t2.to_f, t3.to_f
|
19
|
+
y1, y2, y3 = alt1, alt2, alt3
|
20
|
+
|
21
|
+
# Quadratic interpolation formula
|
22
|
+
denom = (x1 - x2) * (x1 - x3) * (x2 - x3)
|
23
|
+
a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / denom
|
24
|
+
b = (x3 * x3 * (y1 - y2) +
|
25
|
+
x2 * x2 * (y3 - y1) +
|
26
|
+
x1 * x1 * (y2 - y3)) / denom
|
27
|
+
|
28
|
+
# Maximum is at -b/2a
|
29
|
+
max_t = -b / (2 * a)
|
30
|
+
|
31
|
+
::Time.at(max_t)
|
32
|
+
end
|
27
33
|
|
28
|
-
|
29
|
-
|
34
|
+
# Linear interpolation between two points
|
35
|
+
# @param x1 [Numeric] First x value
|
36
|
+
# @param x2 [Numeric] Second x value
|
37
|
+
# @param y1 [Numeric] First y value
|
38
|
+
# @param y2 [Numeric] Second y value
|
39
|
+
# @param target_y [Numeric] Target y value (default: 0)
|
40
|
+
# @return [Numeric] Interpolated x value where y=target_y
|
41
|
+
def self.linear_interpolate(x1, x2, y1, y2, target_y = 0)
|
42
|
+
# Handle horizontal line case (avoid division by zero)
|
43
|
+
if (y2 - y1).abs < 1e-10
|
44
|
+
# If target_y matches the line's y-value (within precision), return
|
45
|
+
# midpoint. Otherwise, return one of the endpoints (no unique solution
|
46
|
+
# exists)
|
47
|
+
return ((target_y - y1).abs < 1e-10) ? (x1 + x2) / 2.0 : x1
|
30
48
|
end
|
31
49
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# @return [Array<Interger|Float>] Normalized values
|
37
|
-
def normalize_angles_for_interpolation(angles, full_circle: 360)
|
38
|
-
normalized = angles.dup
|
39
|
-
|
40
|
-
(1...normalized.size).each do |i|
|
41
|
-
prev_angle = normalized[i - 1]
|
42
|
-
|
43
|
-
while normalized[i] - prev_angle > full_circle / 2
|
44
|
-
normalized[i] -= full_circle
|
45
|
-
end
|
46
|
-
while normalized[i] - prev_angle < -full_circle / 2
|
47
|
-
normalized[i] += full_circle
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
normalized
|
50
|
+
# Handle vertical line case
|
51
|
+
if (x2 - x1).abs < 1e-10
|
52
|
+
# For a vertical line, there's only one x-value possible
|
53
|
+
return x1
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def interpolate_3_terms(terms, factor)
|
58
|
-
y1, y2, y3 = terms
|
59
|
-
|
60
|
-
a = y2 - y1
|
61
|
-
b = y3 - y2
|
62
|
-
c = b - a
|
56
|
+
# Standard linear interpolation formula
|
57
|
+
x1 + (target_y - y1) * (x2 - x1) / (y2 - y1)
|
58
|
+
end
|
63
59
|
|
64
|
-
|
60
|
+
# Creates an array of evenly spaced values between start and stop.
|
61
|
+
# @param start [Numeric] The starting value of the sequence
|
62
|
+
# @param stop [Numeric] The end value of the sequence
|
63
|
+
# @param num [Integer] Number of samples to generate. Default is 50
|
64
|
+
# @param endpoint [Boolean] If true, stop is the last sample. Otherwise,
|
65
|
+
# it is not included. Default is true
|
66
|
+
# @return [Array<Numeric>] Array of evenly spaced values
|
67
|
+
# @raise [ArgumentError] If num is less than 1
|
68
|
+
def linspace(start, stop, num = 50, endpoint = true)
|
69
|
+
raise ArgumentError, "Number of samples must be at least 1" if num < 1
|
70
|
+
return [start] if num == 1
|
71
|
+
|
72
|
+
step = if endpoint
|
73
|
+
(stop - start) / (num - 1).to_f
|
74
|
+
else
|
75
|
+
(stop - start) / num.to_f
|
65
76
|
end
|
66
77
|
|
67
|
-
|
68
|
-
|
69
|
-
y1, y2, y3, y4, y5 = terms
|
70
|
-
|
71
|
-
a = y2 - y1
|
72
|
-
b = y3 - y2
|
73
|
-
c = y4 - y3
|
74
|
-
d = y5 - y4
|
75
|
-
|
76
|
-
e = b - a
|
77
|
-
f = c - b
|
78
|
-
g = d - c
|
78
|
+
result = Array.new(num)
|
79
|
+
current = start
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
k = j - h
|
84
|
-
|
85
|
-
y3 +
|
86
|
-
factor * ((b + c) / 2.0 - (h + j) / 12.0) +
|
87
|
-
factor**2 * (f / 2.0 - k / 24.0) +
|
88
|
-
factor**3 * (h + j) / 12.0 +
|
89
|
-
factor**4 * k / 24.0
|
81
|
+
(num - 1).times do |i|
|
82
|
+
result[i] = current
|
83
|
+
current += step
|
90
84
|
end
|
85
|
+
|
86
|
+
result[num - 1] = endpoint ? stop : current
|
87
|
+
result
|
91
88
|
end
|
92
89
|
end
|
93
90
|
end
|