@@ -113,6 +113,14 @@ public class SwerveInputStream implements Supplier<ChassisSpeeds>
113113 * Field oriented chassis output is relative to your current alliance.
114114 */
115115 private Optional <BooleanSupplier > allianceRelative = Optional .empty ();
116+ /**
117+ * Heading offset enable state.
118+ */
119+ private Optional <BooleanSupplier > headingOffsetEnabled = Optional .empty ();
120+ /**
121+ * Heading offset to apply during heading based control.
122+ */
123+ private Optional <Rotation2d > headingOffset = Optional .empty ();
116124 /**
117125 * {@link SwerveController} for simple control over heading.
118126 */
@@ -206,6 +214,8 @@ public SwerveInputStream copy()
206214 newStream .translationCube = translationCube ;
207215 newStream .robotRelative = robotRelative ;
208216 newStream .allianceRelative = allianceRelative ;
217+ newStream .headingOffsetEnabled = headingOffsetEnabled ;
218+ newStream .headingOffset = headingOffset ;
209219 return newStream ;
210220 }
211221
@@ -233,6 +243,42 @@ public SwerveInputStream robotRelative(boolean enabled)
233243 return this ;
234244 }
235245
246+ /**
247+ * Heading offset enabled boolean supplier.
248+ *
249+ * @param enabled Enable state
250+ * @return self
251+ */
252+ public SwerveInputStream headingOffset (BooleanSupplier enabled )
253+ {
254+ headingOffsetEnabled = Optional .of (enabled );
255+ return this ;
256+ }
257+
258+ /**
259+ * Heading offset enable
260+ *
261+ * @param enabled Enable state
262+ * @return self
263+ */
264+ public SwerveInputStream headingOffset (boolean enabled )
265+ {
266+ headingOffsetEnabled = enabled ? Optional .of (() -> enabled ) : Optional .empty ();
267+ return this ;
268+ }
269+
270+ /**
271+ * Set the heading offset angle.
272+ *
273+ * @param angle {@link Rotation2d} offset to apply
274+ * @return self
275+ */
276+ public SwerveInputStream headingOffset (Rotation2d angle )
277+ {
278+ headingOffset = Optional .of (angle );
279+ return this ;
280+ }
281+
236282 /**
237283 * Modify the output {@link ChassisSpeeds} so that it is always relative to your alliance.
238284 *
@@ -709,6 +755,24 @@ private Rotation2d applyAllianceAwareRotation(Rotation2d fieldRelativeRotation)
709755 return fieldRelativeRotation ;
710756 }
711757
758+ /**
759+ * Adds offset to rotation if one is set.
760+ *
761+ * @param fieldRelativeRotation Field-relative {@link Rotation2d} to offset
762+ * @return Offsetted {@link Rotation2d}
763+ */
764+ private Rotation2d applyHeadingOffset (Rotation2d fieldRelativeRotation )
765+ {
766+ if (headingOffsetEnabled .isPresent () && headingOffsetEnabled .get ().getAsBoolean ())
767+ {
768+ if (headingOffset .isPresent ())
769+ {
770+ return fieldRelativeRotation .rotateBy (headingOffset .get ());
771+ }
772+ }
773+ return fieldRelativeRotation ;
774+ }
775+
712776 /**
713777 * Gets a {@link ChassisSpeeds}
714778 *
@@ -759,12 +823,14 @@ public ChassisSpeeds get()
759823 case HEADING ->
760824 {
761825 omegaRadiansPerSecond = swerveController .headingCalculate (swerveDrive .getOdometryHeading ().getRadians (),
762- applyAllianceAwareRotation (Rotation2d .fromRadians (
763- swerveController .getJoystickAngle (
764- controllerHeadingX .get ()
765- .getAsDouble (),
766- controllerHeadingY .get ()
767- .getAsDouble ()))).getRadians ());
826+ applyHeadingOffset (
827+ applyAllianceAwareRotation (
828+ Rotation2d .fromRadians (
829+ swerveController .getJoystickAngle (
830+ controllerHeadingX .get ()
831+ .getAsDouble (),
832+ controllerHeadingY .get ()
833+ .getAsDouble ())))).getRadians ());
768834 speeds = new ChassisSpeeds (vxMetersPerSecond , vyMetersPerSecond , omegaRadiansPerSecond );
769835 break ;
770836 }
0 commit comments