diff --git a/lib/astronoby/bodies/sun.rb b/lib/astronoby/bodies/sun.rb index 490a002..4ed24a1 100644 --- a/lib/astronoby/bodies/sun.rb +++ b/lib/astronoby/bodies/sun.rb @@ -56,6 +56,16 @@ def rising_time(observer:) ).to_gst.to_utc end + # @param observer [Astronoby::Observer] Observer of the event + # @return [Astronoby::Angle, nil] Azimuth of sunrise + def rising_azimuth(observer:) + equatorial_coordinates = ecliptic_coordinates.to_equatorial(epoch: @epoch) + Body.new(equatorial_coordinates).rising_azimuth( + latitude: observer.latitude, + vertical_shift: vertical_shift + ) + end + # @param observer [Astronoby::Observer] Observer of the event # @return [Time] Time of sunset def setting_time(observer:) @@ -72,6 +82,16 @@ def setting_time(observer:) ).to_gst.to_utc end + # @param observer [Astronoby::Observer] Observer of the event + # @return [Astronoby::Angle, nil] Azimuth of sunset + def setting_azimuth(observer:) + equatorial_coordinates = ecliptic_coordinates.to_equatorial(epoch: @epoch) + Body.new(equatorial_coordinates).setting_azimuth( + latitude: observer.latitude, + vertical_shift: vertical_shift + ) + end + # @return [Numeric] Earth-Sun distance in meters def earth_distance SEMI_MAJOR_AXIS_IN_METERS / distance_angular_size_factor @@ -172,5 +192,11 @@ def event_local_sidereal_time_for_date(date, observer, event) .to_lst(longitude: observer.longitude) .time end + + def vertical_shift + Astronoby::Body::DEFAULT_REFRACTION_VERTICAL_SHIFT + + Astronoby::GeocentricParallax.angle(distance: earth_distance) + + Astronoby::Angle.as_degrees(angular_size.degrees / 2) + end end end diff --git a/lib/astronoby/body.rb b/lib/astronoby/body.rb index 6f90995..d7e06b0 100644 --- a/lib/astronoby/body.rb +++ b/lib/astronoby/body.rb @@ -21,10 +21,10 @@ def rising_time( apparent: true, vertical_shift: nil ) - ratio = ratio(latitude, apparent, vertical_shift) - return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(ratio) + time_ratio = time_ratio(latitude, apparent, vertical_shift) + return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio) - hour_angle = Angle.acos(ratio) + hour_angle = Angle.acos(time_ratio) local_sidereal_time = LocalSiderealTime.new( date: date, time: @equatorial_coordinates.right_ascension.hours - hour_angle.hours, @@ -35,15 +35,17 @@ def rising_time( end # Source: - # Title: Celestial Calculations - # Author: J. L. Lawrence - # Edition: MIT Press - # Chapter: 5 - Stars in the Nighttime Sky - def rising_azimuth(latitude:) - ar = azimuth_component(latitude) - return nil if ar >= 1 - - Angle.acos(ar) + # Title: Practical Astronomy with your Calculator or Spreadsheet + # Authors: Peter Duffett-Smith and Jonathan Zwart + # Edition: Cambridge University Press + # Chapter: 33 - Rising and setting + def rising_azimuth(latitude:, apparent: true, vertical_shift: nil) + time_ratio = time_ratio(latitude, apparent, vertical_shift) + return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio) + + azimuth_ratio = azimuth_ratio(latitude, apparent, vertical_shift) + + Angle.acos(azimuth_ratio) end # Source: @@ -58,10 +60,10 @@ def setting_time( apparent: true, vertical_shift: nil ) - ratio = ratio(latitude, apparent, vertical_shift) - return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(ratio) + time_ratio = time_ratio(latitude, apparent, vertical_shift) + return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio) - hour_angle = Angle.acos(ratio) + hour_angle = Angle.acos(time_ratio) local_sidereal_time = LocalSiderealTime.new( date: date, time: @equatorial_coordinates.right_ascension.hours + hour_angle.hours, @@ -72,20 +74,22 @@ def setting_time( end # Source: - # Title: Celestial Calculations - # Author: J. L. Lawrence - # Edition: MIT Press - # Chapter: 5 - Stars in the Nighttime Sky - def setting_azimuth(latitude:) - rising_az = rising_azimuth(latitude: latitude) - return nil if rising_az.nil? - - Angle.as_degrees(360 - rising_az.degrees) + # Title: Practical Astronomy with your Calculator or Spreadsheet + # Authors: Peter Duffett-Smith and Jonathan Zwart + # Edition: Cambridge University Press + # Chapter: 33 - Rising and setting + def setting_azimuth(latitude:, apparent: true, vertical_shift: nil) + time_ratio = time_ratio(latitude, apparent, vertical_shift) + return nil unless RISING_SETTING_HOUR_ANGLE_RATIO_RANGE.cover?(time_ratio) + + azimuth_ratio = azimuth_ratio(latitude, apparent, vertical_shift) + + Angle.as_degrees(360 - Angle.acos(azimuth_ratio).degrees) end private - def ratio(latitude, apparent, vertical_shift) + def time_ratio(latitude, apparent, vertical_shift) shift = if vertical_shift vertical_shift elsif apparent @@ -99,6 +103,18 @@ def ratio(latitude, apparent, vertical_shift) ) end + def azimuth_ratio(latitude, apparent, vertical_shift) + shift = if vertical_shift + vertical_shift + elsif apparent + DEFAULT_REFRACTION_VERTICAL_SHIFT + else + Angle.zero + end + + (@equatorial_coordinates.declination.sin + shift.sin * latitude.cos) / (shift.cos * latitude.cos) + end + def azimuth_component(latitude) @equatorial_coordinates.declination.sin / latitude.cos end diff --git a/spec/astronoby/bodies/sun_spec.rb b/spec/astronoby/bodies/sun_spec.rb index 3468cd4..1a74743 100644 --- a/spec/astronoby/bodies/sun_spec.rb +++ b/spec/astronoby/bodies/sun_spec.rb @@ -378,6 +378,67 @@ end end + describe "#rising_azimuth" do + it "returns an Angle" do + date = Date.new + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.zero, + longitude: Astronoby::Angle.zero + ) + sun = described_class.new(epoch: epoch) + + rising_azimuth = sun.rising_azimuth(observer: observer) + + expect(rising_azimuth).to be_a(Astronoby::Angle) + end + + it "returns the Sun's rising azimuth on 2015-02-05" do + date = Date.new(2015, 2, 5) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(38), + longitude: Astronoby::Angle.as_degrees(-78) + ) + sun = Astronoby::Sun.new(epoch: epoch) + + rising_azimuth = sun.rising_azimuth(observer: observer) + + expect(rising_azimuth&.str(:dms)).to eq "+109° 41′ 24.0917″" + # Time from IMCCE: +109° 53′ + end + + it "returns the Sun's rising azimuth on 1986-03-10" do + date = Date.new(1986, 3, 10) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(42.37), + longitude: Astronoby::Angle.as_degrees(-71.05) + ) + sun = described_class.new(epoch: epoch) + + rising_azimuth = sun.rising_azimuth(observer: observer) + + expect(rising_azimuth&.str(:dms)).to eq "+94° 59′ 15.7852″" + # Time from IMCCE: +95° 02′ + end + + it "returns the Sun's rising azimuth on 1991-03-14" do + date = Date.new(1991, 3, 14) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(48.8566), + longitude: Astronoby::Angle.as_degrees(2.3522) + ) + sun = described_class.new(epoch: epoch) + + rising_azimuth = sun.rising_azimuth(observer: observer) + + expect(rising_azimuth&.str(:dms)).to eq "+93° 26′ 26.8564″" + # Time from IMCCE: +93° 26′ + end + end + describe "#setting_time" do it "returns a time" do date = Date.new @@ -450,4 +511,65 @@ # Time from IMCCE: 1991-03-14T17:52:00 end end + + describe "#setting_azimuth" do + it "returns an Angle" do + date = Date.new + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.zero, + longitude: Astronoby::Angle.zero + ) + sun = described_class.new(epoch: epoch) + + setting_azimuth = sun.setting_azimuth(observer: observer) + + expect(setting_azimuth).to be_a(Astronoby::Angle) + end + + it "returns the Sun's setting azimuth on 2015-02-05" do + date = Date.new(2015, 2, 5) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(38), + longitude: Astronoby::Angle.as_degrees(-78) + ) + sun = Astronoby::Sun.new(epoch: epoch) + + setting_azimuth = sun.setting_azimuth(observer: observer) + + expect(setting_azimuth&.str(:dms)).to eq "+250° 18′ 35.9082″" + # Time from IMCCE: +250° 18′ + end + + it "returns the Sun's setting azimuth on 1986-03-10" do + date = Date.new(1986, 3, 10) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(42.37), + longitude: Astronoby::Angle.as_degrees(-71.05) + ) + sun = described_class.new(epoch: epoch) + + setting_azimuth = sun.setting_azimuth(observer: observer) + + expect(setting_azimuth&.str(:dms)).to eq "+265° 0′ 44.2147″" + # Time from IMCCE: +265° 14′ + end + + it "returns the Sun's setting azimuth on 1991-03-14" do + date = Date.new(1991, 3, 14) + epoch = Astronoby::Epoch.from_time(date) + observer = Astronoby::Observer.new( + latitude: Astronoby::Angle.as_degrees(48.8566), + longitude: Astronoby::Angle.as_degrees(2.3522) + ) + sun = described_class.new(epoch: epoch) + + setting_azimuth = sun.setting_azimuth(observer: observer) + + expect(setting_azimuth&.str(:dms)).to eq "+266° 33′ 33.1435″" + # Time from IMCCE: +266° 52′ + end + end end diff --git a/spec/astronoby/body_spec.rb b/spec/astronoby/body_spec.rb index 9123780..83f71ef 100644 --- a/spec/astronoby/body_spec.rb +++ b/spec/astronoby/body_spec.rb @@ -80,7 +80,7 @@ # Authors: Peter Duffett-Smith and Jonathan Zwart # Edition: Cambridge University Press # Chapter: 33 - Rising and setting - it "bob returns the body's rising time" do + it "returns the body's rising time" do coordinates = Astronoby::Coordinates::Equatorial.new( right_ascension: Astronoby::Angle.as_hms(23, 39, 20), declination: Astronoby::Angle.as_dms(21, 42, 0) @@ -113,10 +113,11 @@ body = described_class.new(coordinates) rising_azimuth = body.rising_azimuth( - latitude: Astronoby::Angle.as_degrees(38) + latitude: Astronoby::Angle.as_degrees(38), + apparent: false ) - expect(rising_azimuth&.degrees).to be_within(10**-9).of(80.465577913) + expect(rising_azimuth&.degrees&.ceil(6)).to eq 80.465578 end end @@ -197,7 +198,7 @@ # Authors: Peter Duffett-Smith and Jonathan Zwart # Edition: Cambridge University Press # Chapter: 33 - Rising and setting - it "bob returns the body's rising time" do + it "returns the body's rising time" do coordinates = Astronoby::Coordinates::Equatorial.new( right_ascension: Astronoby::Angle.as_hms(23, 39, 20), declination: Astronoby::Angle.as_dms(21, 42, 0) @@ -230,10 +231,11 @@ body = described_class.new(coordinates) setting_azimuth = body.setting_azimuth( - latitude: Astronoby::Angle.as_degrees(38) + latitude: Astronoby::Angle.as_degrees(38), + apparent: false ) - expect(setting_azimuth&.degrees).to be_within(10**-7).of(279.534422) + expect(setting_azimuth&.degrees&.ceil(6)).to eq 279.534423 end end end