Add shuttle auto-aim

This change prepares the code for shuttle support by adding a new button,
shot table, and shot target location for the shuttle shot.

Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: Ic70f1b0344fcab8316553cbda4a337cf8353dfd6
diff --git a/y2024/autonomous/autonomous_actor.cc b/y2024/autonomous/autonomous_actor.cc
index 6dd6f8a..19ff2b8 100644
--- a/y2024/autonomous/autonomous_actor.cc
+++ b/y2024/autonomous/autonomous_actor.cc
@@ -187,7 +187,7 @@
 void AutonomousActor::Reset() {
   set_intake_goal(control_loops::superstructure::IntakeGoal::NONE);
   set_note_goal(control_loops::superstructure::NoteGoal::NONE);
-  set_auto_aim(false);
+  set_auto_aim(control_loops::superstructure::AutoAimMode::NONE);
   set_fire(false);
   set_preloaded(false);
   SendSuperstructureGoal();
@@ -494,7 +494,7 @@
 }
 
 void AutonomousActor::Aim() {
-  set_auto_aim(true);
+  set_auto_aim(control_loops::superstructure::AutoAimMode::SPEAKER);
   SendSuperstructureGoal();
 }
 
diff --git a/y2024/autonomous/autonomous_actor.h b/y2024/autonomous/autonomous_actor.h
index 8abc8e3..e0b4f33 100644
--- a/y2024/autonomous/autonomous_actor.h
+++ b/y2024/autonomous/autonomous_actor.h
@@ -28,7 +28,9 @@
   void set_note_goal(control_loops::superstructure::NoteGoal note_goal) {
     note_goal_ = note_goal;
   }
-  void set_auto_aim(bool auto_aim) { auto_aim_ = auto_aim; }
+  void set_auto_aim(control_loops::superstructure::AutoAimMode auto_aim) {
+    auto_aim_ = auto_aim;
+  }
   void set_fire(bool fire) { fire_ = fire; }
   void set_preloaded(bool preloaded) { preloaded_ = preloaded; }
 
@@ -80,7 +82,8 @@
   control_loops::superstructure::NoteGoal note_goal_ =
       control_loops::superstructure::NoteGoal::CATAPULT;
 
-  bool auto_aim_ = false;
+  control_loops::superstructure::AutoAimMode auto_aim_ =
+      control_loops::superstructure::AutoAimMode::NONE;
   bool fire_ = false;
   bool preloaded_ = false;
 };
diff --git a/y2024/constants/common.json b/y2024/constants/common.json
index d6d948b..fc91ef0 100644
--- a/y2024/constants/common.json
+++ b/y2024/constants/common.json
@@ -2,6 +2,15 @@
 
 "common": {
   "target_map": {% include 'y2024/vision/maps/target_map.json' %},
+  "shooter_shuttle_interpolation_table": [
+    {
+      "distance_from_goal": 0.0,
+      "shot_params": {
+          "shot_altitude_angle": 0.5,
+          "shot_speed_over_ground": 16.0
+      }
+    },
+  ],
   "shooter_interpolation_table": [
     {
         "distance_from_goal": 0.7,
@@ -227,6 +236,32 @@
         "theta": 0.0
     }
   },
+  "shooter_shuttle_targets": {
+    "red_alliance": {
+        "pos": {
+            "rows": 3,
+            "cols": 1,
+            "storage_order": "ColMajor",
+            //TODO(Filip): Update positions
+            // The data field contains the x, y and z
+            // coordinates of the shuttle target, amp area on the red alliance
+            "data": [5.968526, 2.654766, 0.006045]
+        },
+        "theta": 0.0
+    },
+    "blue_alliance": {
+        "pos": {
+            "rows": 3,
+            "cols": 1,
+            "storage_order": "ColMajor",
+            //TODO(Filip): Update positions
+            // The data field contains the x, y and z
+            // coordinates of the shuttle target, amp area on the blue alliance
+            "data": [-5.968526, 2.655, 0.006]
+        },
+        "theta": 0.0
+    }
+  },
   "altitude_loading_position": 0.02,
   "turret_loading_position": 0.58,
   "catapult_return_position": 0.0,
@@ -260,4 +295,4 @@
     "red": [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16],
     "blue": [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16]
   }
