Example #1
0
Trajectory::Trajectory(
      const std::vector<DeviceThreadReport> &reports  //< Holds the values to fill in
      , struct timeval start                    //< Defines 0 seconds
      , int index                               //< Which value to use from the reports
      , bool arrivalTime                        //< Use arrival time rather than reported time
)
{
  if (index < 0) { return; }

  // Insert all reports that have a value with the specified index.
  // Their time is with respect to the base time.
  for (size_t i = 0; i < reports.size(); i++) {
    if (reports[i].values.size() > index) {
      Entry e;
      e.m_value = reports[i].values[index];
      if (arrivalTime) {
        e.m_time = vrpn_TimevalDurationSeconds(reports[i].arrivalTime, start);
      } else {
        e.m_time = vrpn_TimevalDurationSeconds(reports[i].sampleTime, start);
      }
      m_entries.push_back(e);
    }
  }

  // Sort the resulting vector of values.
  std::sort(m_entries.begin(), m_entries.end());
}
Example #2
0
void vrpn_IMU_SimpleCombiner::mainloop()
{
  // Call generic server mainloop, since we are a server
  server_mainloop();

  // Mainloop() all of the analogs that are defined and the button
  // so that we will get all of the values fresh.
  if (d_acceleration.ana != NULL) { d_acceleration.ana->mainloop(); }
  if (d_rotational_vel.ana != NULL) { d_rotational_vel.ana->mainloop(); }
  if (d_magnetometer.ana != NULL) { d_magnetometer.ana->mainloop(); }

  // Update the matrix based on the change in time since the last
  // update and the current values.
  struct timeval	now;
  vrpn_gettimeofday(&now, NULL);
  double delta_t = vrpn_TimevalDurationSeconds(now, d_prev_update_time);
  update_matrix_based_on_values(delta_t);
  d_prev_update_time = now;

  // See if it has been long enough since our last report.
  // If so, generate a new one.
  double interval = vrpn_TimevalDurationSeconds(now, d_prevtime);
  if (interval >= d_update_interval) {

    // pack and deliver tracker report with info from the current matrix;
    if (d_connection) {
      char	msgbuf[1000];
      int	len = encode_to(msgbuf);
      if (d_connection->pack_message(len, vrpn_Tracker::timestamp,
        position_m_id, d_sender_id, msgbuf,
        vrpn_CONNECTION_LOW_LATENCY)) {
        fprintf(stderr, "vrpn_IMU_SimpleCombiner: "
          "cannot write pose message: tossing\n");
      }
      len = encode_vel_to(msgbuf);
      if (d_connection->pack_message(len, vrpn_Tracker::timestamp,
        velocity_m_id, d_sender_id, msgbuf,
        vrpn_CONNECTION_LOW_LATENCY)) {
        fprintf(stderr, "vrpn_IMU_SimpleCombiner: "
          "cannot write velocity message: tossing\n");
      }
    } else {
      fprintf(stderr, "vrpn_IMU_SimpleCombiner: "
        "No valid connection\n");
    }

    // We just sent a report, so reset the time
    d_prevtime = now;
  }
}
Example #3
0
void VRPN_CALLBACK vrpn_Tracker_FilterOneEuro::handle_tracker_update(void *userdata, const vrpn_TRACKERCB info)
{
  // Get pointer to the object we're dealing with.
  vrpn_Tracker_FilterOneEuro *me = static_cast<vrpn_Tracker_FilterOneEuro *>(userdata);

  // See if this sensor is within our range.  If not, we ignore it.
  if (info.sensor >= me->d_channels) {
    return;
  }

  // Filter the position and orientation and then report the filtered value
  // for this channel.  Keep track of the delta-time, and update our current
  // time so we get the right one next time.
  double dt = vrpn_TimevalDurationSeconds(info.msg_time, me->d_last_report_times[info.sensor]);
  if (dt <= 0) { dt = 1; }  // Avoid divide-by-zero in case of fluke.
  vrpn_float64 pos[3];
  vrpn_float64 quat[4];
  memcpy(pos, info.pos, sizeof(pos));
  memcpy(quat, info.quat, sizeof(quat));
  const vrpn_float64 *filtered = me->d_filters[info.sensor].filter(dt, pos);
  q_vec_copy(me->pos, filtered);
  const double *q_filtered = me->d_qfilters[info.sensor].filter(dt, quat);
  q_normalize(me->d_quat, q_filtered);
  me->timestamp = info.msg_time;
  me->d_sensor = info.sensor;
  me->d_last_report_times[info.sensor] = info.msg_time;

  // Send the filtered report.
  char msgbuf[512];
  int len = me->encode_to(msgbuf);
  if (me->d_connection->pack_message(len, me->timestamp, me->position_m_id,
          me->d_sender_id, msgbuf,vrpn_CONNECTION_LOW_LATENCY)) {
    fprintf(stderr, "vrpn_Tracker_FilterOneEuro: cannot write message: tossing\n");
  }
}
void vrpn_Tracker_ThalmicLabsMyo::_report_lock() {
	vrpn_gettimeofday(&_timestamp, NULL);
	double dt = vrpn_TimevalDurationSeconds(_timestamp, vrpn_Button::timestamp);
	vrpn_Button::timestamp = _timestamp;
	buttons[0] = _locked;
	vrpn_Button::report_changes();
}
void vrpn_Tracker_ThalmicLabsMyo::onOrientationData(myo::Myo* myo, uint64_t timestamp, const myo::Quaternion<float>& rotation)
{
	if (myo != _myo)
		return;
	if (!d_connection) {
		return;
	}
	vrpn_gettimeofday(&_timestamp, NULL);
	double dt = vrpn_TimevalDurationSeconds(_timestamp, vrpn_Button::timestamp);
	vrpn_Tracker::timestamp = _timestamp;
	vrpn_Analog::timestamp = _timestamp;

	d_quat[0] = rotation.x();
	d_quat[1] = rotation.y();
	d_quat[2] = rotation.z();
	d_quat[3] = rotation.w();

	// do the same as analog, with euler angles (maybe offset from when OnArmSync?)
	q_vec_type euler;

	q_to_euler(euler, d_quat);
	channel[ANALOG_ROTATION_X] = euler[Q_ROLL];
	channel[ANALOG_ROTATION_Y] = euler[Q_PITCH];
	channel[ANALOG_ROTATION_Z] = euler[Q_YAW];


	char msgbuf[1000];
	int len = vrpn_Tracker::encode_to(msgbuf);
	if (d_connection->pack_message(len, _timestamp, position_m_id, d_sender_id, msgbuf, vrpn_CONNECTION_LOW_LATENCY)) {
		fprintf(stderr, "Thalmic Lab's myo tracker: can't write message: tossing\n");
	}

	_analogChanged = true;
}
Example #6
0
int main(void)
{
  struct timeval last_time, this_time;
  double skip;
  vrpn_gettimeofday(&last_time, NULL);
  printf("Current time in seconds:microseconds = %lu:%lu\n", 
    last_time.tv_sec, last_time.tv_usec);
  
  printf("Checking for monotonicity and step size\n");
  printf(" (should be no further output if things are working perfectly)\n");
  fflush(stdout);

  vrpn_gettimeofday(&last_time, NULL);
  while (true) {
    vrpn_gettimeofday(&this_time, NULL);
	  skip = vrpn_TimevalDurationSeconds(this_time, last_time);
    if (skip > 200e-6) {
      printf("Skipped forward %lg microseconds\n", skip*1e6);
    }
    if (skip < 0) {
  		printf("** Backwards %lg microseconds\n", skip*1e6);
    }
    last_time = this_time;
  }
	return 0;
}
void vrpn_Tracker_ThalmicLabsMyo::onAccelerometerData(myo::Myo* myo, uint64_t timestamp, const myo::Vector3<float>& accel)
{
	if (myo != _myo)
		return;
	if (!d_connection) {
		return;
	}
	vrpn_gettimeofday(&_timestamp, NULL);
	double dt = vrpn_TimevalDurationSeconds(_timestamp, vrpn_Button::timestamp);
	vrpn_Tracker::timestamp = _timestamp;
	vrpn_Analog::timestamp = _timestamp;
	
	acc[0] = accel[0];
	acc[1] = accel[1];
	acc[2] = accel[2];

	// same for analog
	channel[ANALOG_ACCEL_X] = accel[0];
	channel[ANALOG_ACCEL_Y] = accel[1];
	channel[ANALOG_ACCEL_Z] = accel[2];

	char msgbuf[1000];
	int len = encode_acc_to(msgbuf);
	if (d_connection->pack_message(len, _timestamp, accel_m_id, d_sender_id, msgbuf, vrpn_CONNECTION_LOW_LATENCY)) {
		fprintf(stderr, "Thalmic Lab's myo tracker: can't write message: tossing\n");
	}
	_analogChanged = true;
}
Example #8
0
void vrpn_Tracker_ButtonFly::mainloop()
{
  int i;
  struct timeval  now;
  double	  interval;	// How long since the last report, in secs

  // Call generic server mainloop, since we are a server
  server_mainloop();

  // Mainloop() all of the buttons that are defined and the analog
  // scale values if they are defined so that we will get all of
  // the values fresh.
  for (i = 0; i < d_num_axes; i++) {
    d_axes[i].btn->mainloop();
  }
  if (d_vel_scale != NULL) { d_vel_scale->mainloop(); };
  if (d_rot_scale != NULL) { d_rot_scale->mainloop(); };

  // See if it has been long enough since our last report.
  // If so, generate a new one.
  vrpn_gettimeofday(&now, NULL);
  interval = vrpn_TimevalDurationSeconds(now, d_prevtime);

  if (shouldReport(interval)) {
    // Figure out the new matrix based on the current values and
    // the length of the interval since the last report
    update_matrix_based_on_values(interval);
    d_prevtime = now;

    // Set the time on the report to now.
    vrpn_Tracker::timestamp = now;

    // pack and deliver tracker report;
    if (d_connection) {
      char	msgbuf[1000];
      int	len = encode_to(msgbuf);
      if (d_connection->pack_message(len, vrpn_Tracker::timestamp,
	      position_m_id, d_sender_id, msgbuf,
	      vrpn_CONNECTION_LOW_LATENCY)) {
      fprintf(stderr,"Tracker ButtonFly: cannot write message: tossing\n");
      }
    } else {
      fprintf(stderr,"Tracker ButtonFly: No valid connection\n");
    }

  // We're not always sending reports, but we still want to
  // update the matrix so that we don't integrate over
  // too long a timespan when we do finally report a change.
  } else if (interval >= d_update_interval) {
    // Figure out the new matrix based on the current values and
    // the length of the interval since the last report
    update_matrix_based_on_values(interval);
    d_prevtime = now;
  }
}
Example #9
0
void vrpn_Oculus::mainloop()
{
  vrpn_gettimeofday(&d_timestamp, NULL);

  // See if it has been long enough to send another keep-alive 
  if (vrpn_TimevalDurationSeconds(d_timestamp, d_lastKeepAlive) >=
      d_keepAliveSeconds) {
    writeKeepAlive();
    d_lastKeepAlive = d_timestamp;
  }

  update();
  server_mainloop();
}
Example #10
0
void vrpn_Tracker_DeadReckoning_Rotation::handle_tracker_report(void *userdata,
    const vrpn_TRACKERCB info)
{
    // Find the pointer to the class that registered the callback and get a
    // reference to the RotationState we're supposed to be using.
    vrpn_Tracker_DeadReckoning_Rotation *me =
        static_cast<vrpn_Tracker_DeadReckoning_Rotation *>(userdata);
    if (info.sensor >= me->d_numSensors) {
        me->send_text_message(vrpn_TEXT_WARNING)
            << "Received tracker message from sensor " << info.sensor
            << " but I only have " << me->d_numSensors
            << "sensors.  Discarding.";
        return;
    }
    vrpn_Tracker_DeadReckoning_Rotation::RotationState &state =
        me->d_rotationStates[info.sensor];

    if (!state.d_receivedAngularVelocityReport && me->d_estimateVelocity) {
        // If we have not received any velocity reports, then we estimate
        // the angular velocity using the last report (if any).  The new
        // combined rotation T3 = T2 * T1, where T2 is the difference in
        // rotation between the last time (T1) and now (T3).  We want to
        // solve for T2 (so we can keep applying it going forward).  We
        // find it by right-multiuplying both sides of the equation by
        // T1i (inverse of T1): T3 * T1i = T2.
        if (state.d_lastReportTime.tv_sec != 0) {
            q_type inverted;
            q_invert(inverted, state.d_lastOrientation);
            q_mult(state.d_rotationAmount, info.quat, inverted);
            state.d_rotationInterval = vrpn_TimevalDurationSeconds(
                info.msg_time, state.d_lastReportTime);
            // If we get a zero or negative rotation interval, we're
            // not going to be able to handle it, so we set things back
            // to no rotation over a unit-time interval.
            if (state.d_rotationInterval < 0) {
                state.d_rotationInterval = 1;
                q_make(state.d_rotationAmount, 0, 0, 0, 1);
            }
        }
    }

    // Keep track of the position, orientation and time for the next report
    q_vec_copy(state.d_lastPosition, info.pos);
    q_copy(state.d_lastOrientation, info.quat);
    state.d_lastReportTime = info.msg_time;

    // We have new data, so we send a new prediction.
    me->sendNewPrediction(info.sensor);
}
void vrpn_Tracker_ThalmicLabsMyo::onGyroscopeData(myo::Myo* myo, uint64_t timestamp, const myo::Vector3<float>& gyro)
{
	if (myo != _myo)
		return;
	vrpn_gettimeofday(&_timestamp, NULL);
	double dt = vrpn_TimevalDurationSeconds(_timestamp, vrpn_Button::timestamp);

	vrpn_Analog::timestamp = _timestamp;

	channel[ANALOG_GYRO_X] = gyro[0];
	channel[ANALOG_GYRO_Y] = gyro[1];
	channel[ANALOG_GYRO_Z] = gyro[2];

	_analogChanged = true;
}
void vrpn_Tracker_ThalmicLabsMyo::onPose(myo::Myo* myo, uint64_t timestamp, myo::Pose pose)
{
	if (myo != _myo)
		return;
	vrpn_gettimeofday(&_timestamp, NULL);
	double dt = vrpn_TimevalDurationSeconds(_timestamp, vrpn_Button::timestamp);
	vrpn_Button::timestamp = _timestamp;
	

	//	std::cout << "Myo switched to pose " << pose.toString() << "." << std::endl;

	buttons[0] = _locked;
	// reset all buttons to 0. Maybe we should only do this if rest is on ?
	for (int i = 1; i < libmyo_num_poses + 1; ++i)
		buttons[i] = 0;
	buttons[pose.type() + 1] = 1;

	vrpn_Button::report_changes();
}
Example #13
0
// Request some log files and wait up to a second for the report of these
// files.  Return true if we got a report and the names match.
bool test_logfile_names(const char *local_in, const char *local_out,
                        const char *remote_in, const char *remote_out)
{
  struct timeval  start;
  struct timeval now;

  // Mark no report and the request logging with the specified
  // parameters.
  g_got_report = false;
  if (!g_logger->send_logging_request(local_in, local_out, remote_in, remote_out)) {
    fprintf(stderr, "test_logfile_names: Logging request send failed\n");
    return false;
  }

  // Mainloop the logger for up to five seconds waiting for a response.
  // If we don't get a response, this is a failure.
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
    vrpn_SleepMsecs(1);
  } while ( !g_got_report && (vrpn_TimevalDurationSeconds(now, start) < 5.0));
  if (!g_got_report) {
    fprintf(stderr, "test_logfile_names: Timeout waiting for report of logging from server\n");
    return false;
  }

  // Check to see if the names are the same.  Return true if they all are.
  if ( (strcmp(g_local_in, local_in) == 0) &&
       (strcmp(g_local_out, local_out) == 0) &&
       (strcmp(g_remote_in, remote_in) == 0) &&
       (strcmp(g_remote_out, remote_out) == 0) ) {
    return true;
  } else {
    return false;
  }
}
Example #14
0
void vrpn_Tracker_WiimoteHead::mainloop() {
	struct timeval now;

	// Call generic server mainloop, since we are a server
	server_mainloop();

	// Mainloop() the wiimote to get fresh values
	if (d_ana != NULL) {
		d_ana->mainloop();
	}

	// See if we have new data, or if it has been too long since our last
	// report.  Send a new report in either case.
	vrpn_gettimeofday(&now, NULL);
	double interval = vrpn_TimevalDurationSeconds(now, d_prevtime);

	if (_should_report(interval)) {
		// Figure out the new matrix based on the current values and
		// the length of the interval since the last report
		update_pose();

		report();
	}
}
Example #15
0
void vrpn_IMU_Magnetometer::mainloop()
{
  struct timeval	now;
  double	interval;	// How long since the last report, in secs

  // Call generic server mainloop, since we are a server
  server_mainloop();

  // Mainloop() all of the analogs that are defined
  // so that we will get all of the values fresh.
  if (d_vector.ana != NULL) { d_vector.ana->mainloop(); };

  // Keep track of the minimum and maximum values of
  // our reports.
  for (size_t i = 0; i < 3; i++) {
    if (d_vector.values[i] < d_mins[i]) {
      d_mins[i] = d_vector.values[i];
    }
    if (d_vector.values[i] > d_maxes[i]) {
      d_maxes[i] = d_vector.values[i];
    }
  }

  // See if it has been long enough since our last report.
  // If so, generate a new one.
  vrpn_gettimeofday(&now, NULL);
  interval = vrpn_TimevalDurationSeconds(now, d_prevtime);

  if (interval >= d_update_interval) {

    // Set the time on the report
    timestamp = d_vector.time;

    // Compute the unit vector by finding the normalized
    // distance between the min and max for each axis and
    // converting it to the range (-1,1) and then normalizing
    // the resulting vector.  Watch out for min and max
    // being the same.
    for (size_t i = 0; i < 3; i++) {
      double diff = d_vector.values[i] - d_mins[i];
      if (diff == 0) {
        channel[i] = 0;
      }
      else {
        channel[i] = -1 + 2 * diff / (d_maxes[i] - d_mins[i]);
      }
    }
    double len = sqrt( channel[0] * channel[0] +
      channel[1] * channel[1] + channel[2] * channel[2]);
    if (len == 0) {
      channel[0] = 1;
      channel[1] = 0;
      channel[2] = 0;
    } else {
      channel[0] /= len;
      channel[1] /= len;
      channel[2] /= len;
    }

    // pack and deliver analog unit vector report;
    if (d_connection) {
      if (d_report_changes) {
        vrpn_Analog_Server::report_changes();
      } else {
        vrpn_Analog_Server::report();
      }
    } else {
      fprintf(stderr,"vrpn_IMU_Magnetometer: "
              "No valid connection\n");
    }

    // We just sent a report, so reset the time
    d_prevtime = now;
  }

  // We're not always sending reports, but we still want to
  // update the interval clock so that we don't integrate over
  // too long a timespan when we do finally report a change.
  if (interval >= d_update_interval) {
    d_prevtime = now;
  }
}
Example #16
0
void vrpn_Tracker_AnalogFly::mainloop()
{
  struct timeval	now;
  double	interval;	// How long since the last report, in secs

  // Call generic server mainloop, since we are a server
  server_mainloop();

  // Mainloop() all of the analogs that are defined and the button
  // so that we will get all of the values fresh.
  if (d_x.ana != NULL) { d_x.ana->mainloop(); };
  if (d_y.ana != NULL) { d_y.ana->mainloop(); };
  if (d_z.ana != NULL) { d_z.ana->mainloop(); };
  if (d_sx.ana != NULL) { d_sx.ana->mainloop(); };
  if (d_sy.ana != NULL) { d_sy.ana->mainloop(); };
  if (d_sz.ana != NULL) { d_sz.ana->mainloop(); };
  if (d_reset_button != NULL) { d_reset_button->mainloop(); };
  if (d_clutch_button != NULL) { d_clutch_button->mainloop(); };

  // See if it has been long enough since our last report.
  // If so, generate a new one.
  vrpn_gettimeofday(&now, NULL);
  interval = vrpn_TimevalDurationSeconds(now, d_prevtime);

  if (shouldReport(interval)) {

    // Set the time on the report to now, if not an absolute
    // tracker.  Absolute trackers have their time values set
    // to match the time at which their analog devices gave the
    // last report.
    if (!d_absolute) {
      vrpn_Tracker::timestamp = now;
    }

    // Figure out the new matrix based on the current values and
    // the length of the interval since the last report
    update_matrix_based_on_values(interval);

    // pack and deliver tracker report;
    if (d_connection) {
      char	msgbuf[1000];
      int	len = encode_to(msgbuf);
      if (d_connection->pack_message(len, vrpn_Tracker::timestamp,
	      position_m_id, d_sender_id, msgbuf,
	      vrpn_CONNECTION_LOW_LATENCY)) {
      fprintf(stderr,"Tracker AnalogFly: "
              "cannot write message: tossing\n");
      }
    } else {
      fprintf(stderr,"Tracker AnalogFly: "
              "No valid connection\n");
    }

    // We just sent a report, so reset the time
    d_prevtime = now;
  }

  // We're not always sending reports, but we still want to
  // update the interval clock so that we don't integrate over
  // too long a timespan when we do finally report a change.
  if (interval >= d_update_interval) {
    d_prevtime = now;
  }
}
Example #17
0
int main(int argc, char *argv[]) {

    /* XXX Checking how well the two clocks track each other
    unsigned long    lsec, lusec;
    unsigned long    fsec, fusec;
    long    dsec, dusec;
    int	    i;
    for (i = 0; i < 10; i++) {
	get_time_using_GetLocalTime(lsec, lusec);
	get_time_using_ftime(fsec, fusec);
	dsec = lsec - fsec;
	dusec = lusec - fusec;

	printf("L: %u:%u, F: %u:%u, Difference: %u:%ld\n",
	    lsec, lusec, fsec, fusec, dsec, dusec);

	Sleep(1000);
    }
    */

	/* Check the behavior of the clock */
	SYSTEMTIME sstart, stime;               // System time in funky structure
	FILETIME ftime;     // Time in 100-nsec intervals since Jan 1 1601
	LARGE_INTEGER tics; // ftime stored into a 64-bit quantity
	GetLocalTime(&sstart);
	stime = sstart;
	while (stime.wSecond - sstart.wSecond < 2) {
		GetLocalTime(&stime);
		printf("Seconds %2d, Milliseconds %4d\n", static_cast<int>(stime.wSecond),
			static_cast<int>(stime.wMilliseconds));

		SystemTimeToFileTime(&stime, &ftime);

		// Copy the data into a structure that can be treated as a 64-bit
		// integer
		tics.HighPart = ftime.dwHighDateTime;
		tics.LowPart = ftime.dwLowDateTime;
		printf("  Converted to 64-bit: %llu\n", tics.QuadPart);

		// Convert the 64-bit time into seconds and microseconds since July 1
		// 1601
		unsigned long sec, usec;
		sec = (long)(tics.QuadPart / 10000000L);
		usec = (long)((tics.QuadPart - (((LONGLONG)(sec)) * 10000000L)) / 10);
		printf("  Converted to secs and usecs: %6lu:%6lu\n", sec, usec);
	}

  /* Checking the vrpn_gettimeofday() function for monotonicity and step size */
  struct timeval last_time, this_time;
  double skip;
  vrpn_gettimeofday(&last_time, NULL);
  printf("Checking for monotonicity and step size\n");
  printf(" (should be no further output if things are working perfectly)\n");
  while (true) {
    vrpn_gettimeofday(&this_time, NULL);
	skip = vrpn_TimevalDurationSeconds(this_time, last_time);
    if (skip > 200e-6) {
      printf("Skipped forward %lg microseconds\n", skip*1e6);
    }
    if (skip < 0) {
  		printf("** Backwards %lg microseconds\n", skip*1e6);
    }
    last_time = this_time;
  }

  return 0;
}
Example #18
0
int main (int argc, char * argv [])
{
  const char * devicename = "Logger0@localhost";
  struct timeval  start;
  struct timeval now;
  int ret = 0;

  // parse args
  if (argc == 1) {
    // Fine, use defaults.
  } else if (argc > 2) {
    fprintf(stderr, "Usage:  %s  Device_name\n"
                    "  Device_name:  VRPN name of data source to contact\n"
                    "    example:  Logger0@localhost\n",
            argv[0]);
    exit(0);
  } else {
    devicename = argv[1];
  }

  // Open the logger and set up its callback handler.
  fprintf(stderr, "Logger's name is %s.\n", devicename);
  g_logger = new vrpn_Auxiliary_Logger_Remote (devicename);
  g_logger->register_report_handler(NULL, handle_log_report);

  // Main loop for half a second to get things started on the
  // connection.
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 0.5);

  // Try to create four named log files.  Wait for a second after
  // creation to give it something to log.
  if (!test_logfile_names("temp1_local_in", "temp1_local_out",
                          "temp1_remote_in", "temp1_remote_out") ) {
    fprintf(stderr,"Error creating first set of logs\n");
    ret = -1;
  }
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 5.0);

  // send a log-file-name request
  g_logger->send_logging_status_request( );
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 5.0);

  // Try to create blank log files (no log should be made).  Wait for a second after
  // creation to give it something to log.
  if (!test_logfile_names("", "", "", "") ) {
    fprintf(stderr,"Error turning off logs\n");
    ret = -1;
  }
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 5.0);

  // Try to create just one named log file.  Wait for a second after
  // creation to give it something to log.
  if (!test_logfile_names("temp2_local_in", "", "", "") ) {
    fprintf(stderr,"Error creating second set of logs\n");
    ret = -1;
  }
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 5.0);

  // Try to create blank log files (no log should be made).  Wait for a second after
  // creation to give it something to log.
  if (!test_logfile_names("", "", "", "") ) {
    fprintf(stderr,"Error turning off logs\n");
    ret = -1;
  }
  vrpn_gettimeofday(&start, NULL);
  do {
    g_logger->mainloop();
    vrpn_gettimeofday(&now, NULL);
  } while (vrpn_TimevalDurationSeconds(now, start) < 5.0);

  // Done.
  if (ret == 0) {
    printf("Success!\n");
  } else {
    printf("Make sure that files with the requested names don't already exist.\n");
  }
  delete g_logger;
  return ret;

}   /* main */
int main(int argc, const char *argv[])
{
  // Parse the command line.
  size_t realParams = 0;
  int count = 10;
  bool arrivalTime = false;

  for (size_t i = 1; i < argc; i++) {
    if (argv[i] == std::string("-count")) {
      if (++i > argc) {
        std::cerr << "Error: -count parameter requires value" << std::endl;
        Usage(argv[0]);
      }
      count = atoi(argv[i]);
      if (count < 1) {
        std::cerr << "Error: -count parameter must be >= 1, found "
          << argv[i] << std::endl;
        Usage(argv[0]);
      }
    } else if (argv[i] == std::string("-arrivalTime")) {
      arrivalTime = true;
    } else if (argv[i][0] == '-') {
        Usage(argv[0]);
    } else switch (++realParams) {
      case 1:
        g_arduinoPortName = argv[i];
        break;
      case 2:
        g_arduinoChannel = atoi(argv[i]);
        break;
      default:
        Usage(argv[0]);
    }
  }
  if (realParams != 2) {
    Usage(argv[0]);
  }

  // Construct the thread to handle the photosensor
  // reading from the Ardiuno.
  DeviceThreadVRPNAnalog  arduino(CreateStreamingServer);

  //-----------------------------------------------------------------
  // Wait until we get at least one report from the device
  // or timeout.  Make sure the report sizes are large enough
  // to support the channels we're reading.
  struct timeval start, now;
  vrpn_gettimeofday(&start, NULL);
  size_t arduinoCount = 0;
  std::vector<DeviceThreadReport> r;
  if (g_verbosity > 0) {
    std::cout << "Waiting for reports from Arduino:" << std::endl;
  }
  double lastArduinoValue;
  do {
    r = arduino.GetReports();
    if (r.size() > 0) {
      if (r[0].values.size() <= g_arduinoChannel) {
        std::cerr << "Report size from Arduino: " << r[0].values.size()
          << " is too small for requested channel: " << g_arduinoChannel << std::endl;
        return -3;
      }
      lastArduinoValue = r.back().values[g_arduinoChannel];
    }
    arduinoCount += r.size();

    vrpn_gettimeofday(&now, NULL);
  } while ( (arduinoCount == 0)
            && (vrpn_TimevalDurationSeconds(now, start) < 20) );
  if (arduinoCount == 0) {
    std::cerr << "No reports from Arduino" << std::endl;
    return -5;
  }

  //-----------------------------------------------------------------
  // Construct a RenderManager to use to send images to the screen,
  // and register the display update we'll use to make
  // the images (black or white) to test the rendering latency.
  if (g_verbosity > 0) {
    std::cout << "Run an OSVR server for us to connect to and place"
      << " the photosensor in front of the screen at the location"
      << " whose latency you want to render." << std::endl;
  }
  osvr::clientkit::ClientContext context(
    "com.reliasolve.RenderManagerLatencyTest");
  osvr::renderkit::RenderManager *render =
    osvr::renderkit::createRenderManager(context.get(),"OpenGL");
  if ((render == nullptr) ||
    (!render->doingOkay())) {
    std::cerr << "Could not create RenderManager" << std::endl;
    return 1;
  }

  // Set callback to handle setting up rendering in a display
  render->SetDisplayCallback(SetupDisplay);

  // Open the display and make sure this worked.
  osvr::renderkit::RenderManager::OpenResults ret = render->OpenDisplay();
  if (ret.status == osvr::renderkit::RenderManager::OpenStatus::FAILURE) {
    std::cerr << "Could not open display" << std::endl;
    delete render;
    return 2;
  }

  //-----------------------------------------------------------------
  // Wait a bit for any DirectMode switching/power on to happen.
  vrpn_SleepMsecs(1000);

  //-----------------------------------------------------------------
  // Render a dark scene for half a second and then read the Arduino value
  // to get a baseline for dark.  Half a second is an arbitrary number that
  // should be larger than the latency present in the system.
  // Then render another dark scene right at the end so our vertical
  // retrace timing should be correct for the bright scene.
  g_red = g_green = g_blue = 0;
  render->Render();
  vrpn_SleepMsecs(500);
  r = arduino.GetReports();
  if (r.size() == 0) {
    std::cerr << "Could not read Arduino value after dark rendering" << std::endl;
    delete render;
    return 3;
  }
  double dark = r.back().values[g_arduinoChannel];
  if (g_verbosity > 1) {
    std::cout << "Dark-screen photosensor value: " << dark << std::endl;
  }
  render->Render();

  //-----------------------------------------------------------------
  // Render a bright scene for half a second and then read the Arduino value
  // to get a baseline for bright.  Half a second is an arbitrary number that
  // should be larger than the latency present in the system.
  // Then render another bright scene right at the end so our vertical
  // retrace timing should be correct for the bright scene.
  g_red = g_green = g_blue = 1;
  render->Render();
  vrpn_SleepMsecs(500);
  r = arduino.GetReports();
  if (r.size() == 0) {
    std::cerr << "Could not read Arduino value after bright rendering" << std::endl;
    delete render;
    return 4;
  }
  double bright = r.back().values[g_arduinoChannel];
  if (g_verbosity > 1) {
    std::cout << "Bright-screen photosensor value: " << bright << std::endl;
  }
  double threshold = (dark + bright) / 2;
  if (threshold - dark < 10) {
    std::cerr << "Bright/dark difference insufficient: " << threshold - dark
      << std::endl;
    return 5;
  }
  if (g_verbosity > 1) {
    std::cout << "Threshold photosensor value: " << threshold << std::endl;
  }
  render->Render();

  //-----------------------------------------------------------------
  // Do as many iterations as we're asked for, reporting the latency
  // between when we asked for rendering and when we saw the screen
  // brightness go from below halfway between dark and bright to
  // above halfway between.
  std::vector<double> pre_delays_ms, post_delays_ms;
  for (size_t i = 0; i < count; i++) {
    // Render dark and wait long enough for it to settle.
    // As above, do another render after the sleep so we're running
    // the sytem as if it were rendering every frame.
    g_red = g_green = g_blue = 0;
    render->Render();
    vrpn_SleepMsecs(500);
    r = arduino.GetReports();
    render->Render();

    // Store the time, render bright, store the after-render time,
    // and then wait for the bright to have finished.
    // As above, do another render after the sleep so we're running
    // the sytem as if it were rendering every frame.
    g_red = g_green = g_blue = 1;
    struct timeval pre_render;
    vrpn_gettimeofday(&pre_render, NULL);
    render->Render();
    struct timeval post_render;
    vrpn_gettimeofday(&post_render, NULL);
    vrpn_SleepMsecs(500);
    r = arduino.GetReports();
    render->Render();

    // Find where we cross the threshold from dark to bright and
    // report latency to pre-render and post-render times.
    for (size_t t = 1; t < r.size(); t++) {
      if ((r[t - 1].values[g_arduinoChannel] < threshold) &&
          (r[t].values[g_arduinoChannel] >= threshold)) {
        if (g_verbosity > 1) {
          if (arrivalTime) {
            pre_delays_ms.push_back(vrpn_TimevalDurationSeconds(r[t].arrivalTime, pre_render) * 1e3);
            post_delays_ms.push_back(vrpn_TimevalDurationSeconds(r[t].arrivalTime, post_render) * 1e3);
          } else {
            pre_delays_ms.push_back(vrpn_TimevalDurationSeconds(r[t].sampleTime, pre_render) * 1e3);
            post_delays_ms.push_back(vrpn_TimevalDurationSeconds(r[t].sampleTime, post_render) * 1e3);
          }
          std::cout << "Latency from pre-render: "
            << pre_delays_ms[i]
            << "ms, from post-render: "
            << post_delays_ms[i]
            << std::endl;
        }
        break;
      }
    }
  }

  //-----------------------------------------------------------------
  // Compute and report statistics on the measurements.
  print_stats("Pre-delay (ms)", pre_delays_ms);
  print_stats("Post-delay (ms)", post_delays_ms);

  //-----------------------------------------------------------------
  // We're done.  Shut down the threads and exit.
  return 0;
}