Merge remote-tracking branch 'brian/devel' into claw
diff --git a/aos/aos.swig b/aos/aos.swig
deleted file mode 100644
index fee0700..0000000
--- a/aos/aos.swig
+++ /dev/null
@@ -1,45 +0,0 @@
-%module aos
-
-%include "std_string.i";
-%include "stdint.i";
-
-%{
-#include <time.h>
-#include "aos/common/time.h"
-#include "aos/common/queue.h"
-static_assert(sizeof(int) == sizeof(clockid_t),
-              "Sizeof clockid_t has changed.");
-%}
-
-%apply int { clockid_t };
-
-// Makes the constructors from c++ types public so that this code can be called
-// from other places.
-%typemap(javabody) SWIGTYPE %{
-  private long swigCPtr;
-  protected boolean swigCMemOwn;
-
-  public $javaclassname(long cPtr, boolean cMemoryOwn) {
-    swigCMemOwn = cMemoryOwn;
-    swigCPtr = cPtr;
-  }
-
-  public static long getCPtr($javaclassname obj) {
-    return (obj == null) ? 0 : obj.swigCPtr;
-  }
-%}
-
-%rename(add) operator+;
-%rename(subtract) operator-;
-%rename(multiply) operator*;
-%rename(divide) operator/;
-%rename(modulo) operator%;
-%rename(equalTo) operator==;
-%rename(notEqual) operator!=;
-%rename(greaterThan) operator>;
-%rename(lessThan) operator<;
-%rename(leq) operator<=;
-%rename(geq) operator>=;
-
-%include "aos/common/time.h"
-%include "aos/common/queue.h"
diff --git a/aos/atom_code/ipc_lib/unique_message_ptr.h b/aos/atom_code/ipc_lib/unique_message_ptr.h
new file mode 100644
index 0000000..e30a4c0
--- /dev/null
+++ b/aos/atom_code/ipc_lib/unique_message_ptr.h
@@ -0,0 +1,39 @@
+#include <memory>
+
+#include "aos/atom_code/ipc_lib/queue.h"
+
+namespace aos {
+namespace internal {
+
+template<typename T>
+class queue_free {
+ public:
+  queue_free(RawQueue *queue) : queue_(queue) {}
+
+  void operator()(const T *message) {
+    queue_->FreeMessage(static_cast<const void *>(message));
+  }
+
+ private:
+  RawQueue *const queue_;
+};
+
+}  // namespace internal
+
+template<typename T>
+class unique_message_ptr : public ::std::unique_ptr<T, ::aos::internal::queue_free<T>> {
+ public:
+  unique_message_ptr(RawQueue *queue, T *message = NULL)
+      : ::std::unique_ptr<T, ::aos::internal::queue_free<T>>(message, ::aos::internal::queue_free<T>(queue)) {}
+
+  // Perfectly forward this so that the move functionality of ::std::unique_ptr
+  // works.
+  template <typename... Args>
+  unique_message_ptr<T> &operator=(Args &&... args) {
+        ::std::unique_ptr<T, ::aos::internal::queue_free<T>>::operator=(
+            ::std::forward<Args>(args)...);
+        return *this;
+  }
+};
+
+}  // namespace aos
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index b7775a1..12ac2a1 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -50,23 +50,8 @@
         '<(AOS)/common/common.gyp:time',
         '<(AOS)/common/common.gyp:once',
         'logging_interface',
+        '<(AOS)/common/common.gyp:queue_types',
       ],
     },
-    {
-# Private to make Brian happy.  Don't use elsewhere in so targets or risk things
-# breaking.
-      'target_name': 'aos_swig',
-      'type': 'static_library',
-      'sources': [
-        '<(AOS)/aos.swig',
-      ],
-      'variables': {
-        'package': 'aos',
-      },
-      'dependencies': [
-        '<(AOS)/common/common.gyp:queues',
-      ],
-      'includes': ['../build/swig.gypi'],
-    },
   ],
 }
diff --git a/aos/build/aos.gypi b/aos/build/aos.gypi
index 144bffb..c2c0813 100644
--- a/aos/build/aos.gypi
+++ b/aos/build/aos.gypi
@@ -79,6 +79,15 @@
     'include_dirs': [
       '<(DEPTH)',
     ],