-}
+}
\ No newline at end of file
diff --git a/y2024/constants/constants.fbs b/y2024/constants/constants.fbs
index a558c9b..4cc3502 100644
--- a/y2024/constants/constants.fbs
+++ b/y2024/constants/constants.fbs
@@ -174,6 +174,7 @@
 table Common {
   target_map:frc971.vision.TargetMap (id: 0);
   shooter_interpolation_table: [InterpolationTablePoint] (id: 1);
+  shooter_shuttle_interpolation_table: [InterpolationTablePoint] (id: 30);
   intake_roller_voltages:IntakeRollerVoltages (id : 2);
   intake_pivot_set_points:IntakePivotSetPoints (id: 3);
   intake_pivot:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 4);
@@ -190,6 +191,7 @@
   extend:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 15);
   extend_roller_voltages:ExtendRollerVoltages (id: 16);
   shooter_targets:ShooterTargets (id: 17);
+  shooter_shuttle_targets:ShooterTargets (id: 29);
   altitude_loading_position: double (id: 18);
   retention_roller_voltages:RetentionRollerVoltages (id: 19);
   min_altitude_shooting_angle: double (id: 20);
diff --git a/y2024/control_loops/superstructure/BUILD b/y2024/control_loops/superstructure/BUILD
index 6c67b26..4e33f20 100644
--- a/y2024/control_loops/superstructure/BUILD
+++ b/y2024/control_loops/superstructure/BUILD
@@ -220,6 +220,7 @@
         "aiming.h",
     ],
     deps = [
+        ":superstructure_goal_fbs",
         ":superstructure_status_fbs",
         "//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
         "//frc971/control_loops/aiming",
diff --git a/y2024/control_loops/superstructure/aiming.cc b/y2024/control_loops/superstructure/aiming.cc
index bf68527..200029e 100644
--- a/y2024/control_loops/superstructure/aiming.cc
+++ b/y2024/control_loops/superstructure/aiming.cc
@@ -18,6 +18,10 @@
       interpolation_table_(
           y2024::constants::Values::InterpolationTableFromFlatbuffer(
               robot_constants_->common()->shooter_interpolation_table())),
+      interpolation_table_shuttle_(
+          y2024::constants::Values::InterpolationTableFromFlatbuffer(
+              robot_constants_->common()
+                  ->shooter_shuttle_interpolation_table())),
       joystick_state_fetcher_(
           event_loop_->MakeFetcher<aos::JoystickState>("/aos")) {}
 
@@ -25,7 +29,12 @@
     const frc971::control_loops::drivetrain::Status *status,
     frc971::control_loops::aiming::ShotMode shot_mode,
     frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic
-        *turret_goal) {
+        *turret_goal,
+    AutoAimMode auto_aim_mode) {
+  // Default to aiming at the speaker in the absence of any other targets.
+  if (auto_aim_mode == AutoAimMode::NONE) {
+    auto_aim_mode = AutoAimMode::SPEAKER;
+  }
   if (status == nullptr) {
     return;
   }
@@ -41,22 +50,13 @@
     alliance = joystick_state_fetcher_->alliance();
   }
 
-  const frc971::control_loops::Pose red_alliance_goal(
-      frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
-                                      ->shooter_targets()
-                                      ->red_alliance()
-                                      ->pos()),
-      robot_constants_->common()->shooter_targets()->red_alliance()->theta());
-
-  const frc971::control_loops::Pose blue_alliance_goal(
-      frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
-                                      ->shooter_targets()
-                                      ->blue_alliance()
-                                      ->pos()),
-      robot_constants_->common()->shooter_targets()->blue_alliance()->theta());
+  frc971::shooter_interpolation::InterpolationTable<
+      y2024::constants::Values::ShotParams> *current_interpolation_table =
+      interpolation_tables_.at(auto_aim_mode);
 
   const frc971::control_loops::Pose goal =
-      alliance == aos::Alliance::kRed ? red_alliance_goal : blue_alliance_goal;
+      alliance == aos::Alliance::kRed ? red_alliance_goals_.at(auto_aim_mode)
+                                      : blue_alliance_goals_.at(auto_aim_mode);
 
   const Eigen::Vector2d linear_angular =
       drivetrain_config_.Tlr_to_la() *
