Merge "Mostly cosmetic changes to the code"
diff --git a/aos/starter/BUILD b/aos/starter/BUILD
index a7d7e02..0ebe0f5 100644
--- a/aos/starter/BUILD
+++ b/aos/starter/BUILD
@@ -1,5 +1,6 @@
 load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
 load("//aos:config.bzl", "aos_config")
+load("@com_github_google_flatbuffers//:typescript.bzl", "flatbuffer_ts_library")
 
 exports_files(["roborio_irq_config.json"])
 
@@ -191,6 +192,14 @@
     deps = ["//aos/util:process_info_fbs"],
 )
 
+flatbuffer_ts_library(
+    name = "starter_ts_fbs",
+    srcs = ["starter.fbs"],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = ["//aos/util:process_info_ts_fbs"],
+)
+
 static_flatbuffer(
     name = "kthread_fbs",
     srcs = ["kthread.fbs"],
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 599d477..206bd8f 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -1,6 +1,7 @@
 load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
 load("//aos:flatbuffers.bzl", "cc_static_flatbuffer")
 load("config_validator_macro.bzl", "config_validator_test")
+load("@com_github_google_flatbuffers//:typescript.bzl", "flatbuffer_ts_library")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -373,6 +374,13 @@
     visibility = ["//visibility:public"],
 )
 
+flatbuffer_ts_library(
+    name = "process_info_ts_fbs",
+    srcs = ["process_info.fbs"],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+)
+
 cc_library(
     name = "top",
     srcs = ["top.cc"],
diff --git a/frc971/orin/argus_camera.cc b/frc971/orin/argus_camera.cc
index 42686a1..811efdd 100644
--- a/frc971/orin/argus_camera.cc
+++ b/frc971/orin/argus_camera.cc
@@ -31,8 +31,8 @@
              "Mode to use.  Don't change unless you know what you are doing.");
 DEFINE_int32(camera, 0, "Camera number");
 DEFINE_int32(mode, 0, "Mode number to use.");
-DEFINE_int32(exposure, 200000, "Exposure number to use.");
-DEFINE_int32(gain, 5, "gain number to use.");
+DEFINE_int32(exposure, 100, "Exposure number to use.");
+DEFINE_int32(gain, 10, "gain number to use.");
 DEFINE_int32(width, 1456, "Image width");
 DEFINE_int32(height, 1088, "Image height");
 DEFINE_double(rgain, 1.0, "R gain");
@@ -226,8 +226,8 @@
 
     {
       auto range = i_sensor_mode->getFrameDurationRange();
-      LOG(INFO) << "Min: " << range.min() << ", " << range.max();
-      LOG(INFO) << "type " << i_sensor_mode->getSensorModeType().getName();
+      LOG(INFO) << "Frame duration min: " << range.min() << ", " << range.max()
+                << ", type " << i_sensor_mode->getSensorModeType().getName();
     }
 
     // Create the capture session using the first device and get the core
@@ -326,21 +326,22 @@
         Argus::interface_cast<Argus::IAutoControlSettings>(
             i_request->getAutoControlSettings());
     CHECK(i_auto_control_settings != nullptr);
-    i_auto_control_settings->setAwbMode(Argus::AWB_MODE_OFF);
+    CHECK_EQ(i_auto_control_settings->setAwbMode(Argus::AWB_MODE_OFF),
+             Argus::STATUS_OK);
 
     i_auto_control_settings->setAeLock(false);
     Argus::Range<float> isp_digital_gain_range;
     isp_digital_gain_range.min() = 1;
     isp_digital_gain_range.max() = 1;
-    i_auto_control_settings->setIspDigitalGainRange(isp_digital_gain_range);
+    CHECK_EQ(
+        i_auto_control_settings->setIspDigitalGainRange(isp_digital_gain_range),
+        Argus::STATUS_OK);
 
     Argus::IEdgeEnhanceSettings *i_ee_settings =
         Argus::interface_cast<Argus::IEdgeEnhanceSettings>(request_);
     CHECK(i_ee_settings != nullptr);
 
-    i_ee_settings->setEdgeEnhanceStrength(0);
-
-    i_request->enableOutputStream(output_stream_.get());
+    CHECK_EQ(i_ee_settings->setEdgeEnhanceStrength(0), Argus::STATUS_OK);
 
     Argus::ISourceSettings *i_source_settings =
         Argus::interface_cast<Argus::ISourceSettings>(
@@ -349,17 +350,22 @@
 
     i_source_settings->setFrameDurationRange(
         i_sensor_mode->getFrameDurationRange().min());
-    i_source_settings->setSensorMode(sensor_modes[FLAGS_mode]);
+    CHECK_EQ(i_source_settings->setSensorMode(sensor_modes[FLAGS_mode]),
+             Argus::STATUS_OK);
 
     Argus::Range<float> sensor_mode_analog_gain_range;
     sensor_mode_analog_gain_range.min() = FLAGS_gain;
     sensor_mode_analog_gain_range.max() = FLAGS_gain;
-    i_source_settings->setGainRange(sensor_mode_analog_gain_range);
+    CHECK_EQ(i_source_settings->setGainRange(sensor_mode_analog_gain_range),
+             Argus::STATUS_OK);
 
     Argus::Range<uint64_t> limit_exposure_time_range;
-    limit_exposure_time_range.min() = FLAGS_exposure;
-    limit_exposure_time_range.max() = FLAGS_exposure;
-    i_source_settings->setExposureTimeRange(limit_exposure_time_range);
+    limit_exposure_time_range.min() = FLAGS_exposure * 1000;
+    limit_exposure_time_range.max() = FLAGS_exposure * 1000;
+    CHECK_EQ(i_source_settings->setExposureTimeRange(limit_exposure_time_range),
+             Argus::STATUS_OK);
+
+    i_request->enableOutputStream(output_stream_.get());
   }
 
   void Start() {
@@ -518,6 +524,8 @@
 };
 
 int Main() {
+  std::this_thread::sleep_for(std::chrono::seconds(FLAGS_camera + 1));
+
   aos::FlatbufferDetachedBuffer<aos::Configuration> config =
       aos::configuration::ReadConfig(FLAGS_config);
 
diff --git a/frc971/orin/gpu_apriltag.cc b/frc971/orin/gpu_apriltag.cc
index cc3c7f4..425e45f 100644
--- a/frc971/orin/gpu_apriltag.cc
+++ b/frc971/orin/gpu_apriltag.cc
@@ -26,7 +26,7 @@
     "1.0.");
 DEFINE_double(min_decision_margin, 50.0,
               "Minimum decision margin (confidence) for an apriltag detection");
