Merge changes Ic121ffe5,I2aa3aefd

* changes:
  Undistort image in camera reader
  Bring back use_outdoors flag
diff --git a/scouting/scraping/scrape.go b/scouting/scraping/scrape.go
index 170fe50..19426cf 100644
--- a/scouting/scraping/scrape.go
+++ b/scouting/scraping/scrape.go
@@ -17,14 +17,14 @@
 	BaseUrl string `json:"base_url"`
 }
 
-// Takes in year and FIRST event code and returns all matches in that event according to TBA.
+// Takes in year and FIRST event code and returns requested information according to TBA.
 // Also takes in a file path to the JSON config file that contains your TBA API key.
 // It defaults to <workspace root>/config.json
 // the config is expected to have the following contents:
 //{
 //    api_key:"myTBAapiKey"
 //}
-func AllMatches(year int32, eventCode, configPath string) ([]Match, error) {
+func getJson(year int32, eventCode, configPath, category string) ([]byte, error) {
 	if configPath == "" {
 		configPath = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + "/scouting_config.json"
 	}
@@ -52,7 +52,7 @@
 	eventKey := strconv.Itoa(int(year)) + eventCode
 
 	// Create a get request for the match info.
-	req, err := http.NewRequest("GET", config.BaseUrl+"/api/v3/event/"+eventKey+"/matches", nil)
+	req, err := http.NewRequest("GET", config.BaseUrl+"/api/v3/event/"+eventKey+"/"+category, nil)
 	if err != nil {
 		return nil, errors.New(fmt.Sprint("Failed to build http request: ", err))
 	}
@@ -78,6 +78,17 @@
 		return nil, errors.New(fmt.Sprint("Failed to read TBA API response: ", err))
 	}
 
+	return bodyBytes, nil
+}
+
+// Return all matches in event according to TBA
+func AllMatches(year int32, eventCode, configPath string) ([]Match, error) {
+	bodyBytes, err := getJson(year, eventCode, configPath, "matches")
+
+	if err != nil {
+		return nil, err
+	}
+
 	var matches []Match
 	// Unmarshal json into go usable format.
 	if err := json.Unmarshal([]byte(bodyBytes), &matches); err != nil {
@@ -86,3 +97,20 @@
 
 	return matches, nil
 }
+
+// Return event rankings according to TBA
+func AllRankings(year int32, eventCode, configPath string) (EventRanking, error) {
+	bodyBytes, err := getJson(year, eventCode, configPath, "rankings")
+
+	if err != nil {
+		return EventRanking{}, err
+	}
+
+	var rankings EventRanking
+	// Unmarshal json into go usable format.
+	if err := json.Unmarshal([]byte(bodyBytes), &rankings); err != nil {
+		return EventRanking{}, errors.New(fmt.Sprint("Failed to parse JSON received from TBA: ", err))
+	}
+
+	return rankings, nil
+}
diff --git a/scouting/scraping/scraping_demo.go b/scouting/scraping/scraping_demo.go
index 0ea3e53..69cdbff 100644
--- a/scouting/scraping/scraping_demo.go
+++ b/scouting/scraping/scraping_demo.go
@@ -13,22 +13,43 @@
 
 func main() {
 	jsonPtr := flag.Bool("json", false, "If set, dump as JSON, rather than Go debug output.")
+	demoCategory := flag.String("category", "matches", "Decide whether to demo matches or rankings.")
+
 	flag.Parse()
 
-	// Get all the matches.
-	matches, err := scraping.AllMatches(2016, "nytr", "")
-	if err != nil {
-		log.Fatal("Failed to scrape match list: ", err)
-	}
-
-	// Dump the matches.
-	if *jsonPtr {
-		jsonData, err := json.MarshalIndent(matches, "", "  ")
+	if *demoCategory == "rankings" {
+		// Get all the rankings.
+		rankings, err := scraping.AllRankings(2016, "nytr", "")
 		if err != nil {
-			log.Fatal("Failed to turn match list into JSON: ", err)
+			log.Fatal("Failed to scrape ranking list: ", err)
 		}
-		fmt.Println(string(jsonData))
-	} else {
-		spew.Dump(matches)
+
+		// Dump the rankings.
+		if *jsonPtr {
+			jsonData, err := json.MarshalIndent(rankings, "", "  ")
+			if err != nil {
+				log.Fatal("Failed to turn ranking list into JSON: ", err)
+			}
+			fmt.Println(string(jsonData))
+		} else {
+			spew.Dump(rankings)
+		}
+	} else if *demoCategory == "matches" {
+		// Get all the matches.
+		matches, err := scraping.AllMatches(2016, "nytr", "")
+		if err != nil {
+			log.Fatal("Failed to scrape match list: ", err)
+		}
+
+		// Dump the matches.
+		if *jsonPtr {
+			jsonData, err := json.MarshalIndent(matches, "", "  ")
+			if err != nil {
+				log.Fatal("Failed to turn match list into JSON: ", err)
+			}
+			fmt.Println(string(jsonData))
+		} else {
+			spew.Dump(matches)
+		}
 	}
 }
diff --git a/scouting/scraping/types.go b/scouting/scraping/types.go
index aa0d4be..9283ac2 100644
--- a/scouting/scraping/types.go
+++ b/scouting/scraping/types.go
@@ -1,5 +1,26 @@
 package scraping
 
+type EventRanking struct {
+	Rankings []Rank `json:"rankings"`
+}
+
+type Rank struct {
+	MatchesPlayed int       `json:"matches_played"`
+	QualAverage   int       `json:"qual_average"`
+	ExtraStats    []float64 `json:"extra_stats"`
+	SortOrders    []float64 `json:"sort_orders"`
+	Records       Record    `json:"record"`
+	Rank          int       `json:"rank"`
+	Dq            int       `json:"dq"`
+	TeamKey       string    `json:"team_key"`
+}
+
+type Record struct {
+	Losses int `json:"losses"`
+	Wins   int `json:"wins"`
+	Ties   int `json:"ties"`
+}
+
 // Match holds the TBA data for a given match
 type Match struct {
 	Key             string
diff --git a/y2022/BUILD b/y2022/BUILD
index 83438ec..9e80b5f 100644
--- a/y2022/BUILD
+++ b/y2022/BUILD
@@ -10,6 +10,7 @@
     ],
     data = [
         ":aos_config",
+        ":message_bridge_client.sh",
         "@ctre_phoenix_api_cpp_athena//:shared_libraries",
         "@ctre_phoenix_cci_athena//:shared_libraries",
     ],
diff --git a/y2022/actors/splines/spline_5_ball_1.json b/y2022/actors/splines/spline_5_ball_1.json
index 91a31f6..da3e4cf 100644
--- a/y2022/actors/splines/spline_5_ball_1.json
+++ b/y2022/actors/splines/spline_5_ball_1.json
@@ -1 +1 @@
-{"spline_count": 1, "spline_x": [-0.18145693702491972, -0.1806686149879133, -0.05595918014581436, 5.568337297714338, 2.9263972876382542, 1.8899826868983047], "spline_y": [2.346189480782648, 3.6925675615333544, 4.41262134323365, 2.806871944572777, 2.3713873275272683, 1.4720189432649824], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 3.0}, {"constraint_type": "LATERAL_ACCELERATION", "value": 2.5}, {"constraint_type": "VOLTAGE", "value": 12.0}, {"constraint_type": "VELOCITY", "value": 0.8, "start_distance": 1.0, "end_distance": 1.15}]}
+{"spline_count": 1, "spline_x": [-0.18145693702491972, -0.1806686149879133, -0.05595918014581436, 5.762204620882601, 2.7805678460726355, 1.6146169804687496], "spline_y": [2.346189480782648, 3.6925675615333544, 4.41262134323365, 2.4753395126953124, 2.2341888067461992, 1.3005395681218328], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 3.0}, {"constraint_type": "LATERAL_ACCELERATION", "value": 2.5}, {"constraint_type": "VOLTAGE", "value": 12.0}, {"constraint_type": "VELOCITY", "value": 0.8, "start_distance": 1.0, "end_distance": 1.15}]}
\ No newline at end of file
diff --git a/y2022/actors/splines/spline_5_ball_2.json b/y2022/actors/splines/spline_5_ball_2.json
index 7d7e8c6..3efd1ee 100644
--- a/y2022/actors/splines/spline_5_ball_2.json
+++ b/y2022/actors/splines/spline_5_ball_2.json
@@ -1 +1 @@
-{"spline_count": 1, "spline_x": [1.8977893624261148, 2.493489118397258, 3.0938041880159797, 6.267891145316549, 6.049173922736376, 6.833717690390506], "spline_y": [1.4682096511768088, 2.0094316068927602, 1.5053043317512498, 2.1686488722080637, 2.082692598659578, 2.8053622716611706], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 3.0}, {"constraint_type": "LATERAL_ACCELERATION", "value": 3}, {"constraint_type": "VOLTAGE", "value": 12.0}]}
+{"spline_count": 1, "spline_x": [1.6037446032516893, 2.2055167265625, 2.8212725389450344, 6.148134261553881, 5.92062789622044, 6.7046250148859805], "spline_y": [1.2861465107685808, 1.7993420469805743, 1.286805497714088, 2.0935212995201415, 1.9849658141364017, 2.755576908889358], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 3.0}, {"constraint_type": "LATERAL_ACCELERATION", "value": 3.0}, {"constraint_type": "VOLTAGE", "value": 12.0}]}
\ No newline at end of file
diff --git a/y2022/actors/splines/spline_5_ball_3.json b/y2022/actors/splines/spline_5_ball_3.json
index 8d37834..6b239fc 100644
--- a/y2022/actors/splines/spline_5_ball_3.json
+++ b/y2022/actors/splines/spline_5_ball_3.json
@@ -1 +1 @@
-{"spline_count": 1, "spline_x": [6.8497036863806855, 6.523770855698178, 5.901632869573503, 3.625913896364584, 2.7205221377079836, 2.0513600546151287], "spline_y": [2.7941148215833196, 2.479530267377096, 2.134117802271161, 2.01574966936852, 2.0870521240271422, 1.5304761433039502], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 4}, {"constraint_type": "LATERAL_ACCELERATION", "value": 3}, {"constraint_type": "VOLTAGE", "value": 12.0}]}
+{"spline_count": 1, "spline_x": [6.702231375950168, 6.373881457031249, 5.758688966174009, 3.1788453508620487, 2.273453592205448, 1.6114305300886826], "spline_y": [2.7438724869219806, 2.4293757261929896, 2.0768880836927197, 1.7809922274871859, 1.852294682145808, 1.2821724076488596], "constraints": [{"constraint_type": "LONGITUDINAL_ACCELERATION", "value": 4.0}, {"constraint_type": "LATERAL_ACCELERATION", "value": 3}, {"constraint_type": "VOLTAGE", "value": 12.0}]}
\ No newline at end of file
diff --git a/y2022/constants.cc b/y2022/constants.cc
index acd36b4..fb2c602 100644
--- a/y2022/constants.cc
+++ b/y2022/constants.cc
@@ -162,20 +162,22 @@
       break;
 
     case kCompTeamNumber:
-      climber->potentiometer_offset = -0.0463847608752;
+      climber->potentiometer_offset = -0.0463847608752 - 0.0376876182111;
 
-      intake_front->potentiometer_offset = 2.79628370453323;
+      intake_front->potentiometer_offset =
+          2.79628370453323 - 0.0250288114832881;
       intake_front->subsystem_params.zeroing_constants
-          .measured_absolute_position = 0.248921954833972;
+          .measured_absolute_position = 0.274930238824366;
 
       intake_back->potentiometer_offset = 3.1409576474047;
       intake_back->subsystem_params.zeroing_constants
           .measured_absolute_position = 0.280099007470002;
 
       turret->potentiometer_offset = -9.99970387166721 + 0.06415943 +
-                                     0.073290115367682 - 0.0634440443622909;
+                                     0.073290115367682 - 0.0634440443622909 +
+                                     0.213601224728352 + 0.0657973101027296;
       turret->subsystem_params.zeroing_constants.measured_absolute_position =
-          0.568649928102931;
+          0.27787064956636;
 
       flipper_arm_left->potentiometer_offset = -6.4;
       flipper_arm_right->potentiometer_offset = 5.56;
diff --git a/y2022/control_loops/superstructure/led_indicator.cc b/y2022/control_loops/superstructure/led_indicator.cc
index 54b0b00..3fed503 100644
--- a/y2022/control_loops/superstructure/led_indicator.cc
+++ b/y2022/control_loops/superstructure/led_indicator.cc
@@ -36,7 +36,8 @@
 bool DisconnectedPiServer(
     const aos::message_bridge::ServerStatistics &server_stats) {
   for (const auto *pi_server_status : *server_stats.connections()) {
-    if (pi_server_status->state() == aos::message_bridge::State::DISCONNECTED) {
+    if (pi_server_status->state() == aos::message_bridge::State::DISCONNECTED &&
+        pi_server_status->node()->name()->string_view() != "logger") {
       return true;
     }
   }
@@ -46,7 +47,8 @@
 bool DisconnectedPiClient(
     const aos::message_bridge::ClientStatistics &client_stats) {
   for (const auto *pi_client_status : *client_stats.connections()) {
-    if (pi_client_status->state() == aos::message_bridge::State::DISCONNECTED) {
+    if (pi_client_status->state() == aos::message_bridge::State::DISCONNECTED &&
+        pi_client_status->node()->name()->string_view() != "logger") {
       return true;
     }
   }
diff --git a/y2022/control_loops/superstructure/superstructure.cc b/y2022/control_loops/superstructure/superstructure.cc
index 0e71656..5dbe7e7 100644
--- a/y2022/control_loops/superstructure/superstructure.cc
+++ b/y2022/control_loops/superstructure/superstructure.cc
@@ -257,11 +257,12 @@
       }
       // When IDLE with no specific intake button pressed, allow the goal
       // message to override the intaking stuff.
-      if (have_active_intake_request || turret_goal == nullptr) {
+      if (have_active_intake_request || (turret_goal == nullptr)) {
         turret_goal = &turret_loading_goal_buffer.message();
       }
 
       if (!front_intake_has_ball_ && !back_intake_has_ball_) {
+        last_shot_angle_ = std::nullopt;
         break;
       }
 
@@ -330,6 +331,13 @@
     }
     case SuperstructureState::LOADED: {
       if (unsafe_goal != nullptr) {
+        if (turret_goal == nullptr) {
+          if (last_shot_angle_) {
+            turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
+                *last_shot_angle_);
+          }
+          turret_goal = &turret_loading_goal_buffer.message();
+        }
         if (unsafe_goal->cancel_shot()) {
           // Cancel the shot process
           state_ = SuperstructureState::IDLE;
@@ -345,6 +353,21 @@
       break;
     }
     case SuperstructureState::SHOOTING: {
+      if (turret_goal == nullptr) {
+        if (last_shot_angle_) {
+          turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
+              *last_shot_angle_);
+        }
+        turret_goal = &turret_loading_goal_buffer.message();
+        last_shot_angle_ = turret_goal->unsafe_goal();
+      } else {
+        last_shot_angle_ = std::nullopt;
+      }
+      const bool turret_near_goal =
+          turret_goal != nullptr &&
+          std::abs(turret_goal->unsafe_goal() - turret_.position()) <
+              kTurretGoalThreshold;
+
       // Don't open the flippers until the turret's ready: give them as little
       // time to get bumped as possible.
       if (!turret_near_goal || collided) {
diff --git a/y2022/control_loops/superstructure/superstructure.h b/y2022/control_loops/superstructure/superstructure.h
index 11cd38f..14fa8ab 100644
--- a/y2022/control_loops/superstructure/superstructure.h
+++ b/y2022/control_loops/superstructure/superstructure.h
@@ -94,6 +94,7 @@
   SuperstructureState state_ = SuperstructureState::IDLE;
   bool front_intake_has_ball_ = false;
   bool back_intake_has_ball_ = false;
+  std::optional<double> last_shot_angle_ = std::nullopt;
   RequestedIntake turret_intake_state_ = RequestedIntake::kFront;
 
   DISALLOW_COPY_AND_ASSIGN(Superstructure);
diff --git a/y2022/control_loops/superstructure/turret/aiming.cc b/y2022/control_loops/superstructure/turret/aiming.cc
index e1d24f8..643adbc 100644
--- a/y2022/control_loops/superstructure/turret/aiming.cc
+++ b/y2022/control_loops/superstructure/turret/aiming.cc
@@ -15,7 +15,7 @@
 namespace {
 // Average speed-over-ground of the ball on its way to the target. Our current
 // model assumes constant ball velocity regardless of shot distance.
-constexpr double kBallSpeedOverGround = 12.0;  // m/s
+constexpr double kBallSpeedOverGround = 2.0;  // m/s
 
 // If the turret is at zero, then it will be at this angle at which the shot
 // will leave the robot. I.e., if the turret is at zero, then the shot will go
diff --git a/y2022/joystick_reader.cc b/y2022/joystick_reader.cc
index 653e1c1..fe52db7 100644
--- a/y2022/joystick_reader.cc
+++ b/y2022/joystick_reader.cc
@@ -55,7 +55,7 @@
 const ButtonLocation kCatapultPos(4, 3);
 const ButtonLocation kFire(4, 1);
 const ButtonLocation kTurret(4, 15);
-const ButtonLocation kAutoAim(4, 2);
+const ButtonLocation kAutoAim(4, 16);
 
 const ButtonLocation kClimberExtend(4, 6);
 const ButtonLocation kClimberIntakes(4, 5);
@@ -173,7 +173,7 @@
     double roller_front_speed = 0.0;
     double roller_back_speed = 0.0;
 
-    std::optional<double> turret_pos = 0.0;
+    std::optional<double> turret_pos = std::nullopt;
 
     double climber_position = 0.01;
 
@@ -261,7 +261,6 @@
     if (data.IsPressed(kFire)) {
       fire = true;
       // Provide a default turret goal.
-      turret_pos = 0.0;
     }
 
     if (data.IsPressed(kClimberIntakes)) {
diff --git a/y2022/message_bridge_client.sh b/y2022/message_bridge_client.sh
new file mode 100755
index 0000000..c81076a
--- /dev/null
+++ b/y2022/message_bridge_client.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+while true;
+do
+  ping -c 1 pi1 -W 1 && break;
+  sleep 1
+done
+
+echo Pinged
+
+exec /home/admin/bin/message_bridge_client "$@"
diff --git a/y2022/y2022_pi_template.json b/y2022/y2022_pi_template.json
index 3dff81e..4e47b07 100644
--- a/y2022/y2022_pi_template.json
+++ b/y2022/y2022_pi_template.json
@@ -324,6 +324,7 @@
     {
       "name": "message_bridge_client",
       "executable_name": "message_bridge_client",
+      "args": ["--rt_priority=16"],
       "nodes": [
         "pi{{ NUM }}"
       ]
diff --git a/y2022/y2022_roborio.json b/y2022/y2022_roborio.json
index 6e14bae..fa3406e 100644
--- a/y2022/y2022_roborio.json
+++ b/y2022/y2022_roborio.json
@@ -471,8 +471,8 @@
       ]
     },
     {
-      "name": "message_bridge_client",
-      "executable_name": "message_bridge_client",
+      "name": "roborio_message_bridge_client",
+      "executable_name": "message_bridge_client.sh",
       "args": ["--rt_priority=16"],
       "nodes": [
         "roborio"