@@ -71,7 +71,7 @@
       ShotConfig{goal, shot_mode,
                  frc971::constants::Range::FromFlatbuffer(
                      robot_constants_->common()->turret()->range()),
-                 interpolation_table_.Get(current_goal_.target_distance)
+                 current_interpolation_table->Get(current_goal_.target_distance)
                      .shot_speed_over_ground,
                  /*wrap_mode=*/0.15, M_PI - kTurretZeroOffset},
       RobotState{
diff --git a/y2024/control_loops/superstructure/aiming.h b/y2024/control_loops/superstructure/aiming.h
index 97e319d..de4c117 100644
--- a/y2024/control_loops/superstructure/aiming.h
+++ b/y2024/control_loops/superstructure/aiming.h
@@ -1,6 +1,8 @@
 #ifndef Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_AIMING_H_
 #define Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_AIMING_H_
 
+#include <map>
+
 #include "frc971/control_loops/aiming/aiming.h"
 #include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
 #include "frc971/control_loops/pose.h"
@@ -9,8 +11,8 @@
 #include "y2024/constants.h"
 #include "y2024/constants/constants_generated.h"
 #include "y2024/control_loops/drivetrain/drivetrain_base.h"
+#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
 #include "y2024/control_loops/superstructure/superstructure_status_generated.h"
-
 using y2024::control_loops::superstructure::AimerStatus;
 
 namespace y2024::control_loops::superstructure {
@@ -26,7 +28,8 @@
       const frc971::control_loops::drivetrain::Status *status,
       frc971::control_loops::aiming::ShotMode shot_mode,
       frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic
-          *turret_goal);
+          *turret_goal,
+      AutoAimMode auto_aim_mode);
 
   double DistanceToGoal() const { return current_goal_.virtual_shot_distance; }
 
@@ -45,6 +48,64 @@
       y2024::constants::Values::ShotParams>
       interpolation_table_;
 
+  frc971::shooter_interpolation::InterpolationTable<
+      y2024::constants::Values::ShotParams>
+      interpolation_table_shuttle_;
+
+  std::map<AutoAimMode, frc971::shooter_interpolation::InterpolationTable<
+                            y2024::constants::Values::ShotParams> *>
+      interpolation_tables_ = {
+          {AutoAimMode::SPEAKER, &interpolation_table_},
+          {AutoAimMode::SHUTTLE, &interpolation_table_shuttle_}};
+
+  std::map<AutoAimMode, frc971::control_loops::Pose> red_alliance_goals_ = {
+      {AutoAimMode::SPEAKER,
+       frc971::control_loops::Pose(
+           frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+                                           ->shooter_targets()
+                                           ->red_alliance()
+                                           ->pos()),
+           robot_constants_->common()
+               ->shooter_targets()
+               ->red_alliance()
+               ->theta())},
+      {
+          AutoAimMode::SHUTTLE,
+          frc971::control_loops::Pose(
+              frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+                                              ->shooter_shuttle_targets()
+                                              ->red_alliance()
+                                              ->pos()),
+              robot_constants_->common()
+                  ->shooter_shuttle_targets()
+                  ->red_alliance()
+                  ->theta()),
+      }};
+
+  std::map<AutoAimMode, frc971::control_loops::Pose> blue_alliance_goals_ = {
+      {AutoAimMode::SPEAKER,
+       frc971::control_loops::Pose(
+           frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+                                           ->shooter_targets()
+                                           ->blue_alliance()
+                                           ->pos()),
+           robot_constants_->common()
+               ->shooter_targets()
+               ->blue_alliance()
+               ->theta())},
+      {
+          AutoAimMode::SHUTTLE,
+          frc971::control_loops::Pose(
+              frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+                                              ->shooter_shuttle_targets()
+                                              ->blue_alliance()
+                                              ->pos()),
+              robot_constants_->common()
+                  ->shooter_shuttle_targets()
+                  ->blue_alliance()
+                  ->theta()),
+      }};
+
   aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
 
   frc971::control_loops::aiming::TurretGoal current_goal_;
diff --git a/y2024/control_loops/superstructure/shooter.cc b/y2024/control_loops/superstructure/shooter.cc
index 1cd0191..0c52360 100644
--- a/y2024/control_loops/superstructure/shooter.cc
+++ b/y2024/control_loops/superstructure/shooter.cc
@@ -31,6 +31,10 @@
       interpolation_table_(
           y2024::constants::Values::InterpolationTableFromFlatbuffer(
               robot_constants_->common()->shooter_interpolation_table())),
+      interpolation_table_shuttle_(
+          y2024::constants::Values::InterpolationTableFromFlatbuffer(
+              robot_constants_->common()
+                  ->shooter_shuttle_interpolation_table())),
       debouncer_(std::chrono::milliseconds(100), std::chrono::milliseconds(8)) {
 }
 
@@ -116,7 +120,8 @@
     PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
         altitude_goal_builder.get(),
         robot_constants_->common()->altitude_avoid_extend_collision_position());