+    # These have to be here because apparently gyp evaluates target_conditions
+    # even if the target is never used.
+    'variables': {
+      # Set this to 1 to disable rsyncing the file to the target.
+      'no_rsync%': 0,
+      # Set this to 1 if this file isn't a test that should get run by
+      # `build.sh tests`.
+      'is_special_test%': 0,
+    },
     'conditions': [
       ['DEBUG=="yes"', {
           'cflags': [
@@ -163,9 +172,6 @@
             'NOMINMAX',
           ],
         }, {
-          'variables': {
-            'no_rsync%': 0,
-          },
           'target_conditions': [
 # default to putting outputs into rsync_dir
             ['no_rsync==0 and _type!="static_library"', {
@@ -176,14 +182,6 @@
                 'product_dir': '<(so_dir)',
               }
             ],
-            ['_type=="loadable_module" or _type=="shared_library"', {
-                'ldflags': [
-# Support loading other shared objects that are in the same directory but not
-#   the shared object load path. Required for using the swig-generated libs.
-                  '-Wl,-rpath=\\$$ORIGIN',
-                ],
-              }
-            ],
           ],
           'ldflags': [
             '-pthread',
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 3006820..2584ee2 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -22,6 +22,7 @@
         '../linux_code/starter/starter.gyp:netconsole',
         '../common/common.gyp:queue_test',
         '../common/common.gyp:die_test',
+        '../common/common.gyp:queue_types_test',
         '../common/util/util.gyp:trapezoid_profile_test',
         '../common/util/util.gyp:wrapping_counter_test',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:cows_test',
diff --git a/aos/build/build.sh b/aos/build/build.sh
index bf9d2eb..cf79fc3 100755
--- a/aos/build/build.sh
+++ b/aos/build/build.sh
@@ -86,6 +86,6 @@
       ${OUTDIR}/lib/FRC_UserProgram.out
   fi
   if [[ ${ACTION} == tests ]]; then
-    find ${OUTDIR}/tests -executable -exec {} \;
+    find ${OUTDIR}/tests -executable -exec ${AOS}/build/run_test.sh {} \;
   fi
 fi
diff --git a/aos/build/create_jar b/aos/build/create_jar
deleted file mode 100755
index 2ef496e..0000000
--- a/aos/build/create_jar
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-source `dirname $0`/jdk_tools_common
-
-# This is a helper script that compiles java files into a jar.
-
-SOURCEFILEPARENTDIRS=`echo $4 | tr -d '"'`
-SOURCEFILEDIRS=`echo $1 | tr -d '"'`
-[ -n "${SOURCEFILEPARENTDIRS}" ] && SOURCEFILEDIRS+=" `find ${SOURCEFILEPARENTDIRS} -type d -maxdepth 1`"
-SOURCEFILES=`find ${SOURCEFILEDIRS} -name *.java`
-MANIFEST_FILE=$5
-OUTPUT_JAR=$6
-HEADER_DIR=$7
-GEN_HEADERS=$8
-
-CLASSFILES_DIR=${TMPDIR}/classfiles
-
-[ -a ${CLASSFILES_DIR} ] && rm -r ${CLASSFILES_DIR}
-mkdir ${CLASSFILES_DIR}
-[ -a ${HEADER_DIR} ] && rm -r ${HEADER_DIR}
-mkdir -p ${HEADER_DIR}
-
-javac -d ${CLASSFILES_DIR} -classpath "${EXTRA_CLASSPATH}" ${SOURCEFILES}
-
-jar cfm ${OUTPUT_JAR} ${MANIFEST_FILE} \
-  `find ${CLASSFILES_DIR} -name *.class | \
-  sed "s:${CLASSFILES_DIR}/\(.*\):-C ${CLASSFILES_DIR} \1:g"`
-
-[ -z ${GEN_HEADERS} ] || javah -d ${HEADER_DIR} \
-  -classpath "${EXTRA_CLASSPATH}:${OUTPUT_JAR}" ${GEN_HEADERS}
diff --git a/aos/build/download_externals.sh b/aos/build/download_externals.sh
index 36ce094..9af583b 100755
--- a/aos/build/download_externals.sh
+++ b/aos/build/download_externals.sh
@@ -49,13 +49,6 @@
 [ -f ${EIGEN_DIR}.tar.bz2 ] || wget http://bitbucket.org/eigen/eigen/get/${EIGEN_VERSION}.tar.bz2 -O ${EIGEN_DIR}.tar.bz2
 [ -d ${EIGEN_DIR} ] || ( mkdir ${EIGEN_DIR} && tar --strip-components=1 -C ${EIGEN_DIR} -xf ${EIGEN_DIR}.tar.bz2 )
 
-# get the javacv binaries
-JAVACV_VERSION=0.2
-JAVACV_DIR=${EXTERNALS}/javacv-bin
-JAVACV_ZIP=${EXTERNALS}/javacv-${JAVACV_VERSION}-bin.zip
-[ -f ${JAVACV_ZIP} ] || wget http://javacv.googlecode.com/files/javacv-${JAVACV_VERSION}-bin.zip -O ${JAVACV_ZIP}
-[ -d ${JAVACV_DIR} ] || ( cd ${EXTERNALS} && unzip ${JAVACV_ZIP} )
-
 # get the simple one-jar template jar
 ONEJAR_VERSION=0.97
 ONEJAR_JAR=${EXTERNALS}/one-jar-boot-${ONEJAR_VERSION}.jar
@@ -77,7 +70,7 @@
 
 # get gtest
 GTEST_VERSION=1.6.0
-GTEST_DIR=${EXTERNALS}/gtest-${GTEST_VERSION}
+GTEST_DIR=${EXTERNALS}/gtest-${GTEST_VERSION}-p1
 GTEST_ZIP=${EXTERNALS}/gtest-${GTEST_VERSION}.zip
 [ -f ${GTEST_ZIP} ] || wget http://googletest.googlecode.com/files/gtest-${GTEST_VERSION}.zip -O ${GTEST_ZIP}
 [ -d ${GTEST_DIR} ] || ( unzip ${GTEST_ZIP} -d ${TMPDIR} && mv ${TMPDIR}/gtest-${GTEST_VERSION} ${GTEST_DIR} && cd ${GTEST_DIR} && patch -p1 < ${AOS}/externals/gtest.patch )
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index ee10566..4165574 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -14,7 +14,7 @@
 
 # These versions have to be kept in sync with the ones in download_externals.sh.
     'eigen_version': '3.1.3',
-    'gtest_version': '1.6.0',
+    'gtest_version': '1.6.0-p1',
     'onejar_version': '0.97',
     'ctemplate_version': '129',
     'gflags_version': '2.0',
@@ -75,26 +75,6 @@
       },
     },
     {
-      'target_name': 'javacv',
-      'type': 'none',
-      'variables': {
-        'javacv_dir': '<(externals_abs)/javacv-bin',
-      },
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '/usr/lib/jvm/default-java/include',
-          '/usr/lib/jvm/default-java/include/linux',
-        ],
-        'variables': {
-          'classpath': [
-            '<(javacv_dir)/javacv.jar',
-            '<(javacv_dir)/javacpp.jar',
-            '<(javacv_dir)/javacv-linux-x86.jar',
-          ],
-        },
-      },
-    },
-    {
       'target_name': 'opencv',
       'type': 'none',
       'link_settings': {
@@ -180,9 +160,12 @@
       'direct_dependent_settings': {
         'include_dirs': ['<(externals)/gtest-<(gtest_version)/include'],
         'target_conditions': [
-          ['_type=="executable"', {
+          ['_type=="executable" and is_special_test==0', {
               'product_dir': '<(test_dir)',
             },
+          ], ['_type=="executable" and is_special_test==1', {
+              'product_dir': '<(test_dir)-special',
+            },
           ],
         ],
       },
diff --git a/aos/build/java.gypi b/aos/build/java.gypi
deleted file mode 100644
index d832b0a..0000000
--- a/aos/build/java.gypi
+++ /dev/null
@@ -1,74 +0,0 @@
-# Include this file in any target that is going to build java files.
-#
-# To use, create a target of the following form:
-# {
-#   'target_name': 'whatever',
-#   'variables': {
-#     'srcdirs': ['.', 'java'],
-#   },
-#   'includes': ['path/to/java.gypi'],
-# }
-# See below for more variables.
-# To make any output jars include some loadable modules, set the 'jni_libs'
-#   variable in 'direct_dependent_settings'. Making this easier causes lots of
-#   recursion issues in gyp.
-#   The dependency on these targets also has to be added manually.
-{
-  'type': 'none',
-  'variables': {
-# The manifest file for creating the jar.
-    'manifest%': '/dev/null',
-# Additional jars/directories to add to the classpath when compiling.
-# This target will automatically add itself to this list for any dependents.
-    'classpath': [],
-# Classes to generate JNI headers for.
-# They will be able to be #included as "jni/package_ClassName.h" by targets
-#   that depend on this one.
-    'gen_headers': [],
-# Like 'srcdirs', except not required to exist at gyp time. However, nothing
-#   here will depend on any files in these directories.
-    'gen_srcdirs': ['/dev/null'],
-# Like 'gen_srcdirs', except all folders that are children of this folder will
-#   be used instead.
-    'gen_srcdir_parents%': [],
-    'srcdirs': ['/dev/null'],
-    'jar_dir': '<(PRODUCT_DIR)/jars',
-    'java_files': '<!(find <(srcdirs) -name *.java)',
-    'create_jar': '<(AOS)/build/create_jar',
-    'out_jar': '<(jar_dir)/<(_target_name).jar',
-    'header_dir': '<(SHARED_INTERMEDIATE_DIR)/jni_headers_<!(pwd | sed s:/:_:g)_<(_target_name)',
-    'no_rsync': 1,
-  },
-  'direct_dependent_settings': {
-    'variables': {
-      'classpath': ['<(out_jar)'],
-    },
-    'include_dirs': [
-      '<(header_dir)',
-    ],
-  },
-  'actions': [
-    {
-      'action_name': 'run javac',
-      'message': 'Compiling java code',
-      'inputs': [
-        '<(create_jar)',
-        '<@(java_files)',
-        '>@(classpath)',
-        '>@(gen_srcdirs)',
-        '>(manifest)',
-      ],
-      'outputs': [
-        '<(out_jar)',
-      ],
-      'action': [
-        '<(create_jar)',
-        '<(srcdirs) <(gen_srcdirs)',
-        '<(INTERMEDIATE_DIR)', '>(classpath)',
-        '>(gen_srcdir_parents)',
-        '>(manifest)', '<(out_jar)',
-        '<(header_dir)/jni', '>(gen_headers)',
-      ],
-    },
-  ],
-}
diff --git a/aos/build/mkdirswig b/aos/build/mkdirswig
deleted file mode 100755
index 83403e4..0000000
--- a/aos/build/mkdirswig
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-# Creates the directory specified by the first argument and runs swig with the
-# rest of the arguments.
-mkdir -p $1
-shift
-swig $@
diff --git a/aos/build/onejar.gypi b/aos/build/onejar.gypi
deleted file mode 100644
index 6185f02..0000000
--- a/aos/build/onejar.gypi
+++ /dev/null
@@ -1,53 +0,0 @@
-# Include this file in any target that should get packaged with OneJAR.
-#
-# To use, create a target of the following form:
-# {
-#   'target_name': 'whatever',
-#   'variables': {
-#     'main_jar': 'something',
-#   },
-#   'includes': ['path/to/onejar.gypi'],
-# },
-# See below for more variables.
-{
-  'type': 'none',
-  'variables': {
-# The names of loadable_module targets to add to the jar.
-    'jni_libs': [],
-# Additional jars to add to the output.
-# Named this so that targets from java.gypi will add themselves automatically.
-    'classpath': [],
-    'jar_dir': '<(PRODUCT_DIR)/jars',
-    'create_onejar': '<(AOS)/build/create_onejar',
-    'out_onejar': '<(rsync_dir)/<(_target_name).jar',
-    'main_jar_file': '<(jar_dir)/<(main_jar).jar',
-    'shared_objects': ">!(echo '>(jni_libs)' | sed 's:[^ ]*:<(so_dir)/lib\\0.so:g')",
-    'no_rsync': 1,
-  },
-  'dependencies': [
-    '<(EXTERNALS):onejar',
-  ],
-  'product_dir': '<(PRODUCT_DIR)',
-  'actions': [
-    {
-      'action_name': 'create onejar',
-      'message': 'Creating OneJAR jar',
-      'inputs': [
-        '<(create_onejar)',
-        '>@(classpath)',
-        '<(main_jar_file)',
-        '>@(shared_objects)',
-      ],
-      'outputs': [
-        '<(out_onejar)',
-      ],
-      'action': [
-        '<(create_onejar)',
-        '<(main_jar_file)',
-        '<(INTERMEDIATE_DIR)', '>(classpath)',
-        '<(out_onejar)', '>(onejar_jar)',
-        '>(shared_objects)',
-      ],
-    },
-  ],
-}
diff --git a/aos/build/queues.gypi b/aos/build/queues.gypi
index 7ae0d89..843272d 100644
--- a/aos/build/queues.gypi
+++ b/aos/build/queues.gypi
@@ -28,9 +28,6 @@
     'output_h': '<(out_dir)/<(RULE_INPUT_ROOT).q.h',
     'output_cc': '<(out_dir)/<(RULE_INPUT_ROOT).q.cc',
     'output_main': '<(out_dir)/<(RULE_INPUT_ROOT)_main.cc',
-    'output_swg': '<(out_dir)/<(RULE_INPUT_ROOT).q.swig',
-    'output_java_wrap': '<(out_dir)/<(RULE_INPUT_ROOT)_java_wrap.cc',
-    'java_dir': '<(out_dir)/<(RULE_INPUT_ROOT).q_java',
     'no_rsync': 1,
   },
   'rules': [
@@ -44,19 +41,6 @@
         '<(output_h)',
         '<(output_cc)',
       ],
-      'conditions': [
-        ['OS=="crio"', {
-          'outputs': [
-            # cRIO doesn't do swig for a good reason.
-          ]
-        },{
-          'outputs': [
-            '<(output_swg)',
-            '<(output_java_wrap)',
-            '<(java_dir)',
-          ]
-        }]
-      ],
       'inputs': [
         '<(script)',
         '<!@(find <(AOS)/build/queues/ -name *.rb)',
@@ -64,8 +48,6 @@
         '<(AOS)/common/time.h',
       ],
       'action': ['ruby', '<(script)',
-        '--swig',
-        '--swigccout', '<(output_java_wrap)',
         '-I', '<(DEPTH)',
         '<(RULE_INPUT_PATH)',
         '-cpp_out',
@@ -98,14 +80,7 @@
       'process_outputs_as_sources': 1,
     },
   ],
-  'cflags': [
-# For the swig-generated C++ code.
-    '-fno-strict-aliasing',
-    '-Wno-cast-qual',
-  ],
   'include_dirs': [
-    '/usr/lib/jvm/default-java/include',
-    '/usr/lib/jvm/default-java/include/linux',
     '<(prefix_dir)/<(_target_name)',
   ],
   'direct_dependent_settings': {
@@ -118,6 +93,8 @@
   },
   'dependencies': [
     '<(AOS)/common/common.gyp:queues',
+    '<(AOS)/common/common.gyp:once',
+    '<(AOS)/common/common.gyp:queue_types',
   ],
   'export_dependent_settings': [
     '<(AOS)/common/common.gyp:queues',
diff --git a/aos/build/queues/compiler.rb b/aos/build/queues/compiler.rb
index ffee5d3..899ba85 100644
--- a/aos/build/queues/compiler.rb
+++ b/aos/build/queues/compiler.rb
@@ -1,22 +1,7 @@
-$LOAD_PATH.unshift(".")
-["tokenizer.rb","q_file.rb","queue_group.rb","queue.rb","namespaces.rb",
-"interface.rb","errors.rb", "q_struct.rb"].each do |name|
-	require File.dirname(__FILE__) + "/objects/" + name
-end
-["standard_types.rb","auto_gen.rb","file_pair_types.rb",
-"dep_file_pair.rb","swig.rb"].each do |name|
-	require File.dirname(__FILE__) + "/cpp_pretty_print/" + name
-end
-["q_file.rb","message_dec.rb","queue_dec.rb", "q_struct.rb"].each do |name|
-	require File.dirname(__FILE__) + "/output/" + name
-end
-require "fileutils"
-require "pathname"
+require File.dirname(__FILE__) + '/load.rb'
 
 def parse_args(globals,args)
 	i = 0
-	$swig = false
-	$swigccout_path = ""
 	while(i < args.length)
 		if(args[i] == "-I")
 			args.delete_at(i)
@@ -28,9 +13,6 @@
 			end
 			path = args.delete_at(i)
 			globals.add_path(path)
-		elsif(args[i] == "--swigccout")
-			args.delete_at(i)
-			$swigccout_path = args.delete_at(i)
 		elsif(args[i] == "-cpp_out")
 			args.delete_at(i)
 			path = args.delete_at(i)
@@ -47,9 +29,6 @@
 				exit!(-1)
 			end
 			$cpp_out = path.split(/\\|\//)
-		elsif(args[i] == "--swig")
-			$swig = true
-			args.delete_at(i)
 		elsif(args[i] == "-cpp_base")
 			args.delete_at(i)
 			path = args.delete_at(i)
@@ -79,6 +58,14 @@
 		exit!(-1)
 	end
 end
+def format_pipeline(output)
+  read_in, write_in = IO.pipe()
+  child = Process.spawn('clang-format-3.4 --style=google',
+                        {:in=>read_in, write_in=>:close,
+                         :out=>output})
+  read_in.close
+  [child, write_in]
+end
 def build(filename,globals_template)
 	globals = Globals.new()
 	globals_template.paths.each do |path|
@@ -96,41 +83,30 @@
 
 	h_file_path = $cpp_base + "/" + rel_path + ".h"
 	cc_file_path = $cpp_base + "/" + rel_path + ".cc"
-	swig_file_path = $cpp_base + "/" + rel_path + ".swig"
-	java_directory = $cpp_base + "/" + rel_path + "_java/"
 	cpp_tree.add_cc_include((rel_path + ".h").inspect)
 	cpp_tree.add_cc_include("aos/common/byteorder.h".inspect)
 	cpp_tree.add_cc_include("aos/common/inttypes.h".inspect)
+	cpp_tree.add_cc_include("aos/common/queue_types.h".inspect)
+  cpp_tree.add_cc_include("aos/common/once.h".inspect)
 	cpp_tree.add_cc_using("::aos::to_network")
 	cpp_tree.add_cc_using("::aos::to_host")
-	cpp_tree.add_swig_header_include("aos/common/queue.h".inspect)
-	cpp_tree.add_swig_body_include("aos/linux_code/queue-tmpl.h".inspect)
-	cpp_tree.add_swig_header_include("aos/common/time.h".inspect)
-	cpp_tree.add_swig_include((rel_path + ".h").inspect)
 
 	header_file = File.open(h_file_path,"w+")
 	cc_file = File.open(cc_file_path,"w+")
-	cpp_tree.write_header_file($cpp_base,header_file)
-	cpp_tree.write_cc_file($cpp_base,cc_file)
-	cc_file.close()
-	header_file.close()
-	if ($swig)
-		swig_file = File.open(swig_file_path,"w+")
-		cpp_tree.write_swig_file($cpp_base,swig_file,q_filename)
-		swig_file.close()
-		namespace = q_file.namespace.get_name()[1..-1]
-		FileUtils.mkdir_p(java_directory)
-		includes = globals.paths.collect { |a| "-I#{a}" }
-
-		if (!system('/usr/bin/swig', *(includes + ['-I' + $cpp_base + '/',
-				    '-package', namespace,
-				    '-outdir', java_directory,
-				    '-o', $swigccout_path,
-				    '-c++', '-Wall', '-Wextra', '-java', swig_file_path])))
-			puts "Swig failed."
-			exit -1
-		end
-	end
+  header_child, header_output = format_pipeline(header_file)
+  cc_child, cc_output = format_pipeline(cc_file)
+	cpp_tree.write_header_file($cpp_base,header_output)
+	cpp_tree.write_cc_file($cpp_base,cc_output)
+	cc_output.close()
+	header_output.close()
+  if !Process.wait2(cc_child)[1].success?
+    $stderr.puts "Formatting cc file failed."
+    exit 1
+  end
+  if !Process.wait2(header_child)[1].success?
+    $stderr.puts "Formatting header file failed."
+    exit 1
+  end
 end
 begin
 	args = ARGV.dup
diff --git a/aos/build/queues/cpp_pretty_print/auto_gen.rb b/aos/build/queues/cpp_pretty_print/auto_gen.rb
index 633c7c0..d3fb480 100644
--- a/aos/build/queues/cpp_pretty_print/auto_gen.rb
+++ b/aos/build/queues/cpp_pretty_print/auto_gen.rb
@@ -18,6 +18,34 @@
 		@text = "TODO(#{owner}): #{text}"
 	end
 end
+class CPP::StaticVar
+	class ForwardDec
+		def initialize(func) ; @func = func ; end
+		def pp(state) ; @func.pp_forward_dec(state) ; end
+	end
+	attr_accessor :args, :static
+	def initialize(type_class, val_type, name, args = CPP::Args.new())
+		@type_class = type_class
+		@val_type = val_type
+		@name = name
+		@args = args
+		@static = true
+	end
+	def forward_dec() ; ForwardDec.new(self) ; end
+	def pp_forward_dec(state)
+    if (@static)
+      state.print("static ")
+		end
+		state.print("#{@val_type} #{@name}")
+	end
+	def pp(state)
+		state.print("#{@val_type} #{@type_class.chop_method_prefix}#{@name}(")
+		state.pp(@args)
+		state.print(")")
+	end
+	alias_method :pp_header_file, :pp_forward_dec
+	alias_method :pp_cc_file, :pp
+end
 class CPP::MemberFunc
 	class ForwardDec
 		def initialize(func) ; @func = func ; end
@@ -75,10 +103,6 @@
 		state.pp(@suite)
 		state.v_pad(2)
 	end
-  def pp_pre_swig_file(state)
-  end
-  def pp_post_swig_file(state)
-  end
 	alias_method :pp_header_file, :pp_forward_dec
 	alias_method :pp_cc_file, :pp
 	
diff --git a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
index 571d9e0..c13bb9e 100644
--- a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
+++ b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
@@ -54,36 +54,6 @@
 		end
 	end
 end
-class MemberElementPreSWIG < GroupElement
-	def initialize(elem)
-		@elem = elem
-	end
-	def pp(state)
-		if(@elem.respond_to?(:pp_pre_swig_file))
-			@elem.pp_pre_swig_file(state)
-		else
-			state.pp(@elem)
-		end
-	end
-	def self.check(plan,elem)
-		plan.push(self.new(elem)) if(elem.respond_to?(:pp_pre_swig_file))
-	end
-end
-class MemberElementPostSWIG < GroupElement
-	def initialize(elem)
-		@elem = elem
-	end
-	def pp(state)
-		if(@elem.respond_to?(:pp_post_swig_file))
-			@elem.pp_post_swig_file(state)
-		else
-			state.pp(@elem)
-		end
-	end
-	def self.check(plan,elem)
-		plan.push(self.new(elem)) if(elem.respond_to?(:pp_post_swig_file))
-	end
-end
 class MemberElementCC < GroupElement
 	attr_accessor :pp_override
 	def initialize(elem)
@@ -181,36 +151,6 @@
 		end
 	end
 end
-class SWIGPre_Mask
-	def initialize(elem)
-		@elem = elem
-	end
-	def plan_cc(plan)
-	end
-	def plan_pre_swig(plan)
-		elem = MemberElementPreSWIG.new(@elem)
-		plan.push(elem)
-	end
-	def plan_post_swig(plan)
-	end
-	def plan_header(plan);
-	end
-end
-class SWIGPost_Mask
-	def initialize(elem)
-		@elem = elem
-	end
-	def plan_cc(plan)
-	end
-	def plan_pre_swig(plan)
-	end
-	def plan_post_swig(plan)
-		elem = MemberElementPostSWIG.new(@elem)
-		plan.push(elem)
-	end
-	def plan_header(plan);
-	end
-end
 class CC_Mask
 	def initialize(elem)
 		@elem = elem
@@ -222,10 +162,6 @@
 	end
 	def plan_header(plan);
 	end
-	def plan_pre_swig(plan);
-	end
-	def plan_post_swig(plan);
-	end
 end
 module Types
 	class TypeDef
@@ -246,6 +182,9 @@
 			@protections = {}
 			@deps = []
 		end
+    def parent_class
+      @parent.split(' ')[1] if @parent
+    end
 		class ProtectionGroup
 			def initialize(name)
 				@name = name
@@ -280,30 +219,6 @@
 				@members.push(CC_Mask.new(CPP::Comment.new(arg)))
 			end
 		end
-		def add_pre_swig(arg)
-			@members.push(SWIGPre_Mask.new(arg))
-		end
-		def add_post_swig(arg)
-			@members.push(SWIGPost_Mask.new(arg))
-		end
-		def plan_pre_swig(plan)
-			@members.each do |member|
-				if(member.respond_to?(:plan_pre_swig))
-					member.plan_pre_swig(plan)
-				elsif(member.respond_to?(:pp_pre_swig_file))
-					plan.push(MemberElementPreSWIG.new(member))
-				end
-			end
-		end
-		def plan_post_swig(plan)
-			@members.each do |member|
-				if(member.respond_to?(:plan_post_swig))
-					member.plan_post_swig(plan)
-				elsif(member.respond_to?(:pp_post_swig_file))
-					plan.push(MemberElementPostSWIG.new(member))
-				end
-			end
-		end
 		def plan_cc(plan)
 			@members.each do |member|
 				if(member.respond_to?(:plan_cc))
@@ -391,6 +306,7 @@
 		end
 	end
 	class Struct < Type
+		attr_accessor :members
 		def add_member(member)
 			@members.push(member)
 			return member
@@ -456,12 +372,6 @@
 		def add_cc(arg)
 			@members.push(CC_Mask.new(arg))
 		end
-		def add_pre_swig(arg)
-			@members.push(SWIGPre_Mask.new(arg))
-		end
-		def add_post_swig(arg)
-			@members.push(SWIGPost_Mask.new(arg))
-		end
 		def chop_method_prefix()
 			""
 		end
@@ -474,25 +384,6 @@
 		def add_var_dec(arg)
 			add DepMask.new(arg)
 		end
-		def plan_pre_swig(plan)
-			plan.implicit = ImplicitName.new(@name,plan.implicit)
-			@members.each do |member|
-				if(member.respond_to?(:plan_pre_swig))
-					member.plan_pre_swig(plan)
-				else
-					MemberElementPreSWIG.check(plan,member)
-				end
-			end
-		end
-		def plan_post_swig(plan)
-			@members.each do |member|
-				if(member.respond_to?(:plan_post_swig))
-					member.plan_post_swig(plan)
-				else
-					MemberElementPostSWIG.check(plan,member)
-				end
-			end
-		end
 		def plan_cc(plan)
 			plan.implicit = ImplicitName.new(@name,plan.implicit)
 			@members.each do |member|
@@ -523,23 +414,11 @@
 	def initialize(rel_path)
 		@rel_path = rel_path
 		@cc_includes = []
-		@swig_includes_h = []
-		@swig_includes_swig = []
 		@header_includes = []
 		@spaces = []
 		@cc_usings = []
 		@cache = {}
 	end
-	def add_swig_body_include(inc_path)
-		@swig_includes_swig << CPP::SwigInclude.new(inc_path)
-	end
-	def add_swig_header_include(inc_path)
-		@swig_includes_h << CPP::Include.new(inc_path)
-	end
-	def add_swig_include(inc_path)
-		@swig_includes_h << CPP::Include.new(inc_path)
-		@swig_includes_swig << CPP::SwigInclude.new(inc_path)
-	end
 	def add_cc_include(inc_path)
 		@cc_includes << CPP::Include.new(inc_path)
 	end
@@ -588,30 +467,4 @@
 		include_guard.name = @rel_path.upcase.gsub(/[\.\/\\]/,"_") + "_H_"
 		CPP.pretty_print(include_guard,header_file)
 	end
-	def write_swig_file(cpp_base,swig_file,q_filename)
-		plan_pre_swig = GroupPlan.new(nil, elems_cc = [])
-		plan_post_swig = GroupPlan.new(nil, elems_cc = [])
-		q_filename_underscore = q_filename.gsub(".","_")
-		@spaces.each do |space|
-			space.plan_pre_swig(plan_pre_swig)
-			space.plan_post_swig(plan_post_swig)
-		end
-                header_includes = CPP::SWIGBraces.new(CPP::Suite.new(@swig_includes_h))
-		# TODO(aschuh): I should probably %import any other headers from this queue's dependencies.
-                suite = CPP::Suite.new(["%module \"#{q_filename_underscore}\"",
-				        "%typemap(javaimports) SWIGTYPE, SWIGTYPE * \"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
-				        "%pragma(java) jniclassimports=\"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
-                                        "%pragma(java) moduleimports=\"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
-                                        "%include \"std_string.i\"",
-                                        "%include \"stdint.i\""] +
-                                       [header_includes] +
-                                       #["%import \"aos/common/time.h\"",
-                                        #"%import \"aos/common/queue.h\""] +
-                                       ["%import \"aos/aos.swig\""] +
-                                       [plan_pre_swig] +
-                                       @swig_includes_swig +
-                                       [plan_post_swig]
-                                      )
-		CPP.pretty_print(suite, swig_file)
-	end
 end
diff --git a/aos/build/queues/cpp_pretty_print/swig.rb b/aos/build/queues/cpp_pretty_print/swig.rb
deleted file mode 100644
index ac6d062..0000000
--- a/aos/build/queues/cpp_pretty_print/swig.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-class CPP::SwigPragma
-	attr_accessor :suite
-	def initialize(language, pragmatype, suite = CPP::Suite.new())
-		@suite = suite
-	@language = language
-	@pragmatype = pragmatype
-	end
-	def pp(state)
-		state.needs_semi = false
-		state.suppress_indent()
-		state.print("%pragma(#{@language}) #{@pragmatype}=%{")
-		state.endline()
-		if(@suite.respond_to?(:pp_no_braces))
-			@suite.pp_no_braces(state)
-		else
-			state.pp(@suite)
-		end
-		state.endline()
-		state.needs_semi = false
-		state.suppress_indent()
-		state.print("%}")
-		state.endline()
-		state.endline()
-	end
-end
-class CPP::SWIGBraces
-	attr_accessor :suite
-	def initialize(suite = CPP::Suite.new())
-		@suite = suite
-	end
-	def pp(state)
-		state.needs_semi = false
-		state.suppress_indent()
-		state.print("%{")
-		state.endline()
-		if(@suite.respond_to?(:pp_no_braces))
-			@suite.pp_no_braces(state)
-		else
-			state.pp(@suite)
-		end
-		state.endline()
-		state.needs_semi = false
-		state.suppress_indent()
-		state.print("%}")
-		state.endline()
-		state.endline()
-	end
-end
-class CPP::SwigInclude
-	attr_accessor :filename
-	def initialize(filename)
-		@filename = filename
-	end
-	def pp(state)
-		state.needs_semi = false
-		state.suppress_indent()
-		state.print("%include #{@filename}")
-		state.endline()
-	end
-end
diff --git a/aos/build/queues/load.rb b/aos/build/queues/load.rb
new file mode 100644
index 0000000..c299d80
--- /dev/null
+++ b/aos/build/queues/load.rb
@@ -0,0 +1,14 @@
+$LOAD_PATH.unshift(".")
+["tokenizer.rb","q_file.rb","queue_group.rb","queue.rb","namespaces.rb",
+"interface.rb","errors.rb", "q_struct.rb"].each do |name|
+	require File.dirname(__FILE__) + "/objects/" + name
+end
+["standard_types.rb","auto_gen.rb","file_pair_types.rb",
+"dep_file_pair.rb"].each do |name|
+	require File.dirname(__FILE__) + "/cpp_pretty_print/" + name
+end
+["q_file.rb","message_dec.rb","queue_dec.rb", "q_struct.rb"].each do |name|
+	require File.dirname(__FILE__) + "/output/" + name
+end
+require "fileutils"
+require "pathname"
diff --git a/aos/build/queues/objects/q_file.rb b/aos/build/queues/objects/q_file.rb
index beaf8f0..f73b638 100644
--- a/aos/build/queues/objects/q_file.rb
+++ b/aos/build/queues/objects/q_file.rb
@@ -138,7 +138,7 @@
 				suite << StructStmt.parse(tokens)
 			else
 				tokens.qError(<<ERROR_MSG)
-expected a "package","import","queue","queue_group", or "message" statement rather
+expected a "package","import","queue","struct","queue_group", or "message" statement rather
 	than a #{token.data.inspect}, (whatever that is?)
 	oh! no! a feature request!?
 	Wot. Wot.
diff --git a/aos/build/queues/output/message_dec.rb b/aos/build/queues/output/message_dec.rb
index 579ce0c..fdc03ee 100644
--- a/aos/build/queues/output/message_dec.rb
+++ b/aos/build/queues/output/message_dec.rb
@@ -4,7 +4,121 @@
   require "digest/sha1"
 end
 
-class Target::MessageDec < Target::Node
+class Target::StructBase < Target::Node
+	def create_DoGetType(type_class, cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","DoGetType")
+		member_func.static = true
+		fields = []
+    register_members = []
+		@members.each do |member|
+			tId = member.getTypeID()
+			fieldName = member.name.inspect
+			if(member.respond_to?(:add_TypeRegister))
+        register_members.push(member)
+			end
+			fields << "new ::aos::MessageType::Field{#{tId}, #{fieldName}}"
+		end
+    register_members.uniq do |member|
+      member.type
+    end.each do |member|
+			member.add_TypeRegister(cpp_tree, type_class, member_func)
+    end
+		id = getTypeID()
+		member_func.suite << ("static const ::aos::MessageType kMsgMessageType(#{type_class.parent_class ? type_class.parent_class + '::Size()' : 0}, #{id}, #{(@loc.respond_to?(:queue_name) ? @loc.queue_name(@name) : "#{@loc.get_name()}.#{@name}").inspect}, {" +
+		  "#{fields.join(", ")}})");
+		type_class.add_member(member_func)
+		member_func.suite << "::aos::type_cache::Add(kMsgMessageType)"
+		member_func.suite << CPP::Return.new("&kMsgMessageType")
+	end
+        def create_InOrderConstructor(type_class, cpp_tree)
+          cons = CPP::Constructor.new(type_class)
+          type_class.add_member(cons)
+          @members.each do |member|
+            if member.respond_to?(:type_name)
+              type_name = member.type_name(cpp_tree)
+            else
+              type_name = member.type
+            end
+
+            cons.args << "#{type_name} #{member.name}_in"
+            cons.add_cons(member.name, member.name + '_in')
+          end
+        end
+        def create_DefaultConstructor(type_class, cpp_tree)
+          cons = CPP::Constructor.new(type_class)
+          type_class.add_member(cons)
+          cons.add_cons(type_class.parent_class) if type_class.parent_class
+          cons.suite << CPP::FuncCall.build('Zero')
+        end
+	def create_Zero(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"void","Zero")
+		type_class.add_member(member_func)
+		@members.each do |elem|
+			elem.zeroCall(member_func.suite)
+		end
+                member_func.suite << CPP::FuncCall.new(type_class.parent_class + '::Zero') if type_class.parent_class
+	end
+	def create_Size(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Size")
+		member_func.inline = true
+        member_func.static = true
+		type_class.add_member(member_func.forward_dec)
+		size = 0
+		@members.each do |elem|
+			size += elem.size
+		end
+                if type_class.parent_class
+		  member_func.suite << CPP::Return.new(CPP::Add.new(size,
+							"#{type_class.parent_class}::Size()"))
+                else
+                  member_func.suite << CPP::Return.new(size)
+                end
+	end
+	def create_Serialize(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
+		type_class.add_member(member_func)
+		member_func.args << "char *buffer"
+		member_func.suite << "#{type_class.parent_class}::Serialize(buffer)" if type_class.parent_class
+		member_func.const = true
+		offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+		@members.each do |elem|
+			elem.toNetwork(offset,member_func.suite)
+			offset += " + #{elem.size}";
+		end
+                member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+	end
+	def create_Deserialize(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Deserialize")
+		type_class.add_member(member_func)
+		member_func.args << "const char *buffer"
+		member_func.suite << "#{type_class.parent_class}::Deserialize(buffer)" if type_class.parent_class
+		offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+		@members.each do |elem|
+			elem.toHost(offset,member_func.suite)
+			offset += " + #{elem.size}";
+		end
+                member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+	end
+	def simpleStr()
+		return "{\n" + @members.collect() { |elem| elem.simpleStr() + "\n"}.join("") + "}"
+	end
+	def getTypeID()
+		return "0x" + (((Digest::SHA1.hexdigest(simpleStr())[0..3].to_i(16)) << 16) | size).to_s(16)
+	end
+	def add_member(member)
+		@members << member
+	end
+	def size()
+		return @size if(@size)
+		@size = 0
+		@members.each do |elem|
+			@size += elem.size
+		end
+		return @size
+	end
+end
+
+class Target::MessageDec < Target::StructBase
 	attr_accessor :name,:loc,:parent,:msg_hash
 	def initialize(name)
 		@name = name
@@ -20,9 +134,6 @@
 			return "#{@name}"
 		end
 	end
-	def add_member(member)
-		@members << member
-	end
 	def create_Print(type_class,cpp_tree)
 		member_func = CPP::MemberFunc.new(type_class,"size_t","Print")
 		type_class.add_member(member_func)
@@ -42,54 +153,12 @@
     member_func.suite << "length -= super_size"
     member_func.suite << "return super_size + snprintf(buffer, length, " + ([format] + args).join(", ") + ")";
 	end
-	def create_Serialize(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
+	def create_GetType(type_class, cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
 		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		member_func.args << "char *buffer"
-		member_func.suite << "::aos::Message::Serialize(buffer)"
-		member_func.const = true
-		offset = 0
-		@members.each do |elem|
-			elem.toNetwork(offset,member_func.suite)
-			offset += elem.size;
-		end
-                member_func.suite << "return Size()"
-	end
-	def create_Deserialize(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Deserialize")
-		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		member_func.args << "const char *buffer"
-		member_func.suite << "::aos::Message::Deserialize(buffer)"
-		offset = 0
-		@members.each do |elem|
-			elem.toHost(offset,member_func.suite)
-			offset += elem.size;
-		end
-                member_func.suite << "return Size()"
-	end
-	def create_Zero(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"void","Zero")
-		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		@members.each do |elem|
-			elem.zeroCall(member_func.suite)
-		end
-		member_func.suite << "::aos::Message::Zero()"
-	end
-	def create_Size(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Size")
-		member_func.inline = true
-        member_func.static = true
-		type_class.add_member(member_func.forward_dec)
-		#cpp_tree.cc_file.add_funct(member_func)
-		size = 0
-		@members.each do |elem|
-			size += elem.size
-		end
-		member_func.suite << CPP::Return.new(CPP::Add.new(size,
-							"::aos::Message::Size()"))
+		member_func.static = true
+		member_func.suite << "static ::aos::Once<const ::aos::MessageType> getter(#{type_class.name}::DoGetType)"
+		member_func.suite << CPP::Return.new("getter.Get()")
 	end
 	def self.builder_loc(loc)
 		return @builder_loc if(@builder_loc)
@@ -116,9 +185,7 @@
 		end
 		cpp_tree.set(self,type_class)
 		type_class.set_parent("public ::aos::Message")
-		ts = (@members.collect { |elem|
-			elem.type + " " + elem.name
-		}).join(";")
+		ts = self.simpleStr()
 		self.msg_hash = "0x#{Digest::SHA1.hexdigest(ts)[-8..-1]}"
 		type_class.add_member("enum {kQueueLength = 1234, kHash = #{self.msg_hash}}")
 		@members.each do |elem|
@@ -130,6 +197,10 @@
 		create_Zero(type_class,cpp_tree)
 		create_Size(type_class,cpp_tree)
 		create_Print(type_class,cpp_tree)
+		create_GetType(type_class, cpp_tree)
+		create_DoGetType(type_class, cpp_tree)
+    create_DefaultConstructor(type_class, cpp_tree)
+    create_InOrderConstructor(type_class, cpp_tree)
 
 		b_namespace = cpp_tree.get(b_loc = self.class.builder_loc(@loc))
 
@@ -146,7 +217,6 @@
 		msg_bld_t = "MessageBuilder< #{t}>"
 		safetemplate.add_member(:private,"#{safemsg_ptr_t} msg_ptr_")
 		template.add_member(:private,"#{msg_ptr_t} msg_ptr_")
-		namespace.add_pre_swig("%feature(\"valuewrapper\") #{safemsg_bld_t}")
 		template.add_member(:private,"#{msg_bld_t}(const #{msg_bld_t}&)")
 		template.add_member(:private,"void operator=(const #{msg_bld_t}&)")
 		safetemplate.add_member(:private,"friend class ::aos::Queue< #{t}>")
@@ -170,13 +240,6 @@
 		DefineMembers(cpp_tree, safetemplate, safemsg_bld_t)
 		DefineMembers(cpp_tree, template, msg_bld_t)
 
-		java_type_name = java_type_name(cpp_tree)
-		namespace.add_post_swig("%template(#{java_type_name}) ::aos::Queue< #{t}>")
-		namespace.add_post_swig("%template(#{java_ptr_name(cpp_tree)}) ::aos::SafeScopedMessagePtr< #{t}>")
-		namespace.add_post_swig("%template(#{java_builder_name(cpp_tree)}) ::aos::SafeMessageBuilder< #{t}>")
-		# TODO(aschuh): Figure out why this doesn't work and fix it.
-		#namespace.add_post_swig("%typemap(javabase) #{@name} \"aos.Message\"")
-
 	end
 	def DefineMembers(cpp_tree, template, msg_bld_t) 
 		send = template.def_func("bool","Send")
@@ -196,15 +259,6 @@
 
 		end
 	end
-	def java_ptr_name(cpp_tree)
-		return "#{@name}MessagePtr"
-	end
-	def java_builder_name(cpp_tree)
-		return "#{@name}MessageBuilder"
-	end
-	def java_type_name(cpp_tree)
-		return "#{@name}Queue"
-	end
 end
 class Target::MessageElement < Target::Node
 	attr_accessor :name,:loc,:size,:zero,:type,:printformat
@@ -218,19 +272,25 @@
 		"#{@type} #{@name}"
 	end
 	def toNetwork(offset,suite, parent = "")
-		offset = (offset == 0) ? "" : "#{offset} + "
 		suite << f_call = CPP::FuncCall.build("to_network",
 									 "&#{parent}#{@name}",
-									 "&buffer[#{offset}::aos::Message::Size()]")
+									 "&buffer[#{offset}]")
 		f_call.args.dont_wrap = true
 	end
 	def toHost(offset,suite, parent = "")
-		offset = (offset == 0) ? "" : "#{offset} + "
 		suite << f_call = CPP::FuncCall.build("to_host",
-									 "&buffer[#{offset}::aos::Message::Size()]",
+									 "&buffer[#{offset}]",
 									 "&#{parent}#{@name}")
 		f_call.args.dont_wrap = true
 	end
+	def getTypeID()
+		Digest::SHA1.hexdigest(@type)[0..7].to_i(16) |
+                0x2000 | # marks it as primitive
+                size
+	end
+	def simpleStr()
+		"#{@type} #{@name}"
+	end
 	def set_message_builder(suite)
 		suite << "msg_ptr_->#{@name} = #{@name}"
 	end
diff --git a/aos/build/queues/output/q_file.rb b/aos/build/queues/output/q_file.rb
index 1ef47a4..a39799d7 100644
--- a/aos/build/queues/output/q_file.rb
+++ b/aos/build/queues/output/q_file.rb
@@ -98,7 +98,6 @@
 		type_class.set_parent("public ::aos::QueueGroup")
 		@queues.each do |queue|
 			type_class.add_member(:public,queue.create_usage(cpp_tree))
-			namespace.add_pre_swig("%immutable #{@name}::#{queue.name}")
 		end
 		create_Constructor(type_class,cpp_tree)
 		namespace.add(type_class)
@@ -155,7 +154,6 @@
 		cons = CPP::Constructor.new(init_class)
 		init_class.add_member(:public,cons)
 		cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
-		if_stmt.suite << "printf(#{"making a #{@name}!\n".inspect})"
 
 		cons_call = CPP::FuncCall.new("new #{type_name}")
 		cons_call.args.push(@loc.queue_name(@name).inspect)
@@ -165,12 +163,10 @@
 			cons_call.args.push(@loc.queue_name(@name + "." + queue.name).inspect)
 		end
 		if_stmt.suite << CPP::Assign.new("#{@name}_ptr",cons_call)
-		if_stmt.else_suite << CPP::FuncCall.build("printf","already made a #{@name}\n".inspect)
 
 
 		destruct = CPP::Destructor.new(init_class)
 		destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
-		if_stmt.suite << "printf(#{"deleting a #{@name}!! :) !!\n".inspect})"
 		if_stmt.suite << "delete #{@name}_ptr"
 		if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
 
@@ -182,15 +178,11 @@
 Create a reference to the new object in the pointer.  Since we have already
 created the initializer
 COMMENT_END
-		comments = str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}
-		comments << "static UNUSED_VARIABLE #{type_name} &#{@name} = " +
-		            "#{@name}_initializer.get()"
-		namespace.add_post_swig("%immutable #{@name}_initializer")
-		namespace.add_post_swig(CPP::SwigPragma.new("java", "modulecode", CPP::Suite.new(["public static final #{@name} = get#{@name.capitalize}_initializer().get()"])))
-		ifdef_statement = CPP::IfnDef.new(CPP::Suite.new(comments))
-		ifdef_statement.name = "SWIG"
-		namespace.add(ifdef_statement)
-
+		str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}.each do |comment|
+      namespace.add(comment)
+    end
+		namespace.add("static UNUSED_VARIABLE #{type_name} &#{@name}" +
+                  " = #{@name}_initializer.get()")
 
 		get = init_class.def_func(type_name,"get") #.add_dep(type)
 		get.pre_func_types = "&"
diff --git a/aos/build/queues/output/q_struct.rb b/aos/build/queues/output/q_struct.rb
index 6ce1794..05c2932 100644
--- a/aos/build/queues/output/q_struct.rb
+++ b/aos/build/queues/output/q_struct.rb
@@ -1,4 +1,4 @@
-class Target::StructDec < Target::Node
+class Target::StructDec < Target::StructBase
 	attr_accessor :name,:loc,:parent, :extern
 	def initialize(name)
 		@name = name
@@ -7,6 +7,13 @@
 	def add_member(member)
 		@members << member
 	end
+	def create_GetType(type_class, cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
+		type_class.add_member(member_func)
+		member_func.static = true
+		member_func.suite << "static ::aos::Once<const ::aos::MessageType> getter(#{type_class.name}::DoGetType)"
+		member_func.suite << CPP::Return.new("getter.Get()")
+	end
 	def create(cpp_tree)
 		return self if(@extern)
 		orig_namespace = namespace = cpp_tree.get(@loc)
@@ -20,16 +27,16 @@
 		@members.each do |elem|
 			type_class.add_member(elem.create_usage(cpp_tree))
 		end
+		create_DoGetType(type_class, cpp_tree)
+		create_GetType(type_class, cpp_tree)
+    create_DefaultConstructor(type_class, cpp_tree)
+    create_InOrderConstructor(type_class, cpp_tree)
+    create_Zero(type_class, cpp_tree)
+    create_Size(type_class, cpp_tree)
+    create_Serialize(type_class, cpp_tree)
+    create_Deserialize(type_class, cpp_tree)
 		return type_class
 	end
-	def size()
-		return @size if(@size)
-		@size = 0
-		@members.each do |elem|
-			@size += elem.size
-		end
-		return @size
-	end
 	def getPrintFormat()
 		return "{" + @members.collect { |elem| elem.toPrintFormat() }.join(", ") + "}"
 	end
@@ -41,13 +48,13 @@
 	def toHost(offset, suite, parent)
 		@members.each do |elem|
 			elem.toHost(offset, suite, parent)
-			offset += elem.size()
+			offset += " + #{elem.size()}"
 		end
 	end
 	def toNetwork(offset, suite, parent)
 		@members.each do |elem|
 			elem.toNetwork(offset, suite, parent)
-			offset += elem.size()
+			offset += " + #{elem.size()}"
 		end
 	end
 	def zeroCall(suite, parent)
@@ -81,6 +88,11 @@
 	def create_usage(cpp_tree)
 		return "#{type_name(cpp_tree)} #{@name}"
 	end
+	def add_TypeRegister(cpp_tree, o_type, member_func)
+		type = cpp_tree.get(@type)
+		tName = @type.loc.to_cpp_id(type.name)
+		member_func.suite << "#{tName}::GetType()"
+	end
 	def fetchPrintArgs(args, parent = "")
 		@type.fetchPrintArgs(args, parent + "#{@name}.")
 	end
@@ -97,5 +109,10 @@
 	def zeroCall(suite, parent = "")
 		@type.zeroCall(suite, parent + "#{@name}.")
 	end
-	
+	def simpleStr()
+		"#{@type.simpleStr()} #{@name}"
+	end
+	def getTypeID()
+		return @type.getTypeID()
+	end
 end
diff --git a/aos/build/queues/output/queue_dec.rb b/aos/build/queues/output/queue_dec.rb
index 8c3bd51..db4e1af 100644
--- a/aos/build/queues/output/queue_dec.rb
+++ b/aos/build/queues/output/queue_dec.rb
@@ -6,10 +6,6 @@
 	def msg_hash()
 		return @type.msg_hash
 	end
-	def java_type_name(cpp_tree)
-		type = cpp_tree.get(@type)
-		return "#{type.name}Queue"
-	end
 	def full_message_name(cpp_tree)
 		type = cpp_tree.get(@type)
 		return @type.loc.to_cpp_id(type.name)
@@ -62,18 +58,15 @@
 		cons = CPP::Constructor.new(init_class)
 		init_class.add_member(:public,cons)
 		cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
-		if_stmt.suite << "printf(#{"making a #{@name} queue!\n".inspect})"
 
 		cons_call = CPP::FuncCall.new("new #{type_name}")
 		cons_call.args.push(@loc.queue_name(@name).inspect)
 		if_stmt.suite << CPP::Assign.new("#{@name}_ptr",cons_call)
-		if_stmt.else_suite << CPP::FuncCall.build("printf","already made a #{@name}\n".inspect)
 
 
 		destruct = CPP::Destructor.new(init_class)
 		init_class.add_member(:public,destruct)
 		destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
-		if_stmt.suite << "printf(#{"deleting a #{@name}!! :) !!\n".inspect})"
 		if_stmt.suite << "delete #{@name}_ptr"
 		if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
 
@@ -84,16 +77,11 @@
 Create a reference to the new object in the pointer.  Since we have already
 created the initializer
 COMMENT_END
-		comments = str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}
-		comments << "static UNUSED_VARIABLE #{type_name} &#{@name} = " +
-		            "#{@name}_initializer.get()"
-		namespace.add_post_swig("%immutable #{@name}_initializer")
-		java_type_name = java_type_name(cpp_tree)
-		namespace.add_post_swig(CPP::SwigPragma.new("java", "modulecode", CPP::Suite.new(["public static final #{java_type_name} #{@name} = get#{@name.capitalize}_initializer().get()"])))
-
-		ifdef_statement = CPP::IfnDef.new(CPP::Suite.new(comments))
-		ifdef_statement.name = "SWIG"
-		namespace.add(ifdef_statement)
+		str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}.each do |comment|
+      namespace.add(comment)
+    end
+		namespace.add("static UNUSED_VARIABLE #{type_name} &#{@name}" +
+                  " = #{@name}_initializer.get()")
 
 		get = init_class.def_func(full_type_name,"get")
 		get.pre_func_types = "&"
diff --git a/aos/build/queues/print_field.rb b/aos/build/queues/print_field.rb
new file mode 100644
index 0000000..7a9fbb4
--- /dev/null
+++ b/aos/build/queues/print_field.rb
@@ -0,0 +1,54 @@
+require File.dirname(__FILE__) + '/load.rb'
+
+TypeNames = [8, 16, 32, 64].collect do |size|
+  ["uint#{size}_t", "int#{size}_t"]
+end.flatten + ['bool', 'float', 'char', 'double']
+
+File.open(ARGV[0], 'w') do |output|
+  output.puts <<END
+// This file is generated by #{File.expand_path(__FILE__)}.
+// DO NOT EDIT BY HAND!
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "aos/common/byteorder.h"
+
+namespace aos {
+
+bool PrintField(char *output, size_t *output_bytes, const void *input,
+                size_t *input_bytes, uint32_t type) {
+  switch (type) {
+#{TypeNames.collect do |name|
+  message_element = Target::MessageElement.new(name, 'value')
+  statement = MessageElementStmt.new(name, 'value')
+  message_element.size = statement.size
+  print_args = []
+  message_element.fetchPrintArgs(print_args)
+  next <<END2
+    case #{message_element.getTypeID()}:
+      {
+        if (*input_bytes < #{statement.size}) return false;
+        *input_bytes -= #{statement.size};
+        #{name} value;
+        to_host(static_cast<const char *>(input), &value);
+        int ret = snprintf(output, *output_bytes,
+                           "#{statement.toPrintFormat()}",
+                           #{print_args[0]});
+        if (ret < 0) return false;
+        if (static_cast<unsigned int>(ret) >= *output_bytes) return false;
+        *output_bytes -= ret + 1;
+        return true;
+      }
+END2
+end.join('')}
+    default:
+      return false;
+  }
+}
+
+}  // namespace aos
+END
+end
diff --git a/aos/build/run_test.sh b/aos/build/run_test.sh
new file mode 100755
index 0000000..9da830b
--- /dev/null
+++ b/aos/build/run_test.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# This gets called by build.sh to run a test.
+
+EXECUTABLE=$1
+
+echo "Running $(basename ${EXECUTABLE})."
+${EXECUTABLE}
+exit $?
diff --git a/aos/build/swig.gypi b/aos/build/swig.gypi
deleted file mode 100644
index 2e99a95..0000000
--- a/aos/build/swig.gypi
+++ /dev/null
@@ -1,73 +0,0 @@
-# Include this file in any target that needs to use swig wrappers.
-#
-# To use, create a target of the following form:
-# {
-#   'target_name': 'my_target_javawrap',
-#   'type': 'static_library', # or any other type that can handle .cc files
-#   'sources': [
-#     'aos/example/target.swig',
-#   ],
-#   'variables': {
-#     'package': 'aos.test',
-#   },
-#   'includes': ['path/to/swig.gypi'],
-# },
-# Code that depends on this target will be able to use the swig wrapped
-# java classes.
-#
-# using <http://src.chromium.org/svn/trunk/src/build/protoc.gypi> as an
-# example of how this should work
-{
-  'variables': {
-    'prefix_dir': '<(SHARED_INTERMEDIATE_DIR)/',
-    'out_dir': '<(prefix_dir)/<(_target_name)/',
-    'output_java_wrap': '<(out_dir)/<(RULE_INPUT_ROOT)_wrap.cc',
-    'java_dir': '<(out_dir)/<(RULE_INPUT_ROOT)_java',
-    'no_rsync': 1,
-  },
-  'rules': [
-    {
-      'rule_name': 'genswig',
-      'extension': 'swig',
-      'outputs': [
-        '<(output_java_wrap)',
-        '<(java_dir)',
-      ],
-      'action': [
-        '<(DEPTH)/aos/build/mkdirswig',
-        '<(java_dir)',
-        '-I<(DEPTH)',
-        '-outdir', ' <(java_dir)',
-        '-package', '<(package)',
-        '-o', '<(output_java_wrap)',
-        '-c++',
-        '-Wall',
-        '-Wextra',
-        '-java',
-        '<(RULE_INPUT_PATH)'],
-      'message': 'Generating C++ code from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).swig',
-      'process_outputs_as_sources': 1,
-    },
-  ],
-  'cflags': [
-# For the swig-generated C++ code.
-    '-fno-strict-aliasing',
-    '-Wno-cast-qual',
-  ],
-  'include_dirs': [
-    '<(prefix_dir)/<(_target_name)',
-    '/usr/lib/jvm/default-java/include',
-    '/usr/lib/jvm/default-java/include/linux',
-  ],
-  'direct_dependent_settings': {
-    'include_dirs': [
-      '<(prefix_dir)/<(_target_name)',
-      '/usr/lib/jvm/default-java/include',
-      '/usr/lib/jvm/default-java/include/linux',
-    ],
-    'variables': {
-      'gen_srcdir_parents': ['<(out_dir)'],
-    },
-  },
-  'hard_dependency': 1,
-}
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 508f94f..906b5b2 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -1,7 +1,7 @@
 {
   'targets': [
     {
-      'target_name': 'queue_test_queue',
+      'target_name': 'test_queue',
       'type': 'static_library',
       'sources': [
         '<(AOS)/common/test_queue.q',
@@ -42,6 +42,52 @@
       ],
     },
     {
+      'target_name': 'queue_types',
+      'type': 'static_library',
+      'variables': {
+        'print_field_cc': '<(SHARED_INTERMEDIATE_DIR)/print_field.cc',
+      },
+      'sources': [
+        'queue_types.cc',
+        '<(print_field_cc)',
+      ],
+      'dependencies': [
+        '<(AOS)/build/aos.gyp:logging_interface',
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:core_lib',
+        'mutex',
+      ],
+      'actions': [
+        {
+          'variables': {
+            'script': '<(AOS)/build/queues/print_field.rb',
+          },
+          'action_name': 'gen_print_field',
+          'inputs': [
+            '<(script)',
+            '<!@(find <(AOS)/build/queues/ -name *.rb)',
+          ],
+          'outputs': [
+            '<(print_field_cc)',
+          ],
+          'action': ['ruby', '<(script)', '<(print_field_cc)'],
+          'message': 'Generating print_field.cc',
+        },
+      ],
+    },
+    {
+      'target_name': 'queue_types_test',
+      'type': 'executable',
+      'sources': [
+        'queue_types_test.cc',
+      ],
+      'dependencies': [
+        'queue_types',
+        '<(EXTERNALS):gtest',
+        'test_queue',
+      ],
+    },
+    {
       'target_name': 'queues',
       'type': 'static_library',
       'sources': [
@@ -136,6 +182,8 @@
         'timing',
         'time',
         'control_loop_queues',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
       'export_dependent_settings': [
         '<(AOS)/common/messages/messages.gyp:aos_queues',
@@ -143,6 +191,8 @@
         'timing',
         'time',
         'control_loop_queues',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
     },
     {
@@ -154,8 +204,9 @@
       'dependencies': [
         '<(EXTERNALS):gtest',
         'queue_testutils',
-        'queue_test_queue',
+        'test_queue',
         '<(AOS)/common/util/util.gyp:thread',
+        'die',
       ],
     },
     {
@@ -267,6 +318,7 @@
       'dependencies': [
         '<(EXTERNALS):gtest',
         'mutex',
+        'die',
       ],
     },
     {
@@ -284,6 +336,7 @@
         '<(AOS)/build/aos.gyp:logging',
         'queue_testutils',
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:core_lib',
+        'die',
        ],
     },
     {
diff --git a/aos/common/condition_test.cc b/aos/common/condition_test.cc
index a10275b..65a1028 100644
--- a/aos/common/condition_test.cc
+++ b/aos/common/condition_test.cc
@@ -13,6 +13,7 @@
 #include "aos/linux_code/ipc_lib/core_lib.h"
 #include "aos/common/logging/logging.h"
 #include "aos/common/macros.h"
+#include "aos/common/die.h"
 
 using ::aos::time::Time;
 using ::aos::common::testing::GlobalCoreInstance;
@@ -43,6 +44,11 @@
     time::SleepFor(::Time::InSeconds(0.008));
   }
 
+ protected:
+  void SetUp() override {
+    SetDieTestMode(true);
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ConditionTest);
 };
diff --git a/aos/common/control_loop/ControlLoop-tmpl.h b/aos/common/control_loop/ControlLoop-tmpl.h
index 69c3121..b5714d5 100644
--- a/aos/common/control_loop/ControlLoop-tmpl.h
+++ b/aos/common/control_loop/ControlLoop-tmpl.h
@@ -3,6 +3,7 @@
 #include "aos/common/logging/logging.h"
 #include "aos/common/control_loop/Timing.h"
 #include "aos/common/messages/RobotState.q.h"
+#include "aos/common/logging/queue_logging.h"
 
 namespace aos {
 namespace control_loops {
@@ -10,6 +11,10 @@
 // TODO(aschuh): Tests.
 
 template <class T, bool has_position, bool fail_no_position>
+constexpr ::aos::time::Time
+    ControlLoop<T, has_position, fail_no_position>::kStaleLogInterval;
+
+template <class T, bool has_position, bool fail_no_position>
 void ControlLoop<T, has_position, fail_no_position>::ZeroOutputs() {
   aos::ScopedMessagePtr<OutputType> output =
       control_loop_->output.MakeMessage();
@@ -19,9 +24,6 @@
 
 template <class T, bool has_position, bool fail_no_position>
 void ControlLoop<T, has_position, fail_no_position>::Iterate() {
-  // Temporary storage for printing out inputs and outputs.
-  char state[1024];
-
   // Fetch the latest control loop goal and position.  If there is no new
   // goal, we will just reuse the old one.
   // If there is no goal, we haven't started up fully.  It isn't worth
@@ -31,12 +33,11 @@
   // goals.
   const GoalType *goal = control_loop_->goal.get();
   if (goal == NULL) {
-    LOG(ERROR, "No prior control loop goal.\n");
+    LOG_INTERVAL(no_prior_goal_);
     ZeroOutputs();
     return;
   }
-  goal->Print(state, sizeof(state));
-  LOG(DEBUG, "goal={%s}\n", state);
+  LOG_STRUCT(DEBUG, "goal", *goal);
 
   // Only pass in a position if we got one this cycle.
   const PositionType *position = NULL;
@@ -51,15 +52,15 @@
       if (control_loop_->position.get()) {
         int msec_age = control_loop_->position.Age().ToMSec();
         if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
-          LOG(ERROR, "Stale position. %d ms > %d ms.  Outputs disabled.\n",
-              msec_age, kPositionTimeoutMs);
+          LOG_INTERVAL(very_stale_position_);
           ZeroOutputs();
           return;
         } else {
-          LOG(ERROR, "Stale position. %d ms\n", msec_age);
+          LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
+              kPositionTimeoutMs);
         }
       } else {
-        LOG(ERROR, "Never had a position.\n");
+        LOG_INTERVAL(no_prior_position_);
         if (fail_no_position) {
           ZeroOutputs();
           return;
@@ -67,8 +68,7 @@
       }
     }
     if (position) {
-      position->Print(state, sizeof(state));
-      LOG(DEBUG, "position={%s}\n", state);
+      LOG_STRUCT(DEBUG, "position", *position);
     }
   }
 
@@ -81,10 +81,9 @@
     outputs_enabled = true;
   } else {
     if (aos::robot_state.get()) {
-      int msec_age = aos::robot_state.Age().ToMSec();
-      LOG(ERROR, "Driver Station packet is too old (%d ms).\n", msec_age);
+      LOG_INTERVAL(driver_station_old_);
     } else {
-      LOG(ERROR, "No Driver Station packet.\n");
+      LOG_INTERVAL(no_driver_station_);
     }
   }
 
@@ -100,8 +99,7 @@
         control_loop_->output.MakeMessage();
     RunIteration(goal, position, output.get(), status.get());
 
-    output->Print(state, sizeof(state));
-    LOG(DEBUG, "output={%s}\n", state);
+    LOG_STRUCT(DEBUG, "output", *output);
     output.Send();
   } else {
     // The outputs are disabled, so pass NULL in for the output.
@@ -109,8 +107,7 @@
     ZeroOutputs();
   }
 
-  status->Print(state, sizeof(state));
-  LOG(DEBUG, "status={%s}\n", state);
+  LOG_STRUCT(DEBUG, "status", *status);
   status.Send();
 }
 
diff --git a/aos/common/control_loop/ControlLoop.h b/aos/common/control_loop/ControlLoop.h
index 6af7235..e48d259 100644
--- a/aos/common/control_loop/ControlLoop.h
+++ b/aos/common/control_loop/ControlLoop.h
@@ -7,6 +7,7 @@
 #include "aos/common/type_traits.h"
 #include "aos/common/queue.h"
 #include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
 
 namespace aos {
 namespace control_loops {
@@ -129,6 +130,25 @@
  private:
   // Pointer to the queue group
   T *control_loop_;
+
+  typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+  static constexpr ::aos::time::Time kStaleLogInterval =
+      ::aos::time::Time::InSeconds(0.1);
+  SimpleLogInterval very_stale_position_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "outputs disabled because position is very stale");
+  SimpleLogInterval no_prior_goal_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no prior goal");
+  SimpleLogInterval no_prior_position_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no prior position");
+  SimpleLogInterval no_driver_station_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no driver station packet");
+  SimpleLogInterval driver_station_old_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "driver station packet is too old");
 };
 
 }  // namespace control_loops
diff --git a/aos/common/die.cc b/aos/common/die.cc
index 467b81d..0ea7007 100644
--- a/aos/common/die.cc
+++ b/aos/common/die.cc
@@ -6,13 +6,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <string.h>
-#ifdef __VXWORKS__
-#include <taskLib.h>
-// Have to re-declare it with __attribute__((noreturn)).
-extern "C" void abort() __attribute__((noreturn));
-#include <usrLib.h>
-#include <dbgLib.h>
-#endif
+#include <signal.h>
 
 #include <string>
 
@@ -28,6 +22,7 @@
 }
 
 namespace {
+
 // Calculates the filename to dump the message into.
 const std::string GetFilename() {
 #ifdef __VXWORKS__
@@ -50,6 +45,9 @@
   }
 #endif
 }
+
+bool test_mode = false;
+
 }  // namespace
 
 void VDie(const char *format, va_list args_in) {
@@ -60,31 +58,28 @@
   fputs("aos fatal: ERROR!! details following\n", stderr);
   va_copy(args1, args_in);
   vfprintf(stderr, format, args1);
-  fputs("aos fatal: ERROR!! see stderr for details\n", stdout);
+  if (!test_mode) {
+    fputs("aos fatal: ERROR!! see stderr for details\n", stdout);
 
-  const std::string filename = GetFilename();
-  if (!filename.empty()) {
-    FILE *error_file = fopen(filename.c_str(), "w");
-    if (error_file != NULL) {
-      va_copy(args2, args_in);
-      vfprintf(error_file, format, args2);
-      fclose(error_file);
-    } else {
-      fprintf(stderr, "aos fatal: fopen('%s', \"w\") failed with %d (%s)\n",
-              filename.c_str(), errno, strerror(errno));
+    const std::string filename = GetFilename();
+    if (!filename.empty()) {
+      FILE *error_file = fopen(filename.c_str(), "w");
+      if (error_file != NULL) {
+        va_copy(args2, args_in);
+        vfprintf(error_file, format, args2);
+        fclose(error_file);
+      } else {
+        fprintf(stderr, "aos fatal: fopen('%s', \"w\") failed with %d (%s)\n",
+                filename.c_str(), errno, strerror(errno));
+      }
     }
   }
 
-#ifdef __VXWORKS__
-  printf("I am 0x%x suspending for debugging purposes.\n", taskIdSelf());
-  printf("\t`tt 0x%x` will give you a stack trace.\n", taskIdSelf());
-  fputs("\t`lkAddr` will reverse lookup a symbol for you.\n", stdout);
-  fputs("\t`dbgHelp` and `help` have some useful commands in them.\n", stdout);
-  taskSuspend(0);
-  printf("You weren't supposed to resume 0x%x!!. Going to really die now.\n",
-         taskIdSelf());
-#endif
   abort();
 }
 
+void SetDieTestMode(bool new_test_mode) {
+  test_mode = new_test_mode;
+}
+
 }  // namespace aos
diff --git a/aos/common/die.h b/aos/common/die.h
index 28519a8..d973b42 100644
--- a/aos/common/die.h
+++ b/aos/common/die.h
@@ -15,6 +15,11 @@
     __attribute__((noreturn))
     __attribute__((format(gnu_printf, 1, 0)));
 
+// Turns on (or off) "test mode", where (V)Die doesn't write out files and
+// doesn't print to stdout.
+// Test mode defaults to false.
+void SetDieTestMode(bool test_mode);
+
 }  // namespace aos
 
 #endif  // AOS_COMMON_DIE_H_
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
index b6b339a..e230e47 100644
--- a/aos/common/logging/logging.gyp
+++ b/aos/common/logging/logging.gyp
@@ -11,5 +11,20 @@
         '<(AOS)/build/aos.gyp:logging',
       ],
     },
+    {
+      'target_name': 'queue_logging',
+      'type': 'static_library',
+      'sources': [
+        'queue_logging.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/common.gyp:die',
+        '<(AOS)/common/common.gyp:queue_types',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
   ],
 }
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index c71ee46..673cc9a 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -71,41 +71,45 @@
 #define LOG_SOURCENAME __FILE__
 
 // The basic logging call.
-#define LOG(level, format, args...) do {\
-  log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": %s: " format, \
-         LOG_CURRENT_FUNCTION, ##args); \
-  /* so that GCC knows that it won't return */ \
-  if (level == FATAL) { \
-    fprintf(stderr, "log_do(FATAL) fell through!!!!!\n"); \
-    printf("see stderr\n"); \
-    abort(); \
-  } \
-} while (0)
+#define LOG(level, format, args...)                                        \
+  do {                                                                     \
+    log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": %s: " format, \
+           LOG_CURRENT_FUNCTION, ##args);                                  \
+    /* so that GCC knows that it won't return */                           \
+    if (level == FATAL) {                                                  \
+      fprintf(stderr, "log_do(FATAL) fell through!!!!!\n");                \
+      printf("see stderr\n");                                              \
+      abort();                                                             \
+    }                                                                      \
+  } while (0)
 
 // Allows format to not be a string constant.
-#define LOG_DYNAMIC(level, format, args...) do { \
-	static char log_buf[LOG_MESSAGE_LEN]; \
-	int ret = snprintf(log_buf, sizeof(log_buf), format, ##args); \
-	if (ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN) { \
-		LOG(ERROR, "next message was too long so not subbing in args\n"); \
-		LOG(level, "%s", format); \
-	}else{ \
-		LOG(level, "%s", log_buf); \
-	} \
-} while (0)
+#define LOG_DYNAMIC(level, format, args...)                             \
+  do {                                                                  \
+    static char log_buf[LOG_MESSAGE_LEN];                               \
+    int ret = snprintf(log_buf, sizeof(log_buf), format, ##args);       \
+    if (ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN) {                 \
+      LOG(ERROR, "next message was too long so not subbing in args\n"); \
+      LOG(level, "%s", format);                                         \
+    } else {                                                            \
+      LOG(level, "%s", log_buf);                                        \
+    }                                                                   \
+  } while (0)
 
 // Allows "bottling up" multiple log fragments which can then all be logged in
 // one message with LOG_UNCORK.
 // Calls from a given thread/task will be grouped together.
-#define LOG_CORK(format, args...) do { \
-  log_cork(__LINE__, LOG_CURRENT_FUNCTION, format, ##args); \
-} while (0)
+#define LOG_CORK(format, args...)                             \
+  do {                                                        \
+    log_cork(__LINE__, LOG_CURRENT_FUNCTION, format, ##args); \
+  } while (0)
 // Actually logs all of the saved up log fragments (including format and args on
 // the end).
-#define LOG_UNCORK(level, format, args...) do { \
-  log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, \
-             format, ##args); \
-} while (0)
+#define LOG_UNCORK(level, format, args...)                                    \
+  do {                                                                        \
+    log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, format, \
+               ##args);                                                       \
+  } while (0)
 
 #ifdef __cplusplus
 }
@@ -154,8 +158,8 @@
 // controlled by NDEBUG, so the check will be executed regardless of
 // compilation mode.  Therefore, it is safe to do things like:
 //    CHECK(fp->Write(x) == 4)
-#define CHECK(condition)  \
-  if (__builtin_expect(!(condition), 0)) { \
+#define CHECK(condition)                          \
+  if (__builtin_expect(!(condition), 0)) {        \
     LOG(FATAL, "CHECK(%s) failed\n", #condition); \
   }
 
@@ -163,16 +167,16 @@
 // The (int, int) specialization works around the issue that the compiler
 // will not instantiate the template version of the function on values of
 // unnamed enum type.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
-  template <typename T1, typename T2> \
-  inline void LogImpl##name(const T1& v1, const T2& v2,    \
-                            const char* exprtext) { \
-    if (!__builtin_expect(v1 op v2, 1)) { \
-      LOG(FATAL, "CHECK(%s) failed\n", exprtext); \
-    } \
-  } \
-  inline void LogImpl##name(int v1, int v2, const char* exprtext) { \
-    ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
+#define DEFINE_CHECK_OP_IMPL(name, op)                              \
+  template <typename T1, typename T2>                               \
+  inline void LogImpl##name(const T1 &v1, const T2 &v2,             \
+                            const char *exprtext) {                 \
+    if (!__builtin_expect(v1 op v2, 1)) {                           \
+      LOG(FATAL, "CHECK(%s) failed\n", exprtext);                   \
+    }                                                               \
+  }                                                                 \
+  inline void LogImpl##name(int v1, int v2, const char *exprtext) { \
+    ::aos::LogImpl##name<int, int>(v1, v2, exprtext);               \
   }
 
 // We use the full name Check_EQ, Check_NE, etc. in case the file including
@@ -186,7 +190,7 @@
 DEFINE_CHECK_OP_IMPL(Check_GE, >=)
 DEFINE_CHECK_OP_IMPL(Check_GT, > )
 
-#define CHECK_OP(name, op, val1, val2) \
+#define CHECK_OP(name, op, val1, val2)  \
   ::aos::LogImplCheck##name(val1, val2, \
                             STRINGIFY(val1) STRINGIFY(op) STRINGIFY(val2))
 
