diff --git a/wpilibc/src/main/native/include/frc/interfaces/Gyro.h b/wpilibc/src/main/native/include/frc/interfaces/Gyro.h index 7e322c4313..b74a3cf5d6 100644 --- a/wpilibc/src/main/native/include/frc/interfaces/Gyro.h +++ b/wpilibc/src/main/native/include/frc/interfaces/Gyro.h @@ -66,6 +66,10 @@ class Gyro { /** * Return the heading of the robot as a Rotation2d. * + * The angle is continuous, that is it will continue from 360 to 361 degrees. + * This allows algorithms that wouldn't want to see a discontinuity in the + * gyro output as it sweeps past from 360 to 0 on the second time around. + * * The angle is expected to increase as the gyro turns counterclockwise when * looked at from the top. It needs to follow the NWU axis convention. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java index 1d1c919d57..27377be03e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java @@ -52,6 +52,10 @@ public interface Gyro extends AutoCloseable { /** * Return the heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}. * + *

The angle is continuous, that is it will continue from 360 to 361 degrees. This allows + * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from + * 360 to 0 on the second time around. + * *

The angle is expected to increase as the gyro turns counterclockwise when looked at from the * top. It needs to follow the NWU axis convention. * diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java index 8845ea5f9b..6caefa5d7b 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java @@ -15,15 +15,21 @@ import java.util.Objects; /** * A rotation in a 2D coordinate frame represented by a point on the unit circle (cosine and sine). + * + *

The angle is continuous, that is if a Rotation2d is constructed with 361 degrees, it will + * return 361 degrees. This allows algorithms that wouldn't want to see a discontinuity in the + * rotations as it sweeps past from 360 to 0 on the second time around. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) public class Rotation2d implements Interpolatable { + private final double m_value; private final double m_cos; private final double m_sin; /** Constructs a Rotation2d with a default angle of 0 degrees. */ public Rotation2d() { + m_value = 0.0; m_cos = 1.0; m_sin = 0.0; } @@ -35,7 +41,9 @@ public class Rotation2d implements Interpolatable { */ @JsonCreator public Rotation2d(@JsonProperty(required = true, value = "radians") double value) { - this(Math.cos(value), Math.sin(value)); + m_value = value; + m_cos = Math.cos(value); + m_sin = Math.sin(value); } /** @@ -53,12 +61,13 @@ public class Rotation2d implements Interpolatable { m_sin = 0.0; m_cos = 1.0; } + m_value = Math.atan2(m_sin, m_cos); } /** * Constructs and returns a Rotation2d with the given radian value. * - * @param radians The value of the angle in degrees. + * @param radians The value of the angle in radians. * @return The rotation object with the desired angle value. */ public static Rotation2d fromRadians(double radians) { @@ -118,7 +127,7 @@ public class Rotation2d implements Interpolatable { * @return The inverse of the current rotation. */ public Rotation2d unaryMinus() { - return new Rotation2d(m_cos, -m_sin); + return new Rotation2d(-m_value); } /** @@ -128,7 +137,7 @@ public class Rotation2d implements Interpolatable { * @return The new scaled Rotation2d. */ public Rotation2d times(double scalar) { - return new Rotation2d(getRadians() * scalar); + return new Rotation2d(m_value * scalar); } /** @@ -161,22 +170,22 @@ public class Rotation2d implements Interpolatable { } /** - * Returns the radian value of the Rotation2d within (-pi, pi]. + * Returns the radian value of the Rotation2d. * * @return The radian value of the Rotation2d. */ @JsonProperty public double getRadians() { - return Math.atan2(m_sin, m_cos); + return m_value; } /** - * Returns the degree value of the Rotation2d within (-180, 180]. + * Returns the degree value of the Rotation2d. * * @return The degree value of the Rotation2d. */ public double getDegrees() { - return Math.toDegrees(getRadians()); + return Math.toDegrees(m_value); } /** @@ -185,7 +194,7 @@ public class Rotation2d implements Interpolatable { * @return The number of rotations of the Rotation2d. */ public double getRotations() { - return Units.radiansToRotations(getRadians()); + return Units.radiansToRotations(m_value); } /** @@ -217,7 +226,7 @@ public class Rotation2d implements Interpolatable { @Override public String toString() { - return String.format("Rotation2d(Rads: %.2f, Deg: %.2f)", getRadians(), getDegrees()); + return String.format("Rotation2d(Rads: %.2f, Deg: %.2f)", m_value, Math.toDegrees(m_value)); } /** @@ -237,7 +246,7 @@ public class Rotation2d implements Interpolatable { @Override public int hashCode() { - return Objects.hash(getRadians()); + return Objects.hash(m_value); } @Override diff --git a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h index be3315aa81..e40f3facaf 100644 --- a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h +++ b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h @@ -17,6 +17,11 @@ namespace frc { /** * A rotation in a 2D coordinate frame represented by a point on the unit circle * (cosine and sine). + * + * The angle is continuous, that is if a Rotation2d is constructed with 361 + * degrees, it will return 361 degrees. This allows algorithms that wouldn't + * want to see a discontinuity in the rotations as it sweeps past from 360 to 0 + * on the second time around. */ class WPILIB_DLLEXPORT Rotation2d { public: @@ -124,18 +129,18 @@ class WPILIB_DLLEXPORT Rotation2d { constexpr Rotation2d RotateBy(const Rotation2d& other) const; /** - * Returns the radian value of the rotation within (-pi, pi]. + * Returns the radian value of the rotation. * * @return The radian value of the rotation. */ - constexpr units::radian_t Radians() const; + constexpr units::radian_t Radians() const { return m_value; } /** - * Returns the degree value of the rotation within (-180, 180]. + * Returns the degree value of the rotation. * * @return The degree value of the rotation. */ - constexpr units::degree_t Degrees() const; + constexpr units::degree_t Degrees() const { return m_value; } /** * Returns the cosine of the rotation. @@ -159,6 +164,7 @@ class WPILIB_DLLEXPORT Rotation2d { constexpr double Tan() const { return Sin() / Cos(); } private: + units::radian_t m_value = 0_rad; double m_cos = 1; double m_sin = 0; }; diff --git a/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc b/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc index 9b5e45355d..eb31ebded6 100644 --- a/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc +++ b/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc @@ -13,10 +13,11 @@ namespace frc { constexpr Rotation2d::Rotation2d(units::radian_t value) - : Rotation2d(std::is_constant_evaluated() ? gcem::cos(value.to()) - : std::cos(value.to()), - std::is_constant_evaluated() ? gcem::sin(value.to()) - : std::sin(value.to())) {} + : m_value(value), + m_cos(std::is_constant_evaluated() ? gcem::cos(value.to()) + : std::cos(value.to())), + m_sin(std::is_constant_evaluated() ? gcem::sin(value.to()) + : std::sin(value.to())) {} constexpr Rotation2d::Rotation2d(units::degree_t value) : Rotation2d(units::radian_t{value}) {} @@ -30,14 +31,17 @@ constexpr Rotation2d::Rotation2d(double x, double y) { m_sin = 0.0; m_cos = 1.0; } + m_value = + units::radian_t{std::is_constant_evaluated() ? gcem::atan2(m_sin, m_cos) + : std::atan2(m_sin, m_cos)}; } constexpr Rotation2d Rotation2d::operator-() const { - return Rotation2d{m_cos, -m_sin}; + return Rotation2d{-m_value}; } constexpr Rotation2d Rotation2d::operator*(double scalar) const { - return Rotation2d(Radians() * scalar); + return Rotation2d(m_value * scalar); } constexpr Rotation2d Rotation2d::operator+(const Rotation2d& other) const { @@ -63,14 +67,4 @@ constexpr Rotation2d Rotation2d::RotateBy(const Rotation2d& other) const { Cos() * other.Sin() + Sin() * other.Cos()}; } -constexpr units::radian_t Rotation2d::Radians() const { - return units::radian_t{std::is_constant_evaluated() - ? gcem::atan2(m_sin, m_cos) - : std::atan2(m_sin, m_cos)}; -} - -constexpr units::degree_t Rotation2d::Degrees() const { - return Radians(); -} - } // namespace frc diff --git a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java index c6e75d4741..6702f1d186 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java @@ -13,15 +13,6 @@ import org.junit.jupiter.api.Test; class Rotation2dTest { private static final double kEpsilon = 1E-9; - @Test - void testInScope() { - var rot1 = Rotation2d.fromRadians(Math.PI * 5 / 2); - var rot2 = Rotation2d.fromRadians(Math.PI * 7 / 2); - - assertEquals(Math.PI / 2, rot1.getRadians(), kEpsilon); - assertEquals(-Math.PI / 2, rot2.getRadians(), kEpsilon); - } - @Test void testRadiansToDegrees() { var rot1 = Rotation2d.fromRadians(Math.PI / 3); @@ -80,7 +71,7 @@ class Rotation2dTest { var rot = Rotation2d.fromDegrees(10.0); assertEquals(30.0, rot.times(3.0).getDegrees(), kEpsilon); - assertEquals(50.0, rot.times(41.0).getDegrees(), kEpsilon); + assertEquals(410.0, rot.times(41.0).getDegrees(), kEpsilon); } @Test diff --git a/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp b/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp index 25cbecf9b6..4e2e6835c2 100644 --- a/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp +++ b/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp @@ -10,16 +10,6 @@ using namespace frc; -TEST(Rotation2dTest, InScope) { - const auto rot1 = Rotation2d{units::radian_t{std::numbers::pi * 5 / 2}}; - const auto rot2 = Rotation2d{units::radian_t{std::numbers::pi * 7 / 2}}; - const auto rot3 = Rotation2d{270_deg}; - - EXPECT_DOUBLE_EQ(std::numbers::pi / 2, rot1.Radians().value()); - EXPECT_DOUBLE_EQ(-std::numbers::pi / 2, rot2.Radians().value()); - EXPECT_DOUBLE_EQ(-90, rot3.Degrees().value()); -} - TEST(Rotation2dTest, RadiansToDegrees) { const Rotation2d rot1{units::radian_t{std::numbers::pi / 3.0}}; const Rotation2d rot2{units::radian_t{std::numbers::pi / 4.0}}; @@ -76,8 +66,8 @@ TEST(Rotation2dTest, Inequality) { TEST(Rotation2dTest, Constexpr) { constexpr Rotation2d defaultCtor; - constexpr Rotation2d radianCtor{0.25_rad}; - constexpr Rotation2d degreeCtor{-90_deg}; + constexpr Rotation2d radianCtor{5_rad}; + constexpr Rotation2d degreeCtor{270_deg}; constexpr Rotation2d rotation45{45_deg}; constexpr Rotation2d cartesianCtor{3.5, -3.5}; @@ -86,9 +76,9 @@ TEST(Rotation2dTest, Constexpr) { constexpr auto subtracted = cartesianCtor - degreeCtor; static_assert(defaultCtor.Radians() == 0_rad); - static_assert(degreeCtor.Degrees() == -90_deg); - static_assert(negated.Radians() == -0.25_rad); - static_assert(multiplied.Radians() == 0.5_rad); + static_assert(degreeCtor.Degrees() == 270_deg); + static_assert(negated.Radians() == -5_rad); + static_assert(multiplied.Radians() == 10_rad); static_assert(subtracted == rotation45); static_assert(radianCtor != degreeCtor); }