24 #define OSVR_HAVE_BOOST
32 #include <boost/lexical_cast.hpp>
33 #include <opencv2/highgui/highgui.hpp>
34 #include <vrpn_SerialPort.h>
48 using SerialBufType =
const unsigned char;
53 template <
typename T, std::
size_t N>
55 std::size_t repetitions = 1) {
56 static_assert(
sizeof(T) == 1,
"Can only pass character/byte arrays");
58 if (
'\0' == buf[len - 1]) {
61 for (std::size_t i = 0; i < repetitions; ++i) {
62 port.write(reinterpret_cast<SerialBufType *>(&(buf[0])), len);
69 template <
typename T, std::
size_t N>
71 std::size_t repetitions = 1) {
72 for (std::size_t i = 0; i < repetitions; ++i) {
80 template <std::
size_t N>
82 std::size_t repetitions = 1) {
84 for (std::size_t i = 0; i < repetitions; ++i) {
85 port.write(reinterpret_cast<SerialBufType *>(&(buf[0])), N);
93 void trigger() { handle_trigger(getPort()); }
98 virtual void handle_trigger(vrpn_SerialPort &port) = 0;
100 vrpn_SerialPort &getPort() {
return port_.get(); }
103 std::reference_wrapper<vrpn_SerialPort> port_;
105 using SerialIRLEDPtr = std::unique_ptr<SerialIRLED>;
106 using SerialLEDControllerFactory =
107 std::function<SerialIRLEDPtr(vrpn_SerialPort &)>;
113 static SerialIRLEDPtr make(vrpn_SerialPort &port) {
120 void handle_trigger(vrpn_SerialPort &port)
override {
123 port.drain_output_buffer();
132 static SerialIRLEDPtr make(vrpn_SerialPort &port) {
136 }
catch (std::exception &e) {
137 std::cerr <<
"Error setting up bus pirate: " << e.what()
144 const std::uint8_t resetToTerminal[] = {RESET_TO_TERMINAL};
146 getPort().drain_output_buffer();
151 bool success = enterBitBangMode(port);
153 std::cout <<
"Bus Pirate successfully entered binary bit-bang mode!"
156 std::cout <<
"Couldn't get bus pirate into binary bit-bang mode: "
157 "check that nothing else is using the port and try "
160 throw std::runtime_error(
"Could not transition bus pirate to "
161 "binary bit-bang mode: check that nothing "
162 "else is using the port and try resetting "
166 const std::uint8_t configPortModes[] = {PORT_MODE, ONLY_POWER_ON};
169 port.drain_output_buffer();
171 port.flush_input_buffer();
173 static const std::uint8_t ONE = 0x01;
174 static const std::uint8_t ZERO = 0x00;
177 static const std::uint8_t PORT_MODE = 0x4f;
179 static const std::uint8_t ONLY_POWER_ON = 0xc0;
181 static const std::uint8_t POWER_AND_AUX_ON = 0xd0;
183 static const std::uint8_t RESET_TO_TERMINAL = 0x0f;
184 bool enterBitBangMode(vrpn_SerialPort &port) {
186 SerialBufType enter[] =
"\n";
188 port.flush_input_buffer();
194 port.drain_output_buffer();
195 std::cout <<
"Resetting bus pirate" << std::endl;
196 std::this_thread::sleep_for(std::chrono::seconds(1));
198 port.flush_input_buffer();
200 bool success =
false;
201 const std::uint8_t bitBangReset[] = {ZERO};
202 static const auto desiredReply =
"BBIO1";
204 for (
int i = 0; i < 40; ++i) {
206 std::this_thread::sleep_for(std::chrono::milliseconds(5));
208 auto response = port.read_available_characters(5);
209 if (desiredReply == response) {
211 }
else if (!response.empty()) {
212 std::cout <<
"When waiting for " << desiredReply
213 <<
" Bus Pirate sent us " << response << std::endl;
218 void handle_trigger(vrpn_SerialPort &port)
override {
220 const std::uint8_t turnOn[] = {POWER_AND_AUX_ON};
224 port.drain_output_buffer();
226 port.flush_input_buffer();
228 std::this_thread::sleep_for(std::chrono::milliseconds(5));
230 const std::uint8_t turnOff[] = {ONLY_POWER_ON};
234 port.drain_output_buffer();
236 port.flush_input_buffer();
240 static const std::string windowNameAndInstructions(
241 "OSVR tracking camera preview | q or esc to quit");
242 static const auto BUS_PIRATE_ARGS = {
"--bp",
"--buspirate",
"-b"};
243 static const auto ARDUINO_ARGS = {
"--arduino",
"-a"};
244 static const auto DEVICE_ARG =
"--dev";
245 static const auto DEFAULT_DEVICE =
"COM1";
246 static const auto RATE_ARG =
"--rate";
247 static const auto DEFAULT_RATE = 115200;
248 static const auto MEASUREMENTS_ARG =
"--measurements";
249 static const auto DEFAULT_MEASUREMENTS = 20;
251 static const double CUTOFF_VALUE = 200.;
254 SerialLEDControllerFactory controllerFactory;
255 std::string device = DEFAULT_DEVICE;
256 std::uint32_t rate = DEFAULT_RATE;
257 std::uint32_t measurements = DEFAULT_MEASUREMENTS;
260 int main(
int argc,
char *argv[]) {
262 auto withUsageError = [] {
267 auto args = makeArgList(argc, argv);
268 if (handle_has_any_iswitch_of(args, BUS_PIRATE_ARGS)) {
269 std::cout <<
"Will use the Bus Pirate, IR LED on AUX, controller."
271 g_config.controllerFactory = BusPirateAuxLED::make;
273 if (handle_has_any_iswitch_of(args, ARDUINO_ARGS)) {
274 if (g_config.controllerFactory) {
275 std::cerr <<
"Got an arduino arg, but already was specified to "
278 return withUsageError();
280 g_config.controllerFactory = ArduinoLED::make;
283 if (!g_config.controllerFactory) {
284 std::cerr <<
"No controller type specified! Assuming bus pirate."
286 g_config.controllerFactory = BusPirateAuxLED::make;
290 args, [](std::string
const &a) {
return a == DEVICE_ARG; },
291 [&](std::string
const &val) {
292 std::cout <<
"Setting serial device to " << val << std::endl;
293 g_config.device = val;
297 args, [](std::string
const &a) {
return a == RATE_ARG; },
298 [&](std::string
const &val) {
299 g_config.rate = boost::lexical_cast<std::uint32_t>(val);
300 std::cout <<
"Setting serial rate to " << g_config.rate
305 args, [](std::string
const &a) {
return a == MEASUREMENTS_ARG; },
306 [&](std::string
const &val) {
307 g_config.measurements = boost::lexical_cast<std::uint32_t>(val);
308 std::cout <<
"Setting number of measurements to "
309 << g_config.measurements << std::endl;
313 auto cam = osvr::vbtracker::openHDKCameraDirectShow();
315 std::cerr <<
"Warning: Just using OpenCV to open Camera #0, which may not "
316 "be the tracker camera."
318 auto cam = osvr::vbtracker::openOpenCVCamera(0);
320 if (!cam || !cam->ok()) {
321 std::cerr <<
"Couldn't find, open, or read from the OSVR HDK tracking "
323 <<
"Press enter to exit." << std::endl;
328 auto FRAME_DISPLAY_STRIDE = 3u;
331 auto frame = cv::Mat{};
332 auto grayFrame = cv::Mat{};
334 vrpn_SerialPort comPort(g_config.device.c_str(), g_config.rate);
335 if (!comPort.is_open()) {
336 std::cerr <<
"Couldn't open com port!" << std::endl;
340 std::unique_ptr<SerialIRLED> LEDController(
341 g_config.controllerFactory(comPort));
343 if (!LEDController) {
344 std::cerr <<
"Could not start LEDController, exiting." << std::endl;
348 enum class State { StartedTrigger, SawFlash, AwaitingNewTrigger };
354 State s = State::AwaitingNewTrigger;
356 std::random_device rd;
357 std::mt19937 mt(rd());
359 std::uniform_int_distribution<int> dist(5, 50);
360 auto generateNewCountdown = [&mt, &dist] {
return dist(mt); };
361 int countdownToTrigger = generateNewCountdown();
363 std::uint32_t samples = 0;
364 std::ofstream os(
"latency.csv");
367 cam->retrieve(frame, grayFrame, tv);
369 case State::AwaitingNewTrigger: {
370 if (countdownToTrigger == 0) {
372 s = State::StartedTrigger;
374 LEDController->trigger();
377 countdownToTrigger--;
381 case State::StartedTrigger: {
383 double minVal, maxVal;
384 cv::minMaxIdx(grayFrame, &minVal, &maxVal);
386 if (maxVal > CUTOFF_VALUE) {
389 if (tv.microseconds > 0) {
392 std::cout <<
" Current: " << maxVal <<
"\n";
393 std::cout <<
"Latency from trigger to sample time: "
394 << tv.microseconds <<
"us\n";
395 std::cout <<
"Latency from trigger to retrieval time: "
396 << now.microseconds <<
"us\n";
397 cv::imwrite(
"triggered.png", frame);
399 os << tv.microseconds << std::endl;
401 std::cout <<
"Got a spurious flash, negative trigger to "
408 case State::SawFlash: {
410 double minVal, maxVal;
411 cv::minMaxIdx(grayFrame, &minVal, &maxVal);
412 if (maxVal < CUTOFF_VALUE) {
414 s = State::AwaitingNewTrigger;
415 countdownToTrigger = generateNewCountdown();
416 std::cout <<
"Will wait " << countdownToTrigger
417 <<
" frames before triggering again\n";
422 }
while (cam->grab() && samples < g_config.measurements);
424 LEDController.reset();
void trigger()
Call to cause an IR LED pulse to be emitted.
Header containing some simple args-handling routines: nothing sophisticated, just enough to keep the ...
Abstract base class for ways of controlling an IR LED through a serial port.
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
void writeStringWithoutNullTerminator(vrpn_SerialPort &port, T(&buf)[N], std::size_t repetitions=1)
void writeCommand(vrpn_SerialPort &port, T(&buf)[N], std::size_t repetitions=1)
::OSVR_TimeValue TimeValue
C++-friendly typedef for the OSVR_TimeValue structure.
void writeBinaryArray(vrpn_SerialPort &port, SerialBufType(&buf)[N], std::size_t repetitions=1)
void osvrTimeValueDifference(OSVR_TimeValue *tvA, const OSVR_TimeValue *tvB)
Computes the difference between two time values, replacing the first with the result.
int main(int argc, char *argv[])