-DEFINE_int32(pixel_border, 10,
+DEFINE_int32(pixel_border, 150,
              "Size of image border within which to reject detected corners");
 DEFINE_uint64(pose_estimation_iterations, 50,
               "Number of iterations for apriltag pose estimation.");
@@ -282,12 +282,12 @@
       if (!converged) {
         VLOG(1) << "Rejecting detection because Undistort failed to coverge";
 
-        // Send rejected corner points to foxglove in red
+        // Send corner points rejected to to lack of convergence in orange
         std::vector<cv::Point2f> rejected_corner_points =
             MakeCornerVector(gpu_detection);
         foxglove_corners.push_back(frc971::vision::BuildPointsAnnotation(
             builder.fbb(), eof, rejected_corner_points,
-            std::vector<double>{1.0, 0.0, 0.0, 0.5}));
+            std::vector<double>{1.0, 0.65, 0.0, 0.5}));
         rejections_++;
         continue;
       }
diff --git a/frc971/vision/intrinsics_calibration.cc b/frc971/vision/intrinsics_calibration.cc
index d3ddba7..e0ec38c 100644
--- a/frc971/vision/intrinsics_calibration.cc
+++ b/frc971/vision/intrinsics_calibration.cc
@@ -44,6 +44,13 @@
          "camera moves.";
   std::unique_ptr<aos::ExitHandle> exit_handle = event_loop.MakeExitHandle();
 
+  CHECK(aos::network::ParsePiOrOrin(hostname))
+      << "Failed to parse node type from " << hostname
+      << ".  Should be of form orin1-971-1";
+  CHECK(aos::network::ParsePiOrOrinNumber(hostname))
+      << "Failed to parse node number from " << hostname
+      << ".  Should be of form orin2-7971-1";
+
   std::string camera_name = absl::StrCat(
       "/", aos::network::ParsePiOrOrin(hostname).value(),
       std::to_string(aos::network::ParsePiOrOrinNumber(hostname).value()),
diff --git a/y2024/BUILD b/y2024/BUILD
index 7568050..6687dac 100644
--- a/y2024/BUILD
+++ b/y2024/BUILD
@@ -340,6 +340,7 @@
         "//aos/network:log_web_proxy_main",
         "//y2024/www:field_main_bundle.min.js",
         "//y2024/www:files",
+        "//y2024/www:starter_main_bundle.min.js",
     ],
     target_compatible_with = ["@platforms//os:linux"],
 )
diff --git a/y2024/autonomous/auto_splines.cc b/y2024/autonomous/auto_splines.cc
index 66b1b28..9e52648 100644
--- a/y2024/autonomous/auto_splines.cc
+++ b/y2024/autonomous/auto_splines.cc
@@ -180,4 +180,47 @@
                    alliance);
 }
 
+flatbuffers::Offset<frc971::MultiSpline>
+AutonomousSplines::TwoPieceStealSpline1(
+    aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+        *builder,
+    aos::Alliance alliance) {
+  return FixSpline(builder,
+                   aos::CopyFlatBuffer<frc971::MultiSpline>(
+                       two_piece_steal_spline_1_, builder->fbb()),
+                   alliance);
+}
+
+flatbuffers::Offset<frc971::MultiSpline>
+AutonomousSplines::TwoPieceStealSpline2(
+    aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+        *builder,
+    aos::Alliance alliance) {
+  return FixSpline(builder,
+                   aos::CopyFlatBuffer<frc971::MultiSpline>(
+                       two_piece_steal_spline_2_, builder->fbb()),
+                   alliance);
+}
+flatbuffers::Offset<frc971::MultiSpline>
+AutonomousSplines::TwoPieceStealSpline3(
+    aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+        *builder,
+    aos::Alliance alliance) {
+  return FixSpline(builder,
+                   aos::CopyFlatBuffer<frc971::MultiSpline>(
+                       two_piece_steal_spline_3_, builder->fbb()),
+                   alliance);
+}
+
+flatbuffers::Offset<frc971::MultiSpline>
+AutonomousSplines::TwoPieceStealSpline4(
+    aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+        *builder,
+    aos::Alliance alliance) {
+  return FixSpline(builder,
+                   aos::CopyFlatBuffer<frc971::MultiSpline>(
+                       two_piece_steal_spline_4_, builder->fbb()),
+                   alliance);
+}
+
 }  // namespace y2024::autonomous
diff --git a/y2024/autonomous/auto_splines.h b/y2024/autonomous/auto_splines.h
index 93360d3..8eb38e8 100644
--- a/y2024/autonomous/auto_splines.h
+++ b/y2024/autonomous/auto_splines.h
@@ -32,7 +32,19 @@
         four_piece_spline_4_(aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
             "splines/five_note.3.json")),
         four_piece_spline_5_(aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
-            "splines/five_note.4.json")) {}
+            "splines/five_note.4.json")),
+        two_piece_steal_spline_1_(
+            aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
+                "splines/five_note.0.json")),
+        two_piece_steal_spline_2_(
+            aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
+                "splines/five_note.1.json")),
+        two_piece_steal_spline_3_(
+            aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
+                "splines/five_note.2.json")),
+        two_piece_steal_spline_4_(
+            aos::JsonFileToFlatbuffer<frc971::MultiSpline>(
+                "splines/five_note.3.json")) {}
   static flatbuffers::Offset<frc971::MultiSpline> BasicSSpline(
       aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
           *builder,
@@ -71,6 +83,23 @@
           *builder,
       aos::Alliance alliance);
 
+  flatbuffers::Offset<frc971::MultiSpline> TwoPieceStealSpline1(
+      aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+          *builder,
+      aos::Alliance alliance);
+  flatbuffers::Offset<frc971::MultiSpline> TwoPieceStealSpline2(
+      aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+          *builder,
+      aos::Alliance alliance);
+  flatbuffers::Offset<frc971::MultiSpline> TwoPieceStealSpline3(
+      aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+          *builder,
+      aos::Alliance alliance);
+  flatbuffers::Offset<frc971::MultiSpline> TwoPieceStealSpline4(
+      aos::Sender<frc971::control_loops::drivetrain::SplineGoal>::Builder
+          *builder,
+      aos::Alliance alliance);
+
  private:
   aos::FlatbufferDetachedBuffer<frc971::MultiSpline> test_spline_;
   aos::FlatbufferDetachedBuffer<frc971::MultiSpline> mobility_and_shoot_spline_;
@@ -79,6 +108,11 @@
   aos::FlatbufferDetachedBuffer<frc971::MultiSpline> four_piece_spline_3_;
   aos::FlatbufferDetachedBuffer<frc971::MultiSpline> four_piece_spline_4_;
   aos::FlatbufferDetachedBuffer<frc971::MultiSpline> four_piece_spline_5_;