@@ -208,8 +212,7 @@
 
 // Check that the input is non NULL.  This very useful in constructor
 // initializer lists.
-#define CHECK_NOTNULL(val) \
-  ::aos::CheckNotNull(STRINGIFY(val), val)
+#define CHECK_NOTNULL(val) ::aos::CheckNotNull(STRINGIFY(val), val)
 
 }  // namespace aos
 
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
index fa76c86..40909c2 100644
--- a/aos/common/logging/logging_impl.cc
+++ b/aos/common/logging/logging_impl.cc
@@ -6,6 +6,7 @@
 #include "aos/common/time.h"
 #include "aos/common/inttypes.h"
 #include "aos/common/once.h"
+#include "aos/common/queue_types.h"
 
 namespace aos {
 namespace logging {
@@ -14,7 +15,6 @@
 using internal::Context;
 using internal::global_top_implementation;
 
-
 // The root LogImplementation. It only logs to stderr/stdout.
 // Some of the things specified in the LogImplementation documentation doesn't
 // apply here (mostly the parts about being able to use LOG) because this is the
@@ -57,16 +57,15 @@
 
 }  // namespace
 namespace internal {
+namespace {
 
-void FillInMessage(log_level level, const char *format, va_list ap,
-                   LogMessage *message) {
+void FillInMessageBase(log_level level, LogMessage *message) {
   Context *context = Context::Get();
 
-  ExecuteFormat(message->message, sizeof(message->message), format, ap);
-
   message->level = level;
   message->source = context->source;
-  memcpy(message->name, context->name.c_str(), context->name.size() + 1);
+  memcpy(message->name, context->name.c_str(), context->name.size());
+  message->name_length = context->name.size();
 
   time::Time now = time::Time::Now();
   message->seconds = now.sec();
@@ -75,16 +74,100 @@
   message->sequence = context->sequence++;
 }
 
+}  // namespace
+
+void FillInMessageStructure(log_level level,
+                            const ::std::string &message_string, size_t size,
+                            const MessageType *type,
+                            const ::std::function<size_t(char *)> &serialize,
+                            LogMessage *message) {
+  type_cache::AddShm(type->id);
+  message->structure.type_id = type->id;
+
+  FillInMessageBase(level, message);
+
+  if (message_string.size() + size > sizeof(message->structure.serialized)) {
+    LOG(FATAL, "serialized struct %s (size %zd) and message %s too big\n",
+        type->name.c_str(), size, message_string.c_str());
+  }
+  message->structure.string_length = message_string.size();
+  memcpy(message->structure.serialized, message_string.data(),
+         message->structure.string_length);
+
+  message->message_length = serialize(
+      &message->structure.serialized[message->structure.string_length]);
+  message->type = LogMessage::Type::kStruct;
+}
+
+void FillInMessage(log_level level, const char *format, va_list ap,
+                   LogMessage *message) {
+  FillInMessageBase(level, message);
+
+  message->message_length =
+      ExecuteFormat(message->message, sizeof(message->message), format, ap);
+  message->type = LogMessage::Type::kString;
+}
+
 void PrintMessage(FILE *output, const LogMessage &message) {
-  fprintf(output, "%s(%" PRId32 ")(%05" PRIu16 "): %s at"
-          " %010" PRId32 ".%09" PRId32 "s: %s",
-          message.name, static_cast<int32_t>(message.source), message.sequence,
-          log_str(message.level), message.seconds, message.nseconds,
-          message.message);
+#define BASE_FORMAT \
+  "%.*s(%" PRId32 ")(%05" PRIu16 "): %s at %010" PRId32 ".%09" PRId32 "s: "
+#define BASE_ARGS                                             \
+  static_cast<int>(message.name_length), message.name,        \
+      static_cast<int32_t>(message.source), message.sequence, \
+      log_str(message.level), message.seconds, message.nseconds
+  switch (message.type) {
+    case LogMessage::Type::kString:
+      fprintf(output, BASE_FORMAT "%.*s", BASE_ARGS,
+              static_cast<int>(message.message_length), message.message);
+      break;
+    case LogMessage::Type::kStruct:
+      char buffer[LOG_MESSAGE_LEN];
+      size_t output_length = sizeof(buffer);
+      size_t input_length = message.message_length;
+      if (!PrintMessage(
+              buffer, &output_length,
+              message.structure.serialized + message.structure.string_length,
+              &input_length, type_cache::Get(message.structure.type_id))) {
+        LOG(FATAL,
+            "printing message (%.*s) of type %s into %zu-byte buffer failed\n",
+            static_cast<int>(message.message_length), message.message,
+            type_cache::Get(message.structure.type_id).name.c_str(),
+            sizeof(buffer));
+      }
+      if (input_length > 0) {
+        LOG(WARNING, "%zu extra bytes on message of type %s\n", input_length,
+            type_cache::Get(message.structure.type_id).name.c_str());
+      }
+      fprintf(output, BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+              static_cast<int>(message.structure.string_length),
+              message.structure.serialized,
+              static_cast<int>(sizeof(buffer) - output_length), buffer);
+      break;
+  }
 }
 
 }  // namespace internal
 
+void LogImplementation::LogStruct(
+    log_level level, const ::std::string &message, size_t size,
+    const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
+  char serialized[1024];
+  if (size > sizeof(serialized)) {
+    LOG(FATAL, "structure of type %s too big to serialize\n",
+        type->name.c_str());
+  }
+  size_t used = serialize(serialized);
+  char printed[LOG_MESSAGE_LEN];
+  size_t printed_bytes = sizeof(printed);
+  if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+    LOG(FATAL, "PrintMessage(%p, %p(=%zd), %p, %p(=%zd), %p(name=%s)) failed\n",
+        printed, &printed_bytes, printed_bytes, serialized, &used, used, type,
+        type->name.c_str());
+  }
+  DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
+                message.data(), static_cast<int>(printed_bytes), printed);
+}
+
 StreamLogImplementation::StreamLogImplementation(FILE *stream)
     : stream_(stream) {}
 
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index ca19dbc..3d5b387 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -7,13 +7,21 @@
 #include <limits.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdarg.h>
 
 #include <string>
+#include <functional>
 
 #include "aos/common/logging/logging.h"
 #include "aos/common/type_traits.h"
 #include "aos/common/mutex.h"
 
+namespace aos {
+
+class MessageType;
+
+}  // namespace aos
+
 // This file has all of the logging implementation. It can't be #included by C
 // code like logging.h can.
 // It is useful for the rest of the logging implementation and other C++ code
@@ -32,21 +40,30 @@
 // using a gcc function attribute.
 
 // The struct that the code uses for making logging calls.
-// Packed so that it ends up the same under both linux and vxworks.
-struct __attribute__((packed)) LogMessage {
-#ifdef __VXWORKS__
-  static_assert(sizeof(pid_t) == sizeof(int),
-                "we use task IDs (aka ints) and pid_t interchangeably");
-#endif
-  // Actually the task ID (aka a pointer to the TCB) on the cRIO.
+struct LogMessage {
+  enum class Type : uint8_t {
+    kString, kStruct,
+  };
+
+  int32_t seconds, nseconds;
+  // message_length is the length of everything in message for all types.
+  size_t message_length, name_length;
   pid_t source;
   static_assert(sizeof(source) == 4, "that's how they get printed");
   // Per task/thread.
   uint16_t sequence;
+  Type type;
   log_level level;
-  int32_t seconds, nseconds;
   char name[100];
-  char message[LOG_MESSAGE_LEN];
+  union {
+    char message[LOG_MESSAGE_LEN];
+    struct {
+      uint32_t type_id;
+      size_t string_length;
+      // The message string and then the serialized structure.
+      char serialized[LOG_MESSAGE_LEN - sizeof(type) - sizeof(string_length)];
+    } structure;
+  };
 };
 static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
 
@@ -85,6 +102,10 @@
 void LogNext(log_level level, const char *format, ...)
   __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
 
+// Will take a structure and log it.
+template <class T>
+void DoLogStruct(log_level, const ::std::string &, const T &);
+
 // Represents a system that can actually take log messages and do something
 // useful with them.
 // All of the code (transitively too!) in the DoLog here can make
@@ -107,13 +128,38 @@
   // Actually logs the given message. Implementations should somehow create a
   // LogMessage and then call internal::FillInMessage.
   virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
+  void DoLogVariadic(log_level level, const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    DoLog(level, format, ap);
+    va_end(ap);
+  }
 
-  // Function of this class so that it can access DoLog.
-  // Levels is how many LogImplementations to not use off the stack.
+  // Logs the contents of an auto-generated structure. The implementation here
+  // just converts it to a string with PrintMessage and then calls DoLog with
+  // that, however some implementations can be a lot more efficient than that.
+  // size and type are the result of calling Size() and Type() on the type of
+  // the message.
+  // serialize will call Serialize on the message.
+  virtual void LogStruct(log_level level, const ::std::string &message,
+                         size_t size, const MessageType *type,
+                         const ::std::function<size_t(char *)> &serialize);
+
+  // These functions call similar methods on the "current" LogImplementation or
+  // Die if they can't find one.
+  // levels is how many LogImplementations to not use off the stack.
   static void DoVLog(log_level, const char *format, va_list ap, int levels);
-  // Friends so that they can access DoVLog.
+  // This one is implemented in queue_logging.cc.
+  static void DoLogStruct(log_level level, const ::std::string &message,
+                          size_t size, const MessageType *type,
+                          const ::std::function<size_t(char *)> &serialize,
+                          int levels);
+
+  // Friends so that they can access the static Do* functions.
   friend void VLog(log_level, const char *, va_list);
   friend void LogNext(log_level, const char *, ...);
+  template <class T>
+  friend void DoLogStruct(log_level, const ::std::string &, const T &);
 
   LogImplementation *next_;
 };
@@ -208,8 +254,16 @@
   } cork_data;
 };
 
-// Fills in *message according to the given inputs. Used for implementing
-// LogImplementation::DoLog.
+// Fills in all the parts of message according to the given inputs (with type
+// kStruct).
+void FillInMessageStructure(log_level level,
+                            const ::std::string &message_string, size_t size,
+                            const MessageType *type,
+                            const ::std::function<size_t(char *)> &serialize,
+                            LogMessage *message);
+
+// Fills in *message according to the given inputs (with type kString).
+// Used for implementing LogImplementation::DoLog.
 void FillInMessage(log_level level, const char *format, va_list ap,
                    LogMessage *message);
 
@@ -218,8 +272,13 @@
 
 // Prints format (with ap) into output and correctly deals with the result
 // being too long etc.
-void ExecuteFormat(char *output, size_t output_size, const char *format,
-                   va_list ap);
+size_t ExecuteFormat(char *output, size_t output_size, const char *format,
+                     va_list ap);
+
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+void RunWithCurrentImplementation(
+    int levels, ::std::function<void(LogImplementation *)> function);
 
 }  // namespace internal
 }  // namespace logging
diff --git a/aos/common/logging/logging_interface.cc b/aos/common/logging/logging_interface.cc
index 01fb708..b4244fb 100644
--- a/aos/common/logging/logging_interface.cc
+++ b/aos/common/logging/logging_interface.cc
@@ -4,6 +4,8 @@
 #include <stdarg.h>
 #include <string.h>
 
+#include <type_traits>
+
 #include "aos/common/die.h"
 
 // This file only contains the code necessary to link (ie no implementations).
@@ -21,30 +23,28 @@
   cork_data.Reset();
 }
 
-void ExecuteFormat(char *output, size_t output_size,
-                   const char *format, va_list ap) {
-  static const char *continued = "...\n";
+size_t ExecuteFormat(char *output, size_t output_size, const char *format,
+                     va_list ap) {
+  static const char *const continued = "...\n";
   const size_t size = output_size - strlen(continued);
   const int ret = vsnprintf(output, size, format, ap);
+  typedef ::std::common_type<typeof(ret), typeof(size)>::type RetType;
   if (ret < 0) {
     LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n",
         output, size, format, errno, strerror(errno));
-  } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
+  } else if (static_cast<RetType>(ret) >= static_cast<RetType>(size)) {
     // Overwrite the '\0' at the end of the existing data and
     // copy in the one on the end of continued.
     memcpy(&output[size - 1], continued, strlen(continued) + 1);
   }
+  return ::std::min<RetType>(ret, size);
 }
 
-}  // namespace internal
-
-using internal::Context;
-
-void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
-                   int levels) {
+void RunWithCurrentImplementation(
+    int levels, ::std::function<void(LogImplementation *)> function) {
   Context *context = Context::Get();
 
-  LogImplementation *top_implementation = context->implementation;
+  LogImplementation *const top_implementation = context->implementation;
   LogImplementation *new_implementation = top_implementation;
   LogImplementation *implementation = NULL;
   assert(levels >= 1);
@@ -56,12 +56,24 @@
     new_implementation = new_implementation->next();
   }
   context->implementation = new_implementation;
-  implementation->DoLog(level, format, ap);
+  function(implementation);
   context->implementation = top_implementation;
+}
 