-  } else if (shooter_goal == nullptr || !shooter_goal->auto_aim() ||
+  } else if (shooter_goal == nullptr ||
+             (shooter_goal->auto_aim() == AutoAimMode::NONE) ||
              (!piece_loaded && state_ == CatapultState::READY)) {
     // We don't have the note so we should be ready to intake it.
     PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
@@ -137,13 +142,21 @@
   aimer_.Update(
       drivetrain_status_fetcher_.get(),
       frc971::control_loops::aiming::ShotMode::kShootOnTheFly,
-      aiming ? turret_goal_builder.get() : auto_aim_goal_builder.get());
+      aiming ? turret_goal_builder.get() : auto_aim_goal_builder.get(),
+      shooter_goal != nullptr ? shooter_goal->auto_aim() : AutoAimMode::NONE);
 
   // We have a game piece and are being asked to aim.
   constants::Values::ShotParams shot_params;
+  frc971::shooter_interpolation::InterpolationTable<
+      y2024::constants::Values::ShotParams> *interpolation_table =
+      (shooter_goal != nullptr &&
+       shooter_goal->auto_aim() == AutoAimMode::SHUTTLE)
+          ? &interpolation_table_shuttle_
+          : &interpolation_table_;
   if ((piece_loaded || state_ == CatapultState::FIRING) &&
-      shooter_goal != nullptr && shooter_goal->auto_aim() &&
-      interpolation_table_.GetInRange(distance_to_goal, &shot_params)) {
+      shooter_goal != nullptr &&
+      (shooter_goal->auto_aim() != AutoAimMode::NONE) &&
+      interpolation_table->GetInRange(distance_to_goal, &shot_params)) {
     PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
         altitude_goal_builder.get(), shot_params.shot_altitude_angle);
   }
@@ -153,14 +166,16 @@
 
   const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
       *turret_goal =