+
+  aos::FlatbufferDetachedBuffer<frc971::MultiSpline> two_piece_steal_spline_1_;
+  aos::FlatbufferDetachedBuffer<frc971::MultiSpline> two_piece_steal_spline_2_;
+  aos::FlatbufferDetachedBuffer<frc971::MultiSpline> two_piece_steal_spline_3_;
+  aos::FlatbufferDetachedBuffer<frc971::MultiSpline> two_piece_steal_spline_4_;
 };
 
 }  // namespace y2024::autonomous
diff --git a/y2024/autonomous/autonomous_actor.cc b/y2024/autonomous/autonomous_actor.cc
index 9b4f7f8..f50e8f6 100644
--- a/y2024/autonomous/autonomous_actor.cc
+++ b/y2024/autonomous/autonomous_actor.cc
@@ -105,6 +105,25 @@
       starting_position_ = four_piece_splines_.value()[0].starting_position();
       CHECK(starting_position_);
       break;
+    case AutonomousMode::TWO_PIECE_STEAL:
+      AOS_LOG(INFO, "TWO_PIECE_STEAL replanning!");
+      two_piece_steal_splines_ = {
+          PlanSpline(
+              std::bind(&AutonomousSplines::TwoPieceStealSpline1,
+                        &auto_splines_, std::placeholders::_1, alliance_),
+              SplineDirection::kForward),
+          PlanSpline(
+              std::bind(&AutonomousSplines::TwoPieceStealSpline2,
+                        &auto_splines_, std::placeholders::_1, alliance_),
+              SplineDirection::kBackward),
+          PlanSpline(
+              std::bind(&AutonomousSplines::TwoPieceStealSpline3,
+                        &auto_splines_, std::placeholders::_1, alliance_),
+              SplineDirection::kForward),
+          PlanSpline(
+              std::bind(&AutonomousSplines::TwoPieceStealSpline4,
+                        &auto_splines_, std::placeholders::_1, alliance_),
+              SplineDirection::kForward)};
   }
 
   is_planned_ = true;
@@ -134,6 +153,9 @@
     case AutonomousMode::FOUR_PIECE:
       FourPieceAuto();
       break;
+    case AutonomousMode::TWO_PIECE_STEAL:
+      TwoPieceStealAuto();
+      break;
   }
   return true;
 }
@@ -330,6 +352,99 @@
       aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
 }
 