-  if (level == FATAL) {
-    VDie(format, ap);
-  }
+}  // namespace internal
+
+using internal::Context;
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+                               int levels) {
+  internal::RunWithCurrentImplementation(
+      levels, [&](LogImplementation * implementation) {
+    implementation->DoLog(level, format, ap);
+
+    if (level == FATAL) {
+      VDie(format, ap);
+    }
+  });
 }
 
 void VLog(log_level level, const char *format, va_list ap) {
diff --git a/aos/common/logging/queue_logging-tmpl.h b/aos/common/logging/queue_logging-tmpl.h
new file mode 100644
index 0000000..e0a89e4
--- /dev/null
+++ b/aos/common/logging/queue_logging-tmpl.h
@@ -0,0 +1,19 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <functional>
+
+namespace aos {
+namespace logging {
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+                 const T &structure) {
+  LogImplementation::DoLogStruct(level, message, T::Size(), T::GetType(),
+                                 [&structure](char * buffer)->size_t{
+    return structure.Serialize(buffer);
+  },
+                                 1);
+}
+
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/common/logging/queue_logging.cc b/aos/common/logging/queue_logging.cc
new file mode 100644
index 0000000..eefb4ac
--- /dev/null
+++ b/aos/common/logging/queue_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include "aos/common/die.h"
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogStruct(
+    log_level level, const ::std::string &message, size_t size,
+    const MessageType *type, const ::std::function<size_t(char *)> &serialize,
+    int levels) {
+  internal::RunWithCurrentImplementation(
+      levels, [&](LogImplementation * implementation) {
+    implementation->LogStruct(level, message, size, type, serialize);
+
+    if (level == FATAL) {
+      char serialized[1024];
+      if (size > sizeof(serialize)) {
+        Die("LOG(FATAL) structure too big to serialize");
+      }
+      size_t used = serialize(serialized);
+      char printed[LOG_MESSAGE_LEN];
+      size_t printed_bytes = sizeof(printed);
+      if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+        Die("LOG(FATAL) PrintMessage call failed");
+      }
+      Die("%.*s: %.*s\n", static_cast<int>(message.size()), message.data(),
+          static_cast<int>(printed_bytes), printed);
+    }
+  });
+}
+
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/common/logging/queue_logging.h b/aos/common/logging/queue_logging.h
new file mode 100644
index 0000000..191d4c7
--- /dev/null
+++ b/aos/common/logging/queue_logging.h
@@ -0,0 +1,36 @@
+#ifndef AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+#define AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace logging {
+
+#define LOG_STRUCT(level, message, structure)                          \
+  do {                                                                 \
+    static const ::std::string kAosLoggingMessage(                     \
+        LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " message);         \
+    ::aos::logging::DoLogStruct(level, kAosLoggingMessage, structure); \
+    /* so that GCC knows that it won't return */                       \
+    if (level == FATAL) {                                              \
+      fprintf(stderr, "DoLogStruct(FATAL) fell through!!!!!\n");       \
+      printf("see stderr\n");                                          \
+      abort();                                                         \
+    }                                                                  \
+  } while (false)
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+                 const T &structure);
+
+}  // namespace logging
+}  // namespace aos
+
+#include "aos/common/logging/queue_logging-tmpl.h"
+
+#endif  // AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
diff --git a/aos/common/mutex_test.cpp b/aos/common/mutex_test.cpp
index 29e1956..f946dfa 100644
--- a/aos/common/mutex_test.cpp
+++ b/aos/common/mutex_test.cpp
@@ -10,6 +10,7 @@
 #include "gtest/gtest.h"
 
 #include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/die.h"
 
 namespace aos {
 namespace testing {
@@ -17,6 +18,11 @@
 class MutexTest : public ::testing::Test {
  public:
   Mutex test_mutex;
+
+ protected:
+  void SetUp() override {
+    SetDieTestMode(true);
+  }
 };
 
 typedef MutexTest MutexDeathTest;
diff --git a/aos/common/queue.h b/aos/common/queue.h
index 77fdf0d..65bfa95 100644
--- a/aos/common/queue.h
+++ b/aos/common/queue.h
@@ -18,55 +18,11 @@
 
 namespace aos {
 
-#ifndef SWIG
-
-// Prints the value from 1 message field into output.
-// output is where to write the text representation.
-// output_bytes should point to the number of bytes available to write in
-// output. It will be changed to the number of bytes that were actually written.
-// input is where to read the data in from.
-// input_bytes should point to the number of bytes available to read from input.
-// It will be changed to the number of bytes that were actually read.
-// type is the ID of a type to print. It must be a primitive type.
-//
-// The implementation of this is generated by the ruby code.
-// TODO(brians): Actually do that.
-bool PrintField(char *output, size_t *output_bytes, void *input,
-                size_t *input_bytes, uint32_t type);
-
-// The type IDs this uses are 2 parts: a 16 bit size and a 16 bit hash. Sizes
-// for primitive types are stored with 8192 added.
-//
-// Serializing/deserializing includes all of the fields too.
-struct MessageType {
-  struct Field {
-    uint32_t type;
-    const char *name;
-  };
-
-  ~MessageType() {
-    for (int i = 0; i < number_fields; ++i) {
-      delete fields[i];
-    }
-  }
-
-  // Returns -1 for error.
-  ssize_t Serialize(char *buffer, size_t max_bytes) const;
-  // bytes should start out as the number of bytes available in buffer and gets
-  // set to the number actually read before returning.
-  // Returns a new instance allocated with new or NULL for error.
-  static MessageType *Deserialize(const char *buffer, size_t *bytes);
-
-  uint32_t id;
-  const char *name;
-
-  int number_fields;
-  const Field *fields[];
-};
-
-#endif  // SWIG
+class MessageType;
 
 // This class is a base class for all messages sent over queues.
+// All of the methods are overloaded in (generated) subclasses to do the same
+// thing for the whole thing.
 class Message {
  public:
   typedef ::aos::time::Time Time;
@@ -76,17 +32,13 @@
   Message() : sent_time(0, 0) {}
 
   // Zeros out the time.
-  // Overriden to zero the whole message.
   void Zero();
   // Returns the size of the common fields.
-  // Overriden to return the size of the whole message.
   static size_t Size() { return sizeof(Time); }
 
   // Deserializes the common fields from the buffer.
-  // Overriden to deserialize the whole message.
   size_t Deserialize(const char *buffer);
   // Serializes the common fields into the buffer.
-  // Overriden to serialize the whole message.
   size_t Serialize(char *buffer) const;
 
   // Populates sent_time with the current time.
@@ -95,9 +47,7 @@
   // Writes the contents of the message to the provided buffer.
   size_t Print(char *buffer, int length) const;
 
-#ifndef SWIG
-  const MessageType &GetType() const;
-#endif  // SWIG
+  static const MessageType *GetType();
 };
 
 template <class T> class Queue;
diff --git a/aos/common/queue_test.cc b/aos/common/queue_test.cc
index 32d1d23..ebb0bba 100644
--- a/aos/common/queue_test.cc
+++ b/aos/common/queue_test.cc
@@ -6,6 +6,7 @@
 #include "aos/common/test_queue.q.h"
 #include "aos/common/queue_testutils.h"
 #include "aos/common/util/thread.h"
+#include "aos/common/die.h"
 
 using ::aos::time::Time;
 
@@ -15,6 +16,10 @@
 
 class QueueTest : public ::testing::Test {
  protected:
+  void SetUp() override {
+    SetDieTestMode(true);
+  }
+
   GlobalCoreInstance my_core;
   // Create a new instance of the test queue so that it invalidates the queue
   // that it points to.  Otherwise, we will have a pointer to shared memory that
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
new file mode 100644
index 0000000..e2310fc
--- /dev/null
+++ b/aos/common/queue_types.cc
@@ -0,0 +1,282 @@
+#include "aos/common/queue_types.h"
+
+#include <inttypes.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "aos/common/byteorder.h"
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+#include "aos/common/logging/logging.h"
+#include "aos/linux_code/ipc_lib/core_lib.h"
+#include "aos/common/mutex.h"
+
+namespace aos {
+
+ssize_t MessageType::Serialize(char *buffer, size_t max_bytes) const {
+  char *const buffer_start = buffer;
+  size_t fields_size = 0;
+  for (int i = 0; i < number_fields; ++i) {
+    fields_size += sizeof(fields[i]->type);
+    fields_size += sizeof(size_t);
+    fields_size += fields[i]->name.size();
+  }
+  if (max_bytes < sizeof(id) + sizeof(super_size) + sizeof(size_t) +
+                      sizeof(number_fields) + name.size() + fields_size) {
+    return -1;
+  }
+
+  size_t length;
+
+  to_network(&super_size, buffer);
+  buffer += sizeof(super_size);
+  to_network(&id, buffer);
+  buffer += sizeof(id);
+  length = name.size();
+  to_network(&length, buffer);
+  buffer += sizeof(length);
+  to_network(&number_fields, buffer);
+  buffer += sizeof(number_fields);
+  memcpy(buffer, name.data(), name.size());
+  buffer += name.size();
+
+  for (int i = 0; i < number_fields; ++i) {
+    to_network(&fields[i]->type, buffer);
+    buffer += sizeof(fields[i]->type);
+    length = fields[i]->name.size();
+    to_network(&length, buffer);
+    buffer += sizeof(length);
+    memcpy(buffer, fields[i]->name.data(), fields[i]->name.size());
+    buffer += length;
+  }
+
+  return buffer - buffer_start;
+}
+
+MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes) {
+  size_t name_length;
+  decltype(MessageType::super_size) super_size;
+  decltype(MessageType::id) id;
+  decltype(MessageType::number_fields) number_fields;
+  if (*bytes < sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+                   sizeof(number_fields)) {
+    return nullptr;
+  }
+  *bytes -= sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+            sizeof(number_fields);
+
+  to_host(buffer, &super_size);
+  buffer += sizeof(super_size);
+  to_host(buffer, &id);
+  buffer += sizeof(id);
+  to_host(buffer, &name_length);
+  buffer += sizeof(name_length);
+  to_host(buffer, &number_fields);
+  buffer += sizeof(number_fields);
+
+  if (*bytes < name_length) {
+    return nullptr;
+  }
+  *bytes -= name_length;
+
+  Field **fields = new Field *[number_fields];
+  ::std::unique_ptr<MessageType> r(
+      new MessageType(super_size, id, ::std::string(buffer, name_length),
+                      number_fields, fields));
+  buffer += name_length;
+
+  for (int i = 0; i < number_fields; ++i) {
+    size_t field_name_length;
+    if (*bytes < sizeof(fields[i]->type) + sizeof(field_name_length)) {
+      return nullptr;
+    }
+    *bytes -= sizeof(fields[i]->type) + sizeof(field_name_length);
+
+    to_host(buffer, &fields[i]->type);
+    buffer += sizeof(fields[i]->type);
+    to_host(buffer, &field_name_length);
+    buffer += sizeof(field_name_length);
+
+    if (*bytes < field_name_length) {
+      return nullptr;
+    }
+    *bytes -= field_name_length;
+    fields[i]->name = ::std::string(buffer, field_name_length);
+    buffer += field_name_length;
+  }
+
+  return r.release();
+}
+
+bool PrintMessage(char *output, size_t *output_bytes, const void *input,
+                  size_t *input_bytes, const MessageType &type) {
+  *input_bytes -= type.super_size;
+  input = static_cast<const char *>(input) + type.super_size;
+
+  if (*output_bytes < type.name.size() + 1) return false;
+  *output_bytes -= type.name.size() + 1;
+  memcpy(output, type.name.data(), type.name.size());
+  output += type.name.size();
+  *(output++) = '{';
+
+  bool first = true;
+  for (int i = 0; i < type.number_fields; ++i) {
+    if (first) {
+      first = false;
+    } else {
+      if (*output_bytes < 1) return false;
+      *output_bytes -= 1;
+      *(output++) = ',';
+    }
+
+    if (*output_bytes < type.fields[i]->name.size() + 1) return false;
+    *output_bytes -= type.fields[i]->name.size() + 1;
+    memcpy(output, type.fields[i]->name.data(), type.fields[i]->name.size());
+    output += type.fields[i]->name.size();
+    *(output++) = ':';
+
+    const size_t output_bytes_before = *output_bytes,
+                 input_bytes_before = *input_bytes;
+    if (MessageType::IsPrimitive(type.fields[i]->type)) {
+      if (!PrintField(output, output_bytes, input, input_bytes,
+                      type.fields[i]->type)) {
+        return false;
+      }
+    } else {
+      if (!PrintMessage(output, output_bytes, input, input_bytes,
+                        type_cache::Get(type.fields[i]->type))) {
+        return false;
+      }
+    }
+
+    // Update the input and output pointers, ignoring the trailing '\0' that the
+    // subcall put on.
+    output += output_bytes_before - *output_bytes - 1;
+    *output_bytes += 1;
+    input =
+        static_cast<const char *>(input) + input_bytes_before - *input_bytes;
+  }
+  if (*output_bytes < 2) return false;
+  *output_bytes -= 2;
+  *(output++) = '}';
+  *(output++) = '\0';
+  return true;
+}
+
+namespace type_cache {
+namespace {
+
+struct CacheEntry {
+  const MessageType &type;
+  bool in_shm;
+
+  CacheEntry(const MessageType &type, bool in_shm)
+      : type(type), in_shm(in_shm) {}
+};
+
+struct ShmType {
+  uint32_t id;
+  volatile ShmType *next;
+
+  size_t serialized_size;
+  char serialized[];
+};
+
+::std::unordered_map<uint32_t, CacheEntry> cache;
+::aos::Mutex cache_lock;
+
+}  // namespace
+
+void Add(const MessageType &type) {
+  ::aos::MutexLocker locker(&cache_lock);
+  if (cache.count(type.id) == 0) {
+    cache.emplace(::std::piecewise_construct, ::std::forward_as_tuple(type.id),
+                  ::std::forward_as_tuple(type, false));
+  }
+}
+
+const MessageType &Get(uint32_t type_id) {
+  ::aos::MutexLocker locker(&cache_lock);
+  if (cache.count(type_id) > 0) {
+    return cache.at(type_id).type;
+  }
+
+  if (aos_core_is_init()) {
+    // No need to lock because the only thing that happens is somebody adds on
+    // to the end, and they shouldn't be adding the one we're looking for.
+    const volatile ShmType *c = static_cast<volatile ShmType *>(
+        global_core->mem_struct->queue_types.pointer);
+    while (c != nullptr) {
+      if (c->id == type_id) {
+        size_t bytes = c->serialized_size;
+        MessageType *type = MessageType::Deserialize(
+            const_cast<const char *>(c->serialized), &bytes);
+        cache.emplace(::std::piecewise_construct,
+                      ::std::forward_as_tuple(type_id),
+                      ::std::forward_as_tuple(*type, true));
+        return *type;
+      }
+      c = c->next;
+    }
+  } else {
+    LOG(INFO, "FYI: no shm. going to LOG(FATAL) now\n");
+  }
+
+  LOG(FATAL, "MessageType for id 0x%" PRIx32 " not found\n", type_id);
+}
+
+void AddShm(uint32_t type_id) {
+  if (!aos_core_is_init()) {
+    LOG(FATAL, "can't AddShm(%" PRIu32 ") without shm!\n", type_id);
+  }
+
+  ::aos::MutexLocker locker(&cache_lock);
+  CacheEntry &cached = cache.at(type_id);
+  if (cached.in_shm) return;
+
+  if (mutex_lock(&global_core->mem_struct->queue_types.lock) != 0) {
+    LOG(FATAL, "locking queue_types lock failed\n");
+  }
+  volatile ShmType *current = static_cast<volatile ShmType *>(
+      global_core->mem_struct->queue_types.pointer);
+  if (current != nullptr) {
+    while (true) {
+      if (current->id == type_id) {
+        cached.in_shm = true;
+        mutex_unlock(&global_core->mem_struct->queue_types.lock);
+        return;
+      }
+      if (current->next == nullptr) break;
+      current = current->next;
+    }
+  }
+  char buffer[512];
+  ssize_t size = cached.type.Serialize(buffer, sizeof(buffer));
+  if (size == -1) {
+    LOG(FATAL, "type %s is too big to fit into %zd bytes\n",
+        cached.type.name.c_str(), sizeof(buffer));
+  }
+
+  volatile ShmType *shm =
+      static_cast<volatile ShmType *>(shm_malloc(sizeof(ShmType) + size));
+  shm->id = type_id;
+  shm->next = nullptr;
+  shm->serialized_size = size;
+  memcpy(const_cast<char *>(shm->serialized), buffer, size);
+
+  if (current == NULL) {
+    global_core->mem_struct->queue_types.pointer = const_cast<ShmType *>(shm);
+  } else {
+    current->next = shm;
+  }
+  mutex_unlock(&global_core->mem_struct->queue_types.lock);
+
+  for (int i = 0; i < cached.type.number_fields; ++i) {
+    if (!MessageType::IsPrimitive(cached.type.fields[i]->type)) {
+      AddShm(cached.type.fields[i]->type);
+    }
+  }
+}
+
+}  // namespace type_cache
+}  // namespace aos
diff --git a/aos/common/queue_types.h b/aos/common/queue_types.h
new file mode 100644
index 0000000..55d26b4
--- /dev/null
+++ b/aos/common/queue_types.h
@@ -0,0 +1,124 @@
+#ifndef AOS_COMMON_QUEUE_TYPES_H_
+#define AOS_COMMON_QUEUE_TYPES_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <initializer_list>
+#include <string>
+
+#include "aos/common/macros.h"
+
+namespace aos {
+
+// The type IDs this uses are 2 parts: a 16 bit size and a 16 bit hash. Sizes
+// for primitive types are stored with 8192 (0x2000) added.
+//
+// Serializing/deserializing includes all of the fields too.
+struct MessageType {
+  struct Field {
+    // The type ID for the type of this field.
+    uint32_t type;
+    ::std::string name;
+  };
+
+  // Takes ownership of all the Field pointers in fields_initializer.
+  MessageType(size_t super_size, uint32_t id, const ::std::string &name,
+              ::std::initializer_list<const Field *> fields_initializer)
+      : super_size(super_size), id(id), name(name) {
+    number_fields = fields_initializer.size();
+    fields = new const Field *[number_fields];
+    int i = 0;
+    for (const Field *c : fields_initializer) {
+      fields[i++] = c;
+    }
+  }
+
+  ~MessageType() {
+    for (int i = 0; i < number_fields; ++i) {
+      delete fields[i];
+    }
+  }
+
+  // Returns -1 if max_bytes is too small.
+  ssize_t Serialize(char *buffer, size_t max_bytes) const;
+  // bytes should start out as the number of bytes available in buffer and gets
+  // reduced by the number actually read before returning.
+  // Returns a new instance allocated with new or nullptr for error.
+  static MessageType *Deserialize(const char *buffer, size_t *bytes);
+
+  static bool IsPrimitive(uint32_t type_id) {
+    return (type_id & 0x2000) != 0;
+  }
+
+  // How many (serialized) bytes the superclass takes up.
+  size_t super_size;
+  // The type ID for this.
+  uint32_t id;
+  ::std::string name;
+
+  int number_fields;
+  const Field **fields;
+
+ private:
+  // Internal constructor for Deserialize to use.
+  MessageType(size_t super_size, uint32_t id, ::std::string &&name,
+              int number_fields, Field **fields)
+      : super_size(super_size),
+        id(id),
+        name(name),
+        number_fields(number_fields),
+        fields(const_cast<const Field **>(fields)) {
+    for (int i = 0; i < number_fields; ++i) {
+      fields[i] = new Field();
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(MessageType);
+};
+
+// The arguments are the same for both of these.
+//
+// output is where to write the text representation ('\0'-terminated
+// human-readable string).
+// output_bytes should point to the number of bytes available to write in
+// output. It will be reduced by the number of bytes that were actually written.
+// input is where to read the data in from (in network byte order, aka from
+// Serialize).
+// input_bytes should point to the number of bytes available to read from input.
+// It will be reduced by the number of bytes that were actually read.
+// type is which type to print.
+// Returns true for success and false for not.
+//
+// Prints the value from 1 primitive message field into output.
+// The implementation of this is generated by the ruby code.
+bool PrintField(char *output, size_t *output_bytes, const void *input,
+                size_t *input_bytes, uint32_t type)
+    __attribute__((warn_unused_result));
+// Prints a whole message into output.
+// This calls itself recursively and PrintField to print out the whole thing.
+bool PrintMessage(char *output, size_t *output_bytes, const void *input,
+                  size_t *input_bytes, const MessageType &type)
+    __attribute__((warn_unused_result));
+
+// Implements a cache of types which generally works per-process but can (when
+// instructed) put a type in shared memory which other processes will
+// automatically be able to retrieve.
+// All of these functions are thread-safe.
+namespace type_cache {
+
+// Makes sure a type is in the type cache. This will store a reference to type.
+// The types of any non-primitive fields of type must already be added.
+void Add(const MessageType &type);
+// Retrieves a type from the type cache or shm. LOG(FATAL)s if it can't find it.
+const MessageType &Get(uint32_t type_id);
+// Makes sure a type is in the list in shm. Add must have already been called
+// for type.
+// Also adds the types of any non-primitive fields of type.
+void AddShm(uint32_t type_id);
+
+}  // namespace type_cache
+}  // namespace aos
+
+#endif  // AOS_COMMON_QUEUE_TYPES_H_
diff --git a/aos/common/queue_types_test.cc b/aos/common/queue_types_test.cc
new file mode 100644
index 0000000..c665333
--- /dev/null
+++ b/aos/common/queue_types_test.cc
@@ -0,0 +1,178 @@
+#include "aos/common/queue_types.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/test_queue.q.h"
+#include "aos/common/byteorder.h"
+
+using ::aos::common::testing::Structure;
+using ::aos::common::testing::MessageWithStructure;
+using ::aos::common::testing::OtherTestingMessage;
+
+namespace aos {
+namespace testing {
+
+typedef MessageType::Field Field;
+
+static const MessageType kTestType1(5, 0x1234, "TestType1",
+                                    {new Field{0, "field1"},
+                                     new Field{0, "field2"},
+                                     new Field{0, "field3"}});
+
+class QueueTypesTest : public ::testing::Test {
+ public:
+  ::testing::AssertionResult Equal(const MessageType &l, const MessageType &r) {
+    using ::testing::AssertionFailure;
+    if (l.id != r.id) {
+      return AssertionFailure() << "id " << l.id << " != " << r.id;
+    }
+    if (l.name != r.name) {
+      return AssertionFailure() << "name '" << l.name << "' != '" << r.name
+                                << "'";
+    }
+    if (l.number_fields != r.number_fields) {
+      return AssertionFailure() << "number_fields " << l.number_fields
+                                << " != " << r.number_fields;
+    }
+    for (int i = 0; i < l.number_fields; ++i) {
+      SCOPED_TRACE("field " + ::std::to_string(i));
+      if (l.fields[i]->type != r.fields[i]->type) {
+        return AssertionFailure() << "type " << l.fields[i]->type
+                                  << " != " << r.fields[i]->type;
+      }
+      if (l.fields[i]->name != r.fields[i]->name) {
+        return AssertionFailure() << "name '" << l.fields[i]->name << "' != '"
+                                  << r.fields[i]->name << "'";
+      }
+    }
+    return ::testing::AssertionSuccess();
+  }
+};
+
+TEST_F(QueueTypesTest, Serialization) {
+  char buffer[512];
+  ssize_t size;
+  size_t out_size;
+  ::std::unique_ptr<MessageType> deserialized;
+
+  size = kTestType1.Serialize(buffer, sizeof(buffer));
+  ASSERT_GT(size, 1);
+  ASSERT_LE(static_cast<size_t>(size), sizeof(buffer));
+
+  out_size = size;
+  deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+  EXPECT_EQ(0u, out_size);
+  EXPECT_TRUE(Equal(kTestType1, *deserialized));
+
+  out_size = size - 1;
+  deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+  EXPECT_EQ(nullptr, deserialized.get());
+
+  out_size = size + 1;
+  ASSERT_LE(out_size, sizeof(buffer));
+  deserialized.reset(MessageType::Deserialize(buffer, &out_size));
+  EXPECT_EQ(1u, out_size);
+  EXPECT_TRUE(Equal(kTestType1, *deserialized));
+}
+
+class PrintFieldTest : public ::testing::Test {
+ public:
+  char input[128], output[128];
+  size_t input_bytes, output_bytes;
+};
+typedef PrintFieldTest PrintMessageTest;
+
+TEST_F(PrintFieldTest, Basic) {
+  static const uint16_t kData = 971;
+  input_bytes = sizeof(kData);
+  to_network(&kData, input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+                         Structure::GetType()->fields[1]->type));
+  EXPECT_EQ(0u, input_bytes);
+  EXPECT_EQ(sizeof(output) - 4, output_bytes);
+  EXPECT_EQ(::std::string("971\0", 4),
+            ::std::string(output, sizeof(output) - output_bytes));
+}
+
+// Tests PrintField with trailing input bytes and no extra output bytes.
+TEST_F(PrintFieldTest, OtherSizes) {
+  static const float kData = 16.78;
+  static const ::std::string kString("16.780001");
+  static const size_t kExtraInputBytes = 4;
+  input_bytes = sizeof(kData) + kExtraInputBytes;
+  to_network(&kData, input);
+  output_bytes = kString.size() + 1;
+  assert(output_bytes <= sizeof(output));
+  ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+                         Structure::GetType()->fields[2]->type));
+  EXPECT_EQ(kExtraInputBytes, input_bytes);
+  EXPECT_EQ(0u, output_bytes);
+  EXPECT_EQ(kString, ::std::string(output));
+}
+
+TEST_F(PrintFieldTest, InputTooSmall) {
+  static const float kData = 0;
+  input_bytes = sizeof(kData) - 1;
+  output_bytes = sizeof(output);
+  EXPECT_FALSE(PrintField(output, &output_bytes, input, &input_bytes,
+                          Structure::GetType()->fields[2]->type));
+}
+
+TEST_F(PrintFieldTest, OutputTooSmall) {
+  static const uint16_t kData = 12345;
+  input_bytes = sizeof(input);
+  to_network(&kData, input);
+  output_bytes = 5;
+  EXPECT_FALSE(PrintField(output, &output_bytes, input, &input_bytes,
+                          Structure::GetType()->fields[1]->type));
+}
+
+static const OtherTestingMessage kTestMessage1(true, 8971, 3.2);
+static const ::std::string kTestMessage1String =
+    ".aos.common.testing.OtherTestingMessage{test_bool:T,test_int:8971"
+    ",test_double:3.200000}";
+static const Structure kTestStructure1(false, 973, 8.56);
+static const ::std::string kTestStructure1String =
+    ".aos.common.testing.Structure{struct_bool:f,struct_int:973"
+    ",struct_float:8.560000}";
+
+TEST_F(PrintMessageTest, Basic) {
+  assert(sizeof(input) >= kTestMessage1.Size());
+  input_bytes = kTestMessage1.Serialize(input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+  EXPECT_EQ(kTestMessage1String, ::std::string(output));
+  EXPECT_EQ(kTestMessage1String.size() + 1, sizeof(output) - output_bytes);
+}
+
+TEST_F(PrintMessageTest, OutputTooSmall) {
+  assert(sizeof(input) >= kTestMessage1.Size());
+  input_bytes = kTestMessage1.Serialize(input);
+  output_bytes = kTestMessage1String.size();
+  EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, InputTooSmall) {
+  input_bytes = kTestMessage1.Size() - 1;
+  output_bytes = sizeof(output);
+  EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, Structure) {
+  assert(sizeof(input) >= kTestStructure1.Size());
+  input_bytes = kTestStructure1.Serialize(input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestStructure1.GetType()));
+  EXPECT_EQ(kTestStructure1String, ::std::string(output));
+  EXPECT_EQ(kTestStructure1String.size() + 1, sizeof(output) - output_bytes);
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/aos/common/test_queue.q b/aos/common/test_queue.q
index a7b441f..d603802 100644
--- a/aos/common/test_queue.q
+++ b/aos/common/test_queue.q
@@ -1,5 +1,17 @@
 package aos.common.testing;
 
+struct Structure {
+  bool struct_bool;
+  uint16_t struct_int;
+  float struct_float;
+};
+
+message MessageWithStructure {
+  bool other_member;
+  Structure struct1;
+  Structure struct2;
+};
+
 message TestingMessage {
   bool test_bool;
   int32_t test_int;
diff --git a/aos/common/util/log_interval.h b/aos/common/util/log_interval.h
new file mode 100644
index 0000000..188a16c
--- /dev/null
+++ b/aos/common/util/log_interval.h
@@ -0,0 +1,85 @@
+#ifndef AOS_COMMON_UTIL_LOG_INTERVAL_H_
+#define AOS_COMMON_UTIL_LOG_INTERVAL_H_
+
+#include "aos/common/time.h"
+#include "aos/common/logging/logging.h"
+
+#include <string>
+
+namespace aos {
+namespace util {
+
+// A class to help with logging things that happen a lot only occasionally.
+//
+// Intended use {
+//   static LogInterval interval(::aos::time::Time::InSeconds(0.2));
+//
+//   if (WantToLog()) {
+//     interval.WantToLog();
+//   }
+//   if (interval.ShouldLog()) {
+//     LOG(DEBUG, "thingie happened! (%d times)\n", interval.Count());
+//   }
+// }
+class LogInterval {
+ public:
+  constexpr LogInterval(const ::aos::time::Time &interval)
+      : count_(0), interval_(interval), last_done_(0, 0) {}
+
+  void WantToLog() {
+    if (count_ == 0) {
+      last_done_ = ::aos::time::Time::Now();
+    }
+    ++count_;
+  }
+  bool ShouldLog() {
+    const ::aos::time::Time now = ::aos::time::Time::Now();
+    const bool r = (now - last_done_) >= interval_;
+    if (r) {
+      last_done_ = now;
+    }
+    return r;
+  }
+  int Count() {
+    const int r = count_;
+    count_ = 0;
+    return r;
+  }
+
+  const ::aos::time::Time &interval() const { return interval_; }
+
+ private:
+  int count_;
+  const ::aos::time::Time interval_;
+  ::aos::time::Time last_done_;
+};
+
+// This one is even easier to use. It always logs with a message "%s %d
+// times\n".
+class SimpleLogInterval {
+ public:
+  SimpleLogInterval(const ::aos::time::Time &interval, log_level level,
+                    const ::std::string &message)
+      : interval_(interval), level_(level), message_(message) {}
+
+#define LOG_INTERVAL(simple_log) \
+  simple_log.Hit(LOG_SOURCENAME ": " STRINGIFY(__LINE__))
+  void Hit(const char *context) {
+    interval_.WantToLog();
+    if (interval_.ShouldLog()) {
+      log_do(level_, "%s: %.*s %d times over %f sec\n", context,
+             static_cast<int>(message_.size()), message_.data(),
+             interval_.Count(), interval_.interval().ToSeconds());
+    }
+  }
+
+ private:
+  LogInterval interval_;
+  const log_level level_;
+  const ::std::string message_;
+};
+
+}  // namespace util
+}  // namespace aos
+
+#endif  // AOS_COMMON_UTIL_LOG_INTERVAL_H_
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index 79275c0..98f1a3d 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -1,6 +1,21 @@
 {
   'targets': [
     {
+      'target_name': 'log_interval',
+      'type': 'static_library',
+      'sources': [
+        #'log_interval.h',
+      ],
+      'dependencies': [
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
+    {
       'target_name': 'thread',
       'type': 'static_library',
       'sources': [
diff --git a/aos/config/configure-prime.txt b/aos/config/configure-prime.txt
index 87b2818..1adaccd 100644
--- a/aos/config/configure-prime.txt
+++ b/aos/config/configure-prime.txt
@@ -67,10 +67,7 @@
 11. Set the correct date.
     `date` to check if date is correct.
     `date -s <date string>` to set it if it isn't.
-12. Make it export UART1 on boot.
-    Add the following to /boot/uboot/uenv.txt:
-    "optargs=capemgr.enable_partno=BB-UART1"
-13. Fix the locale setup for SSHing in.
+12. Fix the locale setup for SSHing in.
     `dpkg-reconfigure locales`, leave it with "en_US.UTF-8" only being
 	enabled, and then select "None" instead of that for the default in
 	the second screen.
diff --git a/aos/config/starter b/aos/config/starter
index f7a9c18..1fc05a0 100755
--- a/aos/config/starter
+++ b/aos/config/starter
@@ -69,6 +69,7 @@
 	#   1 if daemon was already stopped
 	#   2 if daemon could not be stopped
 	#   other if a failure occurred
+	killall starter_loop.sh
 	start-stop-daemon --stop --quiet --retry=INT/10/KILL/5 --pidfile $PIDFILE
 	RETVAL="$?"
 	[ "$RETVAL" = 2 ] && return 2
@@ -80,7 +81,7 @@
 	# sleep for some time.
 	#start-stop-daemon --stop --quiet --oknodo --retry=INT/10/KILL/5
 	#[ "$?" = 2 ] && return 2
-	sleep 10
+	sleep 5
 	# Many daemons don't delete their pidfiles when they exit.
 	rm -f $PIDFILE
 	return "$RETVAL"
diff --git a/aos/externals/gtest.patch b/aos/externals/gtest.patch
index 6caba01..cbf7294 100644
--- a/aos/externals/gtest.patch
+++ b/aos/externals/gtest.patch
@@ -1,63 +1,50 @@
-diff -rupN gtest-1.6.0-p1/fused-src/gtest/gtest-all.cc gtest-1.6.0/fused-src/gtest/gtest-all.cc
---- gtest-1.6.0-p1/fused-src/gtest/gtest-all.cc	2011-04-15 12:54:57.000000000 -0700
-+++ gtest-1.6.0/fused-src/gtest/gtest-all.cc	2012-11-12 17:42:37.881573135 -0800
-@@ -379,7 +379,25 @@ class GTEST_API_ SingleFailureChecker {
+diff --git src/gtest-port.cc b/src/gtest-port.cc
+index b860d48..acb459b 100644
+--- a/src/gtest-port.cc
++++ b/src/gtest-port.cc
+@@ -98,6 +98,21 @@ size_t GetThreadCount() {
+   }
+ }
  
- // cpplint thinks that the header is already included, so we want to
- // silence it.
-+#ifdef __VXWORKS__
-+# include <time.h>  // NOLINT
-+# include <sys/times.h>  // NOLINT
-+static inline int gettimeofday(struct timeval *tv, void *) {
-+  struct timespec ts;
++#elif GTEST_OS_LINUX
 +
-+  if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
-+    printf("Gettimeofday error\n");
-+    tv->tv_sec = 0;
-+    tv->tv_usec = 0;
-+    return -1;
++size_t GetThreadCount() {
++  size_t thread_count = 0;
++  if (DIR *dir = opendir("/proc/self/task")) {
++    while (dirent *entry = readdir(dir)) {
++      if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
++        ++thread_count;
++      }
++    }
++    closedir(dir);
 +  }
-+  tv->tv_sec  = ts.tv_sec;
-+  tv->tv_usec = ts.tv_nsec/1000;
-+  return 0;
++  return thread_count;
 +}
-+#else
- # include <sys/time.h>  // NOLINT
-+#endif
- # include <unistd.h>  // NOLINT
- 
- #endif  // GTEST_OS_LINUX
-@@ -7751,6 +7769,8 @@ bool FilePath::CreateFolder() const {
-   delete [] unicode;
- #elif GTEST_OS_WINDOWS
-   int result = _mkdir(pathname_.c_str());
-+#elif defined(__VXWORKS__)
-+  int result = mkdir(pathname_.c_str());
- #else
-   int result = mkdir(pathname_.c_str(), 0777);
- #endif  // GTEST_OS_WINDOWS_MOBILE
-@@ -7870,7 +7890,7 @@ void FilePath::Normalize() {
- namespace testing {
- namespace internal {
- 
--#if defined(_MSC_VER) || defined(__BORLANDC__)
-+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__VXWORKS__)
- // MSVC and C++Builder do not provide a definition of STDERR_FILENO.
- const int kStdOutFileno = 1;
- const int kStdErrFileno = 2;
-diff -rupN gtest-1.6.0-p1/include/gtest/internal/gtest-port.h gtest-1.6.0/include/gtest/internal/gtest-port.h
---- gtest-1.6.0-p1/include/gtest/internal/gtest-port.h	2011-04-15 12:49:10.000000000 -0700
-+++ gtest-1.6.0/include/gtest/internal/gtest-port.h	2012-11-12 17:27:33.536801263 -0800
-@@ -197,6 +197,12 @@
- #include <sstream>  // NOLINT
- #include <string>  // NOLINT
- 
-+#ifdef __VXWORKS__
-+int read(int fd, void *buf, size_t count);
-+int write(int fd, const void *buf, size_t count);
-+int close(int fd);
-+#endif
 +
- #define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
- #define GTEST_FLAG_PREFIX_ "gtest_"
- #define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+ #else
+ 
+ size_t GetThreadCount() {
+diff --git a/src/gtest-death-test.cc b/src/gtest-death-test.cc
+index 8b2e413..faad3a4 100644
+--- a/src/gtest-death-test.cc
++++ b/src/gtest-death-test.cc
+@@ -44,6 +44,8 @@
+ # include <fcntl.h>
+ # include <limits.h>
+ # include <stdarg.h>
++# include <sys/time.h>
++# include <sys/resource.h>
+ 
+ # if GTEST_OS_WINDOWS
+ #  include <windows.h>
+@@ -898,6 +900,11 @@ inline char** GetEnviron() { return environ; }
+ // This function is called in a clone()-ed process and thus must avoid
+ // any potentially unsafe operations like malloc or libc functions.
+ static int ExecDeathTestChildMain(void* child_arg) {
++  rlimit core_rlimit;
++  core_rlimit.rlim_cur = 0;
++  core_rlimit.rlim_max = 0;
++  GTEST_DEATH_TEST_CHECK_SYSCALL_(setrlimit(RLIMIT_CORE, &core_limit));
++
+   ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+   GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
diff --git a/aos/linux_code/camera/Buffers.cpp b/aos/linux_code/camera/Buffers.cpp
index e1d22b6..19c1d45 100644
--- a/aos/linux_code/camera/Buffers.cpp
+++ b/aos/linux_code/camera/Buffers.cpp
@@ -61,24 +61,21 @@
 }
 
 void Buffers::Release() {
-  if (message_ != NULL) {
-    queue_->FreeMessage(message_);
-    message_ = NULL;
-  }
+  message_.reset();
 }
-const void *Buffers::GetNext(bool block,
-                       uint32_t *bytesused, timeval *timestamp, uint32_t *sequence) {
+const void *Buffers::GetNext(bool block, uint32_t *bytesused,
+                             timeval *timestamp, uint32_t *sequence) {
   Release();
 
   // TODO(brians) make sure the camera reader process hasn't died
   do {
     if (block) {
-      message_ = static_cast<const Message *>(queue_->ReadMessage(
-              RawQueue::kPeek | RawQueue::kBlock));
+      message_.reset(static_cast<const Message *>(
+          queue_->ReadMessage(RawQueue::kPeek | RawQueue::kBlock)));
     } else {
       static int index = 0;
-      message_ = static_cast<const Message *>(queue_->ReadMessageIndex(
-              RawQueue::kBlock, &index));
+      message_.reset(static_cast<const Message *>(
+          queue_->ReadMessageIndex(RawQueue::kBlock, &index)));
     }
   } while (block && message_ == NULL);
   if (message_ != NULL) {
@@ -132,9 +129,12 @@
   
   return myfds[0];
 }
-Buffers::Buffers() : server_(CreateSocket(connect)), fd_(FetchFD()), message_(NULL) {
+Buffers::Buffers()
+    : server_(CreateSocket(connect)),
+      fd_(FetchFD()),
+      queue_(RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1)),
+      message_(queue_) {
   MMap();
-  queue_ = RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1);
 }
 
 Buffers::~Buffers() {
diff --git a/aos/linux_code/camera/Buffers.h b/aos/linux_code/camera/Buffers.h
index b447468..aedd79f 100644
--- a/aos/linux_code/camera/Buffers.h
+++ b/aos/linux_code/camera/Buffers.h
@@ -8,6 +8,7 @@
 
 #include "aos/linux_code/ipc_lib/queue.h"
 #include "aos/common/type_traits.h"
+#include "aos/atom_code/ipc_lib/unique_message_ptr.h"
 
 namespace aos {
 namespace camera {
@@ -17,6 +18,7 @@
   // It has to do a lot of the same things as all the other ones, but it gets
   // the information from different places (some of it gets sent out by it).
   friend class Reader;
+
   // Not an abstract name so that an existing one can just be unlinked without
   // disturbing it if necessary (like with shm_link).
   static const std::string kFDServerName;
@@ -50,14 +52,17 @@
     uint32_t sequence;
   };
   static_assert(shm_ok<Message>::value, "it's going through queues");
-  // The current one. Sometimes NULL.
-  const Message *message_;
-  static const std::string kQueueName;
+
   // NULL for the Reader one.
-  RawQueue *queue_;
+  RawQueue *const queue_;
+  // The current one. Sometimes NULL.
+  unique_message_ptr<const Message> message_;
+
+  static const std::string kQueueName;
   // Make the actual mmap calls.
   // Called by Buffers() automatically.
   void MMap();
+
  public:
   Buffers();
   // Will clean everything up.
@@ -89,4 +94,3 @@
 } // namespace aos
 
 #endif
-
diff --git a/aos/linux_code/camera/camera.gyp b/aos/linux_code/camera/camera.gyp
index e4f1e04..119d105 100644
--- a/aos/linux_code/camera/camera.gyp
+++ b/aos/linux_code/camera/camera.gyp
@@ -1,44 +1,6 @@
 {
   'targets': [
     {
-      'target_name': 'aos_camera',
-      'type': 'loadable_module',
-      'sources': [
-        'jni.cpp',
-      ],
-      'dependencies': [
-        '<(AOS)/common/network/network.gyp:socket_so',
-        '<(AOS)/common/common.gyp:timing_so',
-        'private_aos_camera_jar',
-        '<(EXTERNALS):libjpeg',
-      ],
-      'export_dependent_settings': [
-        '<(AOS)/common/network/network.gyp:socket_so',
-        '<(AOS)/common/common.gyp:timing_so',
-        'private_aos_camera_jar',
-      ],
-    },
-    {
-      'target_name': 'private_aos_camera_jar',
-      'dependencies': [
-        '<(EXTERNALS):javacv',
-      ],
-      'variables': {
-        'srcdirs': ['java'],
-        'gen_headers': ['aos.Natives'],
-      },
-      'export_dependent_settings': [
-        '<(EXTERNALS):javacv',
-      ],
-      'direct_dependent_settings': {
-        'variables': {
-          'jni_libs': ['aos_camera'],
-        },
-      },
-      'includes': ['../../build/java.gypi'],
-      'hard_dependency': 1,
-    },
-    {
       'target_name': 'buffers',
       'type': 'static_library',
       'sources': [
@@ -47,9 +9,11 @@
       'dependencies': [
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
         '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:scoped_message_ptr',
       ],
       'export_dependent_settings': [
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:scoped_message_ptr',
       ],
     },
     {
diff --git a/aos/linux_code/camera/java/aos/CameraProcessor.java b/aos/linux_code/camera/java/aos/CameraProcessor.java
deleted file mode 100644
index 4f6c68d..0000000
--- a/aos/linux_code/camera/java/aos/CameraProcessor.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package aos;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SocketChannel;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.cpp.opencv_core;
-
-/**
- * Makes implementing code that processes frames from a camera easy.
- */
-public abstract class CameraProcessor {
-	private static final Logger LOG = Logger.getLogger(CameraProcessor.class.getName());
-	protected final ImageGetter getter;
-	protected final ServableImage start = new ServableImage(ImageGetter.width, ImageGetter.height, opencv_core.IPL_DEPTH_8U, 3);
-	
-	/**
-	 * Parses any arguments it recognizes out of {@code args} and initializes stuff appropriately.
-	 * This includes using {@link QueueLogHandler} for all exceptions and {@link Thread#setDefaultUncaughtExceptionHandler}ing.
-	 * @param args from {@code main}
-	 */
-	protected CameraProcessor(String[] args) throws UnknownHostException, IOException {
-		QueueLogHandler.UseForAll();
-		ReadableByteChannel channel = null;
-		for (int i = 0; i < args.length; ++i) {
-			final String c = args[i];
-			if (c.equals("--host")) {
-				String host = args[++i];
-				final SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(Inet4Address.getByName(host), 9714));
-				socketChannel.write(ByteBuffer.wrap(new byte[] {'\r', '\n', '\r', '\n'})); // get it past the read headers stage
-				channel = socketChannel;
-			} else {
-				System.err.println("CameraProcessor: warning: unrecognized argument '" + c + "'. ignoring");
-			}
-		}
-		
-		if (channel != null) {
-			getter = new ChannelImageGetter(channel);
-		} else {
-			System.out.println("creating QueueImageGetter");
-			getter = new QueueImageGetter();
-			System.out.println("done");
-		}
-
-		LOG.log(Level.INFO, "CameraProcessor is up");
-		System.err.println("CameraProcessor is up (on stderr)");
-	}
-	
-	protected abstract void RunIteration();
-	
-	protected void Run() {
-		while (true) {
-			if (!getter.get(start.getImage())) {
-				LOG.log(Level.WARNING, "getting image failed");
-				continue;
-			}
-			RunIteration();
-			start.releaseImage();
-		}
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/ChannelImageGetter.java b/aos/linux_code/camera/java/aos/ChannelImageGetter.java
deleted file mode 100644
index 511b55b..0000000
--- a/aos/linux_code/camera/java/aos/ChannelImageGetter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package aos;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Retrieves images from a {@link InputChannel}. Expects the images in mjpg form.
- * For now, only accepts streams formatted pretty closely to how aos::camera::HTTPStreamer does it.
- */
-public class ChannelImageGetter extends JPEGImageGetter {
-	/**
-	 * What to multiply each length by when it needs to allocate a larger buffer to fit an image.
-	 */
-	private static final double extraLength = 1.2;
-	
-	private static final Logger LOG = Logger.getLogger(ChannelImageGetter.class
-			.getName());
-	private final ReadableByteChannel channel;
-	private final Selector selector = Selector.open();
-	private String separator = "--boundarydonotcross\r\n";
-	private ByteBuffer current;
-	private final ByteBuffer headerBuffer = ByteBuffer.allocateDirect(30);
-	private final Map<String, String> headers = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-	
-	public ChannelImageGetter(ReadableByteChannel channel) throws IOException {
-		this.channel = channel;
-		if (channel instanceof SelectableChannel) {
-			((SelectableChannel)channel).configureBlocking(false);
-		}
-	}
-
-	@Override
-	public ByteBuffer getJPEG() {
-		try {
-			if (!parseHeaders()) {
-				return null;
-			}
-			LOG.log(Level.FINE, "parsed headers " + headers.toString());
-			try {
-				final int length = Integer.parseInt(headers.get("Content-Length"));
-				if (current == null || current.capacity() < length) {
-					LOG.log(Level.INFO, "allocating a new direct buffer of length " + length * extraLength);
-					current = ByteBuffer.allocateDirect((int) (length * extraLength));
-				} else {
-					current.rewind();
-					current.limit(length);
-				}
-			} catch (NumberFormatException e) {
-				LOG.log(Level.WARNING, "couldn't parse '" + headers.get("Content-Length") + "' as a number");
-				return null;
-			}
-			current.put(headerBuffer); // copy out any of the image that got buffered with the headers
-			while (current.hasRemaining()) {
-				channel.read(current);
-			}
-			current.flip();
-		} catch (IOException e) {
-			LOG.log(Level.WARNING, "reading the headers and/or image failed", e);
-			return null;
-		}
-		return current;
-	}
-	// returns success
-	private boolean parseHeaders() throws IOException {
-		// Reads chunks into headerBuffer and parses out headers.
-		// Looks for separator first.
-		
-		headerBuffer.clear();
-		headers.clear();
-		final byte[] separatorBytes = separator.getBytes();
-		int separatorIndex = 0; // how much of the separator has been matched
-		while (headerBuffer.hasRemaining() || headerBuffer.limit() < headerBuffer.capacity()) {
-			if (channel instanceof SelectableChannel) {
-				((SelectableChannel)channel).register(selector, SelectionKey.OP_READ);
-				selector.select();
-			}
-			headerBuffer.limit(headerBuffer.capacity());
-			channel.read(headerBuffer);
-			headerBuffer.flip();
-			if (separatorIndex < separatorBytes.length) {
-				// make sure we don't get part of the way through
-				while (headerBuffer.remaining() >= (separatorBytes.length - separatorIndex)) {
-					final byte c = headerBuffer.get();
-					if (separatorBytes[separatorIndex++] != c) {
-						separatorIndex = 0;
-					}
-					if (separatorIndex == separatorBytes.length) {
-						break;
-					}
-				}
-				headerBuffer.compact();
-			} else {
-				int keyEnd = 0, valueStart = 0;
-				boolean foundEndR = false; // found the end \r
-				while (headerBuffer.hasRemaining()) {
-					final byte c = headerBuffer.get();
-					if (foundEndR) {
-						if (c != '\n') {
-							LOG.log(Level.WARNING, "found \r\n\r but no \n afterwards");
-						} else {
-							return true;
-						}
-					} else if (keyEnd == 0) {
-						if (c == ':') {
-							keyEnd = headerBuffer.position() - 1;
-						} else if (c == '\r') {
-							foundEndR = true;
-						}
-					} else if (valueStart == 0) {
-						if (c != ' ') {
-							valueStart = headerBuffer.position() - 1;
-						}
-					} else {
-						if (c == '\r') {
-							final int valueEnd = headerBuffer.position();
-							final byte[] key = new byte[keyEnd];
-							headerBuffer.position(0);
-							headerBuffer.get(key);
-							final byte[] value = new byte[valueEnd - valueStart - 1];
-							headerBuffer.position(valueStart);
-							headerBuffer.get(value);
-							headers.put(new String(key), new String(value));
-							
-							headerBuffer.get(); // get the \r
-							headerBuffer.get(); // get the \n
-							
-							headerBuffer.compact();
-							headerBuffer.flip();
-							
-							keyEnd = valueStart = 0;
-						}
-					}
-				}
-			}
-		}
-		// if we got here, then it doesn't have space left and we haven't finished
-		LOG.log(Level.WARNING, "got a header that was too long. headerBuffer should be made bigger");
-		return false;
-	}
-
-	@Override
-	public double getTimestamp() {
-		if (headers.containsKey("X-Timestamp")) {
-			return Double.parseDouble(headers.get("X-Timestamp"));
-		} else {
-			throw new UnsupportedOperationException("source stream doesn't have X-Timestamp headers");
-		}
-	}
-
-}
diff --git a/aos/linux_code/camera/java/aos/DebugServer.java b/aos/linux_code/camera/java/aos/DebugServer.java
deleted file mode 100644
index 398cb11..0000000
--- a/aos/linux_code/camera/java/aos/DebugServer.java
+++ /dev/null
@@ -1,357 +0,0 @@
-package aos;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * A server that serves {@link ServableImage}s.
- */
-public class DebugServer {
-	private static final String initialHeaderString = "HTTP/1.0 200 OK\r\n"
-			+ "Connection: close\r\n"
-			+ "Server: AOS/0.0 Vision Code Debug\r\n"
-			+ "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n"
-			+ "Pragma: no-cache\r\n"
-			+ "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n"
-			+ "Content-Type: multipart/x-mixed-replace; boundary=boundarydonotcross\r\n";
-	private static final String intermediateHeaderFormat = "\r\n--boundarydonotcross\r\n"
-			+ "Content-Type: image/bmp\r\n"
-			+ "Content-Length: %d\r\n"
-			+ "X-Timestamp: %f\r\n"
-			+ "\r\n";
-	private static final ByteBuffer initialHeader;
-	private static final Pattern headerPattern;
-	static {
-		initialHeader = ByteBuffer.wrap(initialHeaderString.getBytes())
-				.asReadOnlyBuffer();
-		headerPattern = Pattern.compile("^GET ([^? ]+)(?:\\?i=(\\S*))? HTTP/.*\r\n.*$",
-				Pattern.DOTALL);
-	}
-
-	private static final Logger LOG = Logger.getLogger(DebugServer.class
-			.getName());
-	private final ServerSocketChannel server;
-	private final Collection<Client> clients = new ArrayList<Client>();
-	private final Map<String, ServableImage> images = new HashMap<String, ServableImage>();
-	private final Map<String, Palette> palettes = new HashMap<String, Palette>();
-	private double timestamp;
-	
-	public static class Palette {
-		private final ByteArrayOutputStream out = new ByteArrayOutputStream();
-		private int entries = 0;
-
-		/**
-		 * Adds a new color. All 4 arguments are unsigned bytes.
-		 * @param r red
-		 * @param g green
-		 * @param b blue
-		 * @param a alpha (doesn't seem to work)
-		 * @return this
-		 */
-		public Palette add(int r, int g, int b, int a) {
-			out.write(b);
-			out.write(g);
-			out.write(r);
-			out.write(a);
-			++entries;
-			return this;
-		}
-		
-		private int entries() {
-			return entries;
-		}
-		private int bytes() {
-			return entries * 4;
-		}
-		private void writeTo(ByteBuffer buffer) {
-			buffer.put(out.toByteArray());
-		}
-	}
-
-	private class Client {
-		private final int bmpHeaderSize;
-		private int bmpType;
-		private Palette palette;
-
-		private final SocketChannel channel;
-		private ServableImage image = null;
-		private IplImage img;
-		private int index;
-		private final ByteBuffer[] buffers;
-		private final ByteBuffer initial = initialHeader.duplicate();
-		private final ByteBuffer read = ByteBuffer.allocate(1024);
-
-		public Client(SocketChannel channel) throws IOException {
-			this.channel = channel;
-			channel.configureBlocking(false);
-
-			if (bmpType == 3) {
-				bmpHeaderSize = 122;
-			} else if (bmpType == 0) {
-				bmpHeaderSize = 54;
-			} else {
-				throw new AssertionError("unknown bmpType value " + bmpType);
-			}
-			// [0] gets filled in by createBmpHeader which writes the header into [1]
-			// [2] gets set to the image buffer
-			buffers = new ByteBuffer[] { null, ByteBuffer.allocate(2048), null };
-		}
-
-		public void register(Selector selector) throws ClosedChannelException {
-			channel.register(
-					selector,
-					(image == null) ? SelectionKey.OP_READ
-							: SelectionKey.OP_WRITE, this);
-		}
-
-		public void close() {
-			if (image != null) {
-				image.setDebugging(false);
-			}
-			if (channel != null) {
-				try {
-					channel.close();
-				} catch (IOException e) {
-					LOG.log(Level.WARNING,
-							"encountered error when closing channel", e);
-				}
-			}
-		}
-
-		private void createBmpHeader(ByteBuffer buffer) {
-			// <http://en.wikipedia.org/wiki/BMP_file_format#File_structure>
-			// explains what these all are
-			// signed/unsigned numbers don't matter because they'd better not be
-			// that big
-			final int paletteSize = (palette == null) ? 0 : palette.bytes();
-			buffers[0] = ByteBuffer.wrap(String.format(intermediateHeaderFormat,
-					bmpHeaderSize + paletteSize + image.imageSize(), timestamp).getBytes());
-			buffer.order(ByteOrder.LITTLE_ENDIAN);
-			buffer.put((byte) 'B').put((byte) 'M');
-			buffer.putInt(bmpHeaderSize + paletteSize + image.imageSize());
-			buffer.putInt(0); // skip ahead 4 bytes
-			buffer.putInt(bmpHeaderSize + paletteSize); // offset to start of image data
-			buffer.putInt(bmpHeaderSize - 14); // size of the rest of the header
-			// BMPs expect image data bottom to top, so -height to fix that
-			buffer.putInt(ImageGetter.width).putInt(-ImageGetter.height);
-			buffer.putShort((short) 1).putShort(image.bitsPerPixel());
-			buffer.putInt(bmpType);
-			buffer.putInt(image.imageSize()); // number of bytes in the actual
-												// image
-			buffer.putInt(2835).putInt(2835); // physical resolution; don't
-												// think it matters
-			buffer.putInt((palette == null) ? 0 : palette.entries());
-			buffer.putInt(0); // # of important colors (0 means all)
-			if (palette != null) {
-				palette.writeTo(buffer);
-			}
-			final int expected;
-			if (bmpType == 0) { // BI_RGB
-				expected = bmpHeaderSize + paletteSize;
-			} else if (bmpType == 3) { // BI_BITFIELDS
-				buffer.putInt(0x0000FF00).putInt(0x00FF0000).putInt(0xFF000000)
-						.putInt(0); // RGBA bitmasks
-				buffer.putInt(0x57696E20); // LCS_WINDOWS_COLOR_SPACE
-				expected = bmpHeaderSize - 48;
-			} else {
-				throw new AssertionError("unknown bmpType value " + bmpType);
-			}
-			if (buffer.position() != expected) {
-				throw new AssertionError(
-						"header ended up the wrong size. expected "
-								+ expected + " but got "
-								+ buffer.position());
-			}
-			buffer.limit(bmpHeaderSize + paletteSize);
-			buffer.rewind();
-		}
-
-		/**
-		 * Does anything that this one can right now.
-		 * 
-		 * @return whether or not to {@link #close()} and remove this one
-		 */
-		public boolean run() throws IOException {
-			if (image == null) {
-				final int bytesRead = channel.read(read);
-				final String readString = new String(read.array(), 0,
-						read.position());
-				LOG.log(Level.INFO, "read " + bytesRead
-						+ " header bytes position=" + read.position()
-						+ " string='" + readString + "'");
-
-				final Matcher matcher = headerPattern.matcher(readString);
-				if (matcher.matches()) {
-					final String url = matcher.group(1);
-					image = images.get(url);
-					if (image == null) {
-						LOG.log(Level.INFO, "couldn't find an image for url '"
-								+ url + "'. dropping client");
-						return true;
-					} else {
-						LOG.log(Level.INFO, "found an image for url '"
-								+ url + "'");
-					}
-					palette = palettes.get(url);
-					bmpType = 0; // could change this in the future
-					createBmpHeader(buffers[1]);
-					image.setDebugging(true);
-					final String indexString = matcher.group(2);
-					if (indexString != null) {
-						index = Integer.parseInt(indexString);
-					} else {
-						index = 0;
-					}
-					LOG.log(Level.INFO, "using index " + index);
-				} else if (!read.hasRemaining()) {
-					read.flip();
-					LOG.log(Level.WARNING,
-							"ran out of buffer space reading the header. currently have '"
-									+ readString + "'. dropping connection");
-					return true;
-				} else if (bytesRead == -1) {
-					read.flip();
-					LOG.log(Level.WARNING,
-							"reached end of stream for headers without getting anything valid. currently have "
-									+ read.limit()
-									+ " bytes ='"
-									+ readString
-									+ "'. dropping connection");
-					return true;
-				}
-			} else if (initial.hasRemaining()) {
-				channel.write(initial);
-			} else {
-				if (buffers[2] == null) {
-					img = image.getSnapshot(index);
-					if (img == null) {
-						return false;
-					} else {
-						buffers[2] = img.getByteBuffer();
-						LOG.log(Level.FINE, "got " + buffers[2]
-								+ " from the image");
-					}
-				}
-				channel.write(buffers);
-				boolean remaining = false;
-				for (ByteBuffer c : buffers) {
-					if (c.hasRemaining()) {
-						remaining = true;
-					}
-				}
-				if (!remaining) {
-					for (ByteBuffer c : buffers) {
-						c.rewind();
-					}
-					buffers[2] = null;
-					image.releaseSnapshot(index, img);
-				}
-			}
-			return false;
-		}
-	}
-
-	public DebugServer(int port) throws IOException {
-		server = ServerSocketChannel.open();
-		server.configureBlocking(false);
-		server.socket().bind(new InetSocketAddress(port), 10);
-		new Thread("DebugServer") {
-			@Override
-			public void run() {
-				try {
-					loop();
-				} catch (Throwable e) {
-					LOG.log(Level.SEVERE,
-							"trouble running the server loop", e);
-					System.exit(1);
-				}
-			}
-		}.start();
-	}
-
-	public void addImage(String name, ServableImage image) {
-		if (image.bitsPerPixel() != 24) {
-			throw new IllegalArgumentException("only 24-bit images are supported");
-			// could support 16 and 32 bpp images by using bmpType of 3
-		}
-		images.put(name, image);
-	}
-	public void addImage(String name, ServableImage image, Palette palette) {
-		if (image.bitsPerPixel() != 8) {
-			throw new IllegalArgumentException("only 8-bit images are supported");
-			// everything should work for 1, 2, and 4 bpp ones too except for padding etc
-		}
-		if (palette.entries() > (1 << image.bitsPerPixel())) {
-			throw new IllegalArgumentException("too many colors in the palette");
-		}
-		images.put(name, image);
-		palettes.put(name, palette);
-	}
-	/**
-	 * This timestamp is written out in the debugging images.
-	 * @param timestamp the current timestamp
-	 */
-	public void setTimestamp(double timestamp) {
-		this.timestamp = timestamp;
-	}
-
-	private void loop() throws IOException {
-		final Selector selector = Selector.open();
-		server.register(selector, SelectionKey.OP_ACCEPT);
-		while (true) {
-			try {
-				for (Client c : clients) {
-					c.register(selector);
-				}
-				selector.select();
-				for (final Iterator<SelectionKey> i = selector.selectedKeys()
-						.iterator(); i.hasNext();) {
-					final SelectionKey c = i.next();
-					if (c.isAcceptable()) {
-						// there's only 1 socket there that can accept
-						final SocketChannel channel = server.accept();
-						if (channel != null) {
-							clients.add(new Client(channel));
-						}
-					} else {
-						final Client client = (Client) c.attachment();
-						try {
-							if (client.run()) {
-								client.close();
-								clients.remove(client);
-							}
-						} catch (Exception e) {
-							LOG.log(Level.INFO, "dropping client " + client
-									+ " because it's run() threw an exception",
-									e);
-							client.close();
-							clients.remove(client);
-						}
-					}
-					i.remove();
-				}
-			} catch (IOException e) {
-				LOG.log(Level.WARNING, "trouble running the server loop", e);
-			}
-		}
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/ImageGetter.java b/aos/linux_code/camera/java/aos/ImageGetter.java
deleted file mode 100644
index f0f1063..0000000
--- a/aos/linux_code/camera/java/aos/ImageGetter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * An object that can retrieve images from somewhere.
- */
-public interface ImageGetter {
-	public static int width = 640, height = 480;
-	
-	/**
-	 * Gets an image.
-	 * @param out Where to write the image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image.
-	 * @return whether it succeeded or not
-	 */
-	public boolean get(IplImage out);
-	/**
-	 * Only valid after a successful {@link #get()}.
-	 * @return The timestamp from the most recent frame. Will be in seconds with at least ms accuracy.
-	 */
-	public double getTimestamp();
-}
-
diff --git a/aos/linux_code/camera/java/aos/JPEGDecoder.java b/aos/linux_code/camera/java/aos/JPEGDecoder.java
deleted file mode 100644
index 63d12fd..0000000
--- a/aos/linux_code/camera/java/aos/JPEGDecoder.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Efficiently decodes a JPEG image from a @{class ByteBuffer} into an @{class IplImage}.
- * Instances are not safe for use from multiple threads.
- * The first use of an instance allocates some largish buffers which are never freed. 
- */
-public class JPEGDecoder {
-	private final long[] state = new long[1];
-	
-	/**
-	 * @param in Must be direct. The {@link ByteBuffer#limit()} of it will be respected.
-	 * @param out Where to write the decoded image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image.
-	 * Will be written in the RGB color space.
-	 * @return Whether or not it succeeded. If not, {@code out} is undefined.
-	 */
-	public boolean decode(ByteBuffer in, IplImage out) {
-		if (out.nChannels() != 3 || out.depth() != opencv_core.IPL_DEPTH_8U) {
-			throw new IllegalArgumentException("out is invalid");
-		}
-		return Natives.decodeJPEG(state, in, in.limit(), out.getByteBuffer());
-	}
-}
-
diff --git a/aos/linux_code/camera/java/aos/JPEGImageGetter.java b/aos/linux_code/camera/java/aos/JPEGImageGetter.java
deleted file mode 100644
index 84296e7..0000000
--- a/aos/linux_code/camera/java/aos/JPEGImageGetter.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Helper class for {@link ImageGetter}s that return JPEG images.
- */
-public abstract class JPEGImageGetter implements ImageGetter {
-	
-	private final JPEGDecoder decoder = new JPEGDecoder();
-
-	@Override
-	public boolean get(IplImage out) {
-		final ByteBuffer jpeg = getJPEG();
-		if (jpeg == null) return false;
-		final boolean r = decoder.decode(jpeg, out);
-		release();
-		return r;
-	}
-	
-	protected abstract ByteBuffer getJPEG();
-	protected void release() {}
-
-}
diff --git a/aos/linux_code/camera/java/aos/JavaCVImageGetter.java b/aos/linux_code/camera/java/aos/JavaCVImageGetter.java
deleted file mode 100644
index 67398f0..0000000
--- a/aos/linux_code/camera/java/aos/JavaCVImageGetter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package aos;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.FrameGrabber;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-
-/**
- * Adapts between the JavaCV {@link FrameGrabber} and {@link ImageGetter}.
- * There is (at least) 1 extra copy involved, so this shouldn't be used if you care about speed.
- */
-public class JavaCVImageGetter implements ImageGetter {
-	private static final Logger LOG = Logger.getLogger(JavaCVImageGetter.class
-			.getName());
-	private final FrameGrabber grabber;
-	
-	public JavaCVImageGetter(FrameGrabber grabber) {
-		this.grabber = grabber;
-		if (grabber.getImageWidth() != width || grabber.getImageHeight() != height) {
-			if (grabber.getImageWidth() == 0 && grabber.getImageHeight() == 0) {
-				LOG.log(Level.WARNING, "grabber says it will give 0x0 images at the start. ignoring");
-			} else {
-				throw new IllegalArgumentException("grabber says it will give images that are the wrong size!!");
-			}
-		}
-	}
-
-	@Override
-	public boolean get(IplImage out) {
-		try {
-			final IplImage frame = grabber.grab();
-			if (grabber.getImageWidth() != width || grabber.getImageHeight() != height) {
-				LOG.log(Level.SEVERE, "grabber says it will give the wrong size images");
-				return false;
-			}
-			if (out.imageSize() != frame.imageSize()) {
-				LOG.log(Level.SEVERE, "the grabber gave a " + frame.imageSize() + "-byte image" +
-						"but a " + out.imageSize() + "-byte image was passed in");
-				return false;
-			}
-			out.getByteBuffer().put(frame.getByteBuffer());
-			return true;
-		} catch (FrameGrabber.Exception e) {
-			LOG.log(Level.WARNING, "grabber.grab() threw an exception", e);
-			return false;
-		}
-	}
-
-	@Override
-	public double getTimestamp() {
-		// grabber.getTimestamp seems to be in ms (all the implementations are at least)
-		return grabber.getTimestamp() / 1000.0;
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/NativeBufferError.java b/aos/linux_code/camera/java/aos/NativeBufferError.java
deleted file mode 100644
index 41c794d..0000000
--- a/aos/linux_code/camera/java/aos/NativeBufferError.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package aos;
-
-public class NativeBufferError extends NativeError {
-	private static final long serialVersionUID = -5298480149664213316L;
-
-	public NativeBufferError() {
-		super();
-	}
-
-	public NativeBufferError(String message, Throwable cause) {
-		super(message, cause);
-	}
-
-	public NativeBufferError(String message) {
-		super(message);
-	}
-
-	public NativeBufferError(Throwable cause) {
-		super(cause);
-	}
-
-}
diff --git a/aos/linux_code/camera/java/aos/NativeError.java b/aos/linux_code/camera/java/aos/NativeError.java
deleted file mode 100644
index d410234..0000000
--- a/aos/linux_code/camera/java/aos/NativeError.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package aos;
-
-/**
- * Represents an error from native code.
- */
-public class NativeError extends Error {
-	private static final long serialVersionUID = 7394872852984037261L;
-
-	public NativeError() {
-		super();
-	}
-
-	public NativeError(String message, Throwable cause) {
-		super(message, cause);
-	}
-
-	public NativeError(String message) {
-		super(message);
-	}
-
-	public NativeError(Throwable cause) {
-		super(cause);
-	}
-
-}
diff --git a/aos/linux_code/camera/java/aos/NativeLoader.java b/aos/linux_code/camera/java/aos/NativeLoader.java
deleted file mode 100644
index d4fe7a8..0000000
--- a/aos/linux_code/camera/java/aos/NativeLoader.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package aos;
-
-/**
- * Provides support for dealing with loading native code.
- */
-public class NativeLoader {
-	/**
-	 * Loads a native library.
-	 * @param name the name of the gyp shared_library or loadable_module target to load
-	 */
-	public static void load(String name) {
-		System.load(System.getProperty("one-jar.expand.dir") + "/so_libs/lib" + name + ".so");
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/Natives.java b/aos/linux_code/camera/java/aos/Natives.java
deleted file mode 100644
index 8ea4e01..0000000
--- a/aos/linux_code/camera/java/aos/Natives.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-
-import java.nio.ByteBuffer;
-import java.util.logging.Level;
-
-/**
- * <p>Package-private class that has all of the native functions in it to make them easier to implement.</p>
- * <p>WARNING: The raw native functions are <b>NOT</b> thread-safe!!!!! Any java functions that need to be thread safe MUST be synchronized in JAVA!</p>
- */
-class Natives {
-	static {
-		NativeLoader.load("aos_camera");
-		nativeInit(ImageGetter.width, ImageGetter.height);
-	}
-	private static native void nativeInit(int width, int height);
-	/**
-	 * Empty function to make sure the class gets loaded (which means loading the native library).
-	 */
-	public static void ensureLoaded() {}
-	
-	/**
-	 * Decodes a JPEG from in into out. Both buffers must be direct.
-	 * @param state a long[1] for storing thread-local state
-	 * @param in the JPEG to decode
-	 * @param inBytes how many bytes long the JPEG is
-	 * @param out the buffer to write the decoded image into
-	 * @return Whether or not it succeeded. If not, {@code out} is undefined.
-	 */
-	public static native boolean decodeJPEG(long[] state, ByteBuffer in, int inBytes, ByteBuffer out);
-
-	/**
-	 * Thresholds in into out. Both buffers must be direct.
-	 * All of the short arguments should be unsigned bytes. The min and max parameters specify what colors to accept.
-	 * @param in The image to threshold. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer in the HSV color space.
-	 * @param out Where to write the thresholded image to. Must be a 1-channel {@link opencv_core#IPL_DEPTH_8U} image buffer.
-	 * @param hoffset An offset to be added to the hue value before comparing it to {@code hmin} and {@code hmax}.
-	 * The addition will be performed to a {@code uint8_t}, which will wrap around. This means that it must be positive.
-	 * Useful for finding red values.
-	 */
-	public static native void threshold(ByteBuffer in, ByteBuffer out, short hoffset, char hmin, char hmax,
-			char smin, char smax, char vmin, char vmax);
-	
-	/**
-	 * Converts the colors from in to the format required for dumping them into a BMP image.
-	 * @param in The image to convert. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer in the regular (BGR I think...) color space.
-	 * @param out Where to write the converted image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer.
-	 */
-	public static native void convertBGR2BMP(ByteBuffer in, ByteBuffer out);
-
-	/**
-	 * Retrieves a JPEG image from the queue system. Will block until a new one is ready.
-	 * @param id from {@link #queueInit()}
-	 * @return Will be direct. This buffer <b>must not EVER</b> be written to.
-	 */
-	public static native ByteBuffer queueGetJPEG(long id);
-	/**
-	 * Retrieves the latest frame timestamp from the queue system. Must only be called between {@link #queueGetJPEG} and {@link #queueReleaseJPEG}.
-	 * @param id from {@link #queueInit()}
-	 * @return a timestamp
-	 */
-	public static native double queueGetTimestamp(long id);
-	/**
-	 * Releases the last image retrieved from the queue system. The result of the last {@link #queueGetJPEG()} will now be invalid.
-	 * @param id from {@link #queueInit()}
-	 */
-	public static native void queueReleaseJPEG(long id);
-	/**
-	 * Prepares to start retrieving JPEGs from the queues.
-	 * @return the ID to pass to the other queue functions
-	 */
-	public static native long queueInit();
-	
-	/**
-	 * Puts the given message into the logging framework.
-	 * @param message the complete log message
-	 * @param level the level (from {@link Level#intValue()}
-	 */
-	public static native void LOG(String message, int level);
-}
-
diff --git a/aos/linux_code/camera/java/aos/QueueImageGetter.java b/aos/linux_code/camera/java/aos/QueueImageGetter.java
deleted file mode 100644
index 7ed2f65..0000000
--- a/aos/linux_code/camera/java/aos/QueueImageGetter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-/**
- * Retrieves images from the queue system.<br>
- * {@link #getTimestamp()} returns the value from the v4l2 driver,
- * which is a CLOCK_MONOTONIC time for most (all?) of them and definitely uvcvideo.
- */
-public class QueueImageGetter extends JPEGImageGetter {
-	private final long nativeID;
-	public QueueImageGetter() {
-		nativeID = Natives.queueInit();
-	}
-	
-	@Override
-	public ByteBuffer getJPEG() {
-		final ByteBuffer buf = Natives.queueGetJPEG(nativeID);
-		if (buf == null) {
-			return null;
-		}
-		return buf.asReadOnlyBuffer();
-	}
-
-	@Override
-	public void release() {
-		Natives.queueReleaseJPEG(nativeID);
-	}
-
-	@Override
-	public double getTimestamp() {
-		return Natives.queueGetTimestamp(nativeID);
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/QueueLogHandler.java b/aos/linux_code/camera/java/aos/QueueLogHandler.java
deleted file mode 100644
index 3eb8938..0000000
--- a/aos/linux_code/camera/java/aos/QueueLogHandler.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package aos;
-
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-import java.util.logging.SimpleFormatter;
-
-/**
- * <p>Sends messages to the AOS queue-based logging system. Also sends anything that's at least a {@link Level#WARNING} to {@link System#err}.</p>
- * <p>Writes out each stack frame of exceptions as a separate line after the first one with a \t at the beginning.</p>
- */
-public class QueueLogHandler extends Handler {
-	private Formatter defaultFormatter;
-	
-	/**
-	 * Sets up the logging framework to use an instance of this class for all logging and returns the newly created instance.
-	 */
-	public static QueueLogHandler UseForAll() {
-		Natives.ensureLoaded();
-		final Logger top = Logger.getLogger("");
-		for (Handler c : top.getHandlers()) {
-			top.removeHandler(c);
-		}
-		QueueLogHandler instance = new QueueLogHandler();
-		top.addHandler(instance);
-		top.setLevel(Level.ALL);
-		
-		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
-			@Override
-			public void uncaughtException(Thread thread, Throwable e) {
-				top.log(Level.SEVERE, "uncaught exception in thread " + thread, e);
-				System.exit(1);
-			}
-		});
-		
-		return instance;
-	}
-
-	@Override
-	public void close() throws SecurityException {
-	}
-	@Override
-	public void flush() {
-	}
-
-	private Formatter findFormatter() {
-		final Formatter r = getFormatter();
-		if (r != null) {
-			return r;
-		}
-		if (defaultFormatter != null) {
-			return defaultFormatter;
-		}
-		return defaultFormatter = new SimpleFormatter();
-	}
-	@Override
-	public void publish(LogRecord record) {
-		/*final StringBuilder thrownString = new StringBuilder(0);
-		if (record.getThrown() != null) {
-			thrownString.append(": ");
-			thrownString.append(record.getThrown().toString());
-			for (StackTraceElement c : record.getThrown().getStackTrace()) {
-				thrownString.append(" > ");
-				thrownString.append(c.getClassName());
-				thrownString.append('.');
-				thrownString.append(c.getMethodName());
-				thrownString.append('(');
-				thrownString.append(c.getFileName());
-				thrownString.append(':');
-				thrownString.append(c.getLineNumber());
-				thrownString.append(')');
-			}
-		}
-		Natives.LOG(record.getSourceClassName() + ": " + record.getSourceMethodName() + ": " +
-				findFormatter().formatMessage(record) + thrownString, record.getLevel().intValue());*/
-		if (record.getThrown() instanceof UnsatisfiedLinkError || record.getThrown().getCause() instanceof UnsatisfiedLinkError) {
-			record.setThrown(new UnsatisfiedLinkError("are you running a JVM of the correct bitness?").initCause(record.getThrown()));
-		}
-		Natives.LOG(record.getSourceClassName() + ": " + record.getSourceMethodName() + ": " +
-				findFormatter().formatMessage(record), record.getLevel().intValue());
-		if (record.getThrown() != null) {
-			logException(record.getThrown(), record.getLevel().intValue(), false);
-		}
-		
-		if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
-			System.err.println(findFormatter().format(record));
-		}
-	}
-	private void logException(Throwable e, int level, boolean caused_by) {
-		final StringBuilder thrownString = new StringBuilder();
-		if (caused_by) {
-			thrownString.append("Caused by: ");
-		}
-		thrownString.append(e.getClass().getName());
-		thrownString.append(": ");
-		thrownString.append(e.getLocalizedMessage());
-		Natives.LOG(thrownString.toString(), level);
-		for (StackTraceElement c : e.getStackTrace()) {
-			thrownString.setLength(0);
-			thrownString.append("\t");
-			thrownString.append(c.getClassName());
-			thrownString.append('.');
-			thrownString.append(c.getMethodName());
-			thrownString.append('(');
-			thrownString.append(c.getFileName());
-			thrownString.append(':');
-			thrownString.append(c.getLineNumber());
-			thrownString.append(')');
-			Natives.LOG(thrownString.toString(), level);
-		}
-		if (e.getCause() != null) {
-			logException(e.getCause(), level, true);
-		}
-	}
-
-}
diff --git a/aos/linux_code/camera/java/aos/ServableImage.java b/aos/linux_code/camera/java/aos/ServableImage.java
deleted file mode 100644
index c5db252..0000000
--- a/aos/linux_code/camera/java/aos/ServableImage.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package aos;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Queue;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Provides {@link IplImage}s that can be used with a {@link DebugServer}.
- */
-public class ServableImage {
-	@SuppressWarnings("unused")
-	private static final Logger LOG = Logger.getLogger(ServableImage.class
-			.getName());
-	private final int width, height, depth, channels;
-	private final ArrayList<Queue<IplImage>> queues = new ArrayList<Queue<IplImage>>();
-	private final ArrayList<IplImage> snapshots = new ArrayList<IplImage>();
-	private final IplImage current;
-	private int debugging = 0;
-
-	public ServableImage(int width, int height, int depth, int channels) {
-		this.width = width;
-		this.height = height;
-		this.depth = depth;
-		this.channels = channels;
-
-		current = IplImage.create(width, height, depth, channels);
-	}
-
-	/**
-	 * @return the number of bytes in each image
-	 */
-	public int imageSize() {
-		return width * height * depth * channels / 8;
-	}
-
-	/**
-	 * @return the number of bits in each pixel
-	 */
-	public short bitsPerPixel() {
-		return (short) (depth * channels);
-	}
-
-	/**
-	 * Retrieves an image that should be used for debugging. It clears the value
-	 * when called. {@link #releastSnapshot} MUST be called with the result.
-	 * 
-	 * @param i
-	 *            Which snapshot to retrieve. 0 means the most recent final
-	 *            image.
-	 * @return The most recent image at this index. {@code null} if there isn't
-	 *         a new one since last time this function was called. Will be in
-	 *         the correct color space to dump into a BMP image if this is a
-	 *         3-channel image.
-	 */
-	public synchronized IplImage getSnapshot(int i) {
-		if (snapshots.size() > i) {
-			return snapshots.get(i);
-		} else {
-			return null;
-		}
-	}
-
-	public synchronized void releaseSnapshot(int i, IplImage image) {
-		queues.get(i).add(image);
-	}
-
-	/**
-	 * This function will return the same image if called repeatedly until
-	 * {@link #releaseImage} is called.
-	 * 
-	 * @return the current image
-	 */
-	public synchronized IplImage getImage() {
-		return current;
-	}
-
-	/**
-	 * Releases the current image (to be potentially sent out for debugging and
-	 * then reused).
-	 */
-	public synchronized void releaseImage() {
-		recordSnapshot(0);
-	}
-
-	/**
-	 * Records a copy of the current image for debugging. It will be accessible
-	 * at <{@code <base path>?i=<the value>}>. Does nothing unless
-	 * {@link #isDebugging()}. This method <i>should</i> get called regardless
-	 * of {@link #isDebugging()} to avoid outputting old debugging images. Note:
-	 * 0 is not a valid snapshot number.
-	 * 
-	 * @param i
-	 *            which snapshot this is
-	 */
-	public synchronized void recordSnapshot(int i) {
-		while (queues.size() <= i) {
-			queues.add(null);
-		}
-		if (queues.get(i) == null) {
-			queues.set(i, new ArrayDeque<IplImage>());
-		}
-		while (snapshots.size() <= i) {
-			snapshots.add(null);
-		}
-		if (snapshots.get(i) != null) {
-			releaseSnapshot(i, snapshots.get(i));
-		}
-		if (isDebugging()) {
-			IplImage snapshot = queues.get(i).poll();
-			if (snapshot == null) {
-				snapshot = IplImage.create(width, height, depth, channels);
-			}
-			if (channels == 3) {
-				Natives.convertBGR2BMP(current.getByteBuffer(),
-						snapshot.getByteBuffer());
-			} else {
-				snapshot.getByteBuffer().put(current.getByteBuffer());
-			}
-			snapshots.set(i, snapshot);
-		} else {
-			snapshots.set(i, null);
-		}
-	}
-
-	/**
-	 * @return whether or not to do extra debug work with the current image
-	 */
-	public synchronized boolean isDebugging() {
-		return debugging > 0;
-	}
-
-	/**
-	 * Whoever turns this on should turn it off when they're done.
-	 */
-	synchronized void setDebugging(boolean debugging) {
-		if (debugging) {
-			++this.debugging;
-		} else {
-			--this.debugging;
-		}
-	}
-}
diff --git a/aos/linux_code/camera/java/aos/Thresholder.java b/aos/linux_code/camera/java/aos/Thresholder.java
deleted file mode 100644
index adf1b49..0000000
--- a/aos/linux_code/camera/java/aos/Thresholder.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-public class Thresholder {
-	/**
-	 * Thresholds in into out.
-	 * All of the int arguments should be unsigned bytes except hoffset, which should be a signed byte.
-	 * The min and max parameters specify what colors to accept.
-	 * @param in The image to threshold. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image in the HSV color space.
-	 * @param out Where to write the thresholded image to. Must be a 1-channel {@link opencv_core#IPL_DEPTH_8U} image.
-	 * @param hoffset An offset to be added to the hue value before comparing it to {@code hmin} and {@code hmax}.
-	 * The addition will be performed to a {@code uint8_t}, which will wrap around. This means that it must be positive.
-	 * Useful for finding red values.
-	 */
-	public static void threshold(IplImage in, IplImage out, int hoffset, int hmin, int hmax,
-			int smin, int smax, int vmin, int vmax) {
-		if (in.nChannels() != 3 || in.depth() != opencv_core.IPL_DEPTH_8U) {
-			throw new IllegalArgumentException("in is invalid");
-		}
-		if (out.nChannels() != 1 || out.depth() != opencv_core.IPL_DEPTH_8U) {
-			throw new IllegalArgumentException("out is invalid");
-		}
-		Natives.threshold(in.getByteBuffer(), out.getByteBuffer(), (short)hoffset,
-				(char)hmin, (char)hmax, (char)smin, (char)smax, (char)vmin, (char)vmax);
-	}
-}
diff --git a/aos/linux_code/camera/jni.cpp b/aos/linux_code/camera/jni.cpp
deleted file mode 100644
index c547a9c..0000000
--- a/aos/linux_code/camera/jni.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-#include <setjmp.h>
-
-#include "jni/aos_Natives.h"
-#include "aos/linux_code/camera/Buffers.h"
-#include "aos/externals/libjpeg/include/jpeglib.h"
-#include "aos/common/logging/logging_impl.h"
-#include "aos/linux_code/init.h"
-
-using aos::camera::Buffers;
-
-namespace {
-
-jclass nativeError, bufferError, outOfMemoryError;
-bool findClass(JNIEnv *env, const char *name, jclass *out) {
-  jclass local = env->FindClass(name);
-  if (out == NULL) return true;
-  *out = static_cast<jclass>(env->NewGlobalRef(local));
-  if (out == NULL) return true;
-  env->DeleteLocalRef(local);
-  return false;
-}
-
-// Checks that the size is correct and retrieves the address.
-// An expected_size of 0 means don't check it.
-// If this function returns NULL, a java exception will already have been
-// thrown.
-void *getBufferAddress(JNIEnv *env, jobject obj, jlong expected_size) {
-  if (obj == NULL) {
-    env->ThrowNew(nativeError, "null buffer");
-    return NULL;
-  }
-  if (expected_size != 0 &&
-      expected_size != env->GetDirectBufferCapacity(obj)) {
-    char *str;
-    if (asprintf(&str, "wrong size. expected %lld but got %lld",
-                 expected_size, env->GetDirectBufferCapacity(obj)) < 0) {
-      env->ThrowNew(bufferError, "creating message failed");
-      return NULL;
-    }
-    env->ThrowNew(bufferError, str);
-    free(str);
-    return NULL;
-  }
-  void *const r = env->GetDirectBufferAddress(obj);
-  if (r == NULL) {
-    env->ThrowNew(bufferError, "couldn't get address");
-  }
-  return r;
-}
-
-const int kImagePixels = Buffers::kWidth * Buffers::kHeight;
-
-void jpeg_log_message(jpeg_common_struct *cinfo, log_level level) {
-  char buf[LOG_MESSAGE_LEN];
-  cinfo->err->format_message(cinfo, buf);
-  log_do(level, "libjpeg: %s\n", buf);
-}
-void jpeg_error_exit(jpeg_common_struct *cinfo) __attribute__((noreturn));
-void jpeg_error_exit(jpeg_common_struct *cinfo) {
-  jpeg_log_message(cinfo, ERROR);
-  longjmp(*static_cast<jmp_buf *>(cinfo->client_data), 1);
-}
-void jpeg_emit_message(jpeg_common_struct *cinfo, int msg_level) {
-  if (msg_level < 0) {
-    jpeg_log_message(cinfo, WARNING);
-    longjmp(*static_cast<jmp_buf *>(cinfo->client_data), 2);
-  }
-  // this spews a lot of messages out
-  //jpeg_log_message(cinfo, DEBUG);
-}
-
-// The structure used to hold all of the state for the functions that deal with
-// a Buffers. A pointer to this structure is stored java-side.
-struct BuffersHolder {
-  Buffers buffers;
-  timeval timestamp;
-  BuffersHolder() : buffers() {}
-};
-
-} // namespace
-
-void Java_aos_Natives_nativeInit(JNIEnv *env, jclass, jint width, jint height) {
-  if (findClass(env, "aos/NativeError", &nativeError)) return;
-  if (findClass(env, "aos/NativeBufferError", &bufferError)) return;
-  if (findClass(env, "java/lang/OutOfMemoryError", &outOfMemoryError)) return;
-
-  aos::InitNRT();
-
-  if (width != Buffers::kWidth || height != Buffers::kHeight) {
-    env->ThrowNew(nativeError, "dimensions mismatch");
-    return;
-  }
-
-  LOG(INFO, "nativeInit finished\n");
-}
-
-static_assert(sizeof(jlong) >= sizeof(void *),
-              "can't stick pointers into jlongs");
-
-jboolean Java_aos_Natives_decodeJPEG(JNIEnv *env, jclass, jlongArray stateArray,
-                                     jobject inobj, jint inLength,
-                                     jobject outobj) {
-  unsigned char *const in = static_cast<unsigned char *>(
-      getBufferAddress(env, inobj, 0));
-  if (in == NULL) return false;
-  if (env->GetDirectBufferCapacity(inobj) < inLength) {
-    env->ThrowNew(bufferError, "in is too small");
-    return false;
-  }
-  unsigned char *const out = static_cast<unsigned char *>(
-      getBufferAddress(env, outobj, kImagePixels * 3));
-  if (out == NULL) return false;
-
-  jpeg_decompress_struct *volatile cinfo; // volatile because of the setjmp call
-
-  jlong state;
-  env->GetLongArrayRegion(stateArray, 0, 1, &state);
-  if (env->ExceptionCheck()) return false;
-  if (state == 0) {
-    cinfo = static_cast<jpeg_decompress_struct *>(malloc(sizeof(*cinfo)));
-    if (cinfo == NULL) {
-      env->ThrowNew(outOfMemoryError, "malloc for jpeg_decompress_struct");
-      return false;
-    }
-    cinfo->err = jpeg_std_error(static_cast<jpeg_error_mgr *>(
-            malloc(sizeof(*cinfo->err))));
-    cinfo->client_data = malloc(sizeof(jmp_buf));
-    cinfo->err->error_exit = jpeg_error_exit;
-    cinfo->err->emit_message = jpeg_emit_message;
-    // if the error handler sees a failure, it needs to clean up
-    // (jpeg_abort_decompress) and then return the failure
-    // set cinfo->client_data to the jmp_buf
-    jpeg_create_decompress(cinfo);
-    state = reinterpret_cast<intptr_t>(cinfo);
-    env->SetLongArrayRegion(stateArray, 0, 1, &state);
-    if (env->ExceptionCheck()) return false;
-  } else {
-    cinfo = reinterpret_cast<jpeg_decompress_struct *>(state);
-  }
-
-  // set up the jump buffer
-  // this has to happen each time
-  if (setjmp(*static_cast<jmp_buf *>(cinfo->client_data))) {
-    jpeg_abort_decompress(cinfo);
-    return false;
-  }
-
-  jpeg_mem_src(cinfo, in, inLength);
-  jpeg_read_header(cinfo, TRUE);
-  if (cinfo->image_width != static_cast<unsigned int>(Buffers::kWidth) ||
-      cinfo->image_height != static_cast<unsigned int>(Buffers::kHeight)) {
-    LOG(WARNING, "got (%ux%u) image but expected (%dx%d)\n", cinfo->image_width,
-        cinfo->image_height, Buffers::kWidth, Buffers::kHeight);
-    jpeg_abort_decompress(cinfo);
-    return false;
-  }
-  cinfo->out_color_space = JCS_RGB;
-  jpeg_start_decompress(cinfo);
-  if (cinfo->output_components != 3) {
-    LOG(WARNING, "libjpeg wants to return %d color components instead of 3\n",
-        cinfo->out_color_components);
-    jpeg_abort_decompress(cinfo);
-    return false;
-  }
-  if (cinfo->output_width != static_cast<unsigned int>(Buffers::kWidth) ||
-      cinfo->output_height != static_cast<unsigned int>(Buffers::kHeight)) {
-    LOG(WARNING, "libjpeg wants to return a (%ux%u) image but need (%dx%d)\n",
-        cinfo->output_width, cinfo->output_height,
-        Buffers::kWidth, Buffers::kHeight);
-    jpeg_abort_decompress(cinfo);
-    return false;
-  }
-
-  unsigned char *buffers[Buffers::kHeight];
-  for (int i = 0; i < Buffers::kHeight; ++i) {
-    buffers[i] = &out[i * Buffers::kWidth * 3];
-  }
-  while (cinfo->output_scanline < cinfo->output_height) {
-    jpeg_read_scanlines(cinfo, &buffers[cinfo->output_scanline],
-                        Buffers::kHeight - cinfo->output_scanline);
-  }
-
-  jpeg_finish_decompress(cinfo);
-  return true;
-}
-
-void Java_aos_Natives_threshold(JNIEnv *env, jclass, jobject inobj,
-                                jobject outobj, jshort hoffset, jchar hmin,
-                                jchar hmax, jchar smin, jchar smax, jchar vmin,
-                                jchar vmax) {
-  const unsigned char *__restrict__ const in = static_cast<unsigned char *>(
-      getBufferAddress(env, inobj, kImagePixels * 3));
-  if (in == NULL) return;
-  char *__restrict__ const out = static_cast<char *>(
-      getBufferAddress(env, outobj, kImagePixels));
-  if (out == NULL) return;
-
-  for (int i = 0; i < kImagePixels; ++i) {
-    const uint8_t h = in[i * 3] + static_cast<uint8_t>(hoffset);
-    out[i] = h > hmin && h < hmax &&
-        in[i * 3 + 1] > smin && in[i * 3 + 1] < smax &&
-        in[i * 3 + 2] > vmin && in[i * 3 + 2] < vmax;
-  }
-}
-void Java_aos_Natives_convertBGR2BMP(JNIEnv *env, jclass,
-                                     jobject inobj, jobject outobj) {
-  const char *__restrict__ const in = static_cast<char *>(
-      getBufferAddress(env, inobj, kImagePixels * 3));
-  if (in == NULL) return;
-  char *__restrict__ const out = static_cast<char *>(
-      getBufferAddress(env, outobj, kImagePixels * 3));
-  if (out == NULL) return;
-
-  for (int i = 0; i < kImagePixels; ++i) {
-    out[i * 3 + 0] = in[i * 3 + 2];
-    out[i * 3 + 1] = in[i * 3 + 1];
-    out[i * 3 + 2] = in[i * 3 + 0];
-  }
-}
-
-jlong Java_aos_Natives_queueInit(JNIEnv *, jclass) {
-  return reinterpret_cast<intptr_t>(new BuffersHolder());
-}
-void Java_aos_Natives_queueReleaseJPEG(JNIEnv *, jclass, jlong ptr) {
-  reinterpret_cast<BuffersHolder *>(ptr)->buffers.Release();
-}
-jobject Java_aos_Natives_queueGetJPEG(JNIEnv *env, jclass, jlong ptr) {
-  uint32_t size;
-  BuffersHolder *const holder = reinterpret_cast<BuffersHolder *>(ptr);
-  const void *const r = holder->buffers.GetNext(true, &size,
-                                                &holder->timestamp, NULL);
-  if (r == NULL) return NULL;
-  return env->NewDirectByteBuffer(const_cast<void *>(r), size);
-}
-jdouble Java_aos_Natives_queueGetTimestamp(JNIEnv *, jclass, jlong ptr) {
-  const BuffersHolder *const holder = reinterpret_cast<BuffersHolder *>(ptr);
-  return holder->timestamp.tv_sec + holder->timestamp.tv_usec / 1000000.0;
-}
-
-void Java_aos_Natives_LOG(JNIEnv *env, jclass, jstring message, jint jlevel) {
-  log_level level;
-  if (jlevel >= 1000) {
-    // Don't want to use FATAL because the uncaught java exception that is
-    // likely to come next will be useful.
-    level = ERROR;
-  } else if (jlevel >= 900) {
-    level = WARNING;
-  } else if (jlevel >= 800) {
-    level = INFO;
-  } else {
-    level = DEBUG;
-  }
-  // Can't use Get/ReleaseStringCritical because log_do might block waiting to
-  // put its message into the queue.
-  const char *const message_chars = env->GetStringUTFChars(message, NULL);
-  if (message_chars == NULL) return;
-  log_do(level, "%s\n", message_chars);
-  env->ReleaseStringUTFChars(message, message_chars);
-}
diff --git a/aos/linux_code/configuration.cc b/aos/linux_code/configuration.cc
index ef6ae3e..e5e5583 100644
--- a/aos/linux_code/configuration.cc
+++ b/aos/linux_code/configuration.cc
@@ -26,13 +26,16 @@
 const in_addr *DoGetOwnIPAddress() {
     static const char *kOverrideVariable = "FRC971_IP_OVERRIDE";
     const char *override_ip = getenv(kOverrideVariable);
-    if (override_ip != nullptr) {
+    if (override_ip != NULL) {
+        LOG(INFO, "Override IP is %s\n", override_ip);
         static in_addr r;
         if (inet_aton(override_ip, &r) != 0) {
             return &r;
         } else {
             LOG(WARNING, "error parsing %s value '%s'\n", kOverrideVariable, override_ip);
         }
+    } else {
+        LOG(INFO, "Couldn't get environmental variable.\n");
     }
 
   ifaddrs *addrs;
diff --git a/aos/linux_code/ipc_lib/aos_sync.c b/aos/linux_code/ipc_lib/aos_sync.c
index 52ebed1..a607001 100644
--- a/aos/linux_code/ipc_lib/aos_sync.c
+++ b/aos/linux_code/ipc_lib/aos_sync.c
@@ -18,13 +18,19 @@
   return result;
 }
 
-// this code is based on something that appears to be based on <http://www.akkadia.org/drepper/futex.pdf>, which also has a lot of useful information
-// should probably use <http://lxr.linux.no/linux+v2.6.34/Documentation/robust-futexes.txt> once it becomes available
+// this code is based on something that appears to be based on
+//   <http://www.akkadia.org/drepper/futex.pdf>, which also has a lot of useful
+//   information
+// should probably use
+// <http://lxr.linux.no/linux+v2.6.34/Documentation/robust-futexes.txt> once it
+// becomes available
 //   (sys_set_robust_list appears to be the function name)
 // <http://locklessinc.com/articles/futex_cheat_sheet/> and
 //   <http://locklessinc.com/articles/mutex_cv_futex/> are useful
-// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009 (fairly recent compared to everything else...)
-// can't use PRIVATE futex operations because they use the pid (or something) as part of the hash
+// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
+//   (fairly recent compared to everything else...)
+// can't use PRIVATE futex operations because they use the pid (or something) as
+//   part of the hash
 //
 // Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
 // FUTEX_WAIT, the docs call it EWOULDBLOCK...)
@@ -60,7 +66,6 @@
   if (c == 1) c = xchg(m, 2);
   while (c) {
     /* Wait in the kernel */
-    //printf("sync here %d\n", __LINE__);
     if (sys_futex(m, FUTEX_WAIT, 2, timeout, NULL, 0) == -1) {
       if (signals_fail && errno == EINTR) {
         return 1;
@@ -69,7 +74,6 @@
         return 2;
       }
     }
-    //printf("sync here %d\n", __LINE__);
     c = xchg(m, 2);
   }
   return 0;
@@ -148,6 +152,8 @@
   mutex_unlock(m);
 
   while (1) {
+    // Wait in the kernel iff the value of it doesn't change (ie somebody else
+    // does a wake) from before we unlocked the mutex.
     if (sys_futex(c, FUTEX_WAIT, wait_start, NULL, NULL, 0) == -1) {
       // If it failed for some reason other than somebody else doing a wake
       // before we actually made it to sleep.
@@ -161,8 +167,12 @@
         abort();
       }
     }
+    // Relock the mutex now that we're done waiting.
     // Simplified mutex_lock that always leaves it
     // contended in case anybody else got requeued.
+    // If we got requeued above, this will just succeed the first time because
+    // the person waking us from the above wait (changed to be on the mutex
+    // instead of the condition) will have just set it to 0.
     while (xchg(m, 2) != 0) {
       if (sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0) == -1) {
         // Try again if it was because of a signal or somebody else unlocked it
@@ -180,7 +190,11 @@
 }
 
 void condition_signal(mutex *c) {
+  // This will cause anybody else who is in between unlocking the mutex and
+  // going to sleep in the kernel to not go to sleep and return immediately
+  // instead.
   __sync_fetch_and_add(c, 1);
+  // Wake at most 1 person who is waiting in the kernel.
   if (sys_futex(c, FUTEX_WAKE, 1, NULL, NULL, 0) == -1) {
     fprintf(stderr, "sync: FUTEX_WAKE(%p, 1, NULL, NULL, 0)"
         " failed with %d: %s\n",
@@ -192,7 +206,9 @@
 
 void condition_broadcast(mutex *c, mutex *m) {
   __sync_fetch_and_add(c, 1);
-  // Wake 1 waiter and requeue the rest.
+  // Wake at most 1 waiter and requeue the rest.
+  // Everybody else is going to have to wait for the 1st person to take the
+  // mutex anyways.
   if (sys_futex_requeue(c, FUTEX_REQUEUE, 1, INT_MAX, m) == -1) {
     fprintf(stderr, "sync: FUTEX_REQUEUE(%p, 1, INT_MAX, %p, 0)"
         " failed with %d: %s\n",
diff --git a/aos/linux_code/ipc_lib/aos_sync.h b/aos/linux_code/ipc_lib/aos_sync.h
index 7a81ca3..0d55246 100644
--- a/aos/linux_code/ipc_lib/aos_sync.h
+++ b/aos/linux_code/ipc_lib/aos_sync.h
@@ -48,6 +48,8 @@
 // They are designed for signalling when something happens (possibly to
 // multiple listeners). A mutex manipulated with them can only be set or unset.
 //
+// Another name for this kind of synchronization mechanism is a "notification".
+//
 // They are different from the condition_ functions in that they do NOT work
 // correctly as standard condition variables. While it is possible to keep
 // track of the "condition" using the value part of the futex_* functions, the
diff --git a/aos/linux_code/ipc_lib/condition.cc b/aos/linux_code/ipc_lib/condition.cc
index b764026..0ba4145 100644
--- a/aos/linux_code/ipc_lib/condition.cc
+++ b/aos/linux_code/ipc_lib/condition.cc
@@ -6,8 +6,8 @@
 
 namespace aos {
 
-static_assert(shm_ok<Condition>::value, "Condition should work"
-              " in shared memory");
+static_assert(shm_ok<Condition>::value,
+              "Condition should work in shared memory");
 
 Condition::Condition(Mutex *m) : impl_(), m_(m) {}
 
diff --git a/aos/linux_code/ipc_lib/core_lib.c b/aos/linux_code/ipc_lib/core_lib.c
index cc1ccbb..2bd6c25 100644
--- a/aos/linux_code/ipc_lib/core_lib.c
+++ b/aos/linux_code/ipc_lib/core_lib.c
@@ -5,7 +5,7 @@
 
 #include "aos/linux_code/ipc_lib/shared_mem.h"
 
-static inline uint8_t aos_8max(uint8_t l, uint8_t r) {
+static uint8_t aos_8max(uint8_t l, uint8_t r) {
   return (l > r) ? l : r;
 }
 void *shm_malloc_aligned(size_t length, uint8_t alignment) {
diff --git a/aos/linux_code/ipc_lib/ipc_lib.gyp b/aos/linux_code/ipc_lib/ipc_lib.gyp
index df7b658..08e8df8 100644
--- a/aos/linux_code/ipc_lib/ipc_lib.gyp
+++ b/aos/linux_code/ipc_lib/ipc_lib.gyp
@@ -60,6 +60,7 @@
         'core_lib',
         '<(AOS)/common/common.gyp:queue_testutils',
         '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/common.gyp:die',
       ],
     },
     {
@@ -76,6 +77,22 @@
         'core_lib',
         '<(AOS)/common/common.gyp:die',
       ],
+      'variables': {
+        'is_special_test': 1,
+      },
+    },
+    {
+      'target_name': 'scoped_message_ptr',
+      'type': 'static_library',
+      'sources': [
+        #'scoped_message_ptr.h',
+      ],
+      'dependencies': [
+        'queue',
+      ],
+      'export_dependent_settings': [
+        'queue',
+      ],
     },
   ],
 }
diff --git a/aos/linux_code/ipc_lib/ipc_stress_test.cc b/aos/linux_code/ipc_lib/ipc_stress_test.cc
index 1b55e82..3067a20 100644
--- a/aos/linux_code/ipc_lib/ipc_stress_test.cc
+++ b/aos/linux_code/ipc_lib/ipc_stress_test.cc
@@ -7,7 +7,6 @@
 #include <libgen.h>
 #include <assert.h>
 
-#include <vector>
 #include <string>
 
 #include "aos/common/time.h"
@@ -22,6 +21,10 @@
 // stderr output from each test run and only prints it out (not interleaved with
 // the output from any other run) if the test fails.
 //
+// They have to be run in separate processes because (in addition to various
+// parts of our code not being thread-safe...) gtest does not like multiple
+// threads.
+//
 // It's written in C++ for performance. We need actual OS-level parallelism for
 // this to work, which means that Ruby's out because it doesn't have good
 // support for doing that. My Python implementation ended up pretty heavily disk
@@ -33,14 +36,16 @@
 // arguments to pass to it.
 // Using --gtest_filter is a bad idea because it seems to result in a lot of
 // swapping which causes everything to be disk-bound (at least for me).
-static const ::std::vector< ::std::vector< ::std::string>> kTests = {
+static const size_t kTestMaxArgs = 10;
+static const char * kTests[][kTestMaxArgs] = {
   {"queue_test"},
   {"condition_test"},
   {"mutex_test"},
   {"raw_queue_test"},
 };
+static const size_t kTestsLength = sizeof(kTests) / sizeof(kTests[0]);
 // These arguments get inserted before any per-test arguments.
-static const ::std::vector< ::std::string> kDefaultArgs = {
+static const char *kDefaultArgs[] = {
   "--gtest_repeat=30",
   "--gtest_shuffle",
 };
@@ -75,7 +80,7 @@
 
 // Gets called after each child forks to run a test.
 void __attribute__((noreturn)) DoRunTest(
-    Shared *shared, const ::std::vector< ::std::string> &test, int pipes[2]) {
+    Shared *shared, const char *(*test)[kTestMaxArgs], int pipes[2]) {
   if (close(pipes[0]) == -1) {
     Die("close(%d) of read end of pipe failed with %d: %s\n",
         pipes[0], errno, strerror(errno));
@@ -93,13 +98,15 @@
         pipes[1], STDERR_FILENO, errno, strerror(errno));
   }
 
-  size_t size = test.size();
-  size_t default_size = kDefaultArgs.size();
+  size_t size = kTestMaxArgs;
+  size_t default_size = sizeof(kDefaultArgs) / sizeof(kDefaultArgs[0]);
+  // There's no chance to free this because we either exec or Die.
   const char **args = new const char *[size + default_size + 1];
   // The actual executable to run.
   ::std::string executable;
   int i = 0;
-  for (const ::std::string &c : test) {
+  for (size_t test_i = 0; test_i < size; ++test_i) {
+    const char *c = (*test)[test_i];
     if (i == 0) {
       executable = ::std::string(shared->path) + "/" + c;
       args[0] = executable.c_str();
@@ -107,7 +114,7 @@
         args[++i] = ci.c_str();
       }
     } else {
-      args[i] = c.c_str();
+      args[i] = c;
     }
     ++i;
   }
@@ -120,7 +127,9 @@
 void DoRun(Shared *shared) {
   int iterations = 0;
   // An iterator pointing to a random one of the tests.
-  auto test = kTests.begin() + (getpid() % kTests.size());
+  // We randomize based on PID because otherwise they all end up running the
+  // same test at the same time for the whole test.
+  const char *(*test)[kTestMaxArgs] = &kTests[getpid() % kTestsLength];
   int pipes[2];
   while (time::Time::Now() < shared->stop_time) {
     if (pipe(pipes) == -1) {
@@ -128,7 +137,7 @@
     }
     switch (fork()) {
       case 0:  // in runner
-        DoRunTest(shared, *test, pipes);
+        DoRunTest(shared, test, pipes);
       case -1:
         Die("fork() failed with %d: %s\n", errno, strerror(errno));
     }
@@ -169,22 +178,21 @@
       if (WEXITSTATUS(status) != 0) {
         MutexLocker sync(&shared->output_mutex);
         fprintf(stderr, "Test %s exited with status %d. output:\n",
-                test->at(0).c_str(), WEXITSTATUS(status));
+                (*test)[0], WEXITSTATUS(status));
         fputs(output.c_str(), stderr);
       }
     } else if (WIFSIGNALED(status)) {
       MutexLocker sync(&shared->output_mutex);
-      fprintf(stderr, "Test %s terminated by signal %d: %s.\n",
-              test->at(0).c_str(),
+      fprintf(stderr, "Test %s terminated by signal %d: %s.\n", (*test)[0],
               WTERMSIG(status), strsignal(WTERMSIG(status)));
         fputs(output.c_str(), stderr);
     } else {
       assert(WIFSTOPPED(status));
-      Die("Test %s was stopped.\n", test->at(0).c_str());
+      Die("Test %s was stopped.\n", (*test)[0]);
     }
 
     ++test;
-    if (test == kTests.end()) test = kTests.begin();
+    if (test == kTests + 1) test = kTests;
     ++iterations;
   }
   {
@@ -212,7 +220,10 @@
   new (shared) Shared(time::Time::Now() + kTestTime);
 
   char *temp = strdup(argv[0]);
-  shared->path = strdup(dirname(temp));
+  if (asprintf(const_cast<char **>(&shared->path),
+               "%s/../tests", dirname(temp)) == -1) {
+    Die("asprintf failed with %d: %s\n", errno, strerror(errno));
+  }
   free(temp);
 
   for (int i = 0; i < kTesters; ++i) {
diff --git a/aos/linux_code/ipc_lib/queue.cc b/aos/linux_code/ipc_lib/queue.cc
index e67f22c..8a86b7f 100644
--- a/aos/linux_code/ipc_lib/queue.cc
+++ b/aos/linux_code/ipc_lib/queue.cc
@@ -38,6 +38,7 @@
 struct RawQueue::MessageHeader {
   int ref_count;
   int index;  // in pool_
+  // Gets the message header immediately preceding msg.
   static MessageHeader *Get(const void *msg) {
     return reinterpret_cast<MessageHeader *>(__builtin_assume_aligned(
         static_cast<uint8_t *>(const_cast<void *>(msg)) - sizeof(MessageHeader),
@@ -50,8 +51,8 @@
     memcpy(this, &temp, sizeof(*this));
   }
 };
-static_assert(shm_ok<RawQueue::MessageHeader>::value, "the whole point"
-              " is to stick it in shared memory");
+static_assert(shm_ok<RawQueue::MessageHeader>::value,
+              "the whole point is to stick it in shared memory");
 
 struct RawQueue::ReadData {
   bool writable_start;
@@ -73,7 +74,7 @@
 }
 
 RawQueue::RawQueue(const char *name, size_t length, int hash, int queue_length)
-  : readable_(&data_lock_), writable_(&data_lock_) {
+    : readable_(&data_lock_), writable_(&data_lock_) {
   const size_t name_size = strlen(name) + 1;
   char *temp = static_cast<char *>(shm_malloc(name_size));
   memcpy(temp, name, name_size);
@@ -115,17 +116,17 @@
   if (kFetchDebug) {
     printf("fetching queue %s\n", name);
   }
-  if (mutex_lock(&global_core->mem_struct->queues.alloc_lock) != 0) {
+  if (mutex_lock(&global_core->mem_struct->queues.lock) != 0) {
     return NULL;
   }
   RawQueue *current = static_cast<RawQueue *>(
-      global_core->mem_struct->queues.queue_list);
+      global_core->mem_struct->queues.pointer);
   if (current != NULL) {
     while (true) {
       // If we found a matching queue.
       if (strcmp(current->name_, name) == 0 && current->length_ == length &&
           current->hash_ == hash && current->queue_length_ == queue_length) {
-        mutex_unlock(&global_core->mem_struct->queues.alloc_lock);
+        mutex_unlock(&global_core->mem_struct->queues.lock);
         return current;
       } else {
         if (kFetchDebug) {
@@ -142,12 +143,12 @@
   RawQueue *r = new (shm_malloc(sizeof(RawQueue)))
       RawQueue(name, length, hash, queue_length);
   if (current == NULL) {  // if we don't already have one
-    global_core->mem_struct->queues.queue_list = r;
+    global_core->mem_struct->queues.pointer = r;
   } else {
     current->next_ = r;
   }
 
-  mutex_unlock(&global_core->mem_struct->queues.alloc_lock);
+  mutex_unlock(&global_core->mem_struct->queues.lock);
   return r;
 }
 RawQueue *RawQueue::Fetch(const char *name, size_t length, int hash,
diff --git a/aos/linux_code/ipc_lib/queue_test.cc b/aos/linux_code/ipc_lib/queue_test.cc
index c87bd7b..0a982cc 100644
--- a/aos/linux_code/ipc_lib/queue_test.cc
+++ b/aos/linux_code/ipc_lib/queue_test.cc
@@ -15,6 +15,7 @@
 #include "aos/common/queue_testutils.h"
 #include "aos/common/time.h"
 #include "aos/common/logging/logging.h"
+#include "aos/common/die.h"
 
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
@@ -136,6 +137,9 @@
 
   void SetUp() override {
     ::testing::Test::SetUp();
+
+    SetDieTestMode(true);
+
     fatal_failure = static_cast<char *>(shm_malloc(sizeof(fatal_failure)));
     static bool registered = false;
     if (!registered) {
diff --git a/aos/linux_code/ipc_lib/shared_mem.c b/aos/linux_code/ipc_lib/shared_mem.c
index 4e38117..069fe19 100644
--- a/aos/linux_code/ipc_lib/shared_mem.c
+++ b/aos/linux_code/ipc_lib/shared_mem.c
@@ -21,8 +21,10 @@
 void init_shared_mem_core(aos_shm_core *shm_core) {
   clock_gettime(CLOCK_REALTIME, &shm_core->identifier);
   shm_core->msg_alloc_lock = 0;
-  shm_core->queues.queue_list = NULL;
-  shm_core->queues.alloc_lock = 0;
+  shm_core->queues.pointer = NULL;
+  shm_core->queues.lock = 0;
+  shm_core->queue_types.pointer = NULL;
+  shm_core->queue_types.lock = 0;
 }
 
 ptrdiff_t aos_core_get_mem_usage(void) {
@@ -128,3 +130,7 @@
   }
   return 0;
 }
+
+int aos_core_is_init(void) {
+  return global_core != NULL;
+}
diff --git a/aos/linux_code/ipc_lib/shared_mem.h b/aos/linux_code/ipc_lib/shared_mem.h
index e5059c4..4d5e88e 100644
--- a/aos/linux_code/ipc_lib/shared_mem.h
+++ b/aos/linux_code/ipc_lib/shared_mem.h
@@ -18,10 +18,12 @@
 // can have regular pointers to other stuff in shared memory.
 #define SHM_START 0x20000000
 
-typedef struct aos_queue_global_t {
-  mutex alloc_lock;
-  void *queue_list;  // an aos::Queue* declared in C code
-} aos_queue_global;
+// A structure that represents some kind of global pointer that everything
+// shares.
+typedef struct aos_global_pointer_t {
+  mutex lock;
+  void *pointer;
+} aos_global_pointer;
 
 typedef struct aos_shm_core_t {
   // clock_gettime(CLOCK_REALTIME, &identifier) gets called to identify
@@ -32,7 +34,12 @@
   mutex creation_condition;
   mutex msg_alloc_lock;
   void *msg_alloc;
-  aos_queue_global queues;
+  // A pointer to the head of the linked list of queues.
+  // pointer points to a ::aos::Queue.
+  aos_global_pointer queues;
+  // A pointer to the head of the linked list of queue message types.
+  // pointer points to a ::aos::type_cache::ShmType.
+  aos_global_pointer queue_types;
 } aos_shm_core;
 
 enum aos_core_create {
@@ -62,6 +69,9 @@
 int aos_core_create_shared_mem(enum aos_core_create to_create);
 int aos_core_free_shared_mem(void);
 
+// Returns whether or not the shared memory system is active.
+int aos_core_is_init(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/aos/linux_code/logging/binary_log_file.cc b/aos/linux_code/logging/binary_log_file.cc
index e1e99b4..8c8c8b4 100644
--- a/aos/linux_code/logging/binary_log_file.cc
+++ b/aos/linux_code/logging/binary_log_file.cc
@@ -71,6 +71,9 @@
   }
   position_ += sizeof(LogFileMessageHeader) + r->name_size + r->message_size;
   AlignPosition();
+  if (position_ >= kPageSize) {
+    LOG(FATAL, "corrupt log file running over page size\n");
+  }
   return r;
 }
 
@@ -78,15 +81,19 @@
   msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
 }
 
-void LogFileAccessor::MoveToEnd() {
-  Unmap(current_);
+bool LogFileAccessor::IsLastPage() {
+  if (is_last_page_ != 0) {
+    return is_last_page_ == 2;
+  }
+
   struct stat info;
   if (fstat(fd_, &info) == -1) {
     LOG(FATAL, "fstat(%d, %p) failed with %d: %s\n", fd_, &info, errno,
         strerror(errno));
   }
-  offset_ = info.st_size - kPageSize;
-  MapNextPage();
+  bool r = offset_ == static_cast<off_t>(info.st_size - kPageSize);
+  is_last_page_ = r ? 2 : 1;
+  return r;
 }
 
 void LogFileAccessor::MapNextPage() {
@@ -116,6 +123,7 @@
     LOG(FATAL, "munmap(%p, %zd) failed with %d: %s. aborting\n", location,
         kPageSize, errno, strerror(errno));
   }
+  is_last_page_ = 0;
 }
 
 }  // namespace linux_code
diff --git a/aos/linux_code/logging/binary_log_file.h b/aos/linux_code/logging/binary_log_file.h
index 83f8dde..0f8c3fb 100644
--- a/aos/linux_code/logging/binary_log_file.h
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -35,7 +35,7 @@
     LogFileMessageHeader {
   // Represents the type of an individual message.
   enum class MessageType : uint16_t {
-    // '\0'-terminated string.
+    // char[] (no '\0' on the end).
     kString,
     kStructType,
     kStruct,
@@ -63,8 +63,7 @@
   int32_t source;
   static_assert(sizeof(source) >= sizeof(LogMessage::source), "PIDs won't fit");
 
-  // Both including all of the bytes in that part of the message (ie any '\0's
-  // etc).
+  // Both including all of the bytes in that part of the message.
   uint32_t name_size, message_size;
 
   uint16_t sequence;
@@ -94,8 +93,7 @@
   // Asynchronously syncs all open mappings.
   void Sync() const;
 
-  // TODO(brians): This won't work any more.
-  void MoveToEnd();
+  bool IsLastPage();
 
  private:
   // The size of the chunks that get mmaped/munmapped together. Large enough so
@@ -115,6 +113,9 @@
   char *current_;
   size_t position_;
 
+  // 0 = unknown, 1 = no, 2 = yes
+  int is_last_page_ = 0;
+
   void MapNextPage();
   void Unmap(void *location);
 
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
index 22c635e..67cb166 100644
--- a/aos/linux_code/logging/binary_log_writer.cc
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -10,17 +10,54 @@
 #include <fcntl.h>
 
 #include <map>
+#include <unordered_set>
 
 #include "aos/linux_code/logging/linux_logging.h"
 #include "aos/linux_code/logging/binary_log_file.h"
 #include "aos/linux_code/init.h"
 #include "aos/linux_code/configuration.h"
+#include "aos/common/queue_types.h"
 
 namespace aos {
 namespace logging {
 namespace linux_code {
 namespace {
 
+void CheckTypeWritten(uint32_t type_id, LogFileAccessor &writer) {
+  static ::std::unordered_set<uint32_t> written_type_ids;
+  if (written_type_ids.count(type_id) > 0) return;
+  if (MessageType::IsPrimitive(type_id)) return;
+
+  const MessageType &type = type_cache::Get(type_id);
+  for (int i = 0; i < type.number_fields; ++i) {
+    CheckTypeWritten(type.fields[i]->type, writer);
+  }
+
+  char buffer[1024];
+  ssize_t size = type.Serialize(buffer, sizeof(buffer));
+  if (size == -1) {
+    LOG(WARNING, "%zu-byte buffer is too small to serialize type %s\n",
+        sizeof(buffer), type.name.c_str());
+    return;
+  }
+  LogFileMessageHeader *const output =
+      writer.GetWritePosition(sizeof(LogFileMessageHeader) + size);
+
+  output->time_sec = output->time_nsec = 0;
+  output->source = getpid();
+  output->name_size = 0;
+  output->sequence = 0;
+  output->level = FATAL;
+
+  memcpy(output + 1, buffer, size);
+  output->message_size = size;
+
+  output->type = LogFileMessageHeader::MessageType::kStructType;
+  futex_set(&output->marker);
+
+  written_type_ids.insert(type_id);
+}
+
 int BinaryLogReaderMain() {
   InitNRT();
 
@@ -81,23 +118,52 @@
     const LogMessage *const msg = ReadNext();
     if (msg == NULL) continue;
 
-    // add 1 for terminating '\0'
-    size_t name_size = strlen(msg->name) + 1;
-    size_t message_size = strlen(msg->message) + 1;
-
-    LogFileMessageHeader *const output = writer.GetWritePosition(
-        sizeof(LogFileMessageHeader) + name_size + message_size);
+    size_t output_length =
+        sizeof(LogFileMessageHeader) + msg->name_length + msg->message_length;
+    if (msg->type == LogMessage::Type::kStruct) {
+      output_length += sizeof(msg->structure.type_id) + sizeof(uint32_t) +
+                       msg->structure.string_length;
+      CheckTypeWritten(msg->structure.type_id, writer);
+    }
+    LogFileMessageHeader *const output = writer.GetWritePosition(output_length);;
     char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
-    output->name_size = name_size;
-    output->message_size = message_size;
+    output->name_size = msg->name_length;
+    output->message_size = msg->message_length;
     output->source = msg->source;
     output->level = msg->level;
     output->time_sec = msg->seconds;
     output->time_nsec = msg->nseconds;
     output->sequence = msg->sequence;
-    output->type = LogFileMessageHeader::MessageType::kString;
-    memcpy(output_strings, msg->name, name_size);
-    memcpy(output_strings + name_size, msg->message, message_size);
+    memcpy(output_strings, msg->name, msg->name_length);
+
+    switch (msg->type) {
+      case LogMessage::Type::kString:
+        memcpy(output_strings + msg->name_length, msg->message,
+               msg->message_length);
+        output->type = LogFileMessageHeader::MessageType::kString;
+        break;
+      case LogMessage::Type::kStruct:
+        char *position = output_strings + msg->name_length;
+
+        memcpy(position, &msg->structure.type_id, sizeof(msg->structure.type_id));
+        position += sizeof(msg->structure.type_id);
+        output->message_size += sizeof(msg->structure.type_id);
+
+        uint32_t length = msg->structure.string_length;
+        memcpy(position, &length, sizeof(length));
+        position += sizeof(length);
+        memcpy(position, msg->structure.serialized, length);
+        position += length;
+        output->message_size += sizeof(length) + length;
+
+        memcpy(position,
+               msg->structure.serialized + msg->structure.string_length,
+               msg->message_length);
+
+        output->type = LogFileMessageHeader::MessageType::kStruct;
+        break;
+    }
+
     futex_set(&output->marker);
 
     logging::linux_code::Free(msg);
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
index 985577b..676e3b7 100644
--- a/aos/linux_code/logging/linux_logging.cc
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -25,15 +25,29 @@
 namespace linux_code {
 namespace {
 
-class linuxQueueLogImplementation : public LogImplementation {
-  virtual void DoLog(log_level level, const char *format, va_list ap) {
+class LinuxQueueLogImplementation : public LogImplementation {
+  LogMessage *GetMessageOrDie() {
     LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
     if (message == NULL) {
-      LOG(FATAL, "queue get message failed\n");
+      LOG(FATAL, "%p->GetMessage() failed\n", queue);
+    } else {
+      return message;
     }
+  }
 
+  virtual void DoLog(log_level level, const char *format, va_list ap) override {
+    LogMessage *message = GetMessageOrDie();
     internal::FillInMessage(level, format, ap, message);
+    Write(message);
+  }
 
+  virtual void LogStruct(log_level level, const ::std::string &message_string,
+                         size_t size, const MessageType *type,
+                         const ::std::function<size_t(char *)> &serialize)
+      override {
+    LogMessage *message = GetMessageOrDie();
+    internal::FillInMessageStructure(level, message_string, size, type,
+                                     serialize, message);
     Write(message);
   }
 };
@@ -48,7 +62,7 @@
     Die("logging: couldn't fetch queue\n");
   }
 
-  AddImplementation(new linuxQueueLogImplementation());
+  AddImplementation(new LinuxQueueLogImplementation());
 }
 
 const LogMessage *ReadNext(int flags, int *index) {
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
index dc27c80..98021b5 100644
--- a/aos/linux_code/logging/log_displayer.cc
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -7,8 +7,13 @@
 #include <inttypes.h>
 #include <errno.h>
 
+#include <algorithm>
+
 #include "aos/linux_code/logging/binary_log_file.h"
 #include "aos/common/logging/logging_impl.h"
+#include "aos/common/queue_types.h"
+
+using ::aos::logging::linux_code::LogFileMessageHeader;
 
 namespace {
 
@@ -45,10 +50,16 @@
 
 int main(int argc, char **argv) {
   const char *filter_name = NULL;
+  size_t filter_length = 0;
   log_level filter_level = INFO;
-  bool follow = false, start_at_beginning = true;
+  bool follow = false;
+  // Whether we need to skip everything until we get to the end of the file.
+  bool skip_to_end = false;
   const char *filename = "aos_log-current";
 
+  ::aos::logging::AddImplementation(
+      new ::aos::logging::StreamLogImplementation(stdout));
+
   while (true) {
     static struct option long_options[] = {
       {"name", required_argument, NULL, 'n'},
@@ -80,6 +91,7 @@
         abort();
       case 'n':
         filter_name = optarg;
+        filter_length = strlen(filter_name);
         break;
       case 'l':
         filter_level = ::aos::logging::str_log(optarg);
@@ -93,16 +105,16 @@
         break;
       case 'f':
         follow = true;
-        start_at_beginning = false;
+        skip_to_end = true;
         break;
       case 't':
         follow = false;
         break;
       case 'b':
-        start_at_beginning = true;
+        skip_to_end = false;
         break;
       case 'e':
-        start_at_beginning = false;
+        skip_to_end = true;
         break;
       case 'm':
         abort();
@@ -138,10 +150,12 @@
     exit(EXIT_FAILURE);
   }
   ::aos::logging::linux_code::LogFileAccessor accessor(fd, false);
-  if (!start_at_beginning) {
-    accessor.MoveToEnd();
+
+  if (skip_to_end) {
+    fputs("skipping old logs...\n", stderr);
   }
-  const ::aos::logging::linux_code::LogFileMessageHeader *msg;
+
+  const LogFileMessageHeader *msg;
   ::aos::logging::LogMessage log_message;
   do {
     msg = accessor.ReadNextMessage(follow);
@@ -149,25 +163,81 @@
       fputs("reached end of file\n", stderr);
       return 0;
     }
-    if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
-    if (filter_name != NULL &&
-        strcmp(filter_name,
-               reinterpret_cast<const char *>(msg) + sizeof(*msg)) != 0) {
+
+    if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
+      size_t bytes = msg->message_size;
+      ::aos::MessageType *type = ::aos::MessageType::Deserialize(
+          reinterpret_cast<const char *>(msg + 1), &bytes);
+      ::aos::type_cache::Add(*type);
       continue;
     }
 
+    if (skip_to_end) {
+      if (accessor.IsLastPage()) {
+        fputs("done skipping old logs\n", stderr);
+        skip_to_end = false;
+      } else {
+        continue;
+      }
+    }
+
+    if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
+    if (filter_name != NULL) {
+      if (filter_length != msg->name_size) continue;
+      if (memcmp(filter_name,
+                 reinterpret_cast<const char *>(msg) + sizeof(*msg),
+                 filter_length) !=
+          0) {
+        continue;
+      }
+    }
+
     log_message.source = msg->source;
     log_message.sequence = msg->sequence;
     log_message.level = msg->level;
     log_message.seconds = msg->time_sec;
     log_message.nseconds = msg->time_nsec;
-    strncpy(log_message.name,
-            reinterpret_cast<const char *>(msg) + sizeof(*msg),
-            sizeof(log_message.name));
-    strncpy(log_message.message,
-            reinterpret_cast<const char *>(msg) + sizeof(*msg) +
-            msg->name_size,
-            sizeof(log_message.message));
+    memcpy(log_message.name, reinterpret_cast<const char *>(msg) + sizeof(*msg),
+           ::std::min<size_t>(sizeof(log_message.name), msg->name_size));
+    log_message.message_length = msg->message_size;
+    log_message.name_length = msg->name_size;
+
+    switch (msg->type) {
+      case LogFileMessageHeader::MessageType::kStruct: {
+        const char *position =
+            reinterpret_cast<const char *>(msg + 1) + msg->name_size;
+        memcpy(&log_message.structure.type_id, position,
+               sizeof(log_message.structure.type_id));
+        position += sizeof(log_message.structure.type_id);
+
+        uint32_t length;
+        memcpy(&length, position, sizeof(length));
+        log_message.structure.string_length = length;
+        position += sizeof(length);
+        memcpy(log_message.structure.serialized, position, length);
+        position += length;
+
+        log_message.message_length -=
+            sizeof(log_message.structure.type_id) + sizeof(uint32_t) +
+            log_message.structure.string_length;
+        memcpy(log_message.structure.serialized +
+                   log_message.structure.string_length,
+               position, log_message.message_length);
+
+        log_message.type = ::aos::logging::LogMessage::Type::kStruct;
+        break;
+      }
+      case LogFileMessageHeader::MessageType::kString:
+        memcpy(
+            log_message.message,
+            reinterpret_cast<const char *>(msg) + sizeof(*msg) + msg->name_size,
+            ::std::min<size_t>(sizeof(log_message.message), msg->message_size));
+        log_message.type = ::aos::logging::LogMessage::Type::kString;
+        break;
+      case LogFileMessageHeader::MessageType::kStructType:
+        LOG(FATAL, "shouldn't get here\n");
+        break;
+    };
     ::aos::logging::internal::PrintMessage(stdout, log_message);
   } while (msg != NULL);
 }
diff --git a/aos/linux_code/logging/logging.gyp b/aos/linux_code/logging/logging.gyp
index 95459f3..32df43d 100644
--- a/aos/linux_code/logging/logging.gyp
+++ b/aos/linux_code/logging/logging.gyp
@@ -12,6 +12,7 @@
         '<(AOS)/linux_code/linux_code.gyp:init',
         '<(AOS)/linux_code/linux_code.gyp:configuration',
         'binary_log_file',
+        '<(AOS)/common/common.gyp:queue_types',
       ],
     },
     {
@@ -37,6 +38,7 @@
         '<(AOS)/build/aos.gyp:logging',
         '<(AOS)/linux_code/linux_code.gyp:init',
         'binary_log_file',
+        '<(AOS)/common/common.gyp:queue_types',
       ],
     },
     {
diff --git a/aos/linux_code/queue-tmpl.h b/aos/linux_code/queue-tmpl.h
index 15b8608..84eb02c 100644
--- a/aos/linux_code/queue-tmpl.h
+++ b/aos/linux_code/queue-tmpl.h
@@ -52,7 +52,6 @@
     return msg;
   }
 
-#ifndef SWIG
   operator bool() {
     return msg_ != NULL;
   }
@@ -70,7 +69,6 @@
     assert(msg != NULL);
     return msg;
   }
-#endif  // SWIG
 
   // Sends the message and removes our reference to it.
   // If the queue is full, over-rides the oldest message in it with our new
@@ -114,7 +112,6 @@
     reset();
   }
 
-#ifndef SWIG
   // Implements a move constructor to take the message pointer from the
   // temporary object to save work.
   SafeScopedMessagePtr(SafeScopedMessagePtr<T> &&ptr)
@@ -122,7 +119,6 @@
       msg_(ptr.msg_) {
     ptr.msg_ = NULL;
   }
-#endif  // SWIG
 
   // Copy constructor actually copies the data.
   SafeScopedMessagePtr(const SafeScopedMessagePtr<T> &ptr)
@@ -130,13 +126,11 @@
         msg_(NULL) {
     reset(new T(*ptr.get()));
   }
-#ifndef SWIG
   // Equal operator copies the data.
   void operator=(const SafeScopedMessagePtr<T> &ptr) {
     queue_ = ptr.queue_;
     reset(new T(*ptr.get()));
   }
-#endif  // SWIG
 
  private:
   // Provide access to private constructor.
@@ -254,7 +248,7 @@
 
 
 // This builder uses the safe message pointer so that it can be safely copied
-// and used by SWIG or in places where it could be leaked.
+// in places where it could be leaked.
 template <class T>
 class SafeMessageBuilder {
  public:
diff --git a/aos/linux_code/starter/starter.sh b/aos/linux_code/starter/starter.sh
index 62338af..9d7cc13 100755
--- a/aos/linux_code/starter/starter.sh
+++ b/aos/linux_code/starter/starter.sh
@@ -1,5 +1,7 @@
 #!/bin/bash
 
+echo $$ > /tmp/starter.pid
+
 echo '/home/driver/tmp/robot_logs/%e-%s-%p-%t.coredump' > /proc/sys/kernel/core_pattern
 
 #echo $$ > /var/run/`basename $0`.pid IT FORKS AFTER THIS!!!!
diff --git a/bbb_cape/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index 25db9a5..8efe553 100644
--- a/bbb_cape/src/bbb/bbb.gyp
+++ b/bbb_cape/src/bbb/bbb.gyp
@@ -100,10 +100,12 @@
         '<(DEPTH)/bbb_cape/src/cape/cape.gyp:cows',
         'crc',
         'byte_io',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
       'export_dependent_settings': [
         '<(AOS)/build/aos.gyp:logging',
         '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
     },
     {
@@ -161,12 +163,14 @@
         'crc',
         'sensor_generation',
         '<(AOS)/linux_code/linux_code.gyp:configuration',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
       'export_dependent_settings': [
         'packet_finder',
         'data_struct',
         'cape_manager',
         '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
     },
     {
diff --git a/bbb_cape/src/bbb/cape_manager.cc b/bbb_cape/src/bbb/cape_manager.cc
index b680c4a..b5f2851 100644
--- a/bbb_cape/src/bbb/cape_manager.cc
+++ b/bbb_cape/src/bbb/cape_manager.cc
@@ -11,7 +11,7 @@
 namespace bbb {
 
 CapeManager::CapeManager()
-    : uart_(750000), reset_(2, 5, true), custom_bootloader_(2, 3, false) {}
+    : uart_(750000), reset_(2, 5, true), custom_bootloader_(2, 3, true) {}
 
 void CapeManager::DownloadHex(const ::std::string &filename) {
   HexByteReader file(filename);
diff --git a/bbb_cape/src/bbb/packet_finder.cc b/bbb_cape/src/bbb/packet_finder.cc
index 747eaf8..5decec8 100644
--- a/bbb_cape/src/bbb/packet_finder.cc
+++ b/bbb_cape/src/bbb/packet_finder.cc
@@ -17,6 +17,15 @@
 using ::aos::time::Time;
 
 namespace bbb {
+namespace {
+
+// Enabling all of the debugging logs during normal operation on the BBB causes
+// it to use most of the CPU when it runs into trouble.
+const bool kDebugLogs = false;
+
+}  // namespace
+
+constexpr ::aos::time::Time PacketFinder::kDebugLogInterval;
 
 PacketFinder::PacketFinder(ByteReaderInterface *reader, size_t packet_size)
     : reader_(reader),
@@ -102,7 +111,8 @@
       reinterpret_cast<uint32_t *>(buf_), packet_size_,
       reinterpret_cast<uint32_t *>(unstuffed_data_), packet_size_ - 4);
   if (unstuffed == 0) {
-    LOG(INFO, "invalid packet\n");
+    if (kDebugLogs) LOG(INFO, "invalid\n");
+    LOG_INTERVAL(invalid_packet_);
     return false;
   } else if (unstuffed != (packet_size_ - 4) / 4) {
     LOG(WARNING, "packet is %" PRIu32 " words instead of %zu\n",
@@ -116,8 +126,11 @@
   uint32_t calculated_checksum = cape::CalculateChecksum(
       reinterpret_cast<uint8_t *>(unstuffed_data_), packet_size_ - 8);
   if (sent_checksum != calculated_checksum) {
-    LOG(INFO, "sent checksum: %" PRIx32 " vs calculated: %" PRIx32"\n",
-        sent_checksum, calculated_checksum);
+    if (kDebugLogs) {
+      LOG(INFO, "sent %" PRIx32 " not %" PRIx32 "\n", sent_checksum,
+          calculated_checksum);
+    }
+    LOG_INTERVAL(bad_checksum_);
     return false;
   }
 
@@ -134,7 +147,7 @@
       if (buf_[i] == 0) {
         ++zeros;
         if (zeros == 4) {
-          LOG(INFO, "found another packet start at %zd\n", i);
+          if (kDebugLogs) LOG(INFO, "start at %zd\n", i);
           packet_bytes_ = packet_size_ - (i + 1);
           memmove(buf_, buf_ + i + 1, packet_bytes_);
           return false;
diff --git a/bbb_cape/src/bbb/packet_finder.h b/bbb_cape/src/bbb/packet_finder.h
index 80cb8b8..962291f 100644
--- a/bbb_cape/src/bbb/packet_finder.h
+++ b/bbb_cape/src/bbb/packet_finder.h
@@ -7,6 +7,7 @@
 #include "aos/common/logging/logging.h"
 #include "aos/common/time.h"
 #include "aos/common/macros.h"
+#include "aos/common/util/log_interval.h"
 
 namespace bbb {
 
@@ -64,6 +65,14 @@
   // Whether we've increased the priority of the IRQ yet.
   bool irq_priority_increased_ = false;
 
+  typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+  static constexpr ::aos::time::Time kDebugLogInterval =
+      ::aos::time::Time::InSeconds(0.3);
+  SimpleLogInterval invalid_packet_ =
+      SimpleLogInterval(kDebugLogInterval, INFO, "invalid packet");
+  SimpleLogInterval bad_checksum_ =
+      SimpleLogInterval(kDebugLogInterval, INFO, "bad checksum");
+
   DISALLOW_COPY_AND_ASSIGN(PacketFinder);
 };
 
diff --git a/bbb_cape/src/bbb/sensor_reader.cc b/bbb_cape/src/bbb/sensor_reader.cc
index f9395c2..fd4b79d 100644
--- a/bbb_cape/src/bbb/sensor_reader.cc
+++ b/bbb_cape/src/bbb/sensor_reader.cc
@@ -39,7 +39,7 @@
 
 const DataStruct *SensorReader::ReadPacket() {
   static constexpr ::aos::time::Time kResetTimeout =
-      ::aos::time::Time::InSeconds(2.5);
+      ::aos::time::Time::InSeconds(5);
 
   while (true) {
     ::aos::time::Time next_timeout = last_received_time_ + kResetTimeout;
@@ -67,7 +67,7 @@
       last_cape_timestamp_ = data->timestamp;
       return data;
     }
-    LOG(WARNING, "receiving packet failed\n");
+    LOG_INTERVAL(receive_failed_);
   }
 }
 
diff --git a/bbb_cape/src/bbb/sensor_reader.h b/bbb_cape/src/bbb/sensor_reader.h
index 48c863a..ed2fbcf 100644
--- a/bbb_cape/src/bbb/sensor_reader.h
+++ b/bbb_cape/src/bbb/sensor_reader.h
@@ -7,6 +7,7 @@
 
 #include "aos/common/time.h"
 #include "aos/common/macros.h"
+#include "aos/common/util/log_interval.h"
 
 #include "bbb/packet_finder.h"
 #include "bbb/data_struct.h"
@@ -51,6 +52,10 @@
   ::aos::time::Time last_received_time_ = ::aos::time::Time::InSeconds(0);
   uint64_t last_cape_timestamp_;
 
+  ::aos::util::SimpleLogInterval receive_failed_ =
+      ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.2), WARNING,
+                                     "receive failed");
+
   DISALLOW_COPY_AND_ASSIGN(SensorReader);
 };
 
diff --git a/bbb_cape/src/bbb/uart_reader.cc b/bbb_cape/src/bbb/uart_reader.cc
index a82faa1..c45c8ea 100644
--- a/bbb_cape/src/bbb/uart_reader.cc
+++ b/bbb_cape/src/bbb/uart_reader.cc
@@ -8,7 +8,6 @@
 #include "aos/common/logging/logging.h"
 
 // This is the code for receiving data from the cape via UART.
-// NOTE: In order for this to work, you must have the BB-UART1 device tree
 // fragment active.
 // `su -c "echo BB-UART1 > /sys/devices/bone_capemgr.*/slots"` works, but
 // you have to do it every time. It is also possible to set it up to do that
@@ -16,9 +15,49 @@
 
 namespace bbb {
 namespace {
+
 // TODO(brians): Determine this in some way that allows easy switching for
 // testing with /dev/ttyUSB0 for example.
 const char *device = "/dev/ttyO1";
+
+bool easy_access(const char *path) {
+  if (access(path, R_OK | W_OK) == 0) return true;
+  if (errno == EACCES || errno == ENOENT) return false;
+  LOG(FATAL, "access(%s, F_OK) failed with %d: %s\n", path, errno,
+      strerror(errno));
+}
+
+int open_device() {
+  if (easy_access(device)) {
+    LOG(INFO, "unexporting BB-UART1\n");
+    if (system("bash -c 'echo -$(cat /sys/devices/bone_capemgr.*/slots"
+               " | fgrep BB-UART1"
+               " | cut -c 2) > /sys/devices/bone_capemgr.*/slots'") ==
+        -1) {
+      LOG(FATAL, "system([disable OMAP UART]) failed with %d: %s\n", errno,
+          strerror(errno));
+    }
+    while (easy_access(device)) {
+      ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.1));
+    }
+  }
+
+  LOG(INFO, "exporting BB-UART1\n");
+  // 2 strings to work around a VIM bug where the indenter locks up when they're
+  // combined as 1...
+  if (system("bash -c 'echo BB-UART1 > /sys/devices/bone_capemgr.*"
+             "/slots'") ==
+      -1) {
+    LOG(FATAL, "system([enable OMAP UART]) failed with %d: %s\n", errno,
+        strerror(errno));
+  }
+  while (!easy_access(device)) {
+    ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.1));
+  }
+
+  return open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+}
+
 }  // namespace
 
 extern "C" {
@@ -32,11 +71,10 @@
 }  // extern "C"
 
 UartReader::UartReader(int32_t baud_rate)
-    : fd_(open(device, O_RDWR | O_NOCTTY | O_NONBLOCK)) {
+    : fd_(open_device()) {
   
   if (fd_ < 0) {
-    LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s."
-               " Did you read my note in bbb/uart_reader.cc?\n",
+    LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s\n",
         device, errno, strerror(errno));
   }
 
diff --git a/bbb_cape/src/flasher/stm32_flasher.cc b/bbb_cape/src/flasher/stm32_flasher.cc
index c2e8bf2..48887d2 100644
--- a/bbb_cape/src/flasher/stm32_flasher.cc
+++ b/bbb_cape/src/flasher/stm32_flasher.cc
@@ -174,7 +174,7 @@
   // Read all of the 0xFFs that the parser inserts to pad the data out.
   {
     uint8_t garbage[1024];
-    uint32_t length = start_address;
+    size_t length = start_address;
     while (length > 0) {
       uint32_t read = ::std::min(sizeof(garbage), length);
       if (parser->read(p_st, garbage, &read) != PARSER_ERR_OK) {
@@ -187,7 +187,7 @@
   uint32_t kFlashStart = 0x08000000;
 
   uint8_t buffer[256];  // 256 is the biggest size supported
-  uint32_t completed = 0;
+  size_t completed = 0;
   while (completed < size) {
     uint32_t address = start_address + completed + kFlashStart;
     uint32_t length = ::std::min(size - completed, sizeof(buffer));
@@ -219,7 +219,7 @@
           address, address + length);
     }
     completed += length;
-    printf("\rWrote and verified 0x%08x/0x%08x",
+    printf("\rWrote and verified 0x%08zx/0x%08x",
            completed, size);
     fflush(stdout);
   }
diff --git a/frc971/constants.cc b/frc971/constants.cc
index 66ff80f..672d5ef 100755
--- a/frc971/constants.cc
+++ b/frc971/constants.cc
@@ -38,6 +38,13 @@
                                                   0.47};
 const ShifterHallEffect kPracticeRightDriveShifter{2.070124, 0.838993, 0.62,
                                                    0.55};
+const double shooter_lower_physical_limit=0.0;
+const double shooter_upper_physical_limit=0.0;
+const double shooter_voltage=0.0;
+const double shooter_hall_effect_start_position=0.0;
+const double shooter_zeroing_off_speed=0.0;
+const double shooter_zeroing_speed=0.0;
+const double position=0.0;
 
 const Values *DoGetValues() {
   uint16_t team = ::aos::network::GetTeamNumber();
@@ -53,6 +60,16 @@
           true,
           control_loops::MakeVClutchDrivetrainLoop,
           control_loops::MakeClutchDrivetrainLoop,
+          // ShooterLimits
+          // TODO(ben): make these real numbers
+          {0.0, 100.0, {-3.0, 2.0}, {-1, 4.0}, {2.0, 7.0}},
+          shooter_voltage,
+          // shooter_total_length
+          100.0,
+          shooter_hall_effect_start_position,
+          shooter_zeroing_off_speed,
+          shooter_zeroing_speed,
+          position,
           {0.5,
            0.1,
            0.1,
@@ -76,6 +93,16 @@
           false,
           control_loops::MakeVDogDrivetrainLoop,
           control_loops::MakeDogDrivetrainLoop,
+          // ShooterLimits
+          // TODO(ben): make these real numbers
+          {0.0, 100.0, {-3.0, 2.0}, {-1, 4.0}, {2.0, 7.0}},
+          shooter_voltage,
+          // shooter_total_length
+          100.0,
+          shooter_hall_effect_start_position,
+          shooter_zeroing_off_speed,
+          shooter_zeroing_speed,
+          position,
           {0.5,
            0.2,
            0.1,
diff --git a/frc971/constants.h b/frc971/constants.h
index cb59a96..68dfea4 100755
--- a/frc971/constants.h
+++ b/frc971/constants.h
@@ -25,6 +25,12 @@
 
 // This structure contains current values for all of the things that change.
 struct Values {
+  // This is useful for representing the 2 sides of a hall effect sensor etc.
+  struct Pair {
+    double lower_limit;
+    double upper_limit;
+  };
+
   // The ratio from the encoder shaft to the drivetrain wheels.
   double drivetrain_encoder_ratio;
 
@@ -39,6 +45,23 @@
 
   ::std::function<StateFeedbackLoop<2, 2, 2>()> make_v_drivetrain_loop;
   ::std::function<StateFeedbackLoop<4, 2, 2>()> make_drivetrain_loop;
+  
+  struct ShooterLimits {
+    double lower_limit;
+    double upper_limit;
+    Pair plunger_back;
+    Pair pusher_distal;
+    Pair pusher_proximal;
+  };
+
+  ShooterLimits shooter;
+  
+  double shooter_voltage;
+  double shooter_total_length;
+  double shooter_hall_effect_start_position;
+  double shooter_zeroing_off_speed;
+  double shooter_zeroing_speed;
+  double position;
 
   struct Claws {
     double claw_zeroing_off_speed;
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 982c6e9..e7aae01 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -10,11 +10,12 @@
 #include "aos/common/queue.h"
 #include "aos/controls/polytope.h"
 #include "aos/common/commonmath.h"
+#include "aos/common/logging/queue_logging.h"
+
 #include "frc971/control_loops/state_feedback_loop.h"
 #include "frc971/control_loops/drivetrain/polydrivetrain_cim_plant.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/queues/gyro_angle.q.h"
-#include "frc971/queues/piston.q.h"
 #include "frc971/constants.h"
 
 using frc971::sensors::gyro;
@@ -121,7 +122,6 @@
     }
   }
   void PrintMotors() const {
-    // LOG(DEBUG, "Left Power %f Right Power %f lg %f rg %f le %f re %f gyro %f\n", U[0], U[1], R[0], R[2], Y[0], Y[1], _gyro);
     ::Eigen::Matrix<double, 4, 1> E = loop_->R - loop_->X_hat;
     LOG(DEBUG, "E[0, 0]: %f E[1, 0] %f E[2, 0] %f E[3, 0] %f\n", E(0, 0), E(1, 0), E(2, 0), E(3, 0));
   }
@@ -308,53 +308,31 @@
     }
 
     if (position) {
+      GearLogging gear_logging;
       // Switch to the correct controller.
       // TODO(austin): Un-hard code 0.57
       if (position->left_shifter_position < 0.57) {
         if (position->right_shifter_position < 0.57 || right_gear_ == LOW) {
-          LOG(DEBUG, "Loop Left low, Right low\n");
-          loop_->set_controller_index(0);
+          gear_logging.left_loop_high = false;
+          gear_logging.right_loop_high = false;
+          loop_->set_controller_index(gear_logging.controller_index = 0);
         } else {
-          LOG(DEBUG, "Loop Left low, Right high\n");
-          loop_->set_controller_index(1);
+          gear_logging.left_loop_high = false;
+          gear_logging.right_loop_high = true;
+          loop_->set_controller_index(gear_logging.controller_index = 1);
         }
       } else {
         if (position->right_shifter_position < 0.57 || left_gear_ == LOW) {
-          LOG(DEBUG, "Loop Left high, Right low\n");
-          loop_->set_controller_index(2);
+          gear_logging.left_loop_high = true;
+          gear_logging.right_loop_high = false;
+          loop_->set_controller_index(gear_logging.controller_index = 2);
         } else {
-          LOG(DEBUG, "Loop Left high, Right high\n");
-          loop_->set_controller_index(3);
+          gear_logging.left_loop_high = true;
+          gear_logging.right_loop_high = true;
+          loop_->set_controller_index(gear_logging.controller_index = 3);
         }
       }
-      switch (left_gear_) {
-        case LOW:
-          LOG(DEBUG, "Left is in low\n");
-          break;
-        case HIGH:
-          LOG(DEBUG, "Left is in high\n");
-          break;
-        case SHIFTING_UP:
-          LOG(DEBUG, "Left is shifting up\n");
-          break;
-        case SHIFTING_DOWN:
-          LOG(DEBUG, "Left is shifting down\n");
-          break;
-      }
-      switch (right_gear_) {
-        case LOW:
-          LOG(DEBUG, "Right is in low\n");
-          break;
-        case HIGH:
-          LOG(DEBUG, "Right is in high\n");
-          break;
-        case SHIFTING_UP:
-          LOG(DEBUG, "Right is shifting up\n");
-          break;
-        case SHIFTING_DOWN:
-          LOG(DEBUG, "Right is shifting down\n");
-          break;
-      }
+
       // TODO(austin): Constants.
       if (position->left_shifter_position > 0.9 && left_gear_ == SHIFTING_UP) {
         left_gear_ = HIGH;
@@ -368,6 +346,10 @@
       if (position->right_shifter_position < 0.1 && right_gear_ == SHIFTING_DOWN) {
         right_gear_ = LOW;
       }
+
+      gear_logging.left_state = left_gear_;
+      gear_logging.right_state = right_gear_;
+      LOG_STRUCT(DEBUG, "state", gear_logging);
     }
   }
 
@@ -433,19 +415,30 @@
     const double right_motor_speed =
         MotorSpeed(position_.right_shifter_position, current_right_velocity);
 
-    // Reset the CIM model to the current conditions to be ready for when we shift.
-    if (IsInGear(left_gear_)) {
-      left_cim_->X_hat(0, 0) = left_motor_speed;
-      LOG(DEBUG, "Setting left CIM to %f at robot speed %f\n", left_motor_speed,
-          current_left_velocity);
+    {
+      CIMLogging logging;
+
+      // Reset the CIM model to the current conditions to be ready for when we
+      // shift.
+      if (IsInGear(left_gear_)) {
+        logging.left_in_gear = true;
+        left_cim_->X_hat(0, 0) = left_motor_speed;
+      } else {
+        logging.left_in_gear = false;
+      }
+      logging.left_motor_speed = left_motor_speed;
+      logging.left_velocity = current_left_velocity;
+      if (IsInGear(right_gear_)) {
+        logging.right_in_gear = true;
+        right_cim_->X_hat(0, 0) = right_motor_speed;
+      } else {
+        logging.right_in_gear = false;
+      }
+      logging.right_motor_speed = right_motor_speed;
+      logging.right_velocity = current_right_velocity;
+
+      LOG_STRUCT(DEBUG, "currently", logging);
     }
-    if (IsInGear(right_gear_)) {
-      right_cim_->X_hat(0, 0) = right_motor_speed;
-      LOG(DEBUG, "Setting right CIM to %f at robot speed %f\n",
-          right_motor_speed, current_right_velocity);
-    }
-    LOG(DEBUG, "robot speed l=%f r=%f\n", current_left_velocity,
-        current_right_velocity);
 
     if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
       // FF * X = U (steady state)
@@ -505,7 +498,8 @@
       // Any motor is not in gear.  Speed match.
       ::Eigen::Matrix<double, 1, 1> R_left;
       R_left(0, 0) = left_motor_speed;
-      const double wiggle = (static_cast<double>((counter_ % 4) / 2) - 0.5) * 3.5;
+      const double wiggle =
+          (static_cast<double>((counter_ % 4) / 2) - 0.5) * 3.5;
 
       loop_->U(0, 0) =
           ::aos::Clip((R_left / Kv)(0, 0) + wiggle, -position_.battery_voltage,
@@ -528,14 +522,8 @@
     if (output != NULL) {
       output->left_voltage = loop_->U(0, 0);
       output->right_voltage = loop_->U(1, 0);
-    }
-    // Go in high gear if anything wants to be in high gear.
-    // TODO(austin): Seperate these.
-    if (left_gear_ == HIGH || left_gear_ == SHIFTING_UP ||
-        right_gear_ == HIGH || right_gear_ == SHIFTING_UP) {
-      shifters.MakeWithBuilder().set(false).Send();
-    } else {
-      shifters.MakeWithBuilder().set(true).Send();
+      output->left_high = left_gear_ == HIGH || left_gear_ == SHIFTING_UP;
+      output->right_high = right_gear_ == HIGH || right_gear_ == SHIFTING_UP;
     }
   }
 
@@ -581,7 +569,7 @@
 
   bool bad_pos = false;
   if (position == nullptr) {
-    LOG(WARNING, "no position\n");
+    LOG_INTERVAL(no_position_);
     bad_pos = true;
   }
 
@@ -600,7 +588,7 @@
     const double left_encoder = position->left_encoder;
     const double right_encoder = position->right_encoder;
     if (gyro.FetchLatest()) {
-      LOG(DEBUG, "gyro %f\n", gyro->angle);
+      LOG_STRUCT(DEBUG, "using", *gyro);
       dt_closedloop.SetPosition(left_encoder, right_encoder, gyro->angle,
                                 control_loop_driving);
     } else {
diff --git a/frc971/control_loops/drivetrain/drivetrain.gyp b/frc971/control_loops/drivetrain/drivetrain.gyp
index 7821c39..adb9f94 100644
--- a/frc971/control_loops/drivetrain/drivetrain.gyp
+++ b/frc971/control_loops/drivetrain/drivetrain.gyp
@@ -47,6 +47,8 @@
         '<(DEPTH)/aos/build/externals.gyp:libcdd',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/common/util/util.gyp:log_interval',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
       ],
       'export_dependent_settings': [
         '<(DEPTH)/aos/build/externals.gyp:libcdd',
diff --git a/frc971/control_loops/drivetrain/drivetrain.h b/frc971/control_loops/drivetrain/drivetrain.h
index 249de70..9ee7a27 100644
--- a/frc971/control_loops/drivetrain/drivetrain.h
+++ b/frc971/control_loops/drivetrain/drivetrain.h
@@ -7,6 +7,7 @@
 #include "aos/common/control_loop/ControlLoop.h"
 #include "aos/controls/polytope.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "aos/common/util/log_interval.h"
 
 namespace frc971 {
 namespace control_loops {
@@ -35,6 +36,10 @@
       const control_loops::Drivetrain::Position *position,
       control_loops::Drivetrain::Output *output,
       control_loops::Drivetrain::Status *status);
+
+  typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+  SimpleLogInterval no_position_ = SimpleLogInterval(
+      ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
 };
 
 }  // namespace control_loops
diff --git a/frc971/control_loops/drivetrain/drivetrain.q b/frc971/control_loops/drivetrain/drivetrain.q
index 1dcc947..443282c 100644
--- a/frc971/control_loops/drivetrain/drivetrain.q
+++ b/frc971/control_loops/drivetrain/drivetrain.q
@@ -2,6 +2,23 @@
 
 import "aos/common/control_loop/control_loops.q";
 
+struct GearLogging {
+  int8_t controller_index;
+  bool left_loop_high;
+  bool right_loop_high;
+  int8_t left_state;
+  int8_t right_state;
+};
+
+struct CIMLogging {
+  bool left_in_gear;
+  bool right_in_gear;
+  double left_motor_speed;
+  double right_motor_speed;
+  double left_velocity;
+  double right_velocity;
+};
+
 queue_group Drivetrain {
   implements aos.control_loops.ControlLoop;
 
@@ -28,6 +45,8 @@
   message Output {
     float left_voltage;
     float right_voltage;
+	bool left_high;
+	bool right_high;
   };
 
   message Status {
diff --git a/frc971/control_loops/python/shooter.py b/frc971/control_loops/python/shooter.py
index 11699ac..bb88523 100755
--- a/frc971/control_loops/python/shooter.py
+++ b/frc971/control_loops/python/shooter.py
@@ -132,17 +132,17 @@
   pylab.show()
 
   # Write the generated constants out to a file.
-  if len(argv) != 3:
+  if len(argv) != 5:
     print "Expected .h file name and .cc file name for"
     print "both the plant and unaugmented plant"
   else:
     unaug_shooter = Shooter("RawShooter")
     unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
                                                        [unaug_shooter])
-    #if argv[3][-3:] == '.cc':
-    #  unaug_loop_writer.Write(argv[4], argv[3])
-    #else:
-    #  unaug_loop_writer.Write(argv[3], argv[4])
+    if argv[3][-3:] == '.cc':
+      unaug_loop_writer.Write(argv[4], argv[3])
+    else:
+      unaug_loop_writer.Write(argv[3], argv[4])
 
     loop_writer = control_loop.ControlLoopWriter("Shooter", [shooter])
     if argv[1][-3:] == '.cc':
diff --git a/frc971/control_loops/shooter/shooter.cc b/frc971/control_loops/shooter/shooter.cc
old mode 100644
new mode 100755
index 56fd74b..86991e8
--- a/frc971/control_loops/shooter/shooter.cc
+++ b/frc971/control_loops/shooter/shooter.cc
@@ -1,79 +1,305 @@
-#include "frc971/control_loops/shooters/shooters.h"
+#include "frc971/control_loops/shooter/shooter.h"
 
 #include <stdio.h>
 
 #include <algorithm>
 
 #include "aos/common/control_loop/control_loops.q.h"
+#include "aos/common/control_loop/control_loops.q.h"
 #include "aos/common/logging/logging.h"
 
 #include "frc971/constants.h"
-#include "frc971/control_loops/shooters/top_shooter_motor_plant.h"
-#include "frc971/control_loops/shooters/bottom_shooter_motor_plant.h"
+#include "frc971/control_loops/shooter/shooter_motor_plant.h"
 
 namespace frc971 {
 namespace control_loops {
 
-ShooterMotor::ShooterMotor(control_loops::ShooterLoop *my_shooter)
-    : aos::control_loops::ControlLoop<control_loops::ShooterLoop>(my_shooter),
-      zeroed_joint_(MakeShooterLoop()) {
-  {
-    using ::frc971::constants::GetValues;
-    ZeroedJoint<1>::ConfigurationData config_data;
+using ::aos::time::Time;
+	
+void ZeroedStateFeedbackLoop::CapU() {
+  const double old_voltage = voltage_;
+  voltage_ += U(0, 0);
 
-    config_data.lower_limit = GetValues().shooter_lower_limit;
-    config_data.upper_limit = GetValues().shooter_upper_limit;
-    config_data.hall_effect_start_position[0] =
-        GetValues().shooter_hall_effect_start_position;
-    config_data.zeroing_off_speed = GetValues().shooter_zeroing_off_speed;
-    config_data.zeroing_speed = GetValues().shooter_zeroing_speed;
+  uncapped_voltage_ = voltage_;
 
-    config_data.max_zeroing_voltage = 5.0;
-    config_data.deadband_voltage = 0.0;
+  double limit = zeroing_state() != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
 
-    zeroed_joint_.set_config_data(config_data);
+  // Make sure that reality and the observer can't get too far off.  There is a
+  // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
+  // against last cycle's voltage.
+  if (X_hat(2, 0) > last_voltage_ + 2.0) {
+    voltage_ -= X_hat(2, 0) - (last_voltage_ + 2.0);
+    LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
+  } else if (X_hat(2, 0) < last_voltage_ -2.0) {
+    voltage_ += X_hat(2, 0) - (last_voltage_ - 2.0);
+    LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
   }
+
+  voltage_ = std::min(limit, voltage_);
+  voltage_ = std::max(-limit, voltage_);
+  U(0, 0) = voltage_ - old_voltage;
+  LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
+  LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
+
+  last_voltage_ = voltage_;
 }
 
-// Positive is up, and positive power is up.
+ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
+    : aos::control_loops::ControlLoop<control_loops::ShooterGroup>(my_shooter),
+      shooter_(MakeShooterLoop()),
+		calibration_position_(0.0),
+		state_(STATE_INITIALIZE),
+		loading_problem_end_time_(0,0),
+		shooter_brake_set_time_(0,0),
+		prepare_fire_end_time_(0,0),
+		shot_end_time_(0,0),
+		cycles_not_moved_(0) { }
+
+
+// Positive is out, and positive power is out.
 void ShooterMotor::RunIteration(
-    const ::aos::control_loops::Goal *goal,
-    const control_loops::ShooterLoop::Position *position,
-    ::aos::control_loops::Output *output,
-    ::aos::control_loops::Status * status) {
+    const control_loops::ShooterGroup::Goal *goal,
+    const control_loops::ShooterGroup::Position *position,
+    control_loops::ShooterGroup::Output *output,
+    control_loops::ShooterGroup::Status * status) {
+  constexpr double dt = 0.01;
+
+  // we must always have these or we have issues.
+  if (goal == NULL || status == NULL) {
+      if (output) output->voltage = 0;
+      LOG(ERROR, "Thought I would just check for null and die.\n");
+	  return;
+  }
 
   // Disable the motors now so that all early returns will return with the
   // motors disabled.
-  if (output) {
-    output->voltage = 0;
-  }
+  if (output) output->voltage = 0;
 
-  ZeroedJoint<1>::PositionData transformed_position;
-  ZeroedJoint<1>::PositionData *transformed_position_ptr =
-      &transformed_position;
-  if (!position) {
-    transformed_position_ptr = NULL;
-  } else {
-    transformed_position.position = position->pos;
-    transformed_position.hall_effects[0] = position->hall_effect;
-    transformed_position.hall_effect_positions[0] = position->calibration;
-  }
+  const frc971::constants::Values &values = constants::GetValues();
 
-  const double voltage = zeroed_joint_.Update(transformed_position_ptr,
-    output != NULL,
-    goal->goal, 0.0);
+  double real_position = position->position - calibration_position_;
+
+  // don't even let the control loops run
+  bool shooter_loop_disable = false;
+
+  // adds voltage to take up slack in gears before shot
+  bool apply_some_voltage = false;
+
+  switch (state_) {
+	  case STATE_INITIALIZE:
+		  // start off with the assumption that we are at the value
+		  // futhest back given our sensors
+		  if (position && position->pusher_distal_hall_effect){
+		  	  calibration_position_ = position->position -
+			  	  values.shooter.pusher_distal.lower_limit;
+		  } else if (position && position->pusher_proximal_hall_effect) {
+		  	  calibration_position_ = position->position -
+			  	  values.shooter.pusher_proximal.lower_limit;
+		  } else {
+		  	  calibration_position_ = values.shooter_total_length;
+		  }
+
+      state_ = STATE_REQUEST_LOAD;
+
+		  // zero out initial goal
+		  shooter_.SetGoalPosition(0.0, 0.0);
+      if (position) {
+        output->latch_piston = position->plunger_back_hall_effect;
+      } else {
+          // we don't know what is going on so just close the latch to be safe
+          output->latch_piston = true;
+      }
+      output->brake_piston = false;
+	  	break;
+	  case STATE_REQUEST_LOAD:
+      if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+          // already latched
+          state_ = STATE_PREPARE_SHOT;
+      } else if (position->pusher_distal_hall_effect ||
+              (real_position) < 0) {
+          state_ = STATE_LOAD_BACKTRACK;
+          if (position) {
+              calibration_position_ = position->position;
+          }
+      } else {
+          state_ = STATE_LOAD;
+      }
+
+		  shooter_.SetGoalPosition(0.0, 0.0);
+      if (position && output) output->latch_piston = position->plunger_back_hall_effect;
+      output->brake_piston = false;
+	  	break;
+	  case STATE_LOAD_BACKTRACK:
+      if (real_position < values.shooter.pusher_distal.upper_limit + 0.01) {
+		    shooter_.SetGoalPosition(position->position + values.shooter_zeroing_speed*dt,
+                values.shooter_zeroing_speed);
+      } else {
+          state_ = STATE_LOAD;
+      }
+
+      output->latch_piston = false;
+      output->brake_piston = true;
+	  	  break;
+	  case STATE_LOAD:
+        if (position->pusher_proximal_hall_effect &&
+              !last_position_.pusher_proximal_hall_effect) {
+		  	  calibration_position_ = position->position -
+			  	  values.shooter.pusher_proximal.upper_limit;
+        }
+        if (position->pusher_distal_hall_effect &&
+              !last_position_.pusher_distal_hall_effect) {
+		  	  calibration_position_ = position->position -
+			  	  values.shooter.pusher_distal.lower_limit;
+        }
+
+	  shooter_.SetGoalPosition(calibration_position_, 0.0);
+      if (position && output) output->latch_piston = position->plunger_back_hall_effect;
+      if(output) output->brake_piston = false;
+
+      if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+          state_ = STATE_PREPARE_SHOT;
+      } else if (position->plunger_back_hall_effect &&
+              position->position == PowerToPosition(goal->shot_power)) {
+          //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+          state_ = STATE_LOADING_PROBLEM;
+          loading_problem_end_time_ = Time::Now(Time::kDefaultClock) + Time::InSeconds(3.0);
+      }
+	  	  break;
+	  case STATE_LOADING_PROBLEM:
+      if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+          state_ = STATE_PREPARE_SHOT;
+      } else if (real_position < -0.02 || Time::Now() > loading_problem_end_time_) {
+          state_ = STATE_UNLOAD;
+      }
+
+		  shooter_.SetGoalPosition(position->position - values.shooter_zeroing_speed*dt,
+                values.shooter_zeroing_speed);
+      if (output) output->latch_piston = true;
+      if (output) output->brake_piston = false;
+	  	  break;
+	  case STATE_PREPARE_SHOT:
+        shooter_.SetGoalPosition(
+                PowerToPosition(goal->shot_power), 0.0);
+        //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+        if (position->position == PowerToPosition(goal->shot_power)) {
+            state_ = STATE_READY;
+            output->latch_piston = true;
+            output->brake_piston = true;
+            shooter_brake_set_time_ = Time::Now(Time::kDefaultClock) + Time::InSeconds(3.0);
+        } else {
+            output->latch_piston =true;
+            output->brake_piston = false;
+        }
+	  	  break;
+	  case STATE_READY:
+        if (Time::Now() > shooter_brake_set_time_) {
+          shooter_loop_disable = true;
+          if (goal->unload_requested) {
+              state_ = STATE_UNLOAD;
+          } else if (PowerToPosition(goal->shot_power)
+                  != position->position) {
+              //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+              state_ = STATE_PREPARE_SHOT;
+          }else if (goal->shot_requested) {
+              state_ = STATE_REQUEST_FIRE;
+          }
+
+        }
+        output->latch_piston = true;
+        output->brake_piston = true;
+	  	  break;
+	  case STATE_REQUEST_FIRE:
+        shooter_loop_disable = true;
+        if (position->plunger_back_hall_effect) {
+            prepare_fire_end_time_ = Time::Now(Time::kDefaultClock)
+				+ Time::InMS(40.0);
+            apply_some_voltage = true;
+            state_ = STATE_PREPARE_FIRE;
+        } else {
+            state_ = STATE_REQUEST_LOAD;
+        }
+	  	  break;
+	  case STATE_PREPARE_FIRE:
+        shooter_loop_disable = true;
+        if (Time::Now(Time::kDefaultClock) < prepare_fire_end_time_) {
+            apply_some_voltage = true;
+        } else {
+            state_ = STATE_FIRE;
+            cycles_not_moved_ = 0;
+            shot_end_time_ = Time::Now(Time::kDefaultClock) +
+				Time::InMS(500);
+        }
+
+        output->latch_piston = true;
+        output->brake_piston = true;
+
+	  	  break;
+	  case STATE_FIRE:
+        shooter_loop_disable = true;
+        //TODO_ben: need approamately equal
+        if (last_position_.position - position->position < 7) {
+            cycles_not_moved_++;
+        } else {
+            cycles_not_moved_ = 0;
+        }
+		if ((real_position < 10.0 && cycles_not_moved_ > 5) ||
+				Time::Now(Time::kDefaultClock) > shot_end_time_) {
+			state_ = STATE_REQUEST_LOAD;
+		}
+        output->latch_piston = true;
+        output->brake_piston = true;
+	  	  break;
+	  case STATE_UNLOAD:
+        if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+            shooter_.SetGoalPosition(0.02, 0.0);
+            if (real_position == 0.02) {
+                output->latch_piston = false;
+            }
+        } else {
+            output->latch_piston = false;
+            state_ = STATE_UNLOAD_MOVE;
+        }
+
+        output->brake_piston = false;
+	  	  break;
+	  case STATE_UNLOAD_MOVE:
+        if (position->position > values.shooter_total_length - 0.03) {
+          shooter_.SetGoalPosition(position->position, 0.0);
+          state_ = STATE_READY_UNLOAD;
+        } else {
+            shooter_.SetGoalPosition(
+                    position->position + values.shooter_zeroing_speed*dt,
+                    values.shooter_zeroing_speed);
+        }
+
+        output->latch_piston = false;
+        output->brake_piston = false;
+	  	  break;
+	  case STATE_READY_UNLOAD:
+        if (!goal->unload_requested) {
+            state_ = STATE_REQUEST_LOAD;
+        }
+
+        output->latch_piston = false;
+        output->brake_piston = false;
+	  	  break;
+  }
 
   if (position) {
-    LOG(DEBUG, "pos: %f hall: %s absolute: %f\n",
-        position->pos,
-        position->hall_effect ? "true" : "false",
-        zeroed_joint_.absolute_position());
+  	last_position_ = *position;
+    LOG(DEBUG, "pos:  hall: real: %.2f absolute: %.2f\n",
+        real_position, position->position);
   }
 
-  if (output) {
-    output->voltage = voltage;
+  if (apply_some_voltage) {
+  	  output->voltage = 2.0;
+  } else if (!shooter_loop_disable) {
+  	output->voltage = shooter_.voltage();
+  } else {
+  	output->voltage = 0.0;
   }
-  status->done = ::std::abs(zeroed_joint_.absolute_position() - goal->goal) < 0.004;
+
+  status->done = ::std::abs(position->position - PowerToPosition(goal->shot_power)) < 0.004;
 }
 
 }  // namespace control_loops
diff --git a/frc971/control_loops/shooter/shooter.gyp b/frc971/control_loops/shooter/shooter.gyp
old mode 100644
new mode 100755
index c4e9971..9811918
--- a/frc971/control_loops/shooter/shooter.gyp
+++ b/frc971/control_loops/shooter/shooter.gyp
@@ -23,6 +23,7 @@
       'sources': [
         'shooter.cc',
         'shooter_motor_plant.cc',
+        'unaugmented_shooter_motor_plant.cc',
       ],
       'dependencies': [
         'shooter_loop',
diff --git a/frc971/control_loops/shooter/shooter.h b/frc971/control_loops/shooter/shooter.h
new file mode 100755
index 0000000..4705f86
--- /dev/null
+++ b/frc971/control_loops/shooter/shooter.h
@@ -0,0 +1,215 @@
+#ifndef FRC971_CONTROL_LOOPS_shooter_shooter_H_
+#define FRC971_CONTROL_LOOPS_shooter_shooter_H_
+
+#include <memory>
+
+#include "aos/common/control_loop/ControlLoop.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "aos/common/time.h"
+
+#include "frc971/constants.h"
+#include "frc971/control_loops/shooter/shooter_motor_plant.h"
+#include "frc971/control_loops/shooter/shooter.q.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class ShooterTest_NoWindupPositive_Test;
+class ShooterTest_NoWindupNegative_Test;
+};
+
+using ::aos::time::Time;
+
+// Note: Everything in this file assumes that there is a 1 cycle delay between
+// power being requested and it showing up at the motor.  It assumes that
+// X_hat(2, 1) is the voltage being applied as well.  It will go unstable if
+// that isn't true.
+
+// This class implements the CapU function correctly given all the extra
+// information that we know about from the wrist motor.
+// It does not have any zeroing logic in it, only logic to deal with a delta U
+// controller.
+class ZeroedStateFeedbackLoop : public StateFeedbackLoop<3, 1, 1> {
+ public:
+  ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> loop)
+      : StateFeedbackLoop<3, 1, 1>(loop),
+        voltage_(0.0),
+        last_voltage_(0.0),
+        uncapped_voltage_(0.0),
+        offset_(0.0),
+        encoder_(0.0),
+        last_encoder_(0.0){}
+
+  const static int kZeroingMaxVoltage = 5;
+
+  // Caps U, but this time respects the state of the wrist as well.
+  virtual void CapU();
+
+  // Returns the accumulated voltage.
+  double voltage() const { return voltage_; }
+
+  // Returns the uncapped voltage.
+  double uncapped_voltage() const { return uncapped_voltage_; }
+
+  // Zeros the accumulator.
+  void ZeroPower() { voltage_ = 0.0; }
+
+  enum JointZeroingState {
+    // We don't know where the joint is at all.
+    UNKNOWN_POSITION,
+    // Ready for use during teleop.
+    CALIBRATED
+  };
+
+
+  void set_zeroing_state(JointZeroingState zeroing_state) {
+    zeroing_state_ = zeroing_state;
+  }
+  
+
+  JointZeroingState zeroing_state() const { return zeroing_state_; }
+
+
+  // Sets the calibration offset given the absolute angle and the corrisponding
+  // encoder value.
+  void SetCalibration(double encoder_val, double known_position) {
+    offset_ = known_position - encoder_val;
+  }
+
+
+  bool SetCalibrationOnEdge(const constants::Values::ShooterLimits &shooter_values,
+                            JointZeroingState zeroing_state) {
+    double edge_encoder;
+    double known_position;
+    if (GetPositionOfEdge(shooter_values, &edge_encoder, &known_position)) {
+      LOG(INFO, "Calibration edge.\n");
+      SetCalibration(edge_encoder, known_position);
+      set_zeroing_state(zeroing_state);
+      return true;
+    }
+    return false;
+  }
+
+
+  void SetPositionValues(double position) {
+    Eigen::Matrix<double, 1, 1> Y;
+    Y << position;
+    Correct(Y);
+  }
+
+
+  void SetGoalPosition(double desired_position,
+  		  double desired_velocity) {
+  	  // austin said something about which matrix to set, but I didn't under
+	  // very much of it
+	  //some_matrix = {desired_position, desired_velocity};
+	  R << desired_position, desired_velocity, 0;
+  }
+
+  double position() const { return X_hat(0, 0); }
+  double encoder() const { return encoder_; }
+  double last_encoder() const { return last_encoder_; }
+
+  // Returns true if an edge was detected in the last cycle and then sets the
+  // edge_position to the absolute position of the edge.
+  bool GetPositionOfEdge(const constants::Values::ShooterLimits &shooter,
+                         double *edge_encoder, double *known_position);
+
+#undef COUNT_SETTER_GETTER
+
+ private:
+  // The accumulated voltage to apply to the motor.
+  double voltage_;
+  double last_voltage_;
+  double uncapped_voltage_;
+  double offset_;
+
+  double previous_position_;
+
+  JointZeroingState zeroing_state_;
+  double encoder_;
+  double last_encoder_;
+};
+
+
+class ShooterMotor
+    : public aos::control_loops::ControlLoop<control_loops::ShooterGroup> {
+ public:
+  explicit ShooterMotor(
+      control_loops::ShooterGroup *my_shooter = &control_loops::shooter_queue_group);
+
+  // True if the goal was moved to avoid goal windup.
+  //bool capped_goal() const { return shooter_.capped_goal(); }
+
+  double PowerToPosition(double power) {
+    LOG(WARNING, "power to position not correctly implemented");
+    const frc971::constants::Values &values = constants::GetValues();
+    double new_pos =
+        (power > values.shooter.upper_limit) ? values.shooter.upper_limit
+         : (power < 0.0) ? 0.0 : power;
+
+    return new_pos;
+  }
+
+typedef enum {
+	STATE_INITIALIZE,
+	STATE_REQUEST_LOAD,
+	STATE_LOAD_BACKTRACK,
+	STATE_LOAD,
+	STATE_LOADING_PROBLEM,
+	STATE_PREPARE_SHOT,
+	STATE_READY,
+	STATE_REQUEST_FIRE,
+	STATE_PREPARE_FIRE,
+	STATE_FIRE,
+	STATE_UNLOAD,
+	STATE_UNLOAD_MOVE,
+	STATE_READY_UNLOAD
+} State;
+
+ protected:
+
+  virtual void RunIteration(
+      const ShooterGroup::Goal *goal,
+      const control_loops::ShooterGroup::Position *position,
+      ShooterGroup::Output *output,
+      ShooterGroup::Status *status);
+
+ private:
+  // Friend the test classes for acces to the internal state.
+  friend class testing::ShooterTest_NoWindupPositive_Test;
+  friend class testing::ShooterTest_NoWindupNegative_Test;
+
+  control_loops::ShooterGroup::Position last_position_;
+
+  ZeroedStateFeedbackLoop shooter_;
+
+  // position need to zero
+  double calibration_position_;
+
+  // state machine state
+  State state_;
+
+  // time to giving up on loading problem
+  Time loading_problem_end_time_;
+
+  // wait for brake to set
+  Time shooter_brake_set_time_;
+
+  // we are attempting to take up some of the backlash
+  // in the gears before the plunger hits
+  Time prepare_fire_end_time_;
+
+  // time that shot must have completed
+  Time shot_end_time_;
+
+  // track cycles that we are stuck to detect errors
+  int cycles_not_moved_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShooterMotor);
+};
+
+}  // namespace control_loops
+}  // namespace frc971
+
+#endif  // FRC971_CONTROL_LOOPS_shooter_shooter_H_
diff --git a/frc971/control_loops/shooter/shooter.q b/frc971/control_loops/shooter/shooter.q
old mode 100644
new mode 100755
index 860162e..670b10f
--- a/frc971/control_loops/shooter/shooter.q
+++ b/frc971/control_loops/shooter/shooter.q
@@ -1,35 +1,77 @@
 package frc971.control_loops;
 
-import "aos/common/control_loop/control_looops.q";
+import "aos/common/control_loop/control_loops.q";
 
-queue_group ShooterLoop {
+queue_group ShooterGroup {
   implements aos.control_loops.ControlLoop;
 
+  message Output {
+    double voltage;
+    // true: latch engaged, false: latch open
+    bool latch_piston;
+	// true: brake engaged false: brake released
+    bool brake_piston;
+  };
   message Goal {
-    // The energy to load to in joules.
-    double energy;
+    // encoder ticks of shot energy.
+    double shot_power;
     // Shoots as soon as this is true.
-    bool shoot;
+    bool shot_requested;
+    bool unload_requested;
   };
   message Position {
-    bool back_hall_effect;
+	// back on the plunger
+    bool plunger_back_hall_effect;
+	// truely back on the pusher
+	bool pusher_distal_hall_effect;
+	// warning that we are back on the pusher
+	bool pusher_proximal_hall_effect;
+	// the latch is closed
+	bool latch_hall_effect;
+	// the brake is closed
+	bool brake_hall_effect;
+
+	// count of positive edges
+	int64_t plunger_back_hall_effect_posedge_count;
+	// count of negative edges
+	int64_t plunger_back_hall_effect_negedge_count;
+	// count of positive edges
+	int64_t pusher_distal_hall_effect_posedge_count;
+	// count of negative edges
+	int64_t pusher_distal_hall_effect_negedge_count;
+	// count of positive edges
+	int64_t pusher_proximal_hall_effect_posedge_count;
+	// count of negative edges
+	int64_t pusher_proximal_hall_effect_negedge_count;
+	// count of positive edges
+	int64_t latch_hall_effect_posedge_count;
+	// count of negative edges
+	int64_t latch_hall_effect_negedge_count;
+
     // In meters, out is positive.
     double position;
-    double back_calibration;
+
+	// last positive edge
+	double posedge_value;
+	// last negative edge
+	double negedge_value;
   };
+  // I don't think this is needed, but it is here
+  // so I won't delete it yet.
   message Status {
     // Whether it's ready to shoot right now.
     bool ready;
     // Whether the plunger is in and out of the way of grabbing a ball.
     bool cocked;
     // How many times we've shot.
-    int shots;
+    int32_t shots;
+    bool done;
   };
 
   queue Goal goal;
   queue Position position;
-  queue aos.control_loops.Output output;
+  queue Output output;
   queue Status status;
 };
 
-queue_group ShooterLoop shooter;
+queue_group ShooterGroup shooter_queue_group;
diff --git a/frc971/control_loops/shooter/shooter_lib_test.cc b/frc971/control_loops/shooter/shooter_lib_test.cc
new file mode 100755
index 0000000..b2da13e
--- /dev/null
+++ b/frc971/control_loops/shooter/shooter_lib_test.cc
@@ -0,0 +1,255 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+#include "frc971/control_loops/shooter/shooter.q.h"
+#include "frc971/control_loops/shooter/shooter.h"
+#include "frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+#include "frc971/constants.h"
+
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+// Class which simulates the shooter and sends out queue messages containing the
+// position.
+class ShooterSimulation {
+ public:
+  // Constructs a motor simulation.
+  ShooterSimulation(double initial_position)
+      : shooter_plant_(new StateFeedbackPlant<3, 1, 1>(MakeShooterPlant())),
+        shooter_queue_group_(
+            ".frc971.control_loops.shooter_queue_group", 0xcbf22ba9,
+            ".frc971.control_loops.shooter_queue_group.goal",
+            ".frc971.control_loops.shooter_queue_group.position",
+            ".frc971.control_loops.shooter_queue_group.output",
+            ".frc971.control_loops.shooter_queue_group.status") {
+    Reinitialize(initial_position);
+  }
+  void Reinitialize(double initial_position) {
+    LOG(INFO, "Reinitializing to {top: %f}\n", initial_position);
+    StateFeedbackPlant<3, 1, 1>* plant = shooter_plant_.get();
+    initial_position_ = initial_position;
+    plant->X(0, 0) = initial_position_;
+    plant->X(1, 0) = 0.0;
+    plant->Y = plant->C() * plant->X;
+    last_voltage_ = 0.0;
+    last_plant_position_ = 0.0;
+    SetPhysicalSensors(&last_position_message_);
+  }
+
+
+  // Returns the absolute angle of the wrist.
+  double GetAbsolutePosition() const {
+      return shooter_plant_->Y(0,0);
+  }
+
+
+  // Returns the adjusted angle of the wrist.
+  double GetPosition() const {
+    return GetAbsolutePosition() - initial_position_;
+  }
+
+
+  // Makes sure pos is inside range (inclusive)
+  bool CheckRange(double pos, struct constants::Values::Pair pair) {
+    return (pos >= pair.lower_limit && pos <= pair.upper_limit);
+  }
+
+
+  // Sets the values of the physical sensors that can be directly observed
+  // (encoder, hall effect).
+  void SetPhysicalSensors(control_loops::ShooterGroup::Position *position) {
+    const frc971::constants::Values& values = constants::GetValues();
+    position->position = GetPosition();
+
+    LOG(DEBUG, "Physical shooter at {%f}\n", position->position);
+
+    // Signal that the hall effect sensor has been triggered if it is within
+    // the correct range.
+    position->plunger_back_hall_effect =
+        CheckRange(position->position, values.shooter.plunger_back);
+    position->pusher_distal_hall_effect =
+        CheckRange(position->position, values.shooter.pusher_distal);
+    position->pusher_proximal_hall_effect =
+        CheckRange(position->position, values.shooter.pusher_proximal);
+  }
+
+  void UpdateEffectEdge(bool effect, bool last_effect, double upper_angle,
+                        double lower_angle, double position,
+                        double &posedge_value, double &negedge_value,
+                        int64_t &posedge_count, int64_t &negedge_count) {
+   if (effect && !last_effect) {
+	  	  ++posedge_count;
+		  if (last_position_message_.position < lower_angle) {
+		  	  posedge_value = lower_angle - initial_position_;
+		  } else {
+		  	  posedge_value = upper_angle - initial_position_;
+		  }
+	  }
+
+	if (!effect && last_effect) {
+		++negedge_count;
+		if (position < lower_angle) {
+			negedge_value = lower_angle - initial_position_;
+		} else {
+			negedge_value = upper_angle - initial_position_;
+		}
+	}
+  }
+
+
+  // Sends out the position queue messages.
+  void SendPositionMessage() {
+    const frc971::constants::Values& values = constants::GetValues();
+    ::aos::ScopedMessagePtr<control_loops::ShooterGroup::Position> position =
+        shooter_queue_group_.position.MakeMessage();
+
+    SetPhysicalSensors(position.get());
+
+	// Handle plunger hall effect
+    UpdateEffectEdge(position->plunger_back_hall_effect,
+                     last_position_message_.plunger_back_hall_effect,
+                     values.shooter.plunger_back.upper_limit,
+                     values.shooter.plunger_back.lower_limit,
+                     position->position, position->posedge_value,
+                     position->negedge_value,
+                     position->plunger_back_hall_effect_posedge_count,
+                     position->plunger_back_hall_effect_negedge_count);
+
+ // Handle pusher distal hall effect
+    UpdateEffectEdge(position->pusher_distal_hall_effect,
+                     last_position_message_.pusher_distal_hall_effect,
+                     values.shooter.pusher_distal.upper_limit,
+                     values.shooter.pusher_distal.lower_limit,
+                     position->position, position->posedge_value,
+                     position->negedge_value,
+                     position->pusher_distal_hall_effect_posedge_count,
+                     position->pusher_distal_hall_effect_negedge_count);
+
+ // Handle pusher proximal hall effect
+    UpdateEffectEdge(position->pusher_proximal_hall_effect,
+                     last_position_message_.pusher_proximal_hall_effect,
+                     values.shooter.pusher_proximal.upper_limit,
+                     values.shooter.pusher_proximal.lower_limit,
+                     position->position, position->posedge_value,
+                     position->negedge_value,
+                     position->pusher_proximal_hall_effect_posedge_count,
+                     position->pusher_proximal_hall_effect_negedge_count);
+
+    last_position_message_ = *position;
+    position.Send();
+  }
+
+
+  // Simulates the claw moving for one timestep.
+  void Simulate() {
+    last_plant_position_ = shooter_plant_->Y(0, 0);
+    EXPECT_TRUE(shooter_queue_group_.output.FetchLatest());
+    shooter_plant_->U << last_voltage_;
+    shooter_plant_->Update();
+
+    EXPECT_GE(constants::GetValues().shooter.upper_limit,
+              shooter_plant_->Y(0, 0));
+    EXPECT_LE(constants::GetValues().shooter.lower_limit,
+              shooter_plant_->Y(0, 0));
+    last_voltage_ = shooter_queue_group_.output->voltage;
+  }
+
+
+  // pointer to plant
+  ::std::unique_ptr<StateFeedbackPlant<3, 1, 1>> shooter_plant_;
+
+
+ private:
+
+  ShooterGroup shooter_queue_group_;
+  double initial_position_;
+  double last_voltage_;
+
+  control_loops::ShooterGroup::Position last_position_message_;
+  double last_plant_position_;
+};
+
+
+class ShooterTest : public ::testing::Test {
+ protected:
+  ::aos::common::testing::GlobalCoreInstance my_core;
+
+  // Create a new instance of the test queue so that it invalidates the queue
+  // that it points to.  Otherwise, we will have a pointer to shared memory that
+  // is no longer valid.
+  ShooterGroup shooter_queue_group_;
+
+  // Create a loop and simulation plant.
+  ShooterMotor shooter_motor_;
+  ShooterSimulation shooter_motor_plant_;
+
+  ShooterTest()
+      : shooter_queue_group_(
+            ".frc971.control_loops.shooter_queue_group", 0xcbf22ba9,
+            ".frc971.control_loops.shooter_queue_group.goal",
+            ".frc971.control_loops.shooter_queue_group.position",
+            ".frc971.control_loops.shooter_queue_group.output",
+            ".frc971.control_loops.shooter_queue_group.status"),
+        shooter_motor_(&shooter_queue_group_),
+        shooter_motor_plant_(0.5) {
+    // Flush the robot state queue so we can use clean shared memory for this
+    // test.
+    ::aos::robot_state.Clear();
+    SendDSPacket(true);
+  }
+
+
+  void SendDSPacket(bool enabled) {
+    ::aos::robot_state.MakeWithBuilder().enabled(enabled)
+                                        .autonomous(false)
+                                        .team_id(971).Send();
+    ::aos::robot_state.FetchLatest();
+  }
+
+  void VerifyNearGoal() {
+    shooter_queue_group_.goal.FetchLatest();
+    shooter_queue_group_.position.FetchLatest();
+    double pos = shooter_motor_plant_.GetAbsolutePosition();
+    EXPECT_NEAR(shooter_queue_group_.goal->shot_power, pos, 1e-4);
+  }
+
+  virtual ~ShooterTest() { ::aos::robot_state.Clear(); }
+};
+
+
+TEST_F(ShooterTest, PowerConversion) {
+  // test a couple of values return the right thing
+  EXPECT_EQ(2.1, shooter_motor_.PowerToPosition(2.1));
+  EXPECT_EQ(50.99, shooter_motor_.PowerToPosition(50.99));
+
+  const frc971::constants::Values &values = constants::GetValues();
+  // value too large should get max
+  EXPECT_EQ(values.shooter.upper_limit,
+            shooter_motor_.PowerToPosition(505050.99));
+  // negative values should zero
+  EXPECT_EQ(0.0, shooter_motor_.PowerToPosition(-123.4));
+}
+
+// Tests that the wrist zeros correctly and goes to a position.
+TEST_F(ShooterTest, ZerosCorrectly) {
+  shooter_queue_group_.goal.MakeWithBuilder().shot_power(2.1).Send();
+  for (int i = 0; i < 400; ++i) {
+    shooter_motor_plant_.SendPositionMessage();
+    shooter_motor_.Iterate();
+    shooter_motor_plant_.Simulate();
+    SendDSPacket(true);
+  }
+  VerifyNearGoal();
+}
+
+}  // namespace testing
+}  // namespace control_loops
+}  // namespace frc971
diff --git a/frc971/control_loops/shooter/shooter_main.cc b/frc971/control_loops/shooter/shooter_main.cc
old mode 100644
new mode 100755
index cb6fc8b..e4e25ad
--- a/frc971/control_loops/shooter/shooter_main.cc
+++ b/frc971/control_loops/shooter/shooter_main.cc
@@ -4,7 +4,7 @@
 
 int main() {
   ::aos::Init();
-  frc971::control_loops::ShooterLoop shooter;
+  frc971::control_loops::ShooterMotor shooter;
   shooter.Run();
   ::aos::Cleanup();
   return 0;
diff --git a/frc971/control_loops/shooter/shooter_motor_plant.cc b/frc971/control_loops/shooter/shooter_motor_plant.cc
old mode 100644
new mode 100755
diff --git a/frc971/control_loops/shooter/shooter_motor_plant.h b/frc971/control_loops/shooter/shooter_motor_plant.h
old mode 100644
new mode 100755
diff --git a/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc
new file mode 100755
index 0000000..d395200
--- /dev/null
+++ b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc
@@ -0,0 +1,47 @@
+#include "frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients() {
+  Eigen::Matrix<double, 2, 2> A;
+  A << 1.0, 0.00988697090637, 0.0, 0.977479674375;
+  Eigen::Matrix<double, 2, 1> B;
+  B << 0.000120553991591, 0.0240196135246;
+  Eigen::Matrix<double, 1, 2> C;
+  C << 1, 0;
+  Eigen::Matrix<double, 1, 1> D;
+  D << 0;
+  Eigen::Matrix<double, 1, 1> U_max;
+  U_max << 12.0;
+  Eigen::Matrix<double, 1, 1> U_min;
+  U_min << -12.0;
+  return StateFeedbackPlantCoefficients<2, 1, 1>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 1, 1> MakeRawShooterController() {
+  Eigen::Matrix<double, 2, 1> L;
+  L << 1.87747967438, 87.0117404538;
+  Eigen::Matrix<double, 1, 2> K;
+  K << 343.469306513, 26.4814035344;
+  return StateFeedbackController<2, 1, 1>(L, K, MakeRawShooterPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant() {
+  ::std::vector<StateFeedbackPlantCoefficients<2, 1, 1> *> plants(1);
+  plants[0] = new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawShooterPlantCoefficients());
+  return StateFeedbackPlant<2, 1, 1>(plants);
+}
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop() {
+  ::std::vector<StateFeedbackController<2, 1, 1> *> controllers(1);
+  controllers[0] = new StateFeedbackController<2, 1, 1>(MakeRawShooterController());
+  return StateFeedbackLoop<2, 1, 1>(controllers);
+}
+
+}  // namespace control_loops
+}  // namespace frc971
diff --git a/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h
new file mode 100755
index 0000000..4deea48
--- /dev/null
+++ b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+#define FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients();
+
+StateFeedbackController<2, 1, 1> MakeRawShooterController();
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant();
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop();
+
+}  // namespace control_loops
+}  // namespace frc971
+
+#endif  // FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/update_shooter.sh b/frc971/control_loops/update_shooter.sh
index 8a18289..a9c5807 100755
--- a/frc971/control_loops/update_shooter.sh
+++ b/frc971/control_loops/update_shooter.sh
@@ -5,4 +5,6 @@
 cd $(dirname $0)
 
 ./python/shooter.py shooter/shooter_motor_plant.h \
-    shooter/shooter_motor_plant.cc
+    shooter/shooter_motor_plant.cc \
+    shooter/unaugmented_shooter_motor_plant.h \
+    shooter/unaugmented_shooter_motor_plant.cc
diff --git a/frc971/input/input.gyp b/frc971/input/input.gyp
index 0fef215..e87063b 100644
--- a/frc971/input/input.gyp
+++ b/frc971/input/input.gyp
@@ -31,6 +31,7 @@
         '<(DEPTH)/frc971/frc971.gyp:constants',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:sensor_reader',
         '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
       ],
     },
   ],
diff --git a/frc971/input/joystick_reader.cc b/frc971/input/joystick_reader.cc
index 4372098..e078e53 100644
--- a/frc971/input/joystick_reader.cc
+++ b/frc971/input/joystick_reader.cc
@@ -9,11 +9,9 @@
 
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/queues/gyro_angle.q.h"
-#include "frc971/queues/piston.q.h"
 #include "frc971/autonomous/auto.q.h"
 
 using ::frc971::control_loops::drivetrain;
-using ::frc971::control_loops::shifters;
 using ::frc971::sensors::gyro;
 
 using ::aos::input::driver_station::ButtonLocation;
@@ -32,9 +30,7 @@
 
 class Reader : public ::aos::input::JoystickInput {
  public:
-  Reader() {
-    shifters.MakeWithBuilder().set(true).Send();
-  }
+  Reader() {}
 
   virtual void RunIteration(const ::aos::input::driver_station::Data &data) {
     static bool is_high_gear = false;
diff --git a/frc971/input/sensor_receiver.cc b/frc971/input/sensor_receiver.cc
index 80c1c9f..2d7af87 100644
--- a/frc971/input/sensor_receiver.cc
+++ b/frc971/input/sensor_receiver.cc
@@ -4,12 +4,14 @@
 #include "aos/common/logging/logging.h"
 #include "aos/common/util/wrapping_counter.h"
 #include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
 
 #include "bbb/sensor_reader.h"
 
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/queues/gyro_angle.q.h"
 #include "frc971/constants.h"
+#include "frc971/queues/to_log.q.h"
 
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
@@ -55,8 +57,9 @@
 
 void PacketReceived(const ::bbb::DataStruct *data,
                     const ::aos::time::Time &cape_timestamp) {
-  LOG(DEBUG, "cape timestamp %010" PRId32 ".%09" PRId32 "s\n",
-      cape_timestamp.sec(), cape_timestamp.nsec());
+  ::frc971::logging_structs::CapeReading reading_to_log(cape_timestamp.sec(),
+                                                        cape_timestamp.nsec());
+  LOG_STRUCT(DEBUG, "cape reading", reading_to_log);
   bool bad_gyro;
   if (data->uninitialized_gyro) {
     LOG(DEBUG, "uninitialized gyro\n");
diff --git a/frc971/output/motor_writer.cc b/frc971/output/motor_writer.cc
index c6dbf63..0af5e2b 100644
--- a/frc971/output/motor_writer.cc
+++ b/frc971/output/motor_writer.cc
@@ -5,12 +5,15 @@
 #include "aos/prime/output/motor_output.h"
 #include "aos/common/logging/logging.h"
 #include "aos/linux_code/init.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
 
-#include "frc971/queues/piston.q.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 
+using ::aos::util::SimpleLogInterval;
+
 using ::frc971::control_loops::drivetrain;
-using ::frc971::control_loops::shifters;
 
 namespace frc971 {
 namespace output {
@@ -18,7 +21,8 @@
 class MotorWriter : public ::aos::MotorOutput {
   // Maximum age of an output packet before the motors get zeroed instead.
   static const int kOutputMaxAgeMS = 20;
-  static const int kEnableDrivetrain = true;
+  static constexpr ::aos::time::Time kOldLogInterval =
+      ::aos::time::Time::InSeconds(0.5);
 
   virtual void RunIteration() {
     values_.digital_module = 0;
@@ -26,29 +30,34 @@
     values_.compressor_channel = 1;
     values_.solenoid_module = 0;
 
-    drivetrain.output.FetchLatest();
-    if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS) && kEnableDrivetrain) {
-      SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
-    } else {
-      DisablePWMOutput(2);
-      DisablePWMOutput(3);
-      DisablePWMOutput(5);
-      DisablePWMOutput(6);
-      if (kEnableDrivetrain) {
-        LOG(WARNING, "drivetrain not new enough\n");
+    if (true) {
+      drivetrain.output.FetchLatest();
+      if (drivetrain.output.get()) {
+        LOG_STRUCT(DEBUG, "will output", *drivetrain.output.get());
+      }
+      if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS)) {
+        SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+        SetSolenoid(1, drivetrain.output->left_high);
+        SetSolenoid(2, drivetrain.output->right_high);
+      } else {
+        DisablePWMOutput(2);
+        DisablePWMOutput(3);
+        DisablePWMOutput(5);
+        DisablePWMOutput(6);
+        LOG_INTERVAL(drivetrain_old_);
       }
     }
-    shifters.FetchLatest();
-    if (shifters.get()) {
-      SetSolenoid(1, shifters->set);
-      SetSolenoid(2, !shifters->set);
-    }
   }
+
+  SimpleLogInterval drivetrain_old_ =
+      SimpleLogInterval(kOldLogInterval, WARNING, "drivetrain too old");
 };
 
+constexpr ::aos::time::Time MotorWriter::kOldLogInterval;
+
 }  // namespace output
 }  // namespace frc971
 
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 1d81c69..19fb6d1 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -35,6 +35,9 @@
         '<(DEPTH)/frc971/control_loops/drivetrain/drivetrain.gyp:drivetrain_loop',
         '<(AOS)/common/common.gyp:controls',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/common/util/util.gyp:log_interval',
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
       ],
     },
   ],
diff --git a/frc971/prime/prime.gyp b/frc971/prime/prime.gyp
index 3b699ad..e23272b 100644
--- a/frc971/prime/prime.gyp
+++ b/frc971/prime/prime.gyp
@@ -7,8 +7,8 @@
         '<(AOS)/build/aos_all.gyp:Prime',
         '../control_loops/drivetrain/drivetrain.gyp:drivetrain',
         '../control_loops/drivetrain/drivetrain.gyp:drivetrain_lib_test',
-        '../control_loops/claw/claw.gyp:claw',
-        '../control_loops/claw/claw.gyp:claw_lib_test',
+        '../control_loops/shooter/shooter.gyp:shooter',
+        '../control_loops/shooter/shooter.gyp:shooter_lib_test',
         '../autonomous/autonomous.gyp:auto',
         '../input/input.gyp:joystick_reader',
         '../output/output.gyp:motor_writer',
@@ -17,6 +17,7 @@
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:test_sensor_receiver',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:packet_finder_test',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:cows_test',
+        '<(DEPTH)/bbb_cape/src/flasher/flasher.gyp:stm32_flasher',
       ],
       'copies': [
         {
diff --git a/frc971/prime/scripts/start_list.txt b/frc971/prime/scripts/start_list.txt
index 79f9490..d7d20f4 100644
--- a/frc971/prime/scripts/start_list.txt
+++ b/frc971/prime/scripts/start_list.txt
@@ -1,4 +1,4 @@
-BinaryLogReader
+binary_log_writer
 motor_writer
 joystick_reader
 drivetrain
diff --git a/frc971/queues/piston.q b/frc971/queues/piston.q
deleted file mode 100644
index 5811958..0000000
--- a/frc971/queues/piston.q
+++ /dev/null
@@ -1,8 +0,0 @@
-package frc971.control_loops;
-
-message Piston {
-	bool set;
-};
-
-queue Piston shifters;
-queue Piston hangers;
diff --git a/frc971/queues/queues.gyp b/frc971/queues/queues.gyp
index 277c91c..a67ffe2 100644
--- a/frc971/queues/queues.gyp
+++ b/frc971/queues/queues.gyp
@@ -3,7 +3,7 @@
     'queue_files': [
       'gyro_angle.q',
       'photo_sensor.q',
-      'piston.q',
+      'to_log.q',
     ]
   },
   'targets': [
diff --git a/frc971/queues/to_log.q b/frc971/queues/to_log.q
new file mode 100644
index 0000000..c3cc076
--- /dev/null
+++ b/frc971/queues/to_log.q
@@ -0,0 +1,6 @@
+package frc971.logging_structs;
+
+struct CapeReading {
+  uint32_t sec;
+  uint32_t nsec;
+};
diff --git a/output/downloaded/.gitignore b/output/downloaded/.gitignore
index df9fd00..d91a4c2 100644
--- a/output/downloaded/.gitignore
+++ b/output/downloaded/.gitignore
@@ -4,11 +4,9 @@
 /eigen-3.1.3/
 /gccdist.zip
 /gccdist/
-/gtest-1.6.0/
+/gtest-1.6.0-p1/
 /gtest-1.6.0.zip
 /gyp-1738/
-/javacv-0.2-bin.zip
-/javacv-bin/
 /jpeg-8d/
 /jpegsrc.v8d.tar.gz
 /libjpeg/