Skip to content

Commit

Permalink
Fix IncompatibleArgumentsError on Moon's observation events
Browse files Browse the repository at this point in the history
On some dates, `Moon#observation_events` would throw a
`IncompatibleArgumentsError`.

This was due to the interpolation method. It takes 3 or 5
approximately equidistant values and interpolate a value between
them. Because angles are normalized, an angle that would be 365° would
be normalized into 5°. While it makes sense in the library, it breaks
the suite of numbers when using interpolation. A list that would
initially and logically be `[345, 355, 365]` is changed into
`[345, 355, 5]`, which makes the interpolation logic wrong.

This fixes this issue by denormalizing values for interpolation when it
seems it is needed. When we have consecutive values that are suddenly
very far from each other, the method will fix them so that the suite
stays uniform.

To be honest, this seems a bit hacky and I have no way of making sure
this actually works fine without adding tons and tons of new examples
with different configurations and making sure the results still make
sense. But the library is already quite well test-covered and everything
is green, so I guess this new method is fine.

Fixes #106
  • Loading branch information
rhannequin committed Nov 24, 2024
1 parent e76815f commit 0287959
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 45 deletions.
48 changes: 28 additions & 20 deletions lib/astronoby/events/observation_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,13 @@ def rationalize_decimal_hours(decimal_hours)
def right_ascension_transit
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
]
),
(@final_transit || @initial_transit) + leap_day_portion
)
)
Expand All @@ -234,11 +236,13 @@ def right_ascension_transit
def declination_rising
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
]
),
@final_rising + leap_day_portion
)
)
Expand All @@ -247,11 +251,13 @@ def declination_rising
def declination_transit
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
]
),
(@final_transit || @initial_transit) + leap_day_portion
)
)
Expand All @@ -260,11 +266,13 @@ def declination_transit
def declination_setting
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
]
),
@final_setting + leap_day_portion
)
)
Expand Down
60 changes: 35 additions & 25 deletions lib/astronoby/events/rise_transit_set_iteration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,13 @@ def local_horizontal_altitude_setting
def right_ascension_rising
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
]
),
@initial_rising + leap_day_portion
)
)
Expand All @@ -163,11 +165,13 @@ def right_ascension_rising
def right_ascension_transit
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
]
),
@initial_transit + leap_day_portion
)
)
Expand All @@ -176,11 +180,13 @@ def right_ascension_transit
def right_ascension_setting
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.right_ascension.degrees,
@coordinates_of_the_day.right_ascension.degrees,
@coordinates_of_the_next_day.right_ascension.degrees
]
),
@initial_setting + leap_day_portion
)
)
Expand All @@ -189,11 +195,13 @@ def right_ascension_setting
def declination_rising
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
]
),
@initial_rising + leap_day_portion
)
)
Expand All @@ -202,11 +210,13 @@ def declination_rising
def declination_setting
Angle.from_degrees(
Util::Maths.interpolate(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
],
Util::Maths.normalize_angles_for_interpolation(
[
@coordinates_of_the_previous_day.declination.degrees,
@coordinates_of_the_day.declination.degrees,
@coordinates_of_the_next_day.declination.degrees
]
),
@initial_setting + leap_day_portion
)
)
Expand Down
22 changes: 22 additions & 0 deletions lib/astronoby/util/maths.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,28 @@ def interpolate(values, factor)
"Only 3 or 5 terms are supported for interpolation"
end

# Fixes angles forced to be in range [0, 360] or other angle range, for
# interpolation use
# @param angles [Array<Integer|Float>] Angles values
# @param full_circle [Integer] Full circle value
# @return [Array<Interger|Float>] Normalized values
def normalize_angles_for_interpolation(angles, full_circle: 360)
normalized = angles.dup

(1...normalized.size).each do |i|
prev_angle = normalized[i - 1]

while normalized[i] - prev_angle > full_circle / 2
normalized[i] -= full_circle
end
while normalized[i] - prev_angle < -full_circle / 2
normalized[i] += full_circle
end
end

normalized
end

private

# @return [Float] Interpolated value
Expand Down
38 changes: 38 additions & 0 deletions spec/astronoby/bodies/moon_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,44 @@
expect(rising_time).to eq Time.utc(2024, 11, 10, 18, 49, 45)
end

it "returns moonrise time on 2024-11-12" do
time = Time.utc(2024, 11, 12)
utc_offset = "-06:00"
observer = Astronoby::Observer.new(
latitude: Astronoby::Angle.from_degrees(41.87),
longitude: Astronoby::Angle.from_degrees(-87.62),
utc_offset: utc_offset
)
moon = described_class.new(time: time)
observation_events = moon.observation_events(observer: observer)

rising_time = observation_events.rising_time

expect(rising_time.getlocal(utc_offset))
.to eq Time.new(2024, 11, 12, 14, 39, 55, utc_offset)
# Time from the IMCCE: 2024-11-12T14:41:07-06:00
# Time from the Stellarium: 14:40
end

it "returns moonrise time on 2024-11-13" do
time = Time.utc(2024, 11, 13)
utc_offset = "-06:00"
observer = Astronoby::Observer.new(
latitude: Astronoby::Angle.from_degrees(41.87),
longitude: Astronoby::Angle.from_degrees(-87.62),
utc_offset: utc_offset
)
moon = described_class.new(time: time)
observation_events = moon.observation_events(observer: observer)

rising_time = observation_events.rising_time

expect(rising_time.getlocal(utc_offset))
.to eq Time.new(2024, 11, 13, 15, 4, 20, utc_offset)
# Time from the IMCCE: 2024-11-13T15:05:34-06:00
# Time from the Stellarium: 15:04
end

describe "#rising_azimuth" do
it "returns the moonrise azimuth on 2024-05-31" do
time = Time.utc(2024, 5, 31)
Expand Down
22 changes: 22 additions & 0 deletions spec/astronoby/util/maths_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,26 @@
end
end
end

describe "::normalize_angles_for_interpolation" do
[
[[10, 20, 30], [10, 20, 30], 360],
[[330, 340, 350], [330, 340, 350], 360],
[[343, 356, 9], [343, 356, 369], 360],
[[355, 2, 12], [355, 362, 372], 360],
[[1, 2, 3], [1, 2, 3], 12],
[[21, 22, 23], [21, 22, 23], 24],
[[22, 23, 2], [22, 23, 26], 24],
[[23, 2, 3], [23, 26, 27], 24]
].each do |input, expected_output, full_circle|
it "normalizes the angles for interpolation" do
normalized = described_class.normalize_angles_for_interpolation(
input,
full_circle: full_circle
)

expect(normalized).to eq expected_output
end
end
end
end

0 comments on commit 0287959

Please sign in to comment.