-          (shooter_goal != nullptr && !shooter_goal->auto_aim() &&
+          (shooter_goal != nullptr &&
+           (shooter_goal->auto_aim() == AutoAimMode::NONE) &&
            (piece_loaded || state_ == CatapultState::FIRING || climbing) &&
            shooter_goal->has_turret_position())
               ? shooter_goal->turret_position()
               : &turret_goal_builder->AsFlatbuffer();
 
   const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
-      *altitude_goal = (shooter_goal != nullptr && !shooter_goal->auto_aim() &&
+      *altitude_goal = (shooter_goal != nullptr &&
+                        (shooter_goal->auto_aim() == AutoAimMode::NONE) &&
                         (piece_loaded || state_ == CatapultState::FIRING) &&
                         shooter_goal->has_altitude_position())
                            ? shooter_goal->altitude_position()
diff --git a/y2024/control_loops/superstructure/shooter.h b/y2024/control_loops/superstructure/shooter.h
index dc57507..f0561a1 100644
--- a/y2024/control_loops/superstructure/shooter.h
+++ b/y2024/control_loops/superstructure/shooter.h
@@ -151,6 +151,10 @@
       y2024::constants::Values::ShotParams>
       interpolation_table_;
 
+  frc971::shooter_interpolation::InterpolationTable<
+      y2024::constants::Values::ShotParams>
+      interpolation_table_shuttle_;
+
   Debouncer debouncer_;
 
   uint32_t shot_count_ = 0;
diff --git a/y2024/control_loops/superstructure/superstructure_goal.fbs b/y2024/control_loops/superstructure/superstructure_goal.fbs
index c217bcc..2a5fb00 100644
--- a/y2024/control_loops/superstructure/superstructure_goal.fbs
+++ b/y2024/control_loops/superstructure/superstructure_goal.fbs
@@ -26,11 +26,16 @@
     STOWED = 2,
 }
 
+enum AutoAimMode: ubyte {
+    NONE = 0, // No auto aim.
+    SPEAKER = 1, // Auto aim for the speaker shot.
+    SHUTTLE = 2, // Auto aim for the shuttle shot.
+}
+
 table ShooterGoal {
     catapult_goal:frc971.control_loops.catapult.CatapultGoal (id: 0);
 
-    // If true we ignore the other provided positions
-    auto_aim: bool (id: 1);
+    auto_aim: AutoAimMode (id: 1);
 
     // Position for the turret when we aren't auto aiming
     turret_position: frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemGoal (id: 2);
diff --git a/y2024/control_loops/superstructure/superstructure_lib_test.cc b/y2024/control_loops/superstructure/superstructure_lib_test.cc
index 5544fc0..512ccab 100644
--- a/y2024/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2024/control_loops/superstructure/superstructure_lib_test.cc
@@ -375,7 +375,8 @@
         superstructure_status_fetcher_->uncompleted_note_goal() !=
             NoteStatus::TRAP) {
       if (superstructure_goal_fetcher_->shooter_goal()->has_turret_position() &&
-          !superstructure_goal_fetcher_->shooter_goal()->auto_aim()) {
+          (superstructure_goal_fetcher_->shooter_goal()->auto_aim() ==
+           AutoAimMode::NONE)) {
         EXPECT_NEAR(
             superstructure_goal_fetcher_->shooter_goal()
                 ->turret_position()
@@ -388,7 +389,8 @@
     if (superstructure_goal_fetcher_->has_shooter_goal()) {
       if (superstructure_goal_fetcher_->shooter_goal()
               ->has_altitude_position() &&
-          !superstructure_goal_fetcher_->shooter_goal()->auto_aim() &&
+          (superstructure_goal_fetcher_->shooter_goal()->auto_aim() ==
+           AutoAimMode::NONE) &&
           (superstructure_status_fetcher_->uncompleted_note_goal() !=
                NoteStatus::AMP &&
            superstructure_status_fetcher_->uncompleted_note_goal() !=
@@ -561,7 +563,7 @@
 
     shooter_goal_builder.add_turret_position(turret_offset);
     shooter_goal_builder.add_altitude_position(altitude_offset);
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -610,7 +612,7 @@
 
     shooter_goal_builder.add_turret_position(turret_offset);
     shooter_goal_builder.add_altitude_position(altitude_offset);
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
     shooter_goal_builder.add_preloaded(true);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
@@ -661,7 +663,7 @@
 
     shooter_goal_builder.add_turret_position(turret_offset);
     shooter_goal_builder.add_altitude_position(altitude_offset);
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -706,7 +708,7 @@
 
     shooter_goal_builder.add_turret_position(turret_offset);
     shooter_goal_builder.add_altitude_position(altitude_offset);
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -961,7 +963,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -1002,7 +1004,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -1041,7 +1043,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -1109,7 +1111,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -1188,7 +1190,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
     shooter_goal_builder.add_catapult_goal(catapult_offset);
     shooter_goal_builder.add_altitude_position(altitude_offset);
     shooter_goal_builder.add_turret_position(turret_offset);
@@ -1232,7 +1234,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(false);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::NONE);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
@@ -1357,7 +1359,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
     shooter_goal_builder.add_preloaded(true);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
@@ -1409,7 +1411,7 @@
     ShooterGoal::Builder shooter_goal_builder =
         builder.MakeBuilder<ShooterGoal>();
 
-    shooter_goal_builder.add_auto_aim(true);
+    shooter_goal_builder.add_auto_aim(AutoAimMode::SPEAKER);
 
     flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
         shooter_goal_builder.Finish();
diff --git a/y2024/joystick_reader.cc b/y2024/joystick_reader.cc
index fcece24..a915507 100644
--- a/y2024/joystick_reader.cc
+++ b/y2024/joystick_reader.cc
@@ -59,7 +59,7 @@
 const ButtonLocation kShoot(0, 0);
 const ButtonLocation kRaiseClimber(3, 2);
 const ButtonLocation kRaiseFastClimber(3, 1);
-const ButtonLocation kExtraButtonOne(0, 0);
+const ButtonLocation kAimShuttle(2, 10);
 const ButtonLocation kExtraButtonTwo(0, 0);
 const ButtonLocation kExtraButtonThree(0, 0);
 const ButtonLocation kExtraButtonFour(0, 0);
@@ -134,7 +134,13 @@
           superstructure::NoteGoal::NONE);
     }
     auto shooter_goal = superstructure_goal_builder->add_shooter_goal();
-    shooter_goal->set_auto_aim(data.IsPressed(kAutoAim));
+    if (data.IsPressed(kAutoAim)) {
+      shooter_goal->set_auto_aim(
+          control_loops::superstructure::AutoAimMode::SPEAKER);
+    } else if (data.IsPressed(kAimShuttle)) {
+      shooter_goal->set_auto_aim(
+          control_loops::superstructure::AutoAimMode::SHUTTLE);
+    }
 
     // Updating aiming for shooter goal, only one type of aim should be possible
     // at a time, auto-aiming is preferred over the setpoints.