aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Benjamin Barenblat <bbarenblat@galois.com>2013-12-20 13:57:39 -0800
committerGravatar Benjamin Barenblat <bbarenblat@galois.com>2014-01-06 14:44:40 -0800
commita9067eb46b845f70c9282bad59fbe6e8dba0e515 (patch)
tree8bf9dcaf564127e26f07a21ad2fb95c755f66ce5 /src
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am97
-rw-r--r--src/automobile.cpp403
-rw-r--r--src/automobile.h30
-rw-r--r--src/csv-inl.h58
-rw-r--r--src/csv.cpp48
-rw-r--r--src/csv.h71
-rw-r--r--src/main.cpp137
-rw-r--r--src/main.h44
-rw-r--r--src/measurement-inl.h62
-rw-r--r--src/measurement.cpp68
-rw-r--r--src/measurement.h93
-rw-r--r--src/noise-inl.h51
-rw-r--r--src/noise.cpp73
-rw-r--r--src/noise.h72
-rw-r--r--src/vrep-inl.h118
-rw-r--r--src/vrep.cpp72
-rw-r--r--src/vrep.h104
-rw-r--r--src/vrepFfi-inl.h133
-rw-r--r--src/vrepFfi.cpp68
-rw-r--r--src/vrepFfi.h158
20 files changed, 1960 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ae311c6
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,97 @@
+# Makefile.am -- automake script for the car plugin
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+# 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+# Inc.
+# Copyright (C) 2013 Galois, Inc.
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+# or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+# Oregon, 97204-1622.
+
+# We're building a library that gets loaded by dlopen, not a standard .so.
+AM_LDFLAGS = \
+ -avoid-version -module -shared -export-dynamic
+
+lib_LTLIBRARIES = libv_repExtAutomobile.la
+libv_repExtAutomobile_la_SOURCES = \
+ $(srcdir)/automobile.cpp \
+ $(srcdir)/automobile.h \
+ $(srcdir)/csv.cpp \
+ $(srcdir)/csv.h \
+ $(srcdir)/csv-inl.h \
+ $(srcdir)/main.cpp \
+ $(srcdir)/main.h \
+ $(srcdir)/measurement.cpp \
+ $(srcdir)/measurement.h \
+ $(srcdir)/measurement-inl.h \
+ $(srcdir)/noise.cpp \
+ $(srcdir)/noise.h \
+ $(srcdir)/noise-inl.h \
+ $(srcdir)/vrep.cpp \
+ $(srcdir)/vrep.h \
+ $(srcdir)/vrep-inl.h \
+ $(srcdir)/vrepFfi.cpp \
+ $(srcdir)/vrepFfi.h \
+ $(srcdir)/vrepFfi-inl.h
+libv_repExtAutomobile_la_CPPFLAGS = \
+ $(BOOST_CPPFLAGS)
+libv_repExtAutomobile_la_CXXFLAGS = \
+ -Wall \
+ -Wextra \
+ -pedantic \
+ @VREP_CXXFLAGS@
+libv_repExtAutomobile_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -export-symbols-regex '^v_rep' \
+ $(BOOST_FILESYSTEM_LDFLAGS)
+libv_repExtAutomobile_la_LIBADD = \
+ $(BOOST_FILESYSTEM_LIBS)
+
+# In addition to these sources, we also need to bundle a special object built
+# from code in the V-REP distribution. Ideally, I'd just add that source file
+# to 'libv_repExtAutomobile_la_SOURCES', but it's not exactly
+# standards-compliant and generates a ton of warnings. To get around this, use
+# per-object flags emulation (see "Per-Object Flags Emulation" in the Automake
+# manual).
+libv_repExtAutomobile_la_LIBADD += libv_repLib.la
+noinst_LTLIBRARIES = libv_repLib.la
+libv_repLib_la_SOURCES = @VREP@/programming/common/v_repLib.cpp
+libv_repLib_la_CXXFLAGS = @VREP_CXXFLAGS@
+
+# Override install and uninstall targets to stick the libraries in the V-REP
+# directory.
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(VREP)'"; \
+ $(MKDIR_P) "$(VREP)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install --no-warn $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(VREP)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install --no-warn $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(VREP)"; \
+ }
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(VREP)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(VREP)/$$f"; \
+ done
diff --git a/src/automobile.cpp b/src/automobile.cpp
new file mode 100644
index 0000000..365c96b
--- /dev/null
+++ b/src/automobile.cpp
@@ -0,0 +1,403 @@
+/* automobile.cpp -- recording data about the car
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+/* This module assumes only one car is recording data at once. Thus, it follows
+ * a singleton object pattern. To keep everything a bit simpler, the state
+ * variables live in an anonymous namespace, and the functions simply mutate
+ * them as appropriate. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cassert>
+#include <cstdarg>
+
+#include <array>
+#include <fstream>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <boost/filesystem.hpp>
+#include <boost/optional.hpp>
+#include <v_repLib.h>
+
+#include "automobile.h"
+#include "noise.h"
+#include "vrepFfi.h"
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+# include <cassert>
+# include <list>
+#endif
+
+
+namespace {
+
+ // Paths to the output files
+ namespace path {
+
+ std::string dataDir = "/tmp";
+
+ const std::string groundDir = "/ground";
+ const std::string noisyDir = "/noisy";
+
+ const std::string properties = "/properties.csv";
+ const std::string sensor = "/slam_sensor.csv";
+ const std::string pose = "/slam_gps.csv";
+ const std::string control = "/slam_control.csv";
+ const std::string laser = "/slam_laser.csv";
+
+ }
+
+ // Run specifications
+ struct Properties : public csv::Datum {
+ inline Properties(float L, float h, float a, float b, float theta0)
+ : L(L), h(h), a(a), b(b), theta0(theta0) {
+ }
+ inline virtual std::string csvHeader() const;
+ virtual std::string csv() const;
+ inline virtual unsigned int nCols() const;
+ // Distance between front and rear axles
+ float L;
+ // Distance between center of rear axle and encoder on left rear wheel
+ float h;
+ // Lengthwise distance between the rear axle and the lidar
+ float a;
+ // Widthwise distance between the rear axle and the lidar
+ float b;
+ // Initial angle of the car
+ float theta0;
+ };
+
+ // Individual sample records
+ struct Sample : public csv::Datum {
+ inline explicit Sample(const Pose &);
+ inline explicit Sample(const ControlSignals &);
+ inline explicit Sample(const LidarDatum &);
+ inline virtual std::string csvHeader() const;
+ inline virtual std::string csv() const;
+ inline virtual unsigned int nCols() const;
+ float time;
+ unsigned short sensorId;
+ };
+
+
+ // Lidar specifications //
+ namespace laser {
+
+ float MAX_DISTANCE = 10.;
+ float MAX_INTENSITY = 32768.;
+
+ }
+
+
+ // Noise //
+ namespace noise {
+
+ bool requested = false;
+
+ // Dummy noise source that provides no noise
+ GaussianNoiseSource<float> none(0., 0.);
+
+ // Noise sources for the various measurements (can be set via Lua)
+ boost::optional<GaussianNoiseSource<float>> position;
+ boost::optional<GaussianNoiseSource<float>> angle;
+ boost::optional<GaussianNoiseSource<float>> speed;
+ boost::optional<GaussianNoiseSource<float>> steeringAngle;
+ boost::optional<GaussianNoiseSource<float>> distance;
+ boost::optional<GaussianNoiseSource<float>> intensity;
+
+ }
+
+
+ // Prototypes //
+
+ // Lua callbacks
+ typedef simVoid LuaFunc(SLuaCallBack *);
+ LuaFunc init;
+ LuaFunc setNoiseParameters;
+ LuaFunc savePose;
+ LuaFunc saveControls;
+ LuaFunc saveLaser;
+
+ inline void savePropertiesFile(const Properties &);
+
+ template<typename CsvDatum>
+ inline void saveDatum(const std::string &subdir,
+ const std::string &filename, const CsvDatum &);
+
+ void outputDatum(const std::string &filePath, const csv::Datum &);
+
+}
+
+
+// Registering //
+
+void registerLuaFunctions() {
+ vrep::exposeFunction<
+ std::string,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float>(
+ "simExtAutomobileInit",
+ "simExtAutomobileInit(string directoryName, number L, number h, number a, number b, number theta0, number max_distance, number max_intensity)",
+ init);
+ vrep::exposeFunction<
+ std::vector<float>, // x and y
+ std::vector<float>, // angle
+ std::vector<float>, // speed
+ std::vector<float>, // steering angle
+ std::vector<float>, // distance
+ std::vector<float>>( // intensity
+ "simExtAutomobileRequestNoise",
+ "simExtAutomobileRequestNoise(table2 xy, table2 angle, table2 speed, table2 steeringAngle, table2 distance, table2 intensity)",
+ setNoiseParameters);
+ vrep::exposeFunction<float, float, float, float>(
+ "simExtAutomobileSavePose",
+ "simExtAutomobileSavePose(number simulationTime, number x, number y, number theta)",
+ savePose);
+ vrep::exposeFunction<float, float, float>(
+ "simExtAutomobileSaveControls",
+ "simExtAutomobileSaveControls(number simulationTime, number speed, number steeringAngle)",
+ saveControls);
+ /* V-REP's depth sensor part has a maximum field of view narrower than 180
+ * degrees. To compensate, we instead use two 90-degree depth sensors and
+ * combine the results here. */
+ vrep::exposeFunction<
+ float,
+ std::vector<float>, std::vector<float>, // depths
+ std::vector<float>, std::vector<float>>( // grayscale images
+ "simExtAutomobileSaveLaserPair",
+ "simExtAutomobileSaveLaserPair(number simulationTime, table leftDepthBuffer, table rightDepthBuffer, table leftImage, table rightImage)",
+ saveLaser);
+}
+
+
+// Callbacks //
+namespace {
+
+ std::string Properties::csvHeader() const {
+# ifdef HAVE_CXX11_INITIALIZER_LISTS
+ return csv::fromContainer(
+ std::vector<std::string> {"L", "h", "a", "b", "InitialAngle"});
+# else
+ return csv::fromContainer(
+ makeList<const char *>(nCols(), "L", "h", "a", "b",
+ "InitialAngle"));
+# endif
+ }
+
+ std::string Properties::csv() const {
+# ifdef HAVE_CXX11_INITIALIZER_LISTS
+ return csv::fromContainer(
+ std::vector<float> {L, h, a, b, theta0});
+# else
+ return csv::fromContainer(
+ makeList<float>(nCols(), L, h, a, b, theta0));
+# endif
+ }
+
+ unsigned int Properties::nCols() const {
+ return 5;
+ }
+
+ Sample::Sample(const Pose &pose)
+ : time(pose.time), sensorId(1) {
+ }
+
+ Sample::Sample(const ControlSignals &signals)
+ : time(signals.time), sensorId(2) {
+ }
+
+ Sample::Sample(const LidarDatum &datum)
+ : time(datum.time), sensorId(3) {
+ }
+
+ std::string Sample::csvHeader() const {
+ return "Time,Sensor";
+ }
+
+ std::string Sample::csv() const {
+ return std::to_string(time) + "," + std::to_string(sensorId);
+ }
+
+ unsigned int Sample::nCols() const {
+ return 2;
+ }
+
+ simVoid init(SLuaCallBack *const simCall) {
+ // Clean data from previous runs.
+ boost::filesystem::remove_all(path::dataDir + path::groundDir);
+ boost::filesystem::remove_all(path::dataDir + path::noisyDir);
+ boost::filesystem::remove(path::dataDir + path::properties);
+ // Save the passed directory as the base directory for the output.
+ vrep::LuaCall call(simCall);
+ path::dataDir = call.expectAtom<std::string>();
+ // Save the passed properties in the properties file.
+ const float L = call.expectAtom<float>();
+ const float h = call.expectAtom<float>();
+ const float a = call.expectAtom<float>();
+ const float b = call.expectAtom<float>();
+ const float theta0 = call.expectAtom<float>();
+ savePropertiesFile(Properties(L, h, a, b, theta0));
+ // Save the maximum distance and intensity settings.
+ laser::MAX_DISTANCE = call.expectAtom<float>();
+ laser::MAX_INTENSITY = call.expectAtom<float>();
+ // Reset the noise settings.
+ noise::requested = false;
+ const std::array<boost::optional<GaussianNoiseSource<float>> *const, 6> sources =
+ {{&noise::position, &noise::angle, &noise::speed,
+ &noise::steeringAngle, &noise::intensity, &noise::distance}};
+ for (boost::optional<GaussianNoiseSource<float>> *const source : sources) {
+ *source = boost::none;
+ }
+ }
+
+ simVoid setNoiseParameters(SLuaCallBack *const simCall) {
+ vrep::LuaCall call(simCall);
+ const std::array<boost::optional<GaussianNoiseSource<float>> *const, 6> sources =
+ {{&noise::position, &noise::angle, &noise::speed,
+ &noise::steeringAngle, &noise::intensity, &noise::distance}};
+ for (boost::optional<GaussianNoiseSource<float>> *const source : sources) {
+ *source = gaussian(call.expectTable<float>());
+ }
+ noise::requested = true;
+ }
+
+ simVoid savePose(SLuaCallBack *const simCall) {
+ // Build the pose.
+ vrep::LuaCall call(simCall);
+ const float time = call.expectAtom<float>();
+ const float x = call.expectAtom<float>();
+ const float y = call.expectAtom<float>();
+ const float theta = call.expectAtom<float>();
+ const Pose pose(time, x, y, theta);
+ // Record it.
+ saveDatum(path::groundDir, path::pose, pose);
+ if (noise::requested) {
+ saveDatum(path::noisyDir, path::pose,
+ addNoise(pose, noise::position.get_value_or(noise::none),
+ noise::angle.get_value_or(noise::none)));
+ }
+ }
+
+ simVoid saveControls(SLuaCallBack *const simCall) {
+ // Build the control systems object.
+ vrep::LuaCall call(simCall);
+ const float time = call.expectAtom<float>();
+ const float speed = call.expectAtom<float>();
+ const float angle = call.expectAtom<float>();
+ const ControlSignals signals(time, speed, angle);
+ // Record it.
+ saveDatum(path::groundDir, path::control, signals);
+ if (noise::requested) {
+ saveDatum(path::noisyDir, path::control,
+ addNoise(signals, noise::speed.get_value_or(noise::none),
+ noise::steeringAngle.get_value_or(noise::none)));
+ }
+ }
+
+ simVoid saveLaser(SLuaCallBack *const simCall) {
+ // Get the arguments and reconstruct the full lidar measurements.
+ vrep::LuaCall call(simCall);
+ const float time = call.expectAtom<float>();
+ std::vector<float> distance = call.expectTable<float>();
+ const std::vector<float> distanceRight = call.expectTable<float>();
+ distance.insert(distance.end(),
+ distanceRight.begin(), distanceRight.end());
+ std::vector<float> image = call.expectTable<float>();
+ const std::vector<float> imageRight = call.expectTable<float>();
+ image.insert(image.end(), imageRight.begin(), imageRight.end());
+ // Process the lidar measurements.
+# ifdef HAVE_CXX11_CLOSURES
+ std::for_each(distance.begin(), distance.end(),
+ [](float &d) { d *= laser::MAX_DISTANCE; });
+ std::for_each(image.begin(), image.end(),
+ [](float &i) { i *= laser::MAX_INTENSITY; });
+# else
+ for (float &d : distance) {
+ d *= laser::MAX_DISTANCE;
+ }
+ for (float &i : image) {
+ i *= laser::MAX_INTENSITY;
+ }
+# endif
+ // Build the lidar datum and record it.
+ const LidarDatum datum(time, distance, image);
+ saveDatum(path::groundDir, path::laser, datum);
+ if (noise::requested) {
+ saveDatum(path::noisyDir, path::laser,
+ addNoise(datum, noise::distance.get_value_or(noise::none),
+ noise::intensity.get_value_or(noise::none)));
+ }
+ }
+
+ void savePropertiesFile(const Properties &properties) {
+ boost::filesystem::create_directories(path::dataDir);
+ outputDatum(path::dataDir + path::properties, properties);
+ }
+
+ template<typename CsvDatum>
+ void saveDatum(const std::string &subdir, const std::string &filename,
+ const CsvDatum &datum) {
+ const std::string logDir = path::dataDir + subdir;
+ boost::filesystem::create_directories(logDir);
+ outputDatum(logDir + filename, datum);
+ outputDatum(logDir + path::sensor, Sample(datum));
+ }
+
+ void outputDatum(const std::string &filePath, const csv::Datum &datum) {
+ const std::string header = datum.csvHeader();
+ std::fstream file(filePath,
+ std::ios::in | std::ios::out | std::ios::app);
+ // Ensure we're writing correctly-formatted data.
+ std::string firstLine;
+ std::getline(file, firstLine);
+ if (file.eof() && firstLine.empty()) {
+ // The file was empty, so write in the header.
+ file.clear();
+ file << header << std::endl;
+ } else if (firstLine != header) {
+ /* The file was nonempty, but the first line did not match the
+ * expected header. This might occur if an external program
+ * modifies the file in between our writes, but more likely, we've
+ * got a bug somewhere that is causing us to write different data
+ * sets to the same file. */
+ throw std::logic_error(
+ std::string("CSV header mismatch: expected `")
+ + header
+ + "', but got `"
+ + firstLine
+ + "'");
+ } else {
+ /* The file was nonempty, and the headers matched what we expected.
+ * We're good to go. */
+ }
+ // Write the datum.
+ file << datum.csv() << std::endl;
+ }
+
+}
diff --git a/src/automobile.h b/src/automobile.h
new file mode 100644
index 0000000..0d3d2c1
--- /dev/null
+++ b/src/automobile.h
@@ -0,0 +1,30 @@
+/* automobile.h -- recording data about the car
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_AUTOMOBILE_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_AUTOMOBILE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+void registerLuaFunctions();
+
+#endif
diff --git a/src/csv-inl.h b/src/csv-inl.h
new file mode 100644
index 0000000..c1b7cb9
--- /dev/null
+++ b/src/csv-inl.h
@@ -0,0 +1,58 @@
+/* csv-inl.h -- CSV utilities
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_CSV_INL_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_CSV_INL_H
+
+namespace csv {
+
+ template<typename SequenceContainer>
+ std::string fromContainer(const SequenceContainer &columns) {
+ std::ostringstream line;
+ for (typename SequenceContainer::const_iterator col = columns.begin();
+ col != columns.end();
+ col++) {
+ if (col != columns.begin()) {
+ line << ",";
+ }
+ line << *col;
+ }
+ return line.str();
+ }
+
+}
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+
+ template<typename T>
+ std::list<T> makeList(typename std::list<T>::size_type size, ...) {
+ std::list<T> result;
+ va_list args;
+ va_start(args, size);
+ for (typename std::list<T>::size_type i = 0; i < size; i++) {
+ result.push_back(va_arg(args, T));
+ }
+ va_end(args);
+ return result;
+ }
+
+#endif
+
+#endif
diff --git a/src/csv.cpp b/src/csv.cpp
new file mode 100644
index 0000000..88d08b3
--- /dev/null
+++ b/src/csv.cpp
@@ -0,0 +1,48 @@
+/* csv.h -- CSV utilities
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+# include <cstdarg>
+# include <list>
+# include <sstream>
+#endif
+
+#include "csv.h"
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+
+ std::list<float> makeList(typename std::list<float>::size_type size, ...) {
+ std::list<float> result;
+ va_list args;
+ va_start(args, size);
+ for (typename std::list<double>::size_type i = 0;
+ i < static_cast<std::list<double>::size_type>(size);
+ i++) {
+ result.push_back(va_arg(args, double));
+ }
+ va_end(args);
+ return result;
+ }
+
+#endif
diff --git a/src/csv.h b/src/csv.h
new file mode 100644
index 0000000..fd46646
--- /dev/null
+++ b/src/csv.h
@@ -0,0 +1,71 @@
+/* csv.h -- CSV utilities
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_CSV_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_CSV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string>
+#include <sstream>
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+# include <cstdarg>
+# include <list>
+#endif
+
+namespace csv {
+
+ // Interface: Any datum which can be converted to a CSV string.
+ struct Datum {
+ virtual std::string csvHeader() const = 0;
+ virtual std::string csv() const = 0;
+ virtual unsigned int nCols() const = 0;
+ };
+
+ /* Converts a sequence of objects to a CSV string. It uses stringstreams
+ * internally, so all data in the sequence will get converted to strings
+ * using operator<<; however, no escaping will occur. */
+ template<typename SequenceContainer>
+ std::string fromContainer(const SequenceContainer &);
+
+}
+
+#ifndef HAVE_CXX11_INITIALIZER_LISTS
+
+ /* Without C++11 initializer list syntax, creating containers to pass to
+ * csv::fromContainer is really ugly. This function makes it substantially
+ * nicer, at the cost of forcing you to manually specify the length of the
+ * container (an unfortunate varargs limitation). */
+ template<typename T>
+ std::list<T> makeList(typename std::list<T>::size_type, ...);
+
+ /* floats get promoted to doubles in a va_list, so they need special
+ * handling. */
+ template<>
+ std::list<float> makeList(typename std::list<float>::size_type, ...);
+
+#endif
+
+#include "csv-inl.h"
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..6deb436
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,137 @@
+/* main.cpp -- plugin entry points
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstddef>
+
+#include <exception>
+#include <stdexcept>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <v_repLib.h>
+
+#include "automobile.h"
+#include "main.h"
+#include "vrep.h"
+#include "vrepFfi.h"
+
+namespace {
+
+ const unsigned char PLUGIN_VERSION = 1;
+
+ // The V-REP library that will get loaded at runtime
+ const std::string VREP_LIBRARY_NAME = "libv_rep.so";
+ LIBRARY vrepLibrary;
+
+
+ // Prototypes //
+
+ /* The library we think we're loading might not actually be the right
+ * library. */
+ class BadLibraryError : public std::runtime_error {
+ public:
+ explicit inline BadLibraryError();
+ };
+
+ // We might be using a too-old V-REP.
+ class TooOldVrepError : public std::runtime_error {
+ public:
+ explicit inline TooOldVrepError(const simInt version);
+ };
+
+ /* Loads the V-REP library and ensures it has the correct set of symbols.
+ * Throws 'std::exception' if an error occurs. */
+ // This function is declared inline because it is only used once.
+ inline void loadAndValidateVrepLibrary();
+
+ /* Throws 'std::exception' if the current version of V-REP is strictly less
+ than the 'simInt' passed. */
+ // This function is declared inline because it is only used once.
+ inline void requireVrepVersion(const simInt);
+}
+
+unsigned char v_repStart(void *, int) {
+ try {
+ loadAndValidateVrepLibrary();
+ // If we needed a specific version of V-REP, we'd check it here with
+ // 'requireVrepVersion'.
+ vrep::InterfaceLockGuard lockInterface;
+ // Register functions.
+ registerLuaFunctions();
+ // Returning zero indicates a failure condition.
+ static_assert(PLUGIN_VERSION != 0, "Plugin version must be nonzero");
+ return PLUGIN_VERSION;
+ } catch (const std::exception& error) {
+ // Loading the plugin failed. Return 0 to indicate failure.
+ return 0;
+ }
+}
+
+void v_repEnd() {
+ unloadVrepLibrary(vrepLibrary);
+}
+
+void *v_repMessage(int, int *, void *, int *) {
+ return nullptr;
+}
+
+namespace {
+
+ void loadAndValidateVrepLibrary() {
+ try {
+ // Load the V-REP library out of the V-REP directory.
+ const std::string vrepLibraryPath =
+ boost::filesystem::current_path().string()
+ + "/" + VREP_LIBRARY_NAME;
+ vrepLibrary = loadVrepLibrary(vrepLibraryPath.c_str());
+ // Make sure we can actually get code out of the V-REP library.
+ if (getVrepProcAddresses(vrepLibrary) == 0) {
+ throw BadLibraryError();
+ }
+ } catch (const std::exception& e) {
+ // Problems occurred loading the library; unload it before leaving.
+ unloadVrepLibrary(vrepLibrary);
+ throw e;
+ }
+ }
+
+ void requireVrepVersion(const simInt expected) {
+ simInt actual;
+ VREP(simGetIntegerParameter(sim_intparam_program_version, &actual));
+ if (actual < expected) {
+ throw TooOldVrepError(expected);
+ }
+ }
+
+ BadLibraryError::BadLibraryError()
+ : std::runtime_error("could not find all required functions in "
+ + VREP_LIBRARY_NAME) {
+ }
+
+ TooOldVrepError::TooOldVrepError(const simInt version)
+ : std::runtime_error("plugin requires V-REP >= "
+ + std::to_string(version)) {
+ }
+
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..4960210
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,44 @@
+/* main.h -- main plugin interface
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_MAIN_H_
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_MAIN_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* This interface is defined by V-REP itself. See
+ * <http://www.coppeliarobotics.com/helpFiles/en/plugins.htm>. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ unsigned char v_repStart(void *pointer, int integer);
+ void v_repEnd();
+ void *v_repMessage(int message, int *auxiliary_data, void *custom_data,
+ int *reply_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/measurement-inl.h b/src/measurement-inl.h
new file mode 100644
index 0000000..9762cd1
--- /dev/null
+++ b/src/measurement-inl.h
@@ -0,0 +1,62 @@
+/* measurement-inl.h -- data sets we gather
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_MEASUREMENT_INL_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_MEASUREMENT_INL_H
+
+std::string Pose::csvHeader() const {
+ return csvHeader_;
+}
+
+std::string Pose::csv() const {
+# ifdef HAVE_CXX11_INITIALIZER_LISTS
+ return csv::fromContainer(std::vector<float> {time, y, x, theta});
+# else
+ return csv::fromContainer(makeList<float>(nCols(), time, y, x, theta));
+# endif
+}
+
+unsigned int Pose::nCols() const {
+ return 4;
+}
+
+std::string ControlSignals::csvHeader() const {
+ return csvHeader_;
+}
+
+std::string ControlSignals::csv() const {
+# ifdef HAVE_CXX11_INITIALIZER_LISTS
+ return csv::fromContainer(
+ std::vector<float> {time, speed, steeringAngle});
+# else
+ return csv::fromContainer(
+ makeList<float>(nCols(), time, speed, steeringAngle));
+# endif
+}
+
+unsigned int ControlSignals::nCols() const {
+ return 3;
+}
+
+unsigned int LidarDatum::nCols() const {
+ return 1 + distance.size() + intensity.size();
+}
+
+#endif
diff --git a/src/measurement.cpp b/src/measurement.cpp
new file mode 100644
index 0000000..bcf20c3
--- /dev/null
+++ b/src/measurement.cpp
@@ -0,0 +1,68 @@
+/* measurement.cpp -- data sets we gather
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "csv.h"
+#include "measurement.h"
+
+const std::string Pose::csvHeader_ = "TimeGPS,GPSLat,GPSLon,Orientation";
+
+const std::string ControlSignals::csvHeader_ = "Time_VS,Velocity,Steering";
+
+std::string LidarDatum::csvHeader() const {
+ std::string result;
+ const std::string timeLaserHeader = "TimeLaser";
+ const std::string distanceHeader = "Laser";
+ const std::string intensityHeader = "Intensity";
+ result.reserve(timeLaserHeader.size()
+ + distance.size() * (1 + distanceHeader.size())
+ + intensity.size() * (1 + intensityHeader.size()));
+ result.append(timeLaserHeader);
+ for (std::vector<float>::size_type i = 0; i < distance.size(); i++) {
+ result.append(",");
+ result.append(distanceHeader);
+ }
+ for (std::vector<float>::size_type i = 0; i < intensity.size(); i++) {
+ result.append(",");
+ result.append(intensityHeader);
+ }
+ return result;
+}
+
+std::string LidarDatum::csv() const {
+ std::string result = std::to_string(time);
+ /* Yes, there are double braces in the initializer on the next line. See
+ * <http://stackoverflow.com/questions/11400090>. */
+ const std::array<std::vector<float>, 2> dataSets = {{distance, intensity}};
+ for (const std::vector<float> &data : dataSets) {
+ if (! data.empty()) {
+ result.append(",");
+ result.append(csv::fromContainer(data));
+ }
+ }
+ return result;
+}
diff --git a/src/measurement.h b/src/measurement.h
new file mode 100644
index 0000000..bad199c
--- /dev/null
+++ b/src/measurement.h
@@ -0,0 +1,93 @@
+/* measurement.h -- data sets we gather
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_MEASUREMENT_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_MEASUREMENT_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "csv.h"
+
+// Base class for time-series data
+struct Datum {
+ inline Datum(float time)
+ : time(time) {
+ }
+ float time;
+};
+
+
+// Datum definitions //
+
+class Pose : public Datum, public csv::Datum {
+public:
+ inline Pose(float time, float x, float y, float theta)
+ : ::Datum(time), x(x), y(y), theta(theta) {
+ }
+ float x;
+ float y;
+ float theta;
+ inline virtual std::string csvHeader() const;
+ inline virtual std::string csv() const;
+ inline virtual unsigned int nCols() const;
+
+protected:
+ static const std::string csvHeader_;
+};
+
+
+class ControlSignals : public Datum, public csv::Datum {
+public:
+ inline ControlSignals(float time, float speed, float steeringAngle)
+ : ::Datum(time), speed(speed), steeringAngle(steeringAngle) {
+ }
+ float speed;
+ float steeringAngle;
+ inline virtual std::string csvHeader() const;
+ inline virtual std::string csv() const;
+ inline virtual unsigned int nCols() const;
+
+protected:
+ static const std::string csvHeader_;
+};
+
+
+class LidarDatum : public Datum, public csv::Datum {
+public:
+ inline LidarDatum(float time, std::vector<float> distance,
+ std::vector<float> intensity)
+ : ::Datum(time), distance(distance), intensity(intensity) {
+ }
+ std::vector<float> distance;
+ std::vector<float> intensity;
+ virtual std::string csvHeader() const;
+ inline virtual std::string csv() const;
+ inline virtual unsigned int nCols() const;
+};
+
+
+#include "measurement-inl.h"
+
+#endif
diff --git a/src/noise-inl.h b/src/noise-inl.h
new file mode 100644
index 0000000..5013a3c
--- /dev/null
+++ b/src/noise-inl.h
@@ -0,0 +1,51 @@
+/* noise-inl.h -- noisy data
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_NOISE_INL_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_NOISE_INL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+template<typename RealType>
+GaussianNoiseSource<RealType>::GaussianNoiseSource(RealType mean,
+ RealType stddev)
+ : generator(), distribution(mean, stddev) {
+ generator.seed(std::random_device()());
+}
+
+template<typename RealType>
+RealType GaussianNoiseSource<RealType>::get() {
+ return distribution(generator);
+}
+
+GaussianNoiseSource<float> gaussian(const std::vector<float> params) {
+ if (params.size() != 2) {
+ throw std::invalid_argument(std::string()
+ + "expected two-element vector of distribution parameters "
+ + "(got " + std::to_string(params.size()) + "-element "
+ + "vector instead");
+ } else {
+ return GaussianNoiseSource<float>(params[0], params[1]);
+ }
+}
+
+#endif
diff --git a/src/noise.cpp b/src/noise.cpp
new file mode 100644
index 0000000..bee0224
--- /dev/null
+++ b/src/noise.cpp
@@ -0,0 +1,73 @@
+/* noise.h -- noisy data
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_CXX11_CLOSURES
+# include <algorithm>
+#endif
+
+#include "measurement.h"
+#include "noise.h"
+
+Pose addNoise(const Pose &pose,
+ GaussianNoiseSource<float> &positionNoise,
+ GaussianNoiseSource<float> &angleNoise) {
+ Pose result = pose;
+ result.x += positionNoise.get();
+ result.y += positionNoise.get();
+ result.theta += angleNoise.get();
+ return result;
+}
+
+ControlSignals addNoise(const ControlSignals &signals,
+ GaussianNoiseSource<float> &speedNoise,
+ GaussianNoiseSource<float> &steeringAngleNoise) {
+ ControlSignals result = signals;
+ result.speed += speedNoise.get();
+ result.steeringAngle += steeringAngleNoise.get();
+ return result;
+}
+
+LidarDatum addNoise(const LidarDatum &datum,
+ GaussianNoiseSource<float> &distanceNoise,
+ GaussianNoiseSource<float> &intensityNoise) {
+ LidarDatum result = datum;
+# ifdef HAVE_CXX11_CLOSURES
+ std::for_each(result.distance.begin(), result.distance.end(),
+ [&distanceNoise](float &d) {
+ d += distanceNoise.get();
+ });
+ std::for_each(result.intensity.begin(), result.intensity.end(),
+ [&intensityNoise](float &i) {
+ i += intensityNoise.get();
+ });
+# else
+ for (float &d : result.distance) {
+ d += distanceNoise.get();
+ }
+ for (float &i : result.intensity) {
+ i += intensityNoise.get();
+ }
+# endif
+ return result;
+}
diff --git a/src/noise.h b/src/noise.h
new file mode 100644
index 0000000..e0b9980
--- /dev/null
+++ b/src/noise.h
@@ -0,0 +1,72 @@
+/* noise.h -- noisy data
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_NOISE_H
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_NOISE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <random>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "csv.h"
+#include "measurement.h"
+
+
+// Noise sources //
+
+template<typename RealType = double>
+class GaussianNoiseSource {
+public:
+ inline GaussianNoiseSource(RealType mean, RealType stddev);
+ inline RealType get();
+
+private:
+ std::mt19937 generator;
+ std::normal_distribution<RealType> distribution;
+};
+
+/* Convenience function to construct a GaussianNoiseSource<float> from a
+ * two-element float vector representing mean and standard deviation.
+ * Throws a std::invalid_argument if the vector is of the wrong
+ * length. */
+inline GaussianNoiseSource<float> gaussian(const std::vector<float>);
+
+
+// Noisy values //
+
+// Adds noise to a value.
+Pose addNoise(const Pose &, GaussianNoiseSource<float> &positionNoise,
+ GaussianNoiseSource<float> &angleNoise);
+ControlSignals addNoise(const ControlSignals &,
+ GaussianNoiseSource<float> &speedNoise,
+ GaussianNoiseSource<float> &steeringAngleNoise);
+LidarDatum addNoise(const LidarDatum &,
+ GaussianNoiseSource<float> &distanceNoise,
+ GaussianNoiseSource<float> &intensityNoise);
+
+
+#include "noise-inl.h"
+
+#endif
diff --git a/src/vrep-inl.h b/src/vrep-inl.h
new file mode 100644
index 0000000..84d8ef0
--- /dev/null
+++ b/src/vrep-inl.h
@@ -0,0 +1,118 @@
+/* vrep-inl.h -- convenience code to interface with V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_VREP_INL_H_
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_VREP_INL_H_
+
+namespace vrep {
+
+ // class InterfaceLockGuard
+
+ InterfaceLockGuard::InterfaceLockGuard() {
+ lock();
+ }
+
+ InterfaceLockGuard::InterfaceLockGuard(const InterfaceLockGuard &other) {
+ copyState(other);
+ }
+
+ InterfaceLockGuard &InterfaceLockGuard::operator=(
+ const InterfaceLockGuard &other) {
+ copyState(other);
+ return *this;
+ }
+
+ InterfaceLockGuard::InterfaceLockGuard(InterfaceLockGuard &&other) {
+ moveState(other);
+ }
+
+ InterfaceLockGuard &InterfaceLockGuard::operator=(
+ InterfaceLockGuard &&other) {
+ moveState(other);
+ return *this;
+ }
+
+ void InterfaceLockGuard::lock() {
+ VREP(simLockInterface(true));
+ isLocked = true;
+ }
+
+ void InterfaceLockGuard::unlock() {
+ VREP(simLockInterface(false));
+ isLocked = false;
+ }
+
+
+ // class Error
+
+ Error::Error(const std::string &whatArg)
+ : std::runtime_error(whatArg) {
+ }
+
+ Error::Error(const char *whatArg)
+ : Error(std::string(whatArg)) {
+ }
+
+
+ // template<typename T> class FunctionError<T>
+
+ template<typename T>
+ FunctionError<T>::FunctionError(const std::string &invocation,
+ const T result)
+ : Error("V-REP function invocation "
+ + invocation
+ + " failed, returning "
+ + std::to_string(result)) {
+ }
+
+ template<typename T>
+ FunctionError<T>::FunctionError(const char *invocation, const T result)
+ : FunctionError(std::string(invocation), result) {
+ }
+
+
+ // template<typename T> T checkFunction(T, const char[])
+
+ template<typename T>
+ T checkFunction(T result, const char invocation[]) {
+ if (isSuccess(result)) {
+ return result;
+ } else {
+ throw vrep::FunctionError<T>(invocation, result);
+ }
+ }
+
+
+ // template<typename T> bool isSuccess(const T)
+
+ template<>
+ bool isSuccess(const simInt val) {
+ return val != -1;
+ }
+
+ template<>
+ bool isSuccess(const simVoid *const val) {
+ return val != nullptr;
+ }
+
+}
+
+#endif
+
diff --git a/src/vrep.cpp b/src/vrep.cpp
new file mode 100644
index 0000000..2043b45
--- /dev/null
+++ b/src/vrep.cpp
@@ -0,0 +1,72 @@
+/* vrep.cpp -- convenience code to interface with V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdexcept>
+#include <string>
+
+#include <v_repLib.h>
+
+#include "vrep.h"
+
+namespace vrep {
+
+ InterfaceLockGuard::~InterfaceLockGuard() noexcept {
+ if (isLocked) {
+ /* Throwing an exception from a destructor is a bad, bad idea. See
+ * /More Effective C++/ #14. */
+ try {
+ unlock();
+ } catch (const Error& error) {
+ /* Unlocking failed, so we're kind of screwed at this point. We
+ * can't even display a dialog to the user noting the failure,
+ * because the interface is locked! *sigh* Just give up and do
+ * nothing. */
+ }
+ }
+ }
+
+ void InterfaceLockGuard::copyState(const InterfaceLockGuard &other) {
+ if (isLocked) {
+ unlock();
+ }
+ if (other.isLocked) {
+ lock();
+ } else {
+ isLocked = false;
+ }
+ }
+
+ void InterfaceLockGuard::moveState(InterfaceLockGuard &other) {
+ if (isLocked) {
+ unlock();
+ }
+ if (other.isLocked) {
+ lock();
+ other.unlock();
+ } else {
+ isLocked = false;
+ }
+ }
+
+}
diff --git a/src/vrep.h b/src/vrep.h
new file mode 100644
index 0000000..7a1bac0
--- /dev/null
+++ b/src/vrep.h
@@ -0,0 +1,104 @@
+/* vrep.h -- convenience code to interface with V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_VREP_H_
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_VREP_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstddef>
+
+#include <stdexcept>
+#include <string>
+
+#include <v_repLib.h>
+
+/* All the convenience code lives in the 'vrep' namespace to distinguish it from
+ * the actual V-REP functions, which all are prefixed with 'sim'. */
+namespace vrep {
+
+ // A RAII wrapper for 'simLockInterface'.
+ class InterfaceLockGuard {
+ public:
+ inline InterfaceLockGuard();
+ ~InterfaceLockGuard() noexcept;
+ // Copying
+ inline InterfaceLockGuard(const InterfaceLockGuard &);
+ inline InterfaceLockGuard &operator=(const InterfaceLockGuard &);
+ // Moving
+ inline InterfaceLockGuard(InterfaceLockGuard &&);
+ inline InterfaceLockGuard &operator=(InterfaceLockGuard &&);
+
+ private:
+ inline void lock();
+ inline void unlock();
+ void copyState(const InterfaceLockGuard &);
+ void moveState(InterfaceLockGuard &);
+
+ bool isLocked;
+ };
+
+
+ // Error handling //
+
+ // The class for all errors which occur when interfacing with V-REP.
+ class Error : public std::runtime_error {
+ public:
+ explicit inline Error(const std::string &whatArg);
+ explicit inline Error(const char *whatArg);
+ };
+
+ // An error signaling a failed V-REP function invocation.
+ template<typename T>
+ class FunctionError : public Error {
+ public:
+ FunctionError(const std::string &invocation, const T result);
+ inline FunctionError(const char *invocation, const T result);
+ };
+
+/* A nice macro to convert failed V-REP function invocations to
+ * 'FunctionError's. Simply wrap your V-REP function interface--e.g.,
+ *
+ * simInt x = VREP(simGetIntegerParameter(foo, &bar));
+ *
+ * --and you'll be off to the races. 'x' will be assigned the result of the
+ * invocation if and only if the invocation succeeded; otherwise, a
+ * 'FunctionError' describing the failed invocation will be thrown.
+ *
+ * This needs to be a macro so that it can include the code of the failed
+ * invocation itself. */
+#define VREP(invocation) vrep::checkFunction((invocation), #invocation)
+
+ template<typename T>
+ T checkFunction(T result, const char invocation[]);
+
+ /* Returns 'true' iff the passed value signifies a successful V-REP function
+ * invocation. */
+ template<typename T>
+ inline bool isSuccess(const T);
+ template<> inline bool isSuccess(const simInt);
+ template<> inline bool isSuccess(const simVoid *);
+}
+
+#include "vrep-inl.h"
+
+#endif
diff --git a/src/vrepFfi-inl.h b/src/vrepFfi-inl.h
new file mode 100644
index 0000000..554d954
--- /dev/null
+++ b/src/vrepFfi-inl.h
@@ -0,0 +1,133 @@
+/* vrepFfi-inl.h -- exposing functions to V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_VREPFFI_INL_H_
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_VREPFFI_INL_H_
+
+namespace vrep {
+
+ namespace {
+
+ // Converting a variadic template type to a Lua type list //
+
+ std::forward_list<int> toLuaTypes() {
+ return std::forward_list<int>();
+ }
+
+ template<typename Car>
+ std::forward_list<int> toLuaTypes() {
+ std::forward_list<int> luaTypes = toLuaTypes();
+ luaTypes.push_front(LuaType<Car>::id);
+ return luaTypes;
+ }
+
+ template<typename Car, typename Cadr, typename ...Cddr>
+ std::forward_list<int> toLuaTypes() {
+ std::forward_list<int> luaTypes = toLuaTypes<Cadr, Cddr...>();
+ luaTypes.push_front(LuaType<Car>::id);
+ return luaTypes;
+ }
+
+ }
+
+
+ // Exposing C++ functions to Lua //
+
+ template<typename ...Args>
+ void exposeFunction(const std::string name, const std::string callTips,
+ simVoid (*const f)(SLuaCallBack *)) {
+ // Compute the Lua signature.
+ std::vector<int> luaTypes;
+ luaTypes.push_back(0);
+ for (int type : toLuaTypes<Args...>()) {
+ luaTypes.push_back(type);
+ // We just added an argument.
+ luaTypes[0]++;
+ }
+ // Register the function with Lua.
+ VREP(simRegisterCustomLuaFunction(name.c_str(), callTips.c_str(),
+ luaTypes.data(), f));
+ }
+
+
+ // Extracting Lua arguments from the callback structure //
+
+ template<typename T>
+ T LuaCall::expectAtom() {
+ ensureNextArgType(LuaType<T>::id);
+ const T result = unsafeGetAtom<T>();
+ argIdx++;
+ return result;
+ }
+
+ template<typename T>
+ std::vector<T> LuaCall::expectTable() {
+ ensureNextArgType(LuaType<std::vector<T>>::id);
+ const std::vector<T> result = unsafeGetTable<T>();
+ argIdx++;
+ return result;
+ }
+
+ template<typename T>
+ std::vector<T> LuaCall::unsafeGetTable() {
+ // Determine how long the table is.
+ size_t tableLen;
+ if (simCall->inputArgTypeAndSize[2 * argIdx] & sim_lua_arg_table) {
+ tableLen = simCall->inputArgTypeAndSize[2 * argIdx + 1];
+ } else {
+ tableLen = 1;
+ }
+ // Build the result.
+ std::vector<T> result(tableLen);
+ for (size_t i = 0; i < tableLen; i++) {
+ result[i] = unsafeGetAtom<T>();
+ }
+ return result;
+ }
+
+ template<>
+ bool LuaCall::unsafeGetAtom() {
+ return *cursorBool++;
+ }
+
+ template<>
+ int LuaCall::unsafeGetAtom() {
+ return *cursorInt++;
+ }
+
+ template<>
+ float LuaCall::unsafeGetAtom() {
+ return *cursorFloat++;
+ }
+
+
+ // Error handling //
+
+ MarshalingError::MarshalingError(const std::string &whatArg)
+ : Error(whatArg) {
+ }
+
+ MarshalingError::MarshalingError(const char *whatArg)
+ : MarshalingError(std::string(whatArg)) {
+ }
+
+}
+
+#endif
diff --git a/src/vrepFfi.cpp b/src/vrepFfi.cpp
new file mode 100644
index 0000000..4641bbf
--- /dev/null
+++ b/src/vrepFfi.cpp
@@ -0,0 +1,68 @@
+/* vrepFfi.cpp -- exposing functions to V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstring>
+
+#include <string>
+
+#include "vrepFfi.h"
+
+namespace vrep {
+
+ // Extracting Lua arguments from the callback structure //
+
+ LuaCall::LuaCall(SLuaCallBack *simCall)
+ : simCall(simCall), argIdx(0), cursorBool(simCall->inputBool),
+ cursorInt(simCall->inputInt), cursorFloat(simCall->inputFloat),
+ cursorChar(simCall->inputChar) {
+ }
+
+ void LuaCall::ensureNextArgType(const int expected) {
+ const int nextType = simCall->inputArgTypeAndSize[2 * argIdx];
+ if (nextType != expected) {
+ throw MarshalingError("unexpected Lua argument (expected type "
+ + std::to_string(expected)
+ + ", got type "
+ + std::to_string(nextType)
+ + ")");
+ }
+ }
+
+ template<>
+ std::string LuaCall::unsafeGetAtom() {
+ // Determine how long the string is. Lua uses '@' as its sentinel.
+ size_t len;
+ simChar *sentinel = std::strchr(cursorChar, '@');
+ if (sentinel) {
+ len = sentinel - cursorChar;
+ } else {
+ len = std::strlen(cursorChar);
+ }
+ // Build the 'std::string'.
+ const std::string result = std::string(cursorChar, len);
+ cursorChar += len + (sentinel ? 1 : 0);
+ return result;
+ }
+
+}
diff --git a/src/vrepFfi.h b/src/vrepFfi.h
new file mode 100644
index 0000000..0dbab1e
--- /dev/null
+++ b/src/vrepFfi.h
@@ -0,0 +1,158 @@
+/* vrepFfi.h -- exposing functions to V-REP
+ * Copyright (C) 2013 Galois, Inc.
+ *
+ * This library is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * To contact Galois, complete the Web form at <http://corp.galois.com/contact/>
+ * or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland,
+ * Oregon, 97204-1622. */
+
+#ifndef PPAML_VREP_AUTOMOBILE_PLUGIN_VREPFFI_H_
+#define PPAML_VREP_AUTOMOBILE_PLUGIN_VREPFFI_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstddef>
+#include <cstring>
+
+#include <forward_list>
+#include <string>
+#include <vector>
+
+#include <v_repLib.h>
+
+#include "vrep.h"
+
+namespace vrep {
+
+ namespace {
+
+ // C-Lua type equivalences //
+
+ template<typename T, int idVal>
+ struct LuaTypeWrapper {
+ static const int id = idVal;
+ static inline T isNextArg(const SLuaCallBack *);
+ };
+
+ template<typename T>
+ struct LuaType;
+
+ template<>
+ struct LuaType<std::nullptr_t>
+ : public LuaTypeWrapper<std::nullptr_t, sim_lua_arg_nil> {
+ };
+
+ template<>
+ struct LuaType<bool> : public LuaTypeWrapper<bool, sim_lua_arg_bool> {
+ typedef simBool LuaT;
+ };
+
+ template<>
+ struct LuaType<int> : public LuaTypeWrapper<int, sim_lua_arg_int> {
+ typedef simInt LuaT;
+ };
+
+ template<>
+ struct LuaType<float>
+ : public LuaTypeWrapper<float, sim_lua_arg_float> {
+ typedef simFloat LuaT;
+ };
+
+ template<>
+ struct LuaType<std::string>
+ : public LuaTypeWrapper<std::string, sim_lua_arg_string> {
+ typedef simChar LuaT;
+ };
+
+ template<typename U>
+ struct LuaType<std::vector<U>>
+ : public LuaTypeWrapper<std::vector<U>,
+ sim_lua_arg_table | LuaType<U>::id> {
+ typedef typename LuaType<U>::LuaT LuaT;
+ };
+
+
+ // Converting a variadic template type to a Lua type list //
+
+ std::forward_list<int> toLuaTypes();
+
+ template<typename Car>
+ std::forward_list<int> toLuaTypes();
+
+ template<typename Car, typename Cadr, typename ...Cddr>
+ std::forward_list<int> toLuaTypes();
+ }
+
+
+ // Exposing C++ functions to Lua //
+
+ template<typename ...Args>
+ void exposeFunction(const std::string name, const std::string callTips,
+ simVoid (*const f)(SLuaCallBack *));
+
+
+ // Extracting Lua arguments from the callback structure //
+
+ /* A class layered on 'SLuaCallBack' for handling calls from Lua into C++.
+ * Watch out--interacting with this class at all outside of a C++ function
+ * exposed to Lua will almost certainly cause undefined behavior! */
+ class LuaCall {
+ public:
+ LuaCall(SLuaCallBack *);
+
+ template<typename T>
+ T expectAtom();
+
+ template<typename T>
+ std::vector<T> expectTable();
+
+ private:
+ void ensureNextArgType(const int expected);
+
+ template<typename T>
+ T unsafeGetAtom();
+
+ template<typename T>
+ std::vector<T> unsafeGetTable();
+
+ SLuaCallBack *const simCall;
+ size_t argIdx;
+ simBool *cursorBool;
+ simInt *cursorInt;
+ simFloat *cursorFloat;
+ simChar *cursorChar;
+ };
+ template<> inline bool LuaCall::unsafeGetAtom();
+ template<> inline int LuaCall::unsafeGetAtom();
+ template<> inline float LuaCall::unsafeGetAtom();
+ template<> std::string LuaCall::unsafeGetAtom();
+
+
+ // Error handling //
+
+ // An error signaling a failed marshal from Lua to C++.
+ class MarshalingError : public Error {
+ public:
+ explicit inline MarshalingError(const std::string &whatArg);
+ explicit inline MarshalingError(const char *whatArg);
+ };
+
+}
+
+#include "vrepFfi-inl.h"
+
+#endif