Fix UART serialization

Didn't quite think it through before, and the test was buggy too.

Change-Id: Iaf0fa9d95731500850922217bb8614fc57f4cbb6
diff --git a/y2019/jevois/uart.cc b/y2019/jevois/uart.cc
index a776cdd..e3520a4 100644
--- a/y2019/jevois/uart.cc
+++ b/y2019/jevois/uart.cc
@@ -13,15 +13,23 @@
 UartToTeensyBuffer UartPackToTeensy(const Frame &message) {
   std::array<char, uart_to_teensy_size()> buffer;
   gsl::span<char> remaining_space = buffer;
-  for (int i = 0; i < 3; ++i) {
-    memcpy(remaining_space.data(), &message.targets[i].distance, sizeof(float));
-    remaining_space = remaining_space.subspan(sizeof(float));
-    memcpy(remaining_space.data(), &message.targets[i].height, sizeof(float));
-    remaining_space = remaining_space.subspan(sizeof(float));
-    memcpy(remaining_space.data(), &message.targets[i].heading, sizeof(float));
-    remaining_space = remaining_space.subspan(sizeof(float));
-    memcpy(remaining_space.data(), &message.targets[i].skew, sizeof(float));
-    remaining_space = remaining_space.subspan(sizeof(float));
+  remaining_space[0] = message.targets.size();
+  remaining_space = remaining_space.subspan(1);
+  for (size_t i = 0; i < 3; ++i) {
+    if (i < message.targets.size()) {
+      memcpy(remaining_space.data(), &message.targets[i].distance,
+             sizeof(float));
+      remaining_space = remaining_space.subspan(sizeof(float));
+      memcpy(remaining_space.data(), &message.targets[i].height, sizeof(float));
+      remaining_space = remaining_space.subspan(sizeof(float));
+      memcpy(remaining_space.data(), &message.targets[i].heading,
+             sizeof(float));
+      remaining_space = remaining_space.subspan(sizeof(float));
+      memcpy(remaining_space.data(), &message.targets[i].skew, sizeof(float));
+      remaining_space = remaining_space.subspan(sizeof(float));
+    } else {
+      remaining_space = remaining_space.subspan(sizeof(float) * 4);
+    }
   }
   remaining_space[0] = message.age.count();
   remaining_space = remaining_space.subspan(1);
@@ -53,15 +61,23 @@
 
   Frame message;
   gsl::span<const char> remaining_input = buffer;
+  const int number_targets = remaining_input[0];
+  remaining_input = remaining_input.subspan(1);
   for (int i = 0; i < 3; ++i) {
-    memcpy(&message.targets[i].distance, remaining_input.data(), sizeof(float));
-    remaining_input = remaining_input.subspan(sizeof(float));
-    memcpy(&message.targets[i].height, remaining_input.data(), sizeof(float));
-    remaining_input = remaining_input.subspan(sizeof(float));
-    memcpy(&message.targets[i].heading, remaining_input.data(), sizeof(float));
-    remaining_input = remaining_input.subspan(sizeof(float));
-    memcpy(&message.targets[i].skew, remaining_input.data(), sizeof(float));
-    remaining_input = remaining_input.subspan(sizeof(float));
+    if (i < number_targets) {
+      message.targets.push_back({});
+      Target *const target = &message.targets.back();
+      memcpy(&target->distance, remaining_input.data(), sizeof(float));
+      remaining_input = remaining_input.subspan(sizeof(float));
+      memcpy(&target->height, remaining_input.data(), sizeof(float));
+      remaining_input = remaining_input.subspan(sizeof(float));
+      memcpy(&target->heading, remaining_input.data(), sizeof(float));
+      remaining_input = remaining_input.subspan(sizeof(float));
+      memcpy(&target->skew, remaining_input.data(), sizeof(float));
+      remaining_input = remaining_input.subspan(sizeof(float));
+    } else {
+      remaining_input = remaining_input.subspan(sizeof(float) * 4);
+    }
   }
   message.age = camera_duration(remaining_input[0]);
   remaining_input = remaining_input.subspan(1);
diff --git a/y2019/jevois/uart.h b/y2019/jevois/uart.h
index 32ca110..8244fdb 100644
--- a/y2019/jevois/uart.h
+++ b/y2019/jevois/uart.h
@@ -13,7 +13,8 @@
 namespace jevois {
 
 constexpr size_t uart_to_teensy_size() {
-  return 3 /* targets */ * (sizeof(float) * 4 /* fields */) + 1 /* age */ +
+  return 1 /* number of targets */ +
+         3 /* targets */ * (sizeof(float) * 4 /* fields */) + 1 /* age */ +
          2 /* CRC-16 */;
 }
 using UartToTeensyBuffer =
diff --git a/y2019/jevois/uart_test.cc b/y2019/jevois/uart_test.cc
index 82160c7..1e90d84 100644
--- a/y2019/jevois/uart_test.cc
+++ b/y2019/jevois/uart_test.cc
@@ -12,10 +12,41 @@
 TEST(UartToTeensyTest, Basic) {
   Frame input_message;
   for (int i = 0; i < 3; ++i) {
-    input_message.targets[i].distance = i * 7 + 1;
-    input_message.targets[i].height = i * 7 + 2;
-    input_message.targets[i].heading = i * 7 + 3;
-    input_message.targets[i].skew = i * 7 + 5;
+    input_message.targets.push_back({});
+    Target *const target = &input_message.targets.back();
+    target->distance = i * 7 + 1;
+    target->height = i * 7 + 2;
+    target->heading = i * 7 + 3;
+    target->skew = i * 7 + 5;
+  }
+  input_message.age = camera_duration(123);
+  const UartToTeensyBuffer buffer = UartPackToTeensy(input_message);
+  const auto output_message = UartUnpackToTeensy(buffer);
+  ASSERT_TRUE(output_message);
+  EXPECT_EQ(input_message, output_message.value());
+}
+
+// Tests packing and then unpacking a message with arbitrary values and no
+// frames.
+TEST(UartToTeensyTest, NoFrames) {
+  Frame input_message;
+  input_message.age = camera_duration(123);
+  const UartToTeensyBuffer buffer = UartPackToTeensy(input_message);
+  const auto output_message = UartUnpackToTeensy(buffer);
+  ASSERT_TRUE(output_message);
+  EXPECT_EQ(input_message, output_message.value());
+}
+
+// Tests packing and then unpacking a message with just one frame.
+TEST(UartToTeensyTest, OneFrame) {
+  Frame input_message;
+  {
+    input_message.targets.push_back({});
+    Target *const target = &input_message.targets.back();
+    target->distance = 1;
+    target->height = 2;
+    target->heading = 3;
+    target->skew = 5;
   }
   input_message.age = camera_duration(123);
   const UartToTeensyBuffer buffer = UartPackToTeensy(input_message);