diff --git a/components/audio/src/speaker.cpp b/components/audio/src/speaker.cpp
index 64c333851a1cd95e37902b8dd672423bc69dc79b..f9ebd5d6e84451cdc61cb5aa4f48de1685328ad1 100644
--- a/components/audio/src/speaker.cpp
+++ b/components/audio/src/speaker.cpp
@@ -83,6 +83,7 @@ void Speaker::_open(int fsize, int sample, int channels) {
 		buffer_ = new ftl::audio::MonoBuffer16<2000>(sample);
 	}
 
+	#ifdef HAVE_PORTAUDIO
 	auto err = Pa_OpenDefaultStream(
 		&stream_,
 		0,
@@ -111,6 +112,10 @@ void Speaker::_open(int fsize, int sample, int channels) {
 	}
 
 	LOG(INFO) << "Speaker ready.";
+	#else
+	LOG(INFO) << "Built without portaudio (no sound)";
+	#endif
+	
 }
 
 void Speaker::queue(int64_t ts, ftl::audio::Frame &frame) {
diff --git a/components/calibration/include/ftl/calibration/optimize.hpp b/components/calibration/include/ftl/calibration/optimize.hpp
index ea5908fce66b418d49710d608b723f0b731c7e3a..1009a97b21aa9d763801b7c512a4e1d13456b4a3 100644
--- a/components/calibration/include/ftl/calibration/optimize.hpp
+++ b/components/calibration/include/ftl/calibration/optimize.hpp
@@ -119,17 +119,21 @@ public:
 	 */
 	void run();
 
-	/** @brief Calculate MSE error (for one camera)
+	/** @brief Calculate RMS error (for one camera)
 	 */
-	double reprojectionError(int camera);
+	double reprojectionError(const int camera) const;
 
-	/** @brief Calculate MSE error for all cameras
+	/** @brief Calculate RMS error for all cameras
 	 */
-	double reprojectionError();
+	double reprojectionError() const;
 
 protected:
 	double* getCameraPtr(int i) { return cameras_[i]->data; }
 
+	/** @brief Calculate MSE error
+	 */
+	void _reprojectionErrorMSE(const int camera, double &error, double &npoints) const;
+
 	void _buildProblem(ceres::Problem& problem, const BundleAdjustment::Options& options);
 	void _buildBundleAdjustmentProblem(ceres::Problem& problem, const BundleAdjustment::Options& options);
 	void _buildLengthProblem(ceres::Problem& problem, const BundleAdjustment::Options& options);
diff --git a/components/calibration/src/optimize.cpp b/components/calibration/src/optimize.cpp
index 9ea70c4425091acb08a8348f164e8fa406ea1780..e5319e3fc190284d82f250cc8fc58a2df0fa1de5 100644
--- a/components/calibration/src/optimize.cpp
+++ b/components/calibration/src/optimize.cpp
@@ -418,7 +418,7 @@ void BundleAdjustment::run() {
 	run(options);
 }
 
-double BundleAdjustment::reprojectionError(int camera) {
+void BundleAdjustment::_reprojectionErrorMSE(const int camera, double &error, double &npoints) const {
 	vector<Point2d>	observations;
 	vector<Point3d>	points;
 
@@ -435,11 +435,24 @@ double BundleAdjustment::reprojectionError(int camera) {
 	auto rvec = cameras_[camera]->rvec();
 	auto tvec = cameras_[camera]->tvec();
 
-	return ftl::calibration::reprojectionError(observations, points, K, Mat::zeros(1, 5, CV_64FC1), rvec, tvec);
+	error = ftl::calibration::reprojectionError(observations, points, K, Mat::zeros(1, 5, CV_64FC1), rvec, tvec);
+	npoints = points.size();
 }
 
-double BundleAdjustment::reprojectionError() {
+double BundleAdjustment::reprojectionError(const int camera) const {
+	double error, ncameras;
+	_reprojectionErrorMSE(camera, ncameras, error);
+	return sqrt(error);
+}
+
+double BundleAdjustment::reprojectionError() const {
 	double error = 0.0;
-	for (size_t i = 0; i < cameras_.size(); i++) { error += reprojectionError(i); }
-	return error * (1.0 / (double) cameras_.size());
+	double npoints = 0.0;
+	for (size_t i = 0; i < cameras_.size(); i++) {
+		double e, n;
+		_reprojectionErrorMSE(i, e, n);
+		error += e * n;
+		npoints += n;
+	}
+	return sqrt(error / npoints);
 }
diff --git a/components/calibration/test/tests.cpp b/components/calibration/test/tests.cpp
index 178916eab8b9c7aabb87ff99894b48443ad6ecb6..0c7c351f437f5f43f3bb62beb254a9f1ecbec5a0 100644
--- a/components/calibration/test/tests.cpp
+++ b/components/calibration/test/tests.cpp
@@ -1,3 +1,2 @@
 #define CATCH_CONFIG_MAIN
 #include "catch.hpp"
-