diff --git a/lib/astronoby/aberration.rb b/lib/astronoby/aberration.rb index 43dd4a5..c4bd4cc 100644 --- a/lib/astronoby/aberration.rb +++ b/lib/astronoby/aberration.rb @@ -20,25 +20,20 @@ def initialize(coordinates, sun_longitude) # Chapter: 36 - Aberration def apply delta_longitude = Astronoby::Angle.as_degrees( - -20.5 * - Math.cos( - @sun_longitude.radians - @coordinates.longitude.radians - ) / Math.cos(@coordinates.latitude.radians) / 3600 + -20.5 * ( + @sun_longitude - @coordinates.longitude + ).cos / @coordinates.latitude.cos / 3600 ) delta_latitude = Astronoby::Angle.as_degrees( -20.5 * - Math.sin(@sun_longitude.radians - @coordinates.longitude.radians) * - Math.sin(@coordinates.latitude.radians) / 3600 + (@sun_longitude - @coordinates.longitude).sin * + @coordinates.latitude.sin / 3600 ) Astronoby::Coordinates::Ecliptic.new( - latitude: Astronoby::Angle.as_degrees( - @coordinates.latitude.degrees + delta_latitude.degrees - ), - longitude: Astronoby::Angle.as_degrees( - @coordinates.longitude.degrees + delta_longitude.degrees - ) + latitude: @coordinates.latitude + delta_latitude, + longitude: @coordinates.longitude + delta_longitude ) end end diff --git a/lib/astronoby/angle.rb b/lib/astronoby/angle.rb index 0248e1b..fab274b 100644 --- a/lib/astronoby/angle.rb +++ b/lib/astronoby/angle.rb @@ -45,26 +45,59 @@ def as_dms(degree, minute, second) degrees = degree.abs + minute / MINUTES_PER_HOUR + second / SECONDS_PER_HOUR as_degrees(sign * degrees) end + + def asin(ratio) + radians = Math.asin(ratio) + as_radians(radians) + end + + def acos(ratio) + radians = Math.acos(ratio) + as_radians(radians) + end + + def atan(ratio) + radians = Math.atan(ratio) + as_radians(radians) + end end - def radians - @angle + attr_reader :radians + + def initialize(radians) + @radians = if radians.is_a?(Integer) || radians.is_a?(BigDecimal) + BigDecimal(radians) + else + BigDecimal(radians, PRECISION) + end end def degrees - @angle * PI_IN_DEGREES / PI + @radians * PI_IN_DEGREES / PI end def hours - @angle / RADIAN_PER_HOUR + @radians / RADIAN_PER_HOUR end - def initialize(angle) - @angle = if angle.is_a?(Integer) || angle.is_a?(BigDecimal) - BigDecimal(angle) - else - BigDecimal(angle, PRECISION) - end + def +(other) + self.class.as_radians(radians + other.radians) + end + + def -(other) + self.class.as_radians(@radians - other.radians) + end + + def sin + Math.sin(radians) + end + + def cos + Math.cos(radians) + end + + def tan + Math.tan(radians) end def str(format) diff --git a/lib/astronoby/bodies/sun.rb b/lib/astronoby/bodies/sun.rb index dd43130..329982e 100644 --- a/lib/astronoby/bodies/sun.rb +++ b/lib/astronoby/bodies/sun.rb @@ -16,7 +16,7 @@ def ecliptic_coordinates Coordinates::Ecliptic.new( latitude: Angle.zero, longitude: Angle.as_degrees( - (true_anomaly.degrees + longitude_at_perigee.degrees) % 360 + (true_anomaly + longitude_at_perigee).degrees % 360 ) ) end @@ -33,7 +33,7 @@ def horizontal_coordinates(latitude:, longitude:) def mean_anomaly Angle.as_degrees( - (longitude_at_base_epoch.degrees - longitude_at_perigee.degrees) % 360 + (longitude_at_base_epoch - longitude_at_perigee).degrees % 360 ) end @@ -50,7 +50,7 @@ def true_anomaly ) * Math.tan(eccentric_anomaly.radians / 2) Astronoby::Angle.as_degrees( - (Astronoby::Angle.as_radians(Math.atan(tan)).degrees * 2) % 360 + (Astronoby::Angle.atan(tan).degrees * 2) % 360 ) end diff --git a/lib/astronoby/body.rb b/lib/astronoby/body.rb index 612d4cd..a085747 100644 --- a/lib/astronoby/body.rb +++ b/lib/astronoby/body.rb @@ -35,7 +35,7 @@ def rising_azimuth(latitude:) ar = azimuth_component(latitude: latitude) return nil if ar >= 1 - Astronoby::Angle.as_radians(Math.acos(ar)) + Astronoby::Angle.acos(ar) end # Source: @@ -72,17 +72,14 @@ def setting_azimuth(latitude:) private def azimuth_component(latitude:) - Math.sin(@equatorial_coordinates.declination.radians)./( - Math.cos(latitude.radians) - ) + @equatorial_coordinates.declination.sin / latitude.cos end def h2(latitude:) ar = azimuth_component(latitude: latitude) return nil if ar >= 1 - h1 = Math.tan(latitude.radians) * - Math.tan(@equatorial_coordinates.declination.radians) + h1 = latitude.tan * @equatorial_coordinates.declination.tan return nil if h1.abs > 1 Astronoby::Angle.as_radians(Math.acos(-h1) / 15.0) diff --git a/lib/astronoby/coordinates/ecliptic.rb b/lib/astronoby/coordinates/ecliptic.rb index 3579718..461ce7b 100644 --- a/lib/astronoby/coordinates/ecliptic.rb +++ b/lib/astronoby/coordinates/ecliptic.rb @@ -17,25 +17,19 @@ def initialize(latitude:, longitude:) # Chapter: 4 - Orbits and Coordinate Systems def to_equatorial(epoch:) mean_obliquity = Astronoby::MeanObliquity.for_epoch(epoch) - obliquity_in_radians = mean_obliquity.value.radians - longitude_in_radians = @longitude.radians - latitude_in_radians = @latitude.radians + obliquity = mean_obliquity.value y = Astronoby::Angle.as_radians( - Math.sin(longitude_in_radians) * Math.cos(obliquity_in_radians) - - Math.tan(latitude_in_radians) * Math.sin(obliquity_in_radians) - ) - x = Astronoby::Angle.as_radians(Math.cos(longitude_in_radians)) - r = Astronoby::Angle.as_radians(Math.atan(y.radians / x.radians)) - right_ascension = Astronoby::Angle.as_radians( - Astronoby::Util::Trigonometry.adjustement_for_arctangent(y, x, r).radians + @longitude.sin * obliquity.cos - @latitude.tan * obliquity.sin ) + x = Astronoby::Angle.as_radians(@longitude.cos) + r = Astronoby::Angle.atan(y.radians / x.radians) + right_ascension = Astronoby::Util::Trigonometry + .adjustement_for_arctangent(y, x, r) - declination = Astronoby::Angle.as_radians( - Math.asin( - Math.sin(latitude_in_radians) * Math.cos(obliquity_in_radians) + - Math.cos(latitude_in_radians) * Math.sin(obliquity_in_radians) * Math.sin(longitude_in_radians) - ) + declination = Astronoby::Angle.asin( + @latitude.sin * obliquity.cos + + @latitude.cos * obliquity.sin * @longitude.sin ) Equatorial.new( diff --git a/lib/astronoby/coordinates/equatorial.rb b/lib/astronoby/coordinates/equatorial.rb index 9c554ff..721bb74 100644 --- a/lib/astronoby/coordinates/equatorial.rb +++ b/lib/astronoby/coordinates/equatorial.rb @@ -31,20 +31,15 @@ def compute_hour_angle(time:, longitude:) def to_horizontal(time:, latitude:, longitude:) ha = @hour_angle || compute_hour_angle(time: time, longitude: longitude) + t0 = @declination.sin * latitude.sin + + @declination.cos * latitude.cos * ha.cos + altitude = Astronoby::Angle.asin(t0) - latitude_radians = latitude.radians - declination_radians = @declination.radians + t1 = @declination.sin - latitude.sin * altitude.sin + t2 = t1 / (latitude.cos * altitude.cos) + azimuth = Astronoby::Angle.acos(t2) - t0 = Math.sin(declination_radians) * Math.sin(latitude_radians) + - Math.cos(declination_radians) * Math.cos(latitude_radians) * Math.cos(ha.radians) - altitude = Astronoby::Angle.as_radians(Math.asin(t0)) - - t1 = Math.sin(declination_radians) - - Math.sin(latitude_radians) * Math.sin(altitude.radians) - t2 = t1 / (Math.cos(latitude_radians) * Math.cos(altitude.radians)) - sin_hour_angle = Math.sin(ha.radians) - azimuth = Astronoby::Angle.as_radians(Math.acos(t2)) - if sin_hour_angle.positive? + if ha.sin.positive? azimuth = Astronoby::Angle.as_degrees(BigDecimal("360") - azimuth.degrees) end @@ -63,23 +58,19 @@ def to_horizontal(time:, latitude:, longitude:) # Chapter: 4 - Orbits and Coordinate Systems def to_ecliptic(epoch:) mean_obliquity = Astronoby::MeanObliquity.for_epoch(epoch) - obliquity_in_radians = mean_obliquity.value.radians - right_ascension_in_radians = @right_ascension.radians - declination_in_radians = @declination.radians + obliquity = mean_obliquity.value y = Astronoby::Angle.as_radians( - Math.sin(right_ascension_in_radians) * Math.cos(obliquity_in_radians) + - Math.tan(declination_in_radians) * Math.sin(obliquity_in_radians) + @right_ascension.sin * obliquity.cos + + @declination.tan * obliquity.sin ) - x = Astronoby::Angle.as_radians(Math.cos(right_ascension_in_radians)) - r = Astronoby::Angle.as_radians(Math.atan(y.radians / x.radians)) + x = Astronoby::Angle.as_radians(@right_ascension.cos) + r = Astronoby::Angle.atan(y.radians / x.radians) longitude = Astronoby::Util::Trigonometry.adjustement_for_arctangent(y, x, r) - latitude = Astronoby::Angle.as_radians( - Math.asin( - Math.sin(declination_in_radians) * Math.cos(obliquity_in_radians) - - Math.cos(declination_in_radians) * Math.sin(obliquity_in_radians) * Math.sin(right_ascension_in_radians) - ) + latitude = Astronoby::Angle.asin( + @declination.sin * obliquity.cos - + @declination.cos * obliquity.sin * @right_ascension.sin ) Ecliptic.new( diff --git a/lib/astronoby/coordinates/horizontal.rb b/lib/astronoby/coordinates/horizontal.rb index cee8e93..09fc007 100644 --- a/lib/astronoby/coordinates/horizontal.rb +++ b/lib/astronoby/coordinates/horizontal.rb @@ -18,23 +18,19 @@ def initialize( end def to_equatorial(time:) - latitude_radians = @latitude.radians + t0 = @altitude.sin * @latitude.sin + + @altitude.cos * @latitude.cos * @azimuth.cos - t0 = Math.sin(@altitude.radians) * Math.sin(latitude_radians) + - Math.cos(@altitude.radians) * Math.cos(latitude_radians) * Math.cos(@azimuth.radians) + declination = Astronoby::Angle.asin(t0) - declination = Astronoby::Angle.as_radians(Math.asin(t0)) + t1 = @altitude.sin - + @latitude.sin * declination.sin - t1 = Math.sin(@altitude.radians) - - Math.sin(latitude_radians) * Math.sin(declination.radians) - - hour_angle_degrees = Astronoby::Angle.as_radians( - Math.acos( - t1 / (Math.cos(latitude_radians) * Math.cos(declination.radians)) - ) + hour_angle_degrees = Astronoby::Angle.acos( + t1 / (@latitude.cos * declination.cos) ).degrees - if Math.sin(@azimuth.radians).positive? + if @azimuth.sin.positive? hour_angle_degrees = Astronoby::Angle.as_degrees( BigDecimal("360") - hour_angle_degrees ).degrees diff --git a/lib/astronoby/nutation.rb b/lib/astronoby/nutation.rb index 77e135f..799b29c 100644 --- a/lib/astronoby/nutation.rb +++ b/lib/astronoby/nutation.rb @@ -25,7 +25,7 @@ def for_ecliptic_longitude 0, 0, ( - -17.2 * Math.sin(moon_ascending_node_longitude.radians) - + -17.2 * moon_ascending_node_longitude.sin - 1.3 * Math.sin(2 * sun_mean_longitude.radians) ) ) @@ -36,7 +36,7 @@ def for_obliquity_of_the_ecliptic 0, 0, ( - 9.2 * Math.cos(moon_ascending_node_longitude.radians) + + 9.2 * moon_ascending_node_longitude.cos + 0.5 * Math.cos(2 * sun_mean_longitude.radians) ) ) diff --git a/lib/astronoby/precession.rb b/lib/astronoby/precession.rb index 8f441ec..223d5f3 100644 --- a/lib/astronoby/precession.rb +++ b/lib/astronoby/precession.rb @@ -19,15 +19,13 @@ def initialize(coordinates, epoch) # Edition: Cambridge University Press # Chapter: 34 - Precession def precess - right_ascension = @coordinates.right_ascension.radians - declination = @coordinates.declination.radians matrix_a = matrix_for_epoch(@coordinates.epoch) matrix_b = matrix_for_epoch(@epoch).transpose vector = Vector[ - Math.cos(right_ascension) * Math.cos(declination), - Math.sin(right_ascension) * Math.cos(declination), - Math.sin(declination) + @coordinates.right_ascension.cos * @coordinates.declination.cos, + @coordinates.right_ascension.sin * @coordinates.declination.cos, + @coordinates.declination.sin ] s = matrix_a * vector @@ -37,9 +35,9 @@ def precess right_ascension: Astronoby::Util::Trigonometry.adjustement_for_arctangent( Astronoby::Angle.as_radians(w[1]), Astronoby::Angle.as_radians(w[0]), - Astronoby::Angle.as_radians(Math.atan(w[1] / w[0])) + Astronoby::Angle.atan(w[1] / w[0]) ), - declination: Astronoby::Angle.as_radians(Math.asin(w[2])), + declination: Astronoby::Angle.asin(w[2]), epoch: @epoch ) end @@ -51,22 +49,22 @@ def matrix_for_epoch(epoch) Astronoby::Epoch::DAYS_PER_JULIAN_CENTURY ) - ζ = Astronoby::Angle.as_degrees( + zeta = Astronoby::Angle.as_degrees( 0.6406161 * t + 0.0000839 * t * t + 0.000005 * t * t * t - ).radians + ) z = Astronoby::Angle.as_degrees( 0.6406161 * t + 0.0003041 * t * t + 0.0000051 * t * t * t - ).radians - θ = Astronoby::Angle.as_degrees( + ) + theta = Astronoby::Angle.as_degrees( 0.5567530 * t - 0.0001185 * t * t - 0.0000116 * t * t * t - ).radians + ) - cx = Math.cos(ζ) - sx = Math.sin(ζ) - cz = Math.cos(z) - sz = Math.sin(z) - ct = Math.cos(θ) - st = Math.sin(θ) + cx = zeta.cos + sx = zeta.sin + cz = z.cos + sz = z.sin + ct = theta.cos + st = theta.sin Matrix[ [ diff --git a/lib/astronoby/refraction.rb b/lib/astronoby/refraction.rb index c9c3a0c..22d4f5b 100644 --- a/lib/astronoby/refraction.rb +++ b/lib/astronoby/refraction.rb @@ -32,7 +32,7 @@ def refract zenith_angle = Astronoby::Angle.as_degrees( 90 - @coordinates.altitude.degrees ) - 0.00452 * @pressure * Math.tan(zenith_angle.radians) / (273 + @temperature) + 0.00452 * @pressure * zenith_angle.tan / (273 + @temperature) else ( @pressure * @@ -54,9 +54,7 @@ def refract Astronoby::Coordinates::Horizontal.new( azimuth: @coordinates.azimuth, - altitude: Astronoby::Angle.as_degrees( - @coordinates.altitude.degrees + refraction_angle.degrees - ), + altitude: @coordinates.altitude + refraction_angle, latitude: @coordinates.latitude, longitude: @coordinates.longitude ) diff --git a/lib/astronoby/true_obliquity.rb b/lib/astronoby/true_obliquity.rb index 6df6adf..dbffdb9 100644 --- a/lib/astronoby/true_obliquity.rb +++ b/lib/astronoby/true_obliquity.rb @@ -10,11 +10,7 @@ def self.for_epoch(epoch) mean_obliquity = Astronoby::MeanObliquity.for_epoch(epoch) nutation = Astronoby::Nutation.for_obliquity_of_the_ecliptic(epoch: epoch) - new( - Astronoby::Angle.as_degrees( - mean_obliquity.value.degrees + nutation.degrees - ) - ) + new(mean_obliquity.value + nutation) end def value diff --git a/spec/astronoby/angle_spec.rb b/spec/astronoby/angle_spec.rb index b7b4b81..1115153 100644 --- a/spec/astronoby/angle_spec.rb +++ b/spec/astronoby/angle_spec.rb @@ -20,6 +20,51 @@ end end + describe "::asin" do + it "returns an Angle object" do + expect(described_class.asin(1)).to be_a(described_class) + end + + it "initializes an angle with the inverse sine of a ratio" do + ratio = 0.5 + angle = described_class.asin(ratio) + precision = 10**-described_class::PRECISION + radians = described_class::PI / 6 + + expect(angle.radians).to be_within(precision).of(radians) + end + end + + describe "::acos" do + it "returns an Angle object" do + expect(described_class.acos(0.5)).to be_a(described_class) + end + + it "initializes an angle with the inverse sine of a ratio" do + ratio = 0.5 + angle = described_class.acos(ratio) + precision = 10**-described_class::PRECISION + radians = described_class::PI / 3 + + expect(angle.radians).to be_within(precision).of(radians) + end + end + + describe "::atan" do + it "returns an Angle object" do + expect(described_class.atan(1)).to be_a(described_class) + end + + it "initializes an angle with the inverse sine of a ratio" do + ratio = 1 + angle = described_class.atan(ratio) + precision = 10**-described_class::PRECISION + radians = described_class::PI / 4 + + expect(angle.radians).to be_within(precision).of(radians) + end + end + describe "#radians" do it "returns the angle value in radian unit" do radians = described_class.new(described_class::PI).radians @@ -93,6 +138,61 @@ end end + describe "#+" do + it "returns a new angle with a value of the two angles added" do + angle_1 = described_class.as_radians(described_class::PI) + angle_2 = described_class.as_degrees(45) + + new_angle = angle_1 + angle_2 + + expect(new_angle.degrees).to eq 225 + end + end + + describe "#-" do + it "returns a new angle with a value of the two angles substracted" do + angle_1 = described_class.as_radians(described_class::PI) + angle_2 = described_class.as_degrees(45) + + new_angle = angle_1 - angle_2 + + expect(new_angle.degrees).to eq 135 + end + end + + describe "#sin" do + it "returns the sine value of the angle" do + radians = described_class::PI / 6 + angle = described_class.as_radians(radians) + + sine = angle.sin.ceil(described_class::PRECISION) + + expect(sine).to eq 0.5 + end + end + + describe "#cos" do + it "returns the cosine value of the angle" do + radians = described_class::PI / 3 + angle = described_class.as_radians(radians) + + cosine = angle.cos.ceil(described_class::PRECISION) + + expect(cosine).to eq 0.5 + end + end + + describe "#tan" do + it "returns the tangent value of the angle" do + radians = described_class::PI / 4 + angle = described_class.as_radians(radians) + + tangent = angle.tan.ceil(described_class::PRECISION) + + expect(tangent).to eq 1 + end + end + describe "#str" do it "returns a String" do angle = described_class.as_degrees(180)