wge100_camera_node.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2009-2010, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 // TODO: doxygen mainpage
36 
37 // TODO: Timeout receiving packet.
38 // TODO: Check that partial EOF missing frames get caught.
39 // @todo Do the triggering based on a stream of incoming timestamps.
40 
41 #include <ros/node_handle.h>
42 #include <sensor_msgs/Image.h>
43 #include <sensor_msgs/CameraInfo.h>
44 #include <sensor_msgs/fill_image.h>
45 #include <sensor_msgs/SetCameraInfo.h>
46 #include <driver_base/SensorLevels.h>
50 #include <self_test/self_test.h>
51 #include <stdlib.h>
52 #include <limits>
53 #include <math.h>
54 #include <linux/sysctl.h>
55 #include <iostream>
56 #include <fstream>
57 #include <wge100_camera/BoardConfig.h>
58 #include <boost/tokenizer.hpp>
59 #include <boost/format.hpp>
60 #include <driver_base/driver.h>
65 #include <sstream>
66 
67 #include <wge100_camera/WGE100CameraConfig.h>
68 
71 #include "wge100_camera/mt9v.h"
72 
73 #define RMEM_MAX_RECOMMENDED 20000000
74 
75 // @todo this should come straight from camera_calibration_parsers as soon as a release happens.
77 {
78  int rows;
79  int cols;
80  const double* data;
81 
82  SimpleMatrix(int rows, int cols, const double* data)
83  : rows(rows), cols(cols), data(data)
84  {}
85 };
86 
87 std::ostream& operator << (std::ostream& out, const SimpleMatrix& m)
88 {
89  for (int i = 0; i < m.rows; ++i) {
90  for (int j = 0; j < m.cols; ++j) {
91  out << m.data[m.cols*i+j] << " ";
92  }
93  out << std::endl;
94  }
95  return out;
96 }
97 
98 bool writeCalibrationIni(std::ostream& out, const std::string& camera_name,
99  const sensor_msgs::CameraInfo& cam_info)
100 {
101  out.precision(5);
102  out << std::fixed;
103 
104  out << "# Camera intrinsics\n\n";
106  out << "[image]\n\n";
107  out << "width\n" << cam_info.width << "\n\n";
108  out << "height\n" << cam_info.height << "\n\n";
109  out << "[" << camera_name << "]\n\n";
110 
111  out << "camera matrix\n" << SimpleMatrix(3, 3, &cam_info.K[0]);
112  out << "\ndistortion\n" << SimpleMatrix(1, 5, &cam_info.D[0]);
113  out << "\n\nrectification\n" << SimpleMatrix(3, 3, &cam_info.R[0]);
114  out << "\nprojection\n" << SimpleMatrix(3, 4, &cam_info.P[0]);
115 
116  return true;
117 }
118 
119 
120 
121 
122 // The FrameTimeFilter class takes a stream of image arrival times that
123 // include time due to system load and network asynchrony, and generates a
124 // (hopefully) more steady stream of arrival times. The filtering is based
125 // on the assumption that the frame rate is known, and that often the time
126 // stamp is correct. The general idea of the algorithm is:
127 //
128 // anticipated_time_ = previous time stamp + frame_period_
129 // is a good estimate of the current frame time stamp.
130 //
131 // Take the incoming time stamp, or the anticipated_time_, whichever one is
132 // lower. The rationale here is that when the latency is low or zero, the
133 // incoming time stamp is correct and will dominate. If latency occurs, the
134 // anticipated_time_ will be used.
135 //
136 // To avoid problems with clock skew, max_skew indicates the maximum
137 // expected clock skew. frame_period_ is set to correspond to the
138 // slowest expected rate when clock skew is taken into account. If
139 // frame_period_ was less than the actual frame rate, then
140 // anticipated_time_ would always dominate, and the output time stamps
141 // would slowly diverge from the actual time stamps.
142 //
143 // Because the frame rate may sometimes skip around in an unanticipated way
144 // the filter detects if anticipated_time_ has dominated by more than
145 // locked_threshold_ more than max_recovery_frames_ in a row. In that case,
146 // it picks the lowest latency input value that occurs during the
147 // max_recovery_frames_ to reset anticipated_time_.
148 //
149 // Finally, if the filter misses too many frames in a row, it assumes that
150 // its anticipated_time is invalid and immediately resets the filter.
151 
153 {
154 public:
155  FrameTimeFilter(double frame_rate = 1., double late_locked_threshold = 1e-3, double early_locked_threshold = 3e-3, int early_recovery_frames = 5, int max_recovery_frames = 10, double max_skew = 1.001, double max_skipped_frames = 100)
156  {
157  frame_period_ = max_skew / frame_rate;
158  max_recovery_frames_ = max_recovery_frames;
159  early_recovery_frames_ = early_recovery_frames;
160  late_locked_threshold_ = late_locked_threshold;
161  early_locked_threshold_ = early_locked_threshold;
162  max_skipped_frames_ = max_skipped_frames;
163  last_frame_number_ = 0; // Don't really care about this value.
164  reset_filter();
165  }
166 
168  {
169  relock_time_ = anticipated_time_ = std::numeric_limits<double>::infinity();
170  unlocked_count_ = late_locked_threshold_;
171  }
172 
173  double run(double in_time, int frame_number)
174  {
175  double out_time = in_time;
176  int delta = (frame_number - last_frame_number_) & 0xFFFF; // Hack because the frame rate currently wraps around too early.
177 
178  if (delta > max_skipped_frames_)
179  {
180  ROS_WARN_NAMED("frame_time_filter", "FrameTimeFilter missed too many frames from #%u to #%u. Resetting.", last_frame_number_, frame_number);
181  reset_filter();
182  }
183 
184  anticipated_time_ += frame_period_ * delta;
185  relock_time_ += frame_period_ * delta;
186 
187  if (out_time < relock_time_)
188  relock_time_ = out_time;
189 
190  //ROS_DEBUG("in: %f ant: %f", in_time, anticipated_time_);
191 
192  bool early_trigger = false;
193  if (out_time < anticipated_time_ - early_locked_threshold_ && finite(anticipated_time_))
194  {
195  ROS_WARN_NAMED("frame_time_filter", "FrameTimeFilter saw frame #%u was early by %f.", frame_number, anticipated_time_ - out_time);
196  early_trigger = true;
197  }
198  if (out_time < anticipated_time_)
199  {
200  anticipated_time_ = out_time;
201  relock_time_ = std::numeric_limits<double>::infinity();
202  unlocked_count_ = 0;
203  }
204  else if (out_time > anticipated_time_ + late_locked_threshold_)
205  {
206  unlocked_count_++;
207  if (unlocked_count_ > max_recovery_frames_)
208  {
209  ROS_DEBUG_NAMED("frame_time_filter", "FrameTimeFilter lost lock at frame #%u, shifting by %f.", frame_number, relock_time_ - anticipated_time_);
210  anticipated_time_ = relock_time_;
211  relock_time_ = std::numeric_limits<double>::infinity();
212  }
213  //else
214  // ROS_DEBUG("FrameTimeFilter losing lock at frame #%u, lock %i/%i, off by %f.", frame_number, unlocked_count_, max_recovery_frames_, anticipated_time_ - out_time);
215  out_time = anticipated_time_;
216  }
217 
218  last_frame_number_ = frame_number;
219 
220  return early_trigger ? -out_time : out_time;
221  }
222 
223 private:
232  double relock_time_;
234 };
235 
240 {
241 private:
246 
247 public:
248  SlowTriggerFilter(int before_warn, int before_clear)
249  {
250  has_warned_ = false;
251  before_clear_ = before_clear;
252  before_warn_ = before_warn;
253  countdown_ = before_warn_;
254  }
255 
256  bool badFrame()
257  {
258  if (!has_warned_)
259  {
260  if (!countdown_--)
261  {
262  has_warned_ = true;
263  countdown_ = before_warn_;
264  return true;
265  }
266  }
267  else
268  countdown_ = before_clear_;
269 
270  return false;
271  }
272 
273  void goodFrame()
274  {
275  if (has_warned_)
276  {
277  if (!countdown_--)
278  {
279  has_warned_ = false;
280  countdown_ = before_clear_;
281  }
282  }
283  else
284  countdown_ = before_warn_;
285  }
286 };
287 
289 {
290  friend class WGE100CameraNode;
291 
292 public:
293  typedef wge100_camera::WGE100CameraConfig Config;
294  Config config_;
295 
296 private:
297  // Services
299 
300  // Video mode
302 
303  // Statistics
313  unsigned int last_frame_number_;
316 
317  // Timing
320 
321  // Link information
322  sockaddr localMac_;
323  in_addr localIp_;
324 
325  // Camera information
326  std::string hwinfo_;
331  std::string image_encoding_;
336 
337  // Threads
339 
340  // Frame function (gets called when a frame arrives).
341  typedef boost::function<int(size_t, size_t, uint8_t*, ros::Time, bool)> UseFrameFunction;
342  UseFrameFunction useFrame_;
343 
345  {
346  int height;
347  int width;
348  int x_offset;
349  int y_offset;
357  int gain;
358  double exposure;
359  double max_exposure;
360  double imager_rate;
362  bool auto_gain;
364  bool mirror_x;
365  bool mirror_y;
367  };
368 
370  {
371  int bintable[5] = { -1, 0, 1, -1, 2 };
372 
373  if (imager.setMode(cfg.width, cfg.height, bintable[cfg.horizontal_binning], bintable[cfg.vertical_binning], cfg.imager_rate, cfg.x_offset, cfg.y_offset))
374  {
375  setStatusMessage("Error setting video mode.");
376  return 1;
377  }
378 
379  if (imager.setCompanding(cfg.companding))
380  {
381  setStatusMessage("Error setting companding mode.");
382  return 1;
383  }
384 
385  if (imager.setAgcAec(cfg.auto_gain, cfg.auto_exposure))
386  {
387  setStatusMessage("Error setting AGC/AEC mode. Exposure and gain may be incorrect.");
388  return 1;
389  }
390 
391  if (imager.setGain(cfg.gain))
392  {
393  setStatusMessage("Error setting analog gain.");
394  return 1;
395  }
396 
397  if (imager.setMirror(cfg.mirror_x != cfg.rotate_180, cfg.mirror_y != cfg.rotate_180))
398  {
399  setStatusMessage("Error setting mirroring properties.");
400  return 1;
401  }
402 
403  if (imager.setExposure(cfg.exposure))
404  {
405  setStatusMessage("Error setting exposure.");
406  return 1;
407  }
408 
411  {
412  setStatusMessage("Error setting black level.");
413  return 1;
414  }
415 
416  if (imager.setBrightness(cfg.brightness))
417  {
418  setStatusMessage("Error setting brightness.");
419  return 1;
420  }
421 
422  if (imager.setMaxExposure(cfg.max_exposure ? cfg.max_exposure : 0.99 / cfg.imager_rate))
423  {
424  setStatusMessage("Error setting maximum exposure.");
425  return 1;
426  }
427 
428  return 0;
429  }
430 
431 
432 public:
434  trigger_matcher_(5, 60), // Recovers from frame drop in 5 frames, queues at least 1 second of timestamps.
435  no_timestamp_warning_filter_(10, 10) // 10 seconds before warning. 10 frames to clear.
436  {
437  // Create a new IpCamList to hold the camera list
438 // wge100CamListInit(&camList);
439 
440  // Clear statistics
441  last_camera_ok_time_ = 0;
442  last_image_time_ = 0;
443  driver_start_time_ = ros::Time::now().toSec();
444  missed_line_count_ = 0;
445  trigger_matcher_drop_count_ = 0;
446  missed_eof_count_ = 0;
447  dropped_packets_ = 0;
448  lost_image_thread_count_ = 0;
449  massive_frame_losses_ = 0;
450  dropped_packet_event_ = false;
451  }
452 
453  void config_update(const Config &new_cfg, uint32_t level = 0)
454  {
455  config_ = new_cfg;
456 
457  if (config_.horizontal_binning == 3) // 1, 2 and 4 are legal, dynamic reconfigure ensures that 3 is the only bad case.
458  {
459  config_.horizontal_binning = 2;
460  ROS_WARN("horizontal_binning = 3 is invalid. Setting to 2.");
461  }
462 
463  if (config_.vertical_binning == 3) // 1, 2 and 4 are legal, dynamic reconfigure ensures that 3 is the only bad case.
464  {
465  config_.vertical_binning = 2;
466  ROS_WARN("horizontal_binning = 3 is invalid. Setting to 2.");
467  }
468 
469  desired_freq_ = config_.ext_trig ? config_.trig_rate : config_.imager_rate;
470 
472 
473  if (!config_.auto_exposure && config_.exposure > 0.99 / desired_freq_)
474  {
475  ROS_WARN("Exposure (%f s) is greater than 99%% of frame period (%f s). Setting to 99%% of frame period.",
476  config_.exposure, 0.99 / desired_freq_);
477  config_.exposure = 0.99 * 1 / desired_freq_;
478  }
479  /*if (!config_.auto_exposure && config_.exposure > 0.95 / desired_freq_)
480  {
481  ROS_WARN("Exposure (%f s) is greater than 95%% of frame period (%f s). You may not achieve full frame rate.",
482  config_.exposure, 0.95 / desired_freq_);
483  }*/
484  if (config_.auto_exposure && config_.max_exposure && config_.max_exposure > 0.99 / desired_freq_)
485  {
486  ROS_WARN("Maximum exposure (%f s) is greater than 99%% of frame period (%f s). Setting to 99%% of frame period.",
487  config_.max_exposure, 0.99 / desired_freq_);
488  config_.max_exposure = 0.99 * 1 / desired_freq_;
489  }
490  /*if (config_.auto_exposure && config_.max_exposure && config_.max_exposure > 0.95 / desired_freq_)
491  {
492  ROS_WARN("Maximum exposure (%f s) is greater than 95%% of frame period (%f s). You may not achieve full frame rate.",
493  config_.max_exposure, 0.95 / desired_freq_);
494  }*/
495 
496  enable_primary_ = (config_.register_set != wge100_camera::WGE100Camera_AlternateRegisterSet);
497  enable_alternate_ = (config_.register_set != wge100_camera::WGE100Camera_PrimaryRegisterSet);
498  }
499 
501  {
502 // wge100CamListDelAll(&camList);
503  close();
504  }
505 
507  {
508  int rmem_max;
509  {
510  std::ifstream f("/proc/sys/net/core/rmem_max");
511  f >> rmem_max;
512  }
513  return rmem_max;
514  }
515 
516  void doOpen()
517  {
518  if (state_ != CLOSED)
519  {
520  ROS_ERROR("Assertion failing. state_ = %i", state_);
521  ROS_ASSERT(state_ == CLOSED);
522  }
523 
524  ROS_DEBUG("open()");
525 
526  int retval;
527 
528  // Check that rmem_max is large enough.
529  rmem_max_ = get_rmem_max();
530  if (rmem_max_ < RMEM_MAX_RECOMMENDED)
531  {
532  ROS_WARN_NAMED("rmem_max", "rmem_max is %i. Buffer overflows and packet loss may occur. Minimum recommended value is 20000000. Updates may not take effect until the driver is restarted. See http://www.ros.org/wiki/wge100_camera/Troubleshooting", rmem_max_);
533  }
534 
535  ROS_DEBUG("Redoing discovery.");
536  const char *errmsg;
537  int findResult = wge100FindByUrl(config_.camera_url.c_str(), &camera_, SEC_TO_USEC(0.2), &errmsg);
538 
539  if (findResult)
540  {
541  setStatusMessagef("Matching URL %s : %s", config_.camera_url.c_str(), errmsg);
542  strncpy(camera_.cam_name, "unknown", sizeof(camera_.cam_name));
543  camera_.serial = -1;
544  return;
545  }
546 
547  retval = wge100Configure(&camera_, camera_.ip_str, SEC_TO_USEC(0.5));
548  if (retval != 0) {
549  setStatusMessage("IP address configuration failed");
550  return;
551  }
552 
553  ROS_INFO("Configured camera. Complete URLs: serial://%u@%s#%s name://%s@%s#%s",
554  camera_.serial, camera_.ip_str, camera_.ifName, camera_.cam_name, camera_.ip_str, camera_.ifName);
555 
556  camera_.serial = camera_.serial;
557  hwinfo_ = camera_.hwinfo;
558 
559  // We are going to receive the video on this host, so we need our own MAC address
560  if ( wge100EthGetLocalMac(camera_.ifName, &localMac_) != 0 ) {
561  setStatusMessagef("Unable to get local MAC address for interface %s", camera_.ifName);
562  return;
563  }
564 
565  // We also need our local IP address
566  if ( wge100IpGetLocalAddr(camera_.ifName, &localIp_) != 0) {
567  setStatusMessagef("Unable to get local IP address for interface %s", camera_.ifName);
568  return;
569  }
570 
571  if (config_.ext_trig && config_.trig_timestamp_topic.empty())
572  {
573  setStatusMessage("External triggering is selected, but no \"trig_timestamp_topic\" was specified.");
574  return;
575  }
576 
577  // Select data resolution
578  if ( wge100ImagerSetRes( &camera_, config_.width, config_.height ) != 0) {
579  setStatusMessage("Error selecting image resolution.");
580  return;
581  }
582 
583  ImagerSettings primary_settings, alternate_settings;
584 
585  primary_settings.height = config_.height;
586  primary_settings.width = config_.width;
587  primary_settings.x_offset = config_.x_offset;
588  primary_settings.y_offset = config_.y_offset;
589  primary_settings.horizontal_binning = config_.horizontal_binning;
590  primary_settings.vertical_binning = config_.vertical_binning;
591  primary_settings.imager_rate = config_.imager_rate;
592  primary_settings.companding = config_.companding;
593  primary_settings.auto_gain = config_.auto_gain;
594  primary_settings.gain = config_.gain;
595  primary_settings.auto_exposure = config_.auto_exposure;
596  primary_settings.exposure = config_.exposure;
597  primary_settings.mirror_x = config_.mirror_x;
598  primary_settings.mirror_y = config_.mirror_y;
599  primary_settings.rotate_180 = config_.rotate_180;
600  primary_settings.auto_black_level = config_.auto_black_level;
601  primary_settings.black_level_filter_length = config_.black_level_filter_length;
602  primary_settings.black_level_step_size = config_.black_level_step_size;
603  primary_settings.black_level = config_.black_level;
604  primary_settings.brightness = config_.brightness;
605  primary_settings.max_exposure = config_.max_exposure;
606 
607  alternate_settings.height = config_.height;
608  alternate_settings.width = config_.width;
609  alternate_settings.x_offset = config_.x_offset;
610  alternate_settings.y_offset = config_.y_offset;
611  alternate_settings.horizontal_binning = config_.horizontal_binning;
612  alternate_settings.vertical_binning = config_.vertical_binning;
613  alternate_settings.imager_rate = config_.imager_rate;
614  alternate_settings.companding = config_.companding_alternate;
615  alternate_settings.auto_gain = config_.auto_gain_alternate;
616  alternate_settings.gain = config_.gain_alternate;
617  alternate_settings.auto_exposure = config_.auto_exposure_alternate;
618  alternate_settings.exposure = config_.exposure_alternate;
619  alternate_settings.mirror_x = config_.mirror_x;
620  alternate_settings.mirror_y = config_.mirror_y;
621  alternate_settings.rotate_180 = config_.rotate_180;
622  alternate_settings.auto_black_level = config_.auto_black_level;
623  alternate_settings.black_level = config_.black_level;
624  alternate_settings.black_level_filter_length = config_.black_level_filter_length;
625  alternate_settings.black_level_step_size = config_.black_level_step_size;
626  alternate_settings.brightness = config_.brightness;
627  alternate_settings.max_exposure = config_.max_exposure;
628 
629  imager_ = MT9VImager::getInstance(camera_);
630  if (!imager_)
631  {
632  setStatusMessage("Unknown imager model.");
633  return;
634  }
635  alternate_imager_ = imager_->getAlternateContext();
636 
637  if (enable_primary_ && enable_alternate_) // Both
638  {
639  if (!alternate_imager_)
640  {
641  setStatusMessage("Unable to find alternate imager context, but enable_alternate is true.");
642  return;
643  }
644 
645  // Workaround for firmware/imager bug.
646  primary_settings.auto_exposure = config_.auto_exposure_alternate;
647  primary_settings.gain = config_.gain_alternate;
648  primary_settings.companding = config_.companding_alternate;
649  alternate_settings.auto_exposure = config_.auto_exposure;
650  alternate_settings.gain = config_.gain;
651  alternate_settings.companding = config_.companding;
652 
653  if (setImagerSettings(*imager_, primary_settings) ||
654  setImagerSettings(*alternate_imager_, alternate_settings))
655  return;
656  }
657  else if (enable_primary_) // Primary only
658  {
659  if (setImagerSettings(*imager_, primary_settings))
660  return;
661  }
662  else // Alternate only
663  {
664  if (setImagerSettings(*imager_, alternate_settings))
665  return;
666  }
667 
668  if (enable_alternate_ && enable_primary_ && wge100SensorSelect(&camera_, 0, 0x07))
669  {
670  setStatusMessage("Error configuring camera to report alternate frames.");
671  return;
672  }
673 
674  next_is_alternate_ = !enable_primary_;
675 
676  // Initialize frame time filter.
677  frame_time_filter_ = FrameTimeFilter(desired_freq_, 0.001, 0.5 / config_.imager_rate);
678  trigger_matcher_.setTrigDelay(1. / config_.imager_rate);
679 
680  // The modulo below is for cameras that got programmed with the wrong
681  // serial number. There should only be one currently.
682  bool is_027 = ((camera_.serial / 100000) % 100) == 27;
683  bool is_MT9V032 = imager_->getModel() == "MT9V032";
684  if (camera_.serial != 0 && is_027 == is_MT9V032)
685  {
686  boost::recursive_mutex::scoped_lock lock(mutex_);
687  setStatusMessagef("Camera has %s serial number, but has %s imager. This is a serious problem with the hardware.",
688  is_027 ? "027" : "non-027", imager_->getModel().c_str());
689  return;
690  }
691 
692  if (config_.test_pattern && (
693  wge100ReliableSensorWrite( &camera_, 0x7F, 0x3800, NULL ) != 0 ||
694  wge100ReliableSensorWrite( &camera_, 0x0F, 0x0006, NULL ) != 0)) {
695  setStatusMessage("Error turning on test pattern.");
696  return;
697  }
698 
699  setStatusMessage("Camera is opened.");
700  state_ = OPENED;
701  last_camera_ok_time_ = ros::Time::now().toSec(); // If we get here we are communicating with the camera well.
702  }
703 
704  void doClose()
705  {
706  ROS_DEBUG("close()");
707  ROS_ASSERT(state_ == OPENED);
708  state_ = CLOSED;
709  setStatusMessage("Camera is closed.");
710  }
711 
712  void doStart()
713  {
714  last_frame_number_ = 0xFFFF;
715  last_partial_frame_number_ = 0xFFFF;
716 
717  // Select trigger mode.
718  int trig_mode = config_.ext_trig ? TRIG_STATE_EXTERNAL : TRIG_STATE_INTERNAL;
719  if (enable_alternate_ && enable_primary_)
720  trig_mode |= TRIG_STATE_ALTERNATE;
721  if (config_.rising_edge_trig)
722  trig_mode |= TRIG_STATE_RISING;
723  if ( wge100TriggerControl( &camera_, trig_mode ) != 0) {
724  setStatusMessagef("Trigger mode set error. Is %s accessible from interface %s? (Try running route to check.)", camera_.ip_str, camera_.ifName);
725  state_ = CLOSED; // Might have failed because camera was reset. Go to closed.
726  return;
727  }
728 
729  trigger_matcher_.reset();
730 
731  /*
732  if (0) // Use this to dump register values.
733  {
734  for (int i = 0; i <= 255; i++)
735  {
736  uint16_t value;
737  if (wge100ReliableSensorRead(&camera_, i, &value, NULL))
738  exit(1);
739  printf("usleep(50000); stcam_->setRegister(0xFF000, 0x4%02x%04x); printf(\"%08x\\n\", stcam_->getRegister(0xFF000));\n", i, value);
740  }
741  } */
742 
743  ROS_DEBUG("start()");
744  ROS_ASSERT(state_ == OPENED);
745  setStatusMessage("Waiting for first frame...");
746  state_ = RUNNING;
747  image_thread_.reset(new boost::thread(boost::bind(&WGE100CameraDriver::imageThread, this)));
748  }
749 
750  void doStop()
751  {
752  ROS_DEBUG("stop()");
753  if (state_ != RUNNING) // RUNNING can exit asynchronously.
754  return;
755 
757  setStatusMessage("Stopped");
758 
759  state_ = OPENED;
760 
761  if (image_thread_ && !image_thread_->timed_join((boost::posix_time::milliseconds) 2000))
762  {
763  ROS_ERROR("image_thread_ did not die after two seconds. Pretending that it did. This is probably a bad sign.");
764  lost_image_thread_count_++;
765  }
766  image_thread_.reset();
767  }
768 
770  {
771  if ( wge100ReliableSensorWrite( &camera_, 0x7F, mode, NULL ) != 0) {
772  status.summary(2, "Could not set imager into test mode.");
773  status.add("Writing imager test mode", "Fail");
774  return 1;
775  }
776  else
777  {
778  status.add("Writing imager test mode", "Pass");
779  }
780 
781  usleep(100000);
782  uint16_t inmode;
783  if ( wge100ReliableSensorRead( &camera_, 0x7F, &inmode, NULL ) != 0) {
784  status.summary(2, "Could not read imager mode back.");
785  status.add("Reading imager test mode", "Fail");
786  return 1;
787  }
788  else
789  {
790  status.add("Reading imager test mode", "Pass");
791  }
792 
793  if (inmode != mode) {
794  status.summary(2, "Imager test mode read back did not match.");
795  status.addf("Comparing read back value", "Fail (%04x != %04x)", inmode, mode);
796  return 1;
797  }
798  else
799  {
800  status.add("Comparing read back value", "Pass");
801  }
802 
803  return 0;
804  }
805 
806  std::string getID()
807  {
808  if (!camera_.serial)
809  return "unknown";
810  else
811  return (boost::format("WGE100_%05u") % camera_.serial).str();
812  }
813 
814 private:
815  void imageThread()
816  {
817  // Start video
818  frame_time_filter_.reset_filter();
819 
820  first_frame_ = true;
821  int ret_val = wge100VidReceiveAuto( &camera_, config_.height, config_.width, &WGE100CameraDriver::frameHandler, this);
822 
823  if (ret_val == IMAGER_LINENO_OVF)
824  setStatusMessage("Camera terminated streaming with an overflow error.");
825  else if (ret_val == IMAGER_LINENO_ERR)
826  setStatusMessage("Camera terminated streaming with an error.");
827  else if (ret_val)
828  setStatusMessage("wge100VidReceiveAuto exited with an error code.");
829 
830  // At this point we are OPENED with lock held, or RUNNING, or image_thread_ did not die after two seconds (which should not happen).
831  // Thus, it is safe to switch to OPENED, except if the image_thread_
832  // did not die, in which case the if will save us most of the time.
833  // (Can't take the lock because the doStop thread might be holding it.)
834  if (state_ == RUNNING)
835  state_ = OPENED;
836  ROS_DEBUG("Image thread exiting.");
837  }
838 
840  {
841  double since_last_frame_ = ros::Time::now().toSec() - (last_image_time_ ? last_image_time_ : driver_start_time_);
842  if (since_last_frame_ > 10)
843  {
844  stat.summary(2, "Next frame is way past due.");
845  }
846  else if (since_last_frame_ > 5 / desired_freq_)
847  {
848  stat.summary(1, "Next frame is past due.");
849  }
850  else if (rmem_max_ < RMEM_MAX_RECOMMENDED)
851  {
852  stat.summaryf(1, "Frames are streaming, but the receive buffer is small (rmem_max=%u). Please increase rmem_max to %u to avoid dropped frames. For details: http://www.ros.org/wiki/wge100_camera/Troubleshooting", rmem_max_, RMEM_MAX_RECOMMENDED);
853  }
854  else if (dropped_packet_event_ && config_.packet_debug)
855  {
856  // Only warn in diagnostics for dropped packets if we're in debug mode, #3834
857  stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "There have been dropped packets.");
858  dropped_packet_event_ = 0;
859  }
860  else
861  {
862  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Frames are streaming.");
863  dropped_packet_event_ = 0;
864  }
865 
866  stat.addf("Missing image line frames", "%d", missed_line_count_);
867  stat.addf("Missing EOF frames", "%d", missed_eof_count_);
868  stat.addf("Dropped packet estimate", "%d", dropped_packets_);
869  stat.addf("Frames dropped by trigger filter", "%d", trigger_matcher_drop_count_);
870  stat.addf("Massive frame losses", "%d", massive_frame_losses_);
871  stat.addf("Losses of image thread", "%d", lost_image_thread_count_);
872  stat.addf("First packet offset", "%d", config_.first_packet_offset);
873  if (isClosed())
874  {
875  static const std::string not_opened = "not_opened";
876  stat.add("Camera Serial #", not_opened);
877  stat.add("Camera Name", not_opened);
878  stat.add("Camera Hardware", not_opened);
879  stat.add("Camera MAC", not_opened);
880  stat.add("Interface", not_opened);
881  stat.add("Camera IP", not_opened);
882  stat.add("Image encoding", not_opened);
883  }
884  else
885  {
886  stat.add("Camera Serial #", camera_.serial);
887  stat.add("Camera Name", camera_.cam_name);
888  stat.add("Camera Hardware", hwinfo_);
889  stat.addf("Camera MAC", "%02X:%02X:%02X:%02X:%02X:%02X", camera_.mac[0], camera_.mac[1], camera_.mac[2], camera_.mac[3], camera_.mac[4], camera_.mac[5]);
890  stat.add("Interface", camera_.ifName);
891  stat.add("Camera IP", camera_.ip_str);
892  stat.add("Image encoding", image_encoding_);
893  }
894  stat.add("Image width", config_.width);
895  stat.add("Image height", config_.height);
896  stat.add("Image horizontal binning", config_.horizontal_binning);
897  stat.add("Image vertical binning", config_.vertical_binning);
898  stat.add("Requested imager rate", config_.imager_rate);
899  stat.add("Latest frame time", last_image_time_);
900  stat.add("Latest frame #", last_frame_number_);
901  stat.add("External trigger controller", config_.trig_timestamp_topic);
902  stat.add("Trigger mode", config_.ext_trig ? "external" : "internal");
903  boost::shared_ptr<MT9VImager> imager = imager_; // For thread safety.
904  if (imager)
905  {
906  stat.add("Imager model", imager->getModel());
907  stat.addf("Imager version", "0x%04x", imager->getVersion());
908  }
909  }
910 
911  double getTriggeredFrameTime(double firstPacketTime)
912  {
913  /*// Assuming that there was no delay in receiving the first packet,
914  // this should tell us the time at which the first trigger after the
915  // image exposure occurred.
916  double pulseStartTime = 0;
917  controller::TriggerController::getTickStartTimeSec(firstPacketTime, trig_req_);
918 
919  // We can now compute when the exposure ended. By offsetting to the
920  // falling edge of the pulse, going back one pulse duration, and going
921  // forward one camera frame time.
922  double exposeEndTime = pulseStartTime +
923  controller::TriggerController::getTickDurationSec(trig_req_) +
924  1 / imager_freq_ -
925  1 / config_.trig_rate;
926 
927  return exposeEndTime;*/
928  return 0;
929  }
930 
931  double getExternallyTriggeredFrameTime(double firstPacketTime)
932  {
933  // We can now compute when the exposure ended. By offsetting by the
934  // empirically determined first packet offset, going back one pulse
935  // duration, and going forward one camera frame time.
936 
937  double exposeEndTime = firstPacketTime - config_.first_packet_offset +
938  1 / config_.imager_rate -
939  1 / config_.trig_rate;
940 
941  return exposeEndTime;
942  }
943 
944  double getFreeRunningFrameTime(double firstPacketTime)
945  {
946  // This offset is empirical, but fits quite nicely most of the time.
947 
948  return firstPacketTime - config_.first_packet_offset;
949  }
950 
951 // double nowTime = ros::Time::now().toSec();
952 // static double lastExposeEndTime;
953 // static double lastStartTime;
954 // if (fabs(exposeEndTime - lastExposeEndTime - 1 / trig_rate_) > 1e-4)
955 // ROS_INFO("Mistimed frame #%u first %f first-pulse %f now-first %f pulse-end %f end-last %f first-last %f", eofInfo->header.frame_number, frameStartTime, frameStartTime - pulseStartTime, nowTime - pulseStartTime, pulseStartTime - exposeEndTime, exposeEndTime - lastExposeEndTime, frameStartTime - lastStartTime);
956 // lastStartTime = frameStartTime;
957 // lastExposeEndTime = exposeEndTime;
958 //
959 // static double lastImageTime;
960 // static double firstFrameTime;
961 // if (eofInfo->header.frame_number == 100)
962 // firstFrameTime = imageTime;
963 // ROS_INFO("Frame #%u time %f ofs %f ms delta %f Hz %f", eofInfo->header.frame_number, imageTime, 1000 * (imageTime - firstFrameTime - 1. / (29.5/* * 0.9999767*/) * (eofInfo->header.frame_number - 100)), imageTime - lastImageTime, 1. / (imageTime - lastImageTime));
964 // lastImageTime = imageTime;
965 
967 
968  int frameHandler(wge100FrameInfo *frame_info)
969  {
970  boost::recursive_mutex::scoped_lock(diagnostics_lock_);
971 
972  if (state_ != RUNNING)
973  return 1;
974 
975  if (frame_info == NULL)
976  {
977  boost::recursive_mutex::scoped_lock lock(mutex_);
978  // The select call in the driver timed out.
980  if (config_.ext_trig && !trigger_matcher_.hasTimestamp())
981  {
982  const char *msg = "More than one second elapsed without receiving any trigger. Is this normal?";
983  if (no_timestamp_warning_filter_.badFrame())
984  ROS_WARN_NAMED("slow_trigger", "%s", msg);
985  else
986  ROS_DEBUG("%s", msg);
987  return 0;
988  }
989 
991  // simultaneously trying to be killed? The other thread will time out
992  // on the join, but the error message it spits out in that case is
993  // ugly.
994  setStatusMessagef("No data have arrived for more than one second. Assuming that camera is no longer streaming.");
995  return 1;
996  }
997 
998  no_timestamp_warning_filter_.goodFrame();
999 
1000  if (first_frame_)
1001  {
1002  ROS_INFO_COND(first_frame_, "Camera streaming.");
1003  setStatusMessage("Camera streaming.");
1004  }
1005  first_frame_ = false;
1006 
1007  double firstPacketTime = frame_info->startTime.tv_sec + frame_info->startTime.tv_usec / 1e6;
1008 
1009  // Use a minimum filter to eliminate variations in delay from frame
1010  // to frame.
1011  double firstPacketTimeFiltered = fabs(frame_time_filter_.run(firstPacketTime, frame_info->frame_number));
1012 
1013  // If we don't know the trigger times then use the arrival time of the
1014  // first packet run through a windowed minimum filter to estimate the
1015  // trigger times.
1016 
1017  ros::Time imageTime;
1018  if (!config_.ext_trig || config_.trig_timestamp_topic.empty())
1019  {
1020  // Suppose that the first packet arrived a fixed time after the
1021  // trigger signal for the next frame. (I.e., this guess will be used
1022  // as a timestamp for the next frame not for this one.)
1023  double triggerTimeGuess = firstPacketTimeFiltered - config_.first_packet_offset;
1024  // Send the trigger estimate to the trigger matcher.
1025  trigger_matcher_.triggerCallback(triggerTimeGuess);
1026  //ROS_INFO("Added filtered trigger: %f", triggerTimeGuess);
1027  //ROS_INFO("Using firstPacketTime: %f", firstPacketTime);
1028  imageTime = trigger_matcher_.getTimestampNoblock(ros::Time(firstPacketTime));
1029  //ROS_INFO("Got timestamp: %f delta: %f", imageTime.toSec(), imageTime.toSec() - triggerTimeGuess);
1030  }
1031  else
1032  {
1033  //ROS_INFO("Using firstPacketTime: %f", firstPacketTime);
1034  //double pre = ros::Time::now().toSec();
1035  imageTime = trigger_matcher_.getTimestampBlocking(ros::Time(firstPacketTime), 0.01);
1036  //double post = ros::Time::now().toSec();
1037  //ROS_INFO("Got timestamp: %f delta: %f delay: %f", imageTime.toSec(), imageTime.toSec() - firstPacketTime, post - pre);
1038  }
1039 
1040  //static double lastunfiltered;
1041  //ROS_DEBUG("first_packet %f unfilteredImageTime %f frame_id %u deltaunf: %f", frameStartTime, unfilteredImageTime, frame_info->frame_number, unfilteredImageTime - lastunfiltered);
1042  //lastunfiltered = unfilteredImageTime;
1043 
1044  int16_t frame_delta = (frame_info->frame_number - last_partial_frame_number_) & 0xFFFF;
1045  last_partial_frame_number_ = frame_info->frame_number;
1046  if (frame_delta > 1)
1047  {
1048  dropped_packet_event_ = true;
1049  ROS_DEBUG_NAMED("dropped_frame", "Some frames were never seen. The dropped packet count will be incorrect.");
1050  massive_frame_losses_++;
1051  }
1052 
1053  // Check for frame with missing EOF.
1054  if (frame_info->eofInfo == NULL) {
1055  missed_eof_count_++;
1056  dropped_packets_++;
1057  dropped_packet_event_ = true;
1058  ROS_DEBUG_NAMED("no_eof", "Frame %u was missing EOF", frame_info->frame_number);
1059  return 0;
1060  }
1061 
1062  // Check for short packet (video lines were missing)
1063  if (frame_info->shortFrame) {
1064  missed_line_count_++;
1065  dropped_packets_ += frame_info->missingLines;
1066  dropped_packet_event_ = true;
1067  ROS_DEBUG_NAMED("short_frame", "Short frame #%u (%zu video lines were missing, last was %zu)", frame_info->frame_number,
1068  frame_info->missingLines, frame_info->lastMissingLine);
1069  return 0;
1070  }
1071 
1073  {
1074  trigger_matcher_drop_count_++;
1075  ROS_DEBUG_NAMED("missing_timestamp", "TriggerMatcher did not get timestamp for frame #%u", frame_info->frame_number);
1076  return 0;
1077  }
1078 
1080  {
1081  trigger_matcher_drop_count_++;
1082  ROS_DEBUG_NAMED("trigger_match_fail", "TriggerMatcher dropped frame #%u", frame_info->frame_number);
1083  return 0;
1084  }
1085 
1086  last_image_time_ = imageTime.toSec();
1087  last_frame_number_ = frame_info->frame_number;
1088 
1090 
1091  bool alternate = next_is_alternate_;
1092  next_is_alternate_ = enable_alternate_ && (!enable_primary_ || (frame_info->eofInfo->i2c[0] & 0x8000));
1093 
1094  return useFrame_(frame_info->width, frame_info->height, frame_info->frameData, imageTime, alternate);
1095  }
1096 
1097  bool isColor()
1098  {
1099  return imager_->getModel() == "MT9V032";
1100 // Serial number test fails because of the exceptions I have introduced.
1101 // May want to go back to this test if we get dual register set color
1102 // imagers.
1103 // return !(camera_.serial >= 2700000 && camera_.serial < 2800000);
1104  }
1105 
1106  static int frameHandler(wge100FrameInfo *frameInfo, void *userData)
1107  {
1108  WGE100CameraDriver &fa_node = *(WGE100CameraDriver*)userData;
1109  return fa_node.frameHandler(frameInfo);
1110  }
1111 
1112  uint16_t intrinsicsChecksum(uint16_t *data, int words)
1113  {
1114  uint16_t sum = 0;
1115  for (int i = 0; i < words; i++)
1116  sum += htons(data[i]);
1117  return htons(0xFFFF - sum);
1118  }
1119 
1120  bool loadIntrinsics(sensor_msgs::CameraInfo &cam_info)
1121  {
1122  // Retrieve contents of user memory
1123  std::string calbuff(2 * FLASH_PAGE_SIZE, 0);
1124 
1125  if(wge100ReliableFlashRead(&camera_, FLASH_CALIBRATION_PAGENO, (uint8_t *) &calbuff[0], NULL) != 0 ||
1126  wge100ReliableFlashRead(&camera_, FLASH_CALIBRATION_PAGENO+1, (uint8_t *) &calbuff[FLASH_PAGE_SIZE], NULL) != 0)
1127  {
1128  ROS_WARN("Error reading camera intrinsics from flash.\n");
1129  return false;
1130  }
1131 
1132  uint16_t chk = intrinsicsChecksum((uint16_t *) &calbuff[0], calbuff.length() / 2);
1133  if (chk)
1134  {
1135  ROS_WARN_NAMED("no_intrinsics", "Camera intrinsics have a bad checksum. They have probably never been set.");
1136  return false;
1137  }
1138 
1139  std::string name;
1140  bool success = camera_calibration_parsers::parseCalibration(calbuff, "ini", name, cam_info);
1141  // @todo: rescale intrinsics as needed?
1142  if (success && (cam_info.width != (unsigned int) config_.width || cam_info.height != (unsigned int) config_.height)) {
1143  ROS_ERROR("Image resolution from intrinsics file does not match current video setting, "
1144  "intrinsics expect %ix%i but running at %ix%i", cam_info.width, cam_info.height, config_.width, config_.height);
1145  }
1146 
1147  if (!success)
1148  ROS_ERROR("Could not parse camera intrinsics downloaded from camera.");
1149 
1150  return success;
1151  }
1152 
1153  bool saveIntrinsics(const sensor_msgs::CameraInfo &cam_info)
1154  {
1155  char calbuff[2 * FLASH_PAGE_SIZE];
1156 
1157  std::stringstream inifile;
1158  writeCalibrationIni(inifile, camera_.cam_name, cam_info);
1159  strncpy(calbuff, inifile.str().c_str(), 2 * FLASH_PAGE_SIZE - 2);
1160 
1161  uint16_t *chk = ((uint16_t *) calbuff) + FLASH_PAGE_SIZE - 1;
1162  *chk = 0;
1163  *chk = intrinsicsChecksum((uint16_t *) calbuff, sizeof(calbuff) / 2);
1164 
1165  ROS_INFO("Writing camera info:\n%s", calbuff);
1166 
1167  boost::recursive_mutex::scoped_lock lock_(mutex_); // Don't want anybody restarting us behind our back.
1168  goOpened();
1169 
1170  // Retrieve contents of user memory
1171 
1172  bool ret = false;
1173  if (state_ != OPENED)
1174  ROS_ERROR("Error putting camera into OPENED state to write to flash.\n");
1175  else if(wge100ReliableFlashWrite(&camera_, FLASH_CALIBRATION_PAGENO, (uint8_t *) calbuff, NULL) != 0 ||
1176  wge100ReliableFlashWrite(&camera_, FLASH_CALIBRATION_PAGENO+1, (uint8_t *) calbuff + FLASH_PAGE_SIZE, NULL) != 0)
1177  ROS_ERROR("Error writing camera intrinsics to flash.\n");
1178  else
1179  {
1180  ROS_INFO("Camera_info successfully written to camera.");
1181  ret = true;
1182  }
1183 
1184  goClosed(); // Need to close to reread the intrinsics.
1185  goRunning(); // The new intrinsics will get read here.
1186  return ret;
1187  }
1188 
1189  bool boardConfig(wge100_camera::BoardConfig::Request &req, wge100_camera::BoardConfig::Response &rsp)
1190  {
1191  if (state_ != RUNNING)
1192  {
1193  ROS_ERROR("board_config sevice called while camera was not running. To avoid programming the wrong camera, board_config can only be called while viewing the camera stream.");
1194  rsp.success = 0;
1195  return 1;
1196  }
1197 
1198  MACAddress mac;
1199  if (req.mac.size() != 6)
1200  {
1201  ROS_ERROR("board_config service called with %zu bytes in MAC. Should be 6.", req.mac.size());
1202  rsp.success = 0;
1203  return 1;
1204  }
1205  for (int i = 0; i < 6; i++)
1206  mac[i] = req.mac[i];
1207  ROS_INFO("board_config called s/n #%i, and MAC %02x:%02x:%02x:%02x:%02x:%02x.", req.serial, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1208  stop();
1209  rsp.success = !wge100ConfigureBoard( &camera_, req.serial, &mac);
1210 
1211  if (rsp.success)
1212  ROS_INFO("board_config succeeded.");
1213  else
1214  ROS_INFO("board_config failed.");
1215 
1216  return 1;
1217  }
1218 
1219 };
1220 
1221 class WGE100CameraNode : public driver_base::DriverNode<WGE100CameraDriver>
1222 {
1223 public:
1225  driver_base::DriverNode<WGE100CameraDriver>(nh),
1226  camera_node_handle_(nh.resolveName("camera")),
1227  camera_node_handle_alt_(nh.resolveName("camera_alternate")),
1228  cam_pub_diag_(cam_pub_.getTopic(),
1229  diagnostic_,
1230  diagnostic_updater::FrequencyStatusParam(&driver_.desired_freq_, &driver_.desired_freq_, 0.05),
1231  diagnostic_updater::TimeStampStatusParam()),
1232  config_bord_service_(private_node_handle_.advertiseService("board_config", &WGE100CameraDriver::boardConfig, &driver_)),
1233  set_camera_info_service_(camera_node_handle_.advertiseService("set_camera_info", &WGE100CameraNode::setCameraInfo, this)),
1234  wge100_diagnostic_task_("WGE100 Diagnostics", boost::bind(&WGE100CameraDriver::cameraStatus, &driver_, _1)),
1235  calibrated_(false)
1236  {
1237  // Take care not to advertise the same topic twice to work around ros-pkg #4689,
1238  // advertising reconfigure services twice for image_transport plugins.
1239  // In future, image_transport should handle that transparently.
1240  std::string topic = camera_node_handle_.resolveName("image_raw");
1241  std::string alt_topic = camera_node_handle_alt_.resolveName("image_raw");
1243  cam_pub_ = it.advertiseCamera(topic, 1);
1244  if (topic == alt_topic)
1245  cam_pub_alt_ = cam_pub_;
1246  else
1247  cam_pub_alt_ = it.advertiseCamera(alt_topic, 1);
1248 
1249  driver_.useFrame_ = boost::bind(&WGE100CameraNode::publishImage, this, _1, _2, _3, _4, _5);
1250  driver_.setPostOpenHook(boost::bind(&WGE100CameraNode::postOpenHook, this));
1251  }
1252 
1253 private:
1256 
1257  // Publications
1261  sensor_msgs::Image image_;
1262  sensor_msgs::CameraInfo camera_info_;
1266 
1268 
1269  // Calibration
1271 
1272  int publishImage(size_t width, size_t height, uint8_t *frameData, ros::Time t, bool alternate)
1273  {
1274  // Raw data is bayer BGGR pattern
1275  fillImage(image_, driver_.image_encoding_, height, width, width, frameData);
1276 
1277  fflush(stdout);
1278  (alternate ? cam_pub_alt_ : cam_pub_).publish(image_, camera_info_, t);
1279  cam_pub_diag_.tick(t);
1280 
1281  return 0;
1282  }
1283 
1284  virtual void reconfigureHook(int level)
1285  {
1286  ROS_DEBUG("WGE100CameraNode::reconfigureHook called at level %x", level);
1287 
1288  image_.header.frame_id = driver_.config_.frame_id;
1289  camera_info_.header.frame_id = driver_.config_.frame_id;
1290  }
1291 
1293  {
1294  // Try to load camera intrinsics from flash memory
1295  calibrated_ = driver_.loadIntrinsics(camera_info_);
1296  if (calibrated_)
1297  ROS_INFO("Loaded intrinsics from camera.");
1298  else
1299  {
1300  ROS_WARN_NAMED("no_intrinsics", "Failed to load intrinsics from camera");
1301  camera_info_ = sensor_msgs::CameraInfo();
1302  camera_info_.width = driver_.config_.width;
1303  camera_info_.height = driver_.config_.height;
1304  }
1305  // Receive timestamps through callback
1306  if (driver_.config_.ext_trig && !driver_.config_.trig_timestamp_topic.empty())
1307  {
1308  void (timestamp_tools::TriggerMatcher::*cb)(const std_msgs::HeaderConstPtr &) = &timestamp_tools::TriggerMatcher::triggerCallback;
1309  trigger_sub_ = node_handle_.subscribe(driver_.config_.trig_timestamp_topic, 100, cb, &driver_.trigger_matcher_);
1310  }
1311  else
1312  trigger_sub_.shutdown();
1313 
1314  diagnostic_.setHardwareID(driver_.getID());
1315  }
1316 
1317  virtual void addDiagnostics()
1318  {
1319  // Set up diagnostics
1320  driver_status_diagnostic_.addTask(&wge100_diagnostic_task_);
1321  }
1322 
1323  virtual void addRunningTests()
1324  {
1325  self_test_.add( "Streaming Test", this, &WGE100CameraNode::streamingTest);
1326  }
1327 
1328  bool setCameraInfo(sensor_msgs::SetCameraInfo::Request &req, sensor_msgs::SetCameraInfo::Response &rsp)
1329  {
1330  sensor_msgs::CameraInfo &cam_info = req.camera_info;
1331 
1332  if (driver_.getState() != driver_base::Driver::RUNNING)
1333  {
1334  ROS_ERROR("set_camera_info service called while camera was not running. To avoid programming the wrong camera, set_camera_info can only be called while viewing the camera stream.");
1335  rsp.status_message = "Camera not running. Camera must be running to avoid setting the intrinsics of the wrong camera.";
1336  rsp.success = false;
1337  return 1;
1338  }
1339 
1340  if ((cam_info.width != (unsigned int) driver_.config_.width || cam_info.height != (unsigned int) driver_.config_.height)) {
1341  ROS_ERROR("Image resolution of camera_info passed to set_camera_info service does not match current video setting, "
1342  "camera_info contains %ix%i but camera running at %ix%i", cam_info.width, cam_info.height, driver_.config_.width, driver_.config_.height);
1343  rsp.status_message = (boost::format("Camera_info resolution %ix%i does not match camera resolution %ix%i.")%cam_info.width%cam_info.height%driver_.config_.width%driver_.config_.height).str();
1344  rsp.success = false;
1345  }
1346 
1347  rsp.success = driver_.saveIntrinsics(cam_info);
1348  if (!rsp.success)
1349  rsp.status_message = "Error writing camera_info to flash.";
1350 
1351  return 1;
1352  }
1353 
1354  virtual void addOpenedTests()
1355  {
1356  }
1357 
1358  virtual void addStoppedTests()
1359  {
1360  ROS_DEBUG("Adding wge100 camera video mode tests.");
1361  for (int i = 0; i < MT9V_NUM_MODES; i++)
1362  {
1363  if (MT9VModes[i].width != 640)
1364  continue; // Hack, some of the camera firmware doesn't support other modes.
1366  self_test_.add( str(boost::format("Test Pattern in mode %s")%MT9VModes[i].name), f );
1367  }
1368  }
1369 
1371  {
1372  // Try multiple times to improve test reliability by retrying on failure
1373  for (int i = 0; i < 10; i++)
1374  {
1375  cam_pub_diag_.clear_window();
1376  sleep(1);
1377 
1378  cam_pub_diag_.run(status);
1379  if (!status.level)
1380  break;
1381  driver_.start();
1382  }
1383  }
1384 
1386  {
1387  public:
1388  VideoModeTestFrameHandler(diagnostic_updater::DiagnosticStatusWrapper &status, int imager_id) : got_frame_(false), frame_count_(0), status_(status), imager_id_(imager_id)
1389  {}
1390 
1391  volatile bool got_frame_;
1393 
1394  int run(size_t width, size_t height, uint8_t *data, ros::Time stamp, bool alternate)
1395  {
1396  status_.add("Got a frame", "Pass");
1397 
1398  bool final = frame_count_++ > 10;
1399  int mode;
1400  //int error_count = 0;
1401  switch (imager_id_)
1402  {
1403  case 0x1313:
1404  mode = 0;
1405  break;
1406  case 0x1324:
1407  mode = 2;
1408  break;
1409  default:
1410  ROS_WARN("Unexpected imager_id, autodetecting test pattern for this camera.");
1411  mode = -1;
1412  break;
1413  }
1414 
1415  for (size_t y = 0; y < height; y++)
1416  for (size_t x = 0; x < width; x++, data++)
1417  {
1418  std::string msg;
1419 
1420  // Disabled modes other than 640 for now...
1421  //if (width > 320)
1422  msg = check_expected(x, y, *data, final, mode);
1423  //else
1424  //expected = (get_expected(2 * x, 2 * y) + get_expected(2 * x, 2 * y + 1)) / 2;
1425 
1426  if (msg.empty())
1427  continue;
1428 
1429  //if (error_count++ < 50)
1432  // continue;
1433 
1434  if (!final)
1435  {
1436  ROS_WARN("Pattern mismatch, retrying...");
1437  return 0;
1438  }
1439 
1440  status_.summary(2, msg);
1441  status_.add("Frame content", msg);
1442 
1443  got_frame_ = true;
1444  return 1;
1445  }
1446 
1447  status_.addf("Frame content", "Pass");
1448  got_frame_ = true;
1449  return 1;
1450  }
1451 
1452  private:
1453  std::string check_expected(int x, int y, uint8_t actual_col, bool final, int &mode)
1454  {
1455 #define NUM_TEST_PATTERNS 3
1456  // There seem to be two versions of the imager with different
1457  // patterns.
1458  uint8_t col[NUM_TEST_PATTERNS];
1459  col[0] = (x + 2 * y + 25) / 4;
1460  if ((x + 1) / 2 + y < 500)
1461  col[1] = 14 + x / 4;
1462  else
1463  col[1] = 0;
1464  col[2] = (x + 2 * y + 43) / 4;
1465 
1466  std::string msg;
1467 
1468  if (mode != -1)
1469  {
1470  if (col[mode] == actual_col)
1471  return "";
1472 
1473  msg = (boost::format("Unexpected value %u instead of %u at (x=%u, y=%u)")%(int)actual_col%(int)col[mode]%x%y).str();
1474  }
1475  else
1476  {
1477  for (int i = 0; i < NUM_TEST_PATTERNS; i++)
1478  if (col[i] == actual_col)
1479  {
1480  mode = i;
1481  return "";
1482  }
1483 
1484  msg = (boost::format("Unexpected value in first pixel %u, expected: %u, %u or %u")%(int)actual_col%(int)col[0]%(int)col[1]%(int)col[2]).str();
1485  }
1486 
1487  ROS_ERROR("%s", msg.c_str());
1488 
1489  return msg;
1490  }
1493  };
1494 
1496  {
1497  if (mode < 0 || mode > MT9V_NUM_MODES)
1498  {
1499  ROS_ERROR("Tried to call videoModeTest with out of range mode %u.", mode);
1500  return;
1501  }
1502 
1503  const Config old_config = driver_.config_;
1504  WGE100CameraDriver::UseFrameFunction oldUseFrame = driver_.useFrame_;
1505 
1506  Config new_config = driver_.config_;
1507  new_config.width = MT9VModes[mode].width;
1508  new_config.height = MT9VModes[mode].height;
1509  new_config.x_offset = 0;
1510  new_config.y_offset = 0;
1511  new_config.imager_rate = MT9VModes[mode].fps;
1512  new_config.ext_trig = 0;
1513  new_config.register_set = wge100_camera::WGE100Camera_PrimaryRegisterSet;
1514  new_config.test_pattern = true;
1515  driver_.config_update(new_config);
1516  boost::shared_ptr<MT9VImager> imager = driver_.imager_; // For thread safety.
1517  VideoModeTestFrameHandler callback(status, imager ? driver_.imager_->getVersion() : 0);
1518  driver_.useFrame_ = boost::bind(&VideoModeTestFrameHandler::run, boost::ref(callback), _1, _2, _3, _4, _5);
1519 
1520  status.name = (boost::format("Pattern Test: %ix%i at %.1f fps.")%new_config.width%new_config.height%new_config.imager_rate).str();
1521  status.summary(0, "Passed"); // If nobody else fills this, then the test passed.
1522 
1523  int oldcount = driver_.lost_image_thread_count_;
1524  for (int i = 0; i < 50 && !callback.got_frame_; i++)
1525  {
1526  driver_.start(); // In case something goes wrong.
1527  usleep(100000);
1528  }
1529  driver_.close();
1530  if (!callback.got_frame_)
1531  {
1532  ROS_ERROR("Got no frame during pattern test.");
1533  status.summary(2, "Got no frame during pattern test.");
1534  }
1535  if (oldcount < driver_.lost_image_thread_count_)
1536  {
1537  ROS_ERROR("Lost the image_thread. This should never happen.");
1538  status.summary(2, "Lost the image_thread. This should never happen.");
1539  }
1540 
1541  driver_.useFrame_ = oldUseFrame;
1542  driver_.config_update(old_config);
1543 
1544  ROS_INFO("Exiting test %s with status %i: %s", status.name.c_str(), status.level, status.message.c_str());
1545  }
1546 };
1547 
1548 int main(int argc, char **argv)
1549 {
1550  return driver_base::main<WGE100CameraNode>(argc, argv, "wge100_camera");
1551 }
virtual void addStoppedTests()
image_transport::CameraPublisher cam_pub_alt_
msg
#define FLASH_PAGE_SIZE
Definition: ipcam_packet.h:60
int wge100IpGetLocalAddr(const char *ifName, struct in_addr *addr)
Definition: host_netutil.c:231
bool boardConfig(wge100_camera::BoardConfig::Request &req, wge100_camera::BoardConfig::Response &rsp)
virtual bool setBlackLevel(bool manual_override, int calibration_value, int step_size, int filter_length)=0
double run(double in_time, int frame_number)
MT9VImagerPtr alternate_imager_
boost::function< int(size_t, size_t, uint8_t *, ros::Time, bool)> UseFrameFunction
char hwinfo[WGE100_CAMINFO_LEN]
Definition: list.h:302
virtual bool setBrightness(int brightness)=0
static const ros::Time RetryLater
virtual bool setGain(int gain)=0
ros::NodeHandle camera_node_handle_
static const state_t RUNNING
int publishImage(size_t width, size_t height, uint8_t *frameData, ros::Time t, bool alternate)
const struct MT9VMode MT9VModes[MT9V_NUM_MODES]
Definition: mt9v.cpp:41
#define ROS_WARN_NAMED(name,...)
int frameHandler(wge100FrameInfo *frame_info)
f
virtual void reconfigureHook(int level)
void summary(unsigned char lvl, const std::string s)
size_t height
Definition: mt9v.h:71
diagnostic_updater::TopicDiagnostic cam_pub_diag_
int wge100ReliableFlashRead(const IpCamList *camInfo, uint32_t address, uint8_t *pageDataOut, int *retries)
Definition: wge100lib.c:906
int wge100ReliableFlashWrite(const IpCamList *camInfo, uint32_t address, const uint8_t *pageDataIn, int *retries)
Definition: wge100lib.c:1014
size_t lastMissingLine
Definition: wge100lib.h:73
ros::Subscriber trigger_sub_
void videoModeTest(int mode, diagnostic_updater::DiagnosticStatusWrapper &status)
#define TRIG_STATE_INTERNAL
Definition: wge100lib.h:58
bool setCameraInfo(sensor_msgs::SetCameraInfo::Request &req, sensor_msgs::SetCameraInfo::Response &rsp)
uint32_t frame_number
Definition: wge100lib.h:71
int wge100Configure(IpCamList *camInfo, const char *ipAddress, unsigned wait_us)
Definition: wge100lib.c:532
SlowTriggerFilter no_timestamp_warning_filter_
uint8_t MACAddress[6]
Definition: ipcam_packet.h:75
ros::ServiceClient trig_service_
int wge100ReliableSensorRead(const IpCamList *camInfo, uint8_t reg, uint16_t *data, int *retries)
Definition: wge100lib.c:1349
size_t height
Definition: wge100lib.h:69
WGE100CameraNode(ros::NodeHandle &nh)
bool writeCalibrationIni(std::ostream &out, const std::string &camera_name, const sensor_msgs::CameraInfo &cam_info)
virtual void addRunningTests()
uint16_t intrinsicsChecksum(uint16_t *data, int words)
bool loadIntrinsics(sensor_msgs::CameraInfo &cam_info)
FrameTimeFilter(double frame_rate=1., double late_locked_threshold=1e-3, double early_locked_threshold=3e-3, int early_recovery_frames=5, int max_recovery_frames=10, double max_skew=1.001, double max_skipped_frames=100)
VideoModeTestFrameHandler(diagnostic_updater::DiagnosticStatusWrapper &status, int imager_id)
ros::ServiceServer config_bord_service_
diagnostic_updater::FunctionDiagnosticTask wge100_diagnostic_task_
PacketEOF * eofInfo
Definition: wge100lib.h:78
int wge100ReliableSensorWrite(const IpCamList *camInfo, uint8_t reg, uint16_t data, int *retries)
Definition: wge100lib.c:1252
#define ROS_WARN(...)
bool parseCalibration(const std::string &buffer, const std::string &format, std::string &camera_name, sensor_msgs::CameraInfo &cam_info)
double getFreeRunningFrameTime(double firstPacketTime)
void addf(const std::string &key, const char *format,...)
unsigned int last_frame_number_
void cameraStatus(diagnostic_updater::DiagnosticStatusWrapper &stat)
size_t width
Definition: mt9v.h:70
#define RMEM_MAX_RECOMMENDED
virtual bool setMirror(bool mirrorx, bool mirrory)=0
void summaryf(unsigned char lvl, const char *format,...)
#define SEC_TO_USEC(sec)
Definition: host_netutil.h:65
virtual bool setExposure(double exposure)=0
int wge100FindByUrl(const char *url, IpCamList *camera, unsigned wait_us, const char **errmsg)
Definition: wge100lib.c:94
UseFrameFunction useFrame_
virtual void addOpenedTests()
#define ROS_DEBUG_NAMED(name,...)
const double * data
int wge100VidReceiveAuto(IpCamList *camera, size_t height, size_t width, FrameHandler frameHandler, void *userData)
Definition: wge100lib.c:1908
#define IMAGER_LINENO_OVF
Flags this video packet as being an Overflow packet.
Definition: ipcam_packet.h:665
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
#define NUM_TEST_PATTERNS
virtual bool setAgcAec(bool agc_on, bool aec_on)=0
char ip_str[16]
Definition: list.h:296
SlowTriggerFilter(int before_warn, int before_clear)
int wge100TriggerControl(const IpCamList *camInfo, uint32_t triggerType)
Definition: wge100lib.c:1135
void config_update(const Config &new_cfg, uint32_t level=0)
#define ROS_INFO(...)
static int frameHandler(wge100FrameInfo *frameInfo, void *userData)
uint8_t mac[6]
Definition: list.h:294
ros::ServiceServer set_camera_info_service_
int main(int argc, char **argv)
unsigned int last_partial_frame_number_
ros::NodeHandle camera_node_handle_alt_
int wge100ImagerSetRes(const IpCamList *camInfo, uint16_t horizontal, uint16_t vertical)
Definition: wge100lib.c:1548
int wge100EthGetLocalMac(const char *ifName, struct sockaddr *macAddr)
Definition: host_netutil.c:154
virtual void addDiagnostics()
sensor_msgs::Image image_
static const ros::Time DropData
ros::Time getTimestampBlocking(const ros::Time &t)
std::ostream & operator<<(std::ostream &out, const SimpleMatrix &m)
sensor_msgs::CameraInfo camera_info_
size_t missingLines
Definition: wge100lib.h:74
#define MT9V_NUM_MODES
Definition: mt9v.h:66
int setTestMode(uint16_t mode, diagnostic_updater::DiagnosticStatusWrapper &status)
const std::string BAYER_BGGR8
#define TRIG_STATE_EXTERNAL
Definition: wge100lib.h:59
char cam_name[CAMERA_NAME_LEN]
Definition: list.h:304
virtual bool setCompanding(bool activated)=0
void streamingTest(diagnostic_updater::DiagnosticStatusWrapper &status)
int setImagerSettings(MT9VImager &imager, ImagerSettings &cfg)
double getTriggeredFrameTime(double firstPacketTime)
#define IMAGER_LINENO_ERR
Flags this video packet as being a General Error packet.
Definition: ipcam_packet.h:662
#define TRIG_STATE_RISING
Definition: wge100lib.h:61
static MT9VImagerPtr getInstance(IpCamList &cam)
Definition: mt9v.cpp:529
boost::function< void(DiagnosticStatusWrapper &)> TaskFunction
double fps
Definition: mt9v.h:72
diagnostic_updater::DiagnosticStatusWrapper & status_
#define ROS_INFO_COND(cond,...)
int wge100ConfigureBoard(const IpCamList *camInfo, uint32_t serial, MACAddress *mac)
Definition: wge100lib.c:1194
int run(size_t width, size_t height, uint8_t *data, ros::Time stamp, bool alternate)
static Time now()
FrameTimeFilter frame_time_filter_
wge100_camera::WGE100CameraConfig Config
uint8_t * frameData
Definition: wge100lib.h:77
image_transport::CameraPublisher cam_pub_
virtual void run(DiagnosticStatusWrapper &stat)
char ifName[128]
Definition: list.h:293
std::string check_expected(int x, int y, uint8_t actual_col, bool final, int &mode)
void add(const std::string &key, const T &val)
uint32_t serial
Definition: list.h:275
virtual bool setMaxExposure(double exposure)=0
uint16_t i2c[I2C_REGS_PER_FRAME]
Storage for I2C values read during the frame.
Definition: ipcam_packet.h:721
bool saveIntrinsics(const sensor_msgs::CameraInfo &cam_info)
#define ROS_ASSERT(cond)
#define ROS_ERROR(...)
struct timeval startTime
Definition: wge100lib.h:80
double getExternallyTriggeredFrameTime(double firstPacketTime)
SimpleMatrix(int rows, int cols, const double *data)
virtual bool setMode(int x, int y, int binx, int biny, double rate, int xoffset, int yoffset)=0
size_t width
Definition: wge100lib.h:68
boost::shared_ptr< boost::thread > image_thread_
#define FLASH_CALIBRATION_PAGENO
Definition: ipcam_packet.h:63
#define TRIG_STATE_ALTERNATE
Definition: wge100lib.h:60
timestamp_tools::TriggerMatcher trigger_matcher_
ros::Time getTimestampNoblock(const ros::Time &data_time)
#define ROS_DEBUG(...)
int wge100SensorSelect(const IpCamList *camInfo, uint8_t index, uint32_t reg)
Definition: wge100lib.c:1438


wge100_camera
Author(s): Blaise Gassend, Patrick Mihelich, Eric MacIntosh, David Palchak
autogenerated on Mon Jun 10 2019 15:44:15