+void AutonomousActor::TwoPieceStealAuto() {
+  aos::monotonic_clock::time_point start_time = aos::monotonic_clock::now();
+
+  CHECK(two_piece_steal_splines_);
+  auto &splines = *two_piece_steal_splines_;
+
+  uint32_t initial_shot_count = shot_count();
+
+  // Always be aiming & firing.
+  Aim();
+  if (!WaitForPreloaded()) return;
+
+  std::this_thread::sleep_for(chrono::milliseconds(500));
+  Shoot();
+
+  AOS_LOG(
+      INFO, "Shooting Preloaded Note %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  if (!WaitForNoteFired(initial_shot_count, std::chrono::seconds(2))) return;
+
+  AOS_LOG(
+      INFO, "Shot first note %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  Intake();
+  StopFiring();
+
+  AOS_LOG(
+      INFO, "Starting Spline 1 %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  if (!splines[0].WaitForPlan()) return;
+
+  splines[0].Start();
+
+  if (!splines[0].WaitForSplineDistanceRemaining(0.01)) return;
+
+  if (!splines[1].WaitForPlan()) return;
+
+  AOS_LOG(
+      INFO, "Starting second spline %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  splines[1].Start();
+
+  if (!splines[1].WaitForSplineDistanceRemaining(0.01)) return;
+
+  AOS_LOG(
+      INFO, "Finished second spline %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  std::this_thread::sleep_for(chrono::milliseconds(250));
+
+  Shoot();
+
+  if (!WaitForNoteFired(initial_shot_count + 1, std::chrono::seconds(2)))
+    return;
+
+  StopFiring();
+
+  AOS_LOG(
+      INFO, "Shot second note, starting drive %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+
+  if (!splines[2].WaitForPlan()) return;
+
+  AOS_LOG(
+      INFO, "Starting third spline %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+  splines[2].Start();
+
+  if (!splines[2].WaitForSplineDistanceRemaining(0.01)) return;
+
+  if (!splines[3].WaitForPlan()) return;
+
+  AOS_LOG(
+      INFO, "Starting fourth spline %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+  splines[3].Start();
+
+  if (!splines[3].WaitForSplineDistanceRemaining(0.01)) return;
+
+  Shoot();
+
+  if (!WaitForNoteFired(initial_shot_count + 2, std::chrono::seconds(2)))
+    return;
+
+  AOS_LOG(
+      INFO, "Done %lfs\n",
+      aos::time::DurationInSeconds(aos::monotonic_clock::now() - start_time));
+}
+
 void AutonomousActor::SendSuperstructureGoal() {
   aos::Sender<control_loops::superstructure::GoalStatic>::StaticBuilder
       goal_builder = superstructure_goal_sender_.MakeStaticBuilder();
diff --git a/y2024/autonomous/autonomous_actor.h b/y2024/autonomous/autonomous_actor.h
index fd5038d..0de85bf 100644
--- a/y2024/autonomous/autonomous_actor.h
+++ b/y2024/autonomous/autonomous_actor.h
@@ -40,6 +40,7 @@
   void SplineAuto();
   void MobilityAndShoot();
   void FourPieceAuto();
+  void TwoPieceStealAuto();
   void SendSuperstructureGoal();
 
   void Intake();
@@ -70,6 +71,7 @@
   std::optional<SplineHandle> test_spline_;
   std::optional<std::array<SplineHandle, 1>> mobility_and_shoot_splines_;
   std::optional<std::array<SplineHandle, 5>> four_piece_splines_;
+  std::optional<std::array<SplineHandle, 4>> two_piece_steal_splines_;
 
   control_loops::superstructure::IntakeGoal intake_goal_ =
       control_loops::superstructure::IntakeGoal::NONE;
diff --git a/y2024/autonomous/splines/2_piece_steal.0.json b/y2024/autonomous/splines/2_piece_steal.0.json
new file mode 100644
index 0000000..e7f2843
--- /dev/null
+++ b/y2024/autonomous/splines/2_piece_steal.0.json
@@ -0,0 +1,33 @@
+{
+    "spline_count": 1,
+    "spline_x": [
+        -6.860785213920586,
+        -5.203804609147312,
+        -3.7777316994250265,
+        -2.667592923096702,
+        -1.3560879613526007,
+        -0.5092457179511491
+    ],
+    "spline_y": [
+        -1.1157080654374518,
+        -1.1036636998527491,
+        -1.7082745320796768,
+        -2.823974049798384,
+        -2.893366029809447,
+        -3.209959853823472
+    ],
+    "constraints": [
+        {
+            "constraint_type": "LONGITUDINAL_ACCELERATION",
+            "value": 3
+        },
+        {
+            "constraint_type": "LATERAL_ACCELERATION",
+            "value": 2
+        },
+        {
+            "constraint_type": "VOLTAGE",
+            "value": 10
+        }
+    ]
+}
\ No newline at end of file
diff --git a/y2024/autonomous/splines/2_piece_steal.1.json b/y2024/autonomous/splines/2_piece_steal.1.json
new file mode 100644
index 0000000..98c1323
--- /dev/null
+++ b/y2024/autonomous/splines/2_piece_steal.1.json
@@ -0,0 +1,33 @@
+{
+    "spline_count": 1,
+    "spline_x": [
+        -0.5092457179511491,
+        -1.8931992589519813,
+        -3.6060816001326397,
+        -3.8917290706695438,
+        -4.713201267483203,
+        -5.640902947694428
+    ],
+    "spline_y": [
+        -3.209959853823472,
+        -2.6925657969997197,
+        -2.333279192015276,
+        -2.186602322153238,
+        -1.549542211476115,
+        -0.8645502069901454
+    ],
+    "constraints": [
+        {
+            "constraint_type": "LONGITUDINAL_ACCELERATION",
+            "value": 3
+        },
+        {
+            "constraint_type": "LATERAL_ACCELERATION",
+            "value": 2
+        },
+        {
+            "constraint_type": "VOLTAGE",
+            "value": 10
+        }
+    ]
+}
\ No newline at end of file
diff --git a/y2024/autonomous/splines/2_piece_steal.2.json b/y2024/autonomous/splines/2_piece_steal.2.json
new file mode 100644
index 0000000..ef955db
--- /dev/null
+++ b/y2024/autonomous/splines/2_piece_steal.2.json
@@ -0,0 +1,33 @@
+{
+    "spline_count": 1,
+    "spline_x": [
+        -5.640902947694428,
+        -4.662999278601822,
+        -3.239591234160512,
+        -3.047507215729768,
+        -1.061657126562114,
+        -0.47709853308607464
+    ],
+    "spline_y": [
+        -0.8645502069901454,
+        -1.5866101199714517,
+        -3.0049314806309253,
+        -3.286832304945575,
+        -2.0927386168786777,
+        -1.9056608123457113
+    ],
+    "constraints": [
+        {
+            "constraint_type": "LONGITUDINAL_ACCELERATION",
+            "value": 3
+        },
+        {
+            "constraint_type": "LATERAL_ACCELERATION",
+            "value": 2
+        },
+        {
+            "constraint_type": "VOLTAGE",
+            "value": 10
+        }
+    ]
+}
\ No newline at end of file
diff --git a/y2024/autonomous/splines/2_piece_steal.3.json b/y2024/autonomous/splines/2_piece_steal.3.json
new file mode 100644
index 0000000..356d7e9
--- /dev/null
+++ b/y2024/autonomous/splines/2_piece_steal.3.json
@@ -0,0 +1,33 @@
+{
+    "spline_count": 1,
+    "spline_x": [
+        -0.47709853308607464,
+        -1.1147335735554593,
+        -3.3428566977932013,
+        -2.7197061644307787,
+        -4.462847247516866,
+        -5.662538809543391
+    ],
+    "spline_y": [
+        -1.9056608123457113,
+        -2.1097248100280774,
+        -2.6177296382002626,
+        -2.8074138647816946,
+        -1.752456754116137,
+        -0.8676468782753499
+    ],
+    "constraints": [
+        {
+            "constraint_type": "LONGITUDINAL_ACCELERATION",
+            "value": 3
+        },
+        {
+            "constraint_type": "LATERAL_ACCELERATION",
+            "value": 2
+        },
+        {
+            "constraint_type": "VOLTAGE",
+            "value": 10
+        }
+    ]
+}
\ No newline at end of file
diff --git a/y2024/constants/971.json b/y2024/constants/971.json
index c146417..ee1f540 100644
--- a/y2024/constants/971.json
+++ b/y2024/constants/971.json
@@ -8,16 +8,16 @@
 {
   "cameras": [
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_orin-971-c-1_cam-24-05_2024-03-08_17-16-02.325066070.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-06_2024-03-08_17-16-02.325390283.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-07_2024-03-08_17-16-02.325267121.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-0_cam-24-08_2024-03-01_11-02-11.json' %}
     }
   ],
   "robot": {
@@ -48,20 +48,20 @@
     "altitude_constants": {
       {% set _ = altitude_zero.update(
           {
-              "measured_absolute_position" : 0.2135
+              "measured_absolute_position" : 0.18140472352247
           }
       ) %}
       "zeroing_constants": {{ altitude_zero | tojson(indent=2)}},
-      "potentiometer_offset": -0.16416323147786
+      "potentiometer_offset": {{ -0.16416323147786 + 0.0111742298989474 }}
     },
     "turret_constants": {
       {% set _ = turret_zero.update(
           {
-              "measured_absolute_position" : 0.2077
+              "measured_absolute_position" : 1.3715
           }
       ) %}
       "zeroing_constants": {{ turret_zero | tojson(indent=2)}},
-      "potentiometer_offset": {{ -6.47164779835404 - 0.0711209027239817 + 1.0576004531907 - 0.343 }}
+      "potentiometer_offset": {{ -6.47164779835404 - 0.0711209027239817 + 1.0576004531907 - 0.343 - 0.05 }}
     },
     "extend_constants": {
       {% set _ = extend_zero.update(
diff --git a/y2024/constants/9971.json b/y2024/constants/9971.json
index da8ed92..755b56a 100644
--- a/y2024/constants/9971.json
+++ b/y2024/constants/9971.json
@@ -8,16 +8,16 @@
 {
   "cameras": [
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_orin-9971-1-0_cam-24-09_2024-02-24_16-10-16.872521280.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json' %}
     },
     {
-      "calibration": {% include 'y2024/constants/calib_files/calibration_orin-9971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json' %}
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json' %}
     },
   ],
   "robot": {
diff --git a/y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json b/y2024/constants/calib_files/calibration_imu-971-0_cam-24-08_2024-03-01_11-02-11.json
similarity index 100%
rename from y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json
rename to y2024/constants/calib_files/calibration_imu-971-0_cam-24-08_2024-03-01_11-02-11.json
diff --git a/y2024/constants/calib_files/calibration_orin-971-c-1_cam-24-05_2024-03-08_17-16-02.325066070.json b/y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json
similarity index 65%
rename from y2024/constants/calib_files/calibration_orin-971-c-1_cam-24-05_2024-03-08_17-16-02.325066070.json
rename to y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json
index c1d4e60..9a06ca4 100755
--- a/y2024/constants/calib_files/calibration_orin-971-c-1_cam-24-05_2024-03-08_17-16-02.325066070.json
+++ b/y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json
@@ -14,18 +14,18 @@
  ],
  "fixed_extrinsics": {
   "data": [
-   0.999556,
-   -0.026613,
-   0.013428,
-   0.146979,
-   -0.006922,
-   0.230925,
-   0.972947,
-   0.30388,
-   -0.028994,
-   -0.972608,
-   0.230638,
-   0.32572,
+   0.999778,
+   -0.021035,
+   0.001571,
+   0.126097,
+   0.00287,
+   0.209453,
+   0.977814,
+   0.279701,
+   -0.020898,
+   -0.977593,
+   0.209467,
+   0.329598,
    0.0,
    0.0,
    0.0,
@@ -39,7 +39,7 @@
   -0.000078,
   -0.008004
  ],
- "calibration_timestamp": 1709946962325066070,
+ "calibration_timestamp": 1711317267098016437,
  "camera_id": "24-05",
  "camera_number": 1,
  "reprojection_error": 1.058851
diff --git a/y2024/constants/calib_files/calibration_imu-9971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json b/y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json
similarity index 63%
rename from y2024/constants/calib_files/calibration_imu-9971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json
rename to y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json
index 66ea94a..e5f50e3 100755
--- a/y2024/constants/calib_files/calibration_imu-9971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json
+++ b/y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json
@@ -12,6 +12,26 @@
   0.0,
   1.0
  ],
+ "fixed_extrinsics": {
+  "data": [
+   0.0,
+   -0.258819,
+   -0.965926,
+   -0.323293,
+   1.0,
+   0.0,
+   -0.0,
+   0.268249,
+   0.0,
+   -0.965926,
+   0.258819,
+   0.471129,
+   0.0,
+   0.0,
+   0.0,
+   1.0
+  ]
+ },
  "dist_coeffs": [
   -0.251594,
   0.064935,
diff --git a/y2024/constants/calib_files/calibration_imu-9971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json b/y2024/constants/calib_files/calibration_imu-9971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json
deleted file mode 100755
index 7f8b6ad..0000000
--- a/y2024/constants/calib_files/calibration_imu-9971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "node_name": "imu",
- "team_number": 9971,
- "intrinsics": [
-  647.19928,
-  0.0,
-  690.698181,
-  0.0,
-  646.449158,
-  530.162842,
-  0.0,
-  0.0,
-  1.0
- ],
- "dist_coeffs": [
-  -0.249799,
-  0.062593,
-  0.00003,
-  0.000366,
-  -0.006532
- ],
- "calibration_timestamp": 1708833159488095264,
- "camera_id": "24-12",
- "camera_number": 1,
- "reprojection_error": 1.23409
-}
diff --git a/y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json b/y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json
new file mode 100755
index 0000000..530e88a
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "imu",
+ "team_number": 9971,
+ "intrinsics": [
+  647.19928,
+  0.0,
+  690.698181,
+  0.0,
+  646.449158,
+  530.162842,
+  0.0,
+  0.0,
+  1.0
+ ],
+ "fixed_extrinsics": {
+  "data": [
+   0.99969,
+   0.020217,
+   -0.014527,
+   0.160342,
+   0.009501,
+   0.229548,
+   0.973251,
+   0.25208,
+   0.023011,
+   -0.973088,
+   0.229284,
+   0.41504,
+   0.0,
+   0.0,
+   0.0,
+   1.0
+  ]
+ },
+ "dist_coeffs": [
+  -0.249799,
+  0.062593,
+  0.00003,
+  0.000366,
+  -0.006532
+ ],
+ "calibration_timestamp": 1711306369592332476,
+ "camera_id": "24-12",
+ "camera_number": 1,
+ "reprojection_error": 1.23409
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-06_2024-03-08_17-16-02.325390283.json b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-06_2024-03-08_17-16-02.325390283.json
deleted file mode 100755
index 48da468..0000000
--- a/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-06_2024-03-08_17-16-02.325390283.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "node_name": "orin1",
- "team_number": 971,
- "intrinsics": [
-  648.644104,
-  0.0,
-  755.677979,
-  0.0,
-  648.522644,
-  597.744812,
-  0.0,
-  0.0,
-  1.0
- ],
- "fixed_extrinsics": {
-  "data": [
-   -0.997807,
-   0.015704,
-   -0.064302,
-   0.134715,
-   0.058111,
-   -0.25731,
-   -0.96458,
-   -0.273849,
-   -0.031694,
-   -0.966201,
-   0.255833,
-   0.295681,
-   0.0,
-   0.0,
-   0.0,
-   1.0
-  ]
- },
- "dist_coeffs": [
-  -0.25182,
-  0.063137,
-  0.000118,
-  0.000005,
-  -0.006342
- ],
- "calibration_timestamp": 1709946962325390283,
- "camera_id": "24-06",
- "camera_number": 0,
- "reprojection_error": 1.344104
-}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-9971-1-0_cam-24-09_2024-02-24_16-10-16.872521280.json b/y2024/constants/calib_files/calibration_orin-9971-1-0_cam-24-09_2024-02-24_16-10-16.872521280.json
deleted file mode 100755
index 068e3d2..0000000
--- a/y2024/constants/calib_files/calibration_orin-9971-1-0_cam-24-09_2024-02-24_16-10-16.872521280.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "node_name": "orin1",
- "team_number": 9971,
- "intrinsics": [
-  648.187805,
-  0.0,
-  736.903137,
-  0.0,
-  648.028687,
-  557.169861,
-  0.0,
-  0.0,
-  1.0
- ],
- "dist_coeffs": [
-  -0.265564,
-  0.078084,
-  -0.000231,
-  0.000386,
-  -0.010425
- ],
- "calibration_timestamp": 1708819816872521280,
- "camera_id": "24-09",
- "camera_number": 0,
- "reprojection_error": 1.881098
-}
diff --git a/y2024/constants/calib_files/calibration_orin-9971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json b/y2024/constants/calib_files/calibration_orin-9971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json
deleted file mode 100755
index c94dcaa..0000000
--- a/y2024/constants/calib_files/calibration_orin-9971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "node_name": "orin1",
- "team_number": 9971,
- "intrinsics": [
-  649.866699,
-  0.0,
-  709.355713,
-  0.0,
-  648.893066,
-  576.101868,
-  0.0,
-  0.0,
-  1.0
- ],
- "dist_coeffs": [
-  -0.248092,
-  0.060938,
-  0.000313,
-  0.00009,
-  -0.006163
- ],
- "calibration_timestamp": 1708821846986729504,
- "camera_id": "24-11",
- "camera_number": 1,
- "reprojection_error": 1.450069
-}
diff --git a/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json b/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json
new file mode 100755
index 0000000..c7146bb
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+  647.96582,
+  0.0,
+  755.529907,
+  0.0,
+  647.512146,
+  585.378479,
+  0.0,
+  0.0,
+  1.0
+ ],
+ "fixed_extrinsics": {
+  "data": [
+   -0.994734,
+   -0.057157,
+   -0.085073,
+   0.126809,
+   0.095006,
+   -0.202848,
+   -0.97459,
+   -0.218389,
+   0.038448,
+   -0.977541,
+   0.20721,
+   0.322807,
+   0.0,
+   0.0,
+   0.0,
+   1.0
+  ]
+ },
+ "dist_coeffs": [
+  -0.260101,
+  0.072815,
+  -0.000107,
+  0.000376,
+  -0.009155
+ ],
+ "calibration_timestamp": 1711317267098685054,
+ "camera_id": "24-06",
+ "camera_number": 0,
+ "reprojection_error": 2.574085
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-07_2024-03-08_17-16-02.325267121.json b/y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json
similarity index 65%
rename from y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-07_2024-03-08_17-16-02.325267121.json
rename to y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json
index 56072d4..dfda047 100755
--- a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-07_2024-03-08_17-16-02.325267121.json
+++ b/y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json
@@ -14,18 +14,18 @@
  ],
  "fixed_extrinsics": {
   "data": [
-   0.016995,
-   0.002328,
-   0.999853,
-   0.35278,
-   0.999839,
-   -0.005785,
-   -0.016981,
-   0.237526,
-   0.005745,
-   0.999981,
-   -0.002426,
-   0.387276,
+   -0.003277,
+   0.017881,
+   0.999835,
+   0.352681,
+   0.999963,
+   0.008063,
+   0.003133,
+   0.253385,
+   -0.008005,
+   0.999808,
+   -0.017906,
+   0.386766,
    0.0,
    0.0,
    0.0,
@@ -39,7 +39,7 @@
   0.000016,
   -0.005638
  ],
- "calibration_timestamp": 1709946962325267121,
+ "calibration_timestamp": 1711317267098469979,
  "camera_id": "24-07",
  "camera_number": 1,
  "reprojection_error": 1.362672
diff --git a/y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json b/y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json
new file mode 100755
index 0000000..97234c9
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "team_number": 9971,
+ "intrinsics": [
+  648.187805,
+  0.0,
+  736.903137,
+  0.0,
+  648.028687,
+  557.169861,
+  0.0,
+  0.0,
+  1.0
+ ],
+ "fixed_extrinsics": {
+  "data": [
+   -0.999204,
+   -0.034711,
+   -0.019682,
+   0.162103,
+   0.028118,
+   -0.262536,
+   -0.964512,
+   -0.329348,
+   0.028312,
+   -0.964298,
+   0.263303,
+   0.562319,
+   0.0,
+   0.0,
+   0.0,
+   1.0
+  ]
+ },
+ "dist_coeffs": [
+  -0.265564,
+  0.078084,
+  -0.000231,
+  0.000386,
+  -0.010425
+ ],
+ "calibration_timestamp": 1711306369593360533,
+ "camera_id": "24-09",
+ "camera_number": 0,
+ "reprojection_error": 1.881098
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json b/y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json
new file mode 100755
index 0000000..daef89c
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "team_number": 9971,
+ "intrinsics": [
+  649.866699,
+  0.0,
+  709.355713,
+  0.0,
+  648.893066,
+  576.101868,
+  0.0,
+  0.0,
+  1.0
+ ],
+ "fixed_extrinsics": {
+  "data": [
+   -0.014623,
+   0.004459,
+   0.999883,
+   0.345385,
+   0.997965,
+   0.062137,
+   0.014318,
+   0.150131,
+   -0.062066,
+   0.998058,
+   -0.005359,
+   0.570236,
+   0.0,
+   0.0,
+   0.0,
+   1.0
+  ]
+ },
+ "dist_coeffs": [
+  -0.248092,
+  0.060938,
+  0.000313,
+  0.00009,
+  -0.006163
+ ],
+ "calibration_timestamp": 1711306369592886702,
+ "camera_id": "24-11",
+ "camera_number": 1,
+ "reprojection_error": 1.450069
+}
\ No newline at end of file
diff --git a/y2024/constants/common.json b/y2024/constants/common.json
index 52fdb74..8a59b64 100644
--- a/y2024/constants/common.json
+++ b/y2024/constants/common.json
@@ -24,6 +24,13 @@
           "shot_speed_over_ground": 16.0
       }
     },
+    {
+      "distance_from_goal": 2.404,
+      "shot_params": {
+          "shot_altitude_angle": 0.645,
+          "shot_speed_over_ground": 16.0
+      }
+    },
     // 2.2 -> high.
     {
       "distance_from_goal": 2.744,
diff --git a/y2024/constants/constants.fbs b/y2024/constants/constants.fbs
index 99d49a4..626a13a 100644
--- a/y2024/constants/constants.fbs
+++ b/y2024/constants/constants.fbs
@@ -123,6 +123,8 @@
   MOBILITY_AND_SHOOT = 2,
   // Auto to pick up four game pieces.
   FOUR_PIECE = 3,
+  // Auto which picks up two game pieces from the center
+  TWO_PIECE_STEAL = 4,
 }
 
 table RobotConstants {
diff --git a/y2024/control_loops/superstructure/shooter.cc b/y2024/control_loops/superstructure/shooter.cc
index 7002657..b280b2f 100644
--- a/y2024/control_loops/superstructure/shooter.cc
+++ b/y2024/control_loops/superstructure/shooter.cc
@@ -167,10 +167,12 @@
 
   const bool turret_in_range =
       (std::abs(turret_.estimated_position() - turret_goal->unsafe_goal()) <
-       kCatapultActivationTurretThreshold);
+       kCatapultActivationTurretThreshold) &&
+      (std::abs(turret_.estimated_velocity()) < 0.2);
   const bool altitude_in_range =
       (std::abs(altitude_.estimated_position() - altitude_goal->unsafe_goal()) <
-       kCatapultActivationAltitudeThreshold);
+       kCatapultActivationAltitudeThreshold) &&
+      (std::abs(altitude_.estimated_velocity()) < 0.1);
   const bool altitude_above_min_angle =
       (altitude_.estimated_position() >
        robot_constants_->common()->min_altitude_shooting_angle());
diff --git a/y2024/control_loops/superstructure/superstructure.cc b/y2024/control_loops/superstructure/superstructure.cc
index 9edc626..daede87 100644
--- a/y2024/control_loops/superstructure/superstructure.cc
+++ b/y2024/control_loops/superstructure/superstructure.cc
@@ -50,7 +50,7 @@
       extend_(
           robot_constants_->common()->extend(),
           robot_constants_->robot()->extend_constants()->zeroing_constants()),
-      extend_debouncer_(std::chrono::milliseconds(30),
+      extend_debouncer_(std::chrono::milliseconds(12),
                         std::chrono::milliseconds(8)),
       transfer_debouncer_(std::chrono::milliseconds(30),
                           std::chrono::milliseconds(8)) {
@@ -701,6 +701,7 @@
       extend_ready_for_catapult_transfer);
   status_builder.add_extend_beambreak(extend_beambreak);
   status_builder.add_catapult_beambreak(position->catapult_beambreak());
+  status_builder.add_transfer_beambreak(transfer_beambreak);
 
   (void)status->Send(status_builder.Finish());
 }
diff --git a/y2024/control_loops/superstructure/superstructure_lib_test.cc b/y2024/control_loops/superstructure/superstructure_lib_test.cc
index 9e8b3a5..a4e47d7 100644
--- a/y2024/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2024/control_loops/superstructure/superstructure_lib_test.cc
@@ -1272,7 +1272,7 @@
   // Wait until the bot finishes auto-aiming.
   WaitUntilNear(kTurretGoal, kAltitudeGoal);
 
-  RunFor(dt());
+  RunFor(10 * dt());
 
   ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
 
diff --git a/y2024/control_loops/superstructure/superstructure_status.fbs b/y2024/control_loops/superstructure/superstructure_status.fbs
index fd19e8d..0b30edd 100644
--- a/y2024/control_loops/superstructure/superstructure_status.fbs
+++ b/y2024/control_loops/superstructure/superstructure_status.fbs
@@ -181,6 +181,7 @@
 
   extend_beambreak:bool (id: 19);
   catapult_beambreak:bool (id: 20);
+  transfer_beambreak:bool (id: 22);
 
   // Number of shots we have taken.
   shot_count:uint32 (id:21);
diff --git a/y2024/www/BUILD b/y2024/www/BUILD
index 5cb57c4..b6b6b9b 100644
--- a/y2024/www/BUILD
+++ b/y2024/www/BUILD
@@ -55,11 +55,38 @@
     ],
 )
 
+ts_project(
+    name = "starter_main",
+    srcs = [
+        "starter_handler.ts",
+        "starter_main.ts",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        "//aos/network:connect_ts_fbs",
+        "//aos/network:message_bridge_client_ts_fbs",
+        "//aos/network/www:proxy",
+        "//aos/starter:starter_ts_fbs",
+        "@com_github_google_flatbuffers//ts:flatbuffers_ts",
+    ],
+)
+
+rollup_bundle(
+    name = "starter_main_bundle",
+    entry_point = "starter_main.ts",
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//y2024:__subpackages__"],
+    deps = [
+        ":starter_main",
+    ],
+)
+
 aos_downloader_dir(
     name = "www_files",
     srcs = [
         ":field_main_bundle.min.js",
         ":files",
+        ":starter_main_bundle.min.js",
         "//frc971/analysis:plot_index_bundle.min.js",
     ],
     dir = "www",
diff --git a/y2024/www/field.html b/y2024/www/field.html
index 8fd4ebb..ebb03e5 100644
--- a/y2024/www/field.html
+++ b/y2024/www/field.html
@@ -77,12 +77,16 @@
           <th colspan="2">Beambreaks</th>
         </tr>
         <tr>
+          <td>Transfer Beambreak</td>
+          <td id="transfer_beambreak"> NA </td>
+        </tr>
+        <tr>
           <td>Extend Beambreak</td>
-          <td id="extend_beambreak">FALSE</td>
+          <td id="extend_beambreak"> NA </td>
         </tr>
         <tr>
           <td>Catapult Beambreak</td>
-          <td id="catapult_beambreak">FALSE</td>
+          <td id="catapult_beambreak"> NA </td>
         </tr>
         <tr>
           <th colspan="2">Subsytems At Position</th>
diff --git a/y2024/www/field_handler.ts b/y2024/www/field_handler.ts
index 1b4ad1e..7b5e0f4 100644
--- a/y2024/www/field_handler.ts
+++ b/y2024/www/field_handler.ts
@@ -78,6 +78,8 @@
   (document.getElementById('extend_beambreak') as HTMLElement);
   private catapult_beambreak: HTMLElement =
   (document.getElementById('catapult_beambreak') as HTMLElement);
+  private transfer_beambreak: HTMLElement =
+  (document.getElementById('transfer_beambreak') as HTMLElement);
 
   private extend_at_retracted: HTMLElement =
   (document.getElementById('extend_at_retracted') as HTMLElement);
@@ -477,12 +479,11 @@
 
   setBoolean(div: HTMLElement, triggered: boolean): void {
     div.innerHTML = ((triggered) ? "TRUE" : "FALSE")
+    div.className = '';
     if (triggered) {
-      div.classList.remove('false');
-      div.classList.add('true');
+      div.classList.add('lightgreen');
     } else {
-      div.classList.remove('true');
-      div.classList.add('false');
+      div.classList.add('lightcoral');
     }
   }
 
@@ -514,6 +515,8 @@
 
       this.setBoolean(this.catapult_beambreak, this.superstructureStatus.catapultBeambreak());
 
+      this.setBoolean(this.transfer_beambreak, this.superstructureStatus.transferBeambreak());
+
       this.setBoolean(this.extend_ready_for_transfer, this.superstructureStatus.extendReadyForTransfer());
 
       this.setBoolean(this.extend_at_retracted, this.superstructureStatus.extendAtRetracted());
diff --git a/y2024/www/index.html b/y2024/www/index.html
index e4e185e..98ecf42 100644
--- a/y2024/www/index.html
+++ b/y2024/www/index.html
@@ -1,6 +1,7 @@
 <html>
   <body>
     <a href="field.html">Field Visualization</a><br>
-    <a href="plotter.html">Plots</a>
+    <a href="plotter.html">Plots</a><br>
+    <a href="starter.html">AOS Starter Status</a>
   </body>
 </html>
diff --git a/y2024/www/starter.html b/y2024/www/starter.html
new file mode 100644
index 0000000..9060332
--- /dev/null
+++ b/y2024/www/starter.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <script src="starter_main_bundle.min.js" defer></script>
+    <link rel="stylesheet" href="styles.css">
+  </head>
+  <body>
+    <div>
+      <div id="status_list"></div>
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/y2024/www/starter_handler.ts b/y2024/www/starter_handler.ts
new file mode 100644
index 0000000..2b953f3
--- /dev/null
+++ b/y2024/www/starter_handler.ts
@@ -0,0 +1,178 @@
+import {ByteBuffer} from 'flatbuffers'
+import {Connection} from '../../aos/network/www/proxy'
+import {Status, ApplicationStatus, State, LastStopReason, FileState} from '../../aos/starter/starter_generated'
+
+const NODES = ['/orin1', '/imu', '/roborio'];
+
+export class StarterHandler {
+  private statuses = new Map<string, ApplicationStatus[]>();
+
+  private statusList: HTMLElement =
+      (document.getElementById('status_list') as HTMLElement);
+
+  constructor(private readonly connection: Connection) {
+    for (const node in NODES) {
+      this.connection.addConfigHandler(() => {
+        this.connection.addHandler(
+          NODES[node] + '/aos', 'aos.starter.Status', (data) => {
+            this.handleStatus(data, NODES[node]);
+          });
+      });
+    }
+  }
+
+  private handleStatus(data: Uint8Array, node: string): void {
+    const fbBuffer = new ByteBuffer(data);
+    const status: Status = Status.getRootAsStatus(fbBuffer);
+
+    const temp: ApplicationStatus[] = new Array(status.statusesLength());
+
+    for (let i = 0; i < status.statusesLength(); i++) {
+      temp[i] = status.statuses(i);
+    }
+
+    this.statuses.set(node, temp);
+  }
+
+  setStateColor(div: HTMLElement): void {
+    div.className = '';
+    switch (div.innerHTML) {
+      case 'WAITING':
+        div.classList.add('yellow');
+        break;
+      case 'STARTING':
+        div.classList.add('yellowgreen');
+        break;
+      case 'RUNNING':
+        div.classList.add('lightgreen');
+        break;
+      case 'STOPPING':
+        div.classList.add('lightcoral');
+        break;
+      case 'STOPPED':
+        div.classList.add('lightcoral');
+        break;
+    }
+  }
+
+  setFileStateColor(div: HTMLElement): void {
+    div.className = '';
+    switch (div.innerHTML) {
+      case 'NOT_RUNNING':
+        div.classList.add('lightgreen');
+        break;
+      case 'NO_CHANGE':
+        div.classList.add('lightgreen');
+        break;
+      case 'CHANGED_DURING_STARTUP':
+        div.classList.add('lightcoral');
+        break;
+      case 'CHANGED':
+        div.classList.add('lightcoral');
+        break;
+    }
+  }
+
+  private populateStatusList() : void {
+    this.clearStatusList();
+
+    const ELEMENTS: string[] = [
+        'name',
+        'node',
+        'state',
+        'last_exit_code',
+        'pid',
+        // 'id',
+        'last_start_time',
+        'last_stop_reason',
+        // 'process_info',
+        // 'has_active_timing_report',
+        'file_state'
+    ];
+
+    // This is to add the field names as the top row
+    //---------------------------------------------------
+    const row = document.createElement('div');
+    row.className = "column_names";
+
+    for (const e in ELEMENTS) {
+      const element = document.createElement('div');
+      element.className = ELEMENTS[e];
+      element.innerHTML = ELEMENTS[e].toUpperCase();
+      element.style.fontWeight = 'bold';
+      row.appendChild(element);
+    }
+
+    this.statusList.appendChild(row);
+    //---------------------------------------------------
+
+    for (const node in NODES) {
+      const currentStatus = this.statuses.get(NODES[node]);
+
+      if (currentStatus) {
+        currentStatus.forEach(status => {
+          const row = document.createElement('div');
+          row.className = status.name();
+
+          for (const e in ELEMENTS) {
+            const element = document.createElement('div');
+            element.className = ELEMENTS[e];
+
+            switch (element.className) {
+              case 'name':
+                element.innerHTML = status.name();
+                break;
+              case 'node':
+                element.innerHTML = NODES[node];
+                break;
+              case 'state':
+                element.innerHTML = State[status.state()];
+                this.setStateColor(element);
+                break;
+              case 'last_exit_code':
+                element.innerHTML = status.lastExitCode().toString();
+                break;
+              case 'pid':
+                element.innerHTML = status.pid().toString();
+                break;
+              case 'last_start_time':
+                element.innerHTML = Number(status.lastStartTime() / 1000000000n).toString() + ' sec';
+                break;
+              case 'last_stop_reason':
+                element.innerHTML = LastStopReason[status.lastStopReason()];
+                break;
+              case 'file_state':
+                element.innerHTML = FileState[status.fileState()];
+                this.setFileStateColor(element);
+                break;
+              default:
+                element.innerHTML = "NA";
+                break;
+            }
+            row.appendChild(element);
+          }
+
+          this.statusList.appendChild(row);
+        })
+      }
+    }
+  }
+
+  private clearStatusList(): void {
+    if (!this.statusList) {
+        return;
+    }
+
+    while (this.statusList.firstChild) {
+        this.statusList.removeChild(this.statusList.firstChild);
+    }
+  }
+
+  draw(): void {
+    if (this.statuses) {
+      this.populateStatusList();
+    }
+
+    window.requestAnimationFrame(() => this.draw());
+  }
+}
diff --git a/y2024/www/starter_main.ts b/y2024/www/starter_main.ts
new file mode 100644
index 0000000..e9d23fa
--- /dev/null
+++ b/y2024/www/starter_main.ts
@@ -0,0 +1,11 @@
+import {Connection} from '../../aos/network/www/proxy';
+
+import {StarterHandler} from './starter_handler';
+
+const conn = new Connection();
+
+conn.connect();
+
+const starterHandler = new StarterHandler(conn);
+
+starterHandler.draw();
diff --git a/y2024/www/styles.css b/y2024/www/styles.css
index a11187b..c5e0409 100644
--- a/y2024/www/styles.css
+++ b/y2024/www/styles.css
@@ -142,10 +142,34 @@
   width: 100%;
 }
 
-.true {
+#status_list > div {
+  display: table-row;
+  padding: 3px;
+}
+
+#status_list > div > div {
+  display: table-cell;
+  padding: 10px;
+  text-align: left;
+}
+
+
+.yellow {
+  background-color: yellow;
+}
+
+.yellowgreen {
+  background-color: yellowgreen;
+}
+
+.lightgreen {
   background-color: LightGreen;
 }
 
-.false {
+.lightcoral {
+  background-color: lightcoral;
+}
+
+.red {
   background-color: red;
 }