sick_scan_common.cpp
Go to the documentation of this file.
1 
67 #ifdef _MSC_VER // compiling simulation for MS-Visual C++ - not defined for linux system
68 #pragma warning(disable: 4996)
69 #pragma warning(disable: 4267) // due to conversion warning size_t in the ros header file
70 //#define _WIN32_WINNT 0x0501
71 #endif
72 
83 
84 #include "sick_scan/binScanf.hpp"
85 #include "sick_scan/dataDumper.h"
86 // if there is a missing RadarScan.h, try to run catkin_make in der workspace-root
87 //#include <sick_scan/RadarScan.h>
88 
89 #include <cstdio>
90 #include <cstring>
91 
92 #define _USE_MATH_DEFINES
93 
94 #include <math.h>
95 #include <float.h>
96 
97 #ifndef rad2deg
98 #define rad2deg(x) ((x) / M_PI * 180.0)
99 #endif
100 
101 #define deg2rad_const (0.017453292519943295769236907684886f)
102 
103 #include "sick_scan/tcp/colaa.hpp"
104 #include "sick_scan/tcp/colab.hpp"
105 
106 #include <map>
107 #include <climits>
111 
112 #ifdef ROSSIMU
114 #endif
115 
116 #define RETURN_ERROR_ON_RESPONSE_TIMEOUT(result,reply) if(((result)!=ExitSuccess)&&((reply).empty()))return(ExitError)
117 
118 static const int MAX_STR_LEN = 1024;
119 
125 void swap_endian(unsigned char *ptr, int numBytes)
126 {
127  unsigned char *buf = (ptr);
128  unsigned char tmpChar;
129  for (int i = 0; i < numBytes / 2; i++)
130  {
131  tmpChar = buf[numBytes - 1 - i];
132  buf[numBytes - 1 - i] = buf[i];
133  buf[i] = tmpChar;
134  }
135 }
136 
137 
145 std::vector<unsigned char> stringToVector(std::string s)
146 {
147  std::vector<unsigned char> result;
148  for (size_t j = 0; j < s.length(); j++)
149  {
150  result.push_back(s[j]);
151  }
152  return result;
153 
154 }
155 
156 
162 #ifdef USE_DIAGNOSTIC_UPDATER
163 static int getDiagnosticErrorCode() // workaround due to compiling error under Visual C++
164 {
166 }
167 #endif
168 
176 const std::string binScanfGetStringFromVec(std::vector<unsigned char> *replyDummy, int off, long len)
177 {
178  std::string s;
179  s = "";
180  for (int i = 0; i < len; i++)
181  {
182  char ch = (char) ((*replyDummy)[i + off]);
183  s += ch;
184  }
185  return (s);
186 }
187 
188 static std::vector<int> parseFirmwareVersion(const std::string& scannertype, const std::string& deviceIdentStr) // Parse and return firmware version from device ident string
189 {
190  size_t device_idx = deviceIdentStr.find(scannertype); // Get MRS1xxx version from device ident string
191  size_t version_idx = ((device_idx != std::string::npos) ? deviceIdentStr.find("V", device_idx) : std::string::npos);
192  std::vector<int> version_id;
193  if (version_idx != std::string::npos && version_idx + 1 < deviceIdentStr.size())
194  {
195  std::stringstream device_id_stream(deviceIdentStr.substr(version_idx + 1));
196  std::string token;
197  while (std::getline(device_id_stream, token, '.') && version_id.size() < 3)
198  version_id.push_back(std::atoi(token.c_str()));
199  }
200  while (version_id.size() < 3)
201  version_id.push_back('0');
202  ROS_INFO_STREAM(scannertype << " firmware version " << version_id[0] << "." << version_id[1] << "." << version_id[2]);
203  return version_id;
204 }
205 
207 {
208  static bool field_evaluation_active = false;
209  return field_evaluation_active;
210 }
211 
212 namespace sick_scan_xd
213 {
221  unsigned char sick_crc8(unsigned char *msgBlock, int len)
222  {
223  unsigned char xorVal = 0x00;
224  int off = 0;
225  for (int i = off; i < len; i++)
226  {
227 
228  unsigned char val = msgBlock[i];
229  xorVal ^= val;
230  }
231  return (xorVal);
232  }
233 
239  std::string stripControl(std::vector<unsigned char> s, int max_strlen = -1)
240  {
241  bool isParamBinary = false;
242  int spaceCnt = 0x00;
243  int cnt0x02 = 0;
244 
245  for (size_t i = 0; i < s.size(); i++)
246  {
247  if (s[i] != 0x02)
248  {
249  isParamBinary = false;
250 
251  }
252  else
253  {
254  cnt0x02++;
255  }
256  if (i > 4)
257  {
258  break;
259  }
260  }
261  if (4 == cnt0x02)
262  {
263  isParamBinary = true;
264  }
265  std::string dest;
266  if (isParamBinary == true)
267  {
268  int parseState = 0;
269 
270  unsigned long lenId = 0x00;
271  char szDummy[255] = {0};
272  for (size_t i = 0; i < s.size(); i++)
273  {
274  switch (parseState)
275  {
276  case 0:
277  if (s[i] == 0x02)
278  {
279  dest += "<STX>";
280  }
281  else
282  {
283  dest += "?????";
284  }
285  if (i == 3)
286  {
287  parseState = 1;
288  }
289  break;
290  case 1:
291  lenId |= s[i] << (8 * (7 - i));
292  if (i == 7)
293  {
294  sprintf(szDummy, "<Len=%04lu>", lenId);
295  dest += szDummy;
296  parseState = 2;
297  }
298  break;
299  case 2:
300  {
301  unsigned long dataProcessed = i - 8;
302  if (s[i] == ' ')
303  {
304  spaceCnt++;
305  }
306  if (spaceCnt == 2)
307  {
308  parseState = 3;
309  }
310  dest += s[i];
311  if (dataProcessed >= (lenId - 1))
312  {
313  parseState = 4;
314  }
315 
316  break;
317  }
318 
319  case 3:
320  {
321  char ch = dest[dest.length() - 1];
322  if (ch != ' ')
323  {
324  dest += ' ';
325  }
326  sprintf(szDummy, "0x%02x", s[i]);
327  dest += szDummy;
328 
329  unsigned long dataProcessed = i - 8;
330  if (dataProcessed >= (lenId - 1))
331  {
332  parseState = 4;
333  }
334  break;
335  }
336  case 4:
337  {
338  sprintf(szDummy, " CRC:<0x%02x>", s[i]);
339  dest += szDummy;
340  break;
341  }
342  default:
343  break;
344  }
345  }
346  }
347  else
348  {
349  for (size_t i = 0; i < s.size(); i++)
350  {
351 
352  if (s[i] >= ' ')
353  {
354  // <todo> >= 0x80
355  dest += s[i];
356  }
357  else
358  {
359  switch (s[i])
360  {
361  case 0x02:
362  dest += "<STX>";
363  break;
364  case 0x03:
365  dest += "<ETX>";
366  break;
367  }
368  }
369  }
370  }
371  if(max_strlen > 0 && dest.size() > max_strlen)
372  {
373  dest.resize(max_strlen);
374  dest += "...";
375  }
376 
377  return (dest);
378  }
379 
380  /* void SickScanCommon::getConfigUpdateParam(SickScanConfig & cfg)
381  {
382  rosDeclareParam(m_nh, "frame_id", cfg.frame_id);
383  rosGetParam(m_nh, "frame_id", cfg.frame_id);
384 
385  rosDeclareParam(m_nh, "imu_frame_id", cfg.imu_frame_id);
386  rosGetParam(m_nh, "imu_frame_id", cfg.imu_frame_id);
387 
388  rosDeclareParam(m_nh, "intensity", cfg.intensity);
389  rosGetParam(m_nh, "intensity", cfg.intensity);
390 
391  rosDeclareParam(m_nh, "auto_reboot", cfg.auto_reboot);
392  rosGetParam(m_nh, "auto_reboot", cfg.auto_reboot);
393 
394  rosDeclareParam(m_nh, "min_ang", cfg.min_ang);
395  rosGetParam(m_nh, "min_ang", cfg.min_ang);
396 
397  rosDeclareParam(m_nh, "max_ang", cfg.max_ang);
398  rosGetParam(m_nh, "max_ang", cfg.max_ang);
399 
400  rosDeclareParam(m_nh, "ang_res", cfg.ang_res);
401  rosGetParam(m_nh, "ang_res", cfg.ang_res);
402 
403  rosDeclareParam(m_nh, "skip", cfg.skip);
404  rosGetParam(m_nh, "skip", cfg.skip);
405 
406  rosDeclareParam(m_nh, "sw_pll_only_publish", cfg.sw_pll_only_publish);
407  rosGetParam(m_nh, "sw_pll_only_publish", cfg.sw_pll_only_publish);
408 
409  rosDeclareParam(m_nh, "use_generation_timestamp", cfg.use_generation_timestamp);
410  rosGetParam(m_nh, "use_generation_timestamp", cfg.use_generation_timestamp);
411 
412  rosDeclareParam(m_nh, "time_offset", cfg.time_offset);
413  rosGetParam(m_nh, "time_offset", cfg.time_offset);
414 
415  rosDeclareParam(m_nh, "cloud_output_mode", cfg.cloud_output_mode);
416  rosGetParam(m_nh, "cloud_output_mode", cfg.cloud_output_mode);
417  } */
418 
419  /* void SickScanCommon::setConfigUpdateParam(SickScanConfig & cfg)
420  {
421  rosDeclareParam(m_nh, "frame_id", cfg.frame_id);
422  rosSetParam(m_nh, "frame_id", cfg.frame_id);
423 
424  rosDeclareParam(m_nh, "imu_frame_id", cfg.imu_frame_id);
425  rosSetParam(m_nh, "imu_frame_id", cfg.imu_frame_id);
426 
427  rosDeclareParam(m_nh, "intensity", cfg.intensity);
428  rosSetParam(m_nh, "intensity", cfg.intensity);
429 
430  rosDeclareParam(m_nh, "auto_reboot", cfg.auto_reboot);
431  rosSetParam(m_nh, "auto_reboot", cfg.auto_reboot);
432 
433  rosDeclareParam(m_nh, "min_ang", cfg.min_ang);
434  rosSetParam(m_nh, "min_ang", cfg.min_ang);
435 
436  rosDeclareParam(m_nh, "max_ang", cfg.max_ang);
437  rosSetParam(m_nh, "max_ang", cfg.max_ang);
438 
439  rosDeclareParam(m_nh, "ang_res", cfg.ang_res);
440  rosSetParam(m_nh, "ang_res", cfg.ang_res);
441 
442  rosDeclareParam(m_nh, "skip", cfg.skip);
443  rosSetParam(m_nh, "skip", cfg.skip);
444 
445  rosDeclareParam(m_nh, "sw_pll_only_publish", cfg.sw_pll_only_publish);
446  rosSetParam(m_nh, "sw_pll_only_publish", cfg.sw_pll_only_publish);
447 
448  rosDeclareParam(m_nh, "use_generation_timestamp", cfg.use_generation_timestamp);
449  rosGetParam(m_nh, "use_generation_timestamp", cfg.use_generation_timestamp);
450 
451  rosDeclareParam(m_nh, "time_offset", cfg.time_offset);
452  rosSetParam(m_nh, "time_offset", cfg.time_offset);
453 
454  rosDeclareParam(m_nh, "cloud_output_mode", cfg.cloud_output_mode);
455  rosSetParam(m_nh, "cloud_output_mode", cfg.cloud_output_mode);
456  } */
457 
463  // FIXME All Tims have 15Hz
464  {
465  diagnosticPub_ = 0;
466  parser_ = parser;
467  m_nh =nh;
468 
469 #ifdef USE_DIAGNOSTIC_UPDATER
470 #if __ROS_VERSION == 1
471  diagnostics_ = std::make_shared<diagnostic_updater::Updater>(*nh);
472 #elif __ROS_VERSION == 2
473  diagnostics_ = std::make_shared<diagnostic_updater::Updater>(nh);
474 #else
475  diagnostics_ = 0;
476 #endif
477 #endif
478 
480  m_min_intensity = 0.0; // Set range of LaserScan messages to infinity, if intensity < min_intensity (default: 0)
481 
482  setSensorIsRadar(false);
483  init_cmdTables(nh);
484 #if defined USE_DYNAMIC_RECONFIGURE && __ROS_VERSION == 1
486  // f = boost::bind(&sick_scan_xd::SickScanCommon::update_config, this, _1, _2);
487  f = std::bind(&sick_scan_xd::SickScanCommon::update_config, this, std::placeholders::_1, std::placeholders::_2);
488  dynamic_reconfigure_server_.setCallback(f);
489 #elif defined USE_DYNAMIC_RECONFIGURE && __ROS_VERSION == 2
490  rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr parameter_cb_handle =
491  nh->add_on_set_parameters_callback(std::bind(&sick_scan_xd::SickScanCommon::update_config_cb, this, std::placeholders::_1));
492 #else
493  // For simulation under MS Visual c++ the update config is switched off
494  {
495  SickScanConfig cfg;
496  double min_angle = cfg.min_ang, max_angle = cfg.max_ang, res_angle = cfg.ang_res;
497 
498  rosDeclareParam(nh, PARAM_MIN_ANG, min_angle);
499  rosGetParam(nh, PARAM_MIN_ANG, min_angle);
500 
501  rosDeclareParam(nh, PARAM_MAX_ANG, max_angle);
502  rosGetParam(nh, PARAM_MAX_ANG, max_angle);
503 
504  rosDeclareParam(nh, PARAM_RES_ANG, res_angle);
505  rosGetParam(nh, PARAM_RES_ANG, res_angle);
506 
507  cfg.min_ang = min_angle;
508  cfg.max_ang = max_angle;
509  cfg.skip = 0;
510  update_config(cfg);
511  }
512 #endif
513 
514  std::string nodename = parser_->getCurrentParamPtr()->getScannerName();
515  rosDeclareParam(nh, "nodename", nodename);
516  rosGetParam(nh, "nodename", nodename);
517 
519  {
520  // NAV-310 only supports min/max angles of -PI to +PI (resp. 0 to 360 degree in sensor coordinates). To avoid unexpected results, min/max angles can not be set by configuration.
521  config_.min_ang = -M_PI;
522  config_.max_ang = +M_PI;
523  }
524 
525  // datagram publisher (only for debug)
526  rosDeclareParam(nh, "publish_datagram", false);
527  if(rosGetParam(nh, "publish_datagram", publish_datagram_))
528  if (publish_datagram_)
529  {
530  datagram_pub_ = rosAdvertise<ros_std_msgs::String>(nh, nodename + "/datagram", 1000);
531  }
532  rosDeclareParam(nh, "cloud_topic", cloud_topic_val);
533  rosGetParam(nh, "cloud_topic", cloud_topic_val);
534 
535  std::string laserscan_topic = nodename + "/scan";
536  rosDeclareParam(nh, "laserscan_topic", laserscan_topic);
537  rosGetParam(nh, "laserscan_topic", laserscan_topic);
538 
539  rosDeclareParam(nh, "frame_id", config_.frame_id);
540  rosGetParam(nh, "frame_id", config_.frame_id);
541 
542  rosDeclareParam(nh, "imu_frame_id", config_.imu_frame_id);
543  rosGetParam(nh, "imu_frame_id", config_.imu_frame_id);
544 
545  rosDeclareParam(nh, "intensity", config_.intensity);
546  rosGetParam(nh, "intensity", config_.intensity);
547 
548  rosDeclareParam(nh, "auto_reboot", config_.auto_reboot);
549  rosGetParam(nh, "auto_reboot", config_.auto_reboot);
550 
551  rosDeclareParam(nh, "min_ang", config_.min_ang);
552  rosGetParam(nh, "min_ang", config_.min_ang);
553 
554  rosDeclareParam(nh, "max_ang", config_.max_ang);
555  rosGetParam(nh, "max_ang", config_.max_ang);
556 
557  rosDeclareParam(nh, "ang_res", config_.ang_res);
558  rosGetParam(nh, "ang_res", config_.ang_res);
559 
560  rosDeclareParam(nh, "skip", config_.skip);
561  rosGetParam(nh, "skip", config_.skip);
562 
563  rosDeclareParam(nh, "sw_pll_only_publish", config_.sw_pll_only_publish);
564  rosGetParam(nh, "sw_pll_only_publish", config_.sw_pll_only_publish);
565 
566  rosDeclareParam(nh, "use_generation_timestamp", config_.use_generation_timestamp);
567  rosGetParam(nh, "use_generation_timestamp", config_.use_generation_timestamp);
569  ROS_INFO_STREAM("use_generation_timestamp:=0, using lidar send timestamp instead of generation timestamp for software pll converted message timestamp.");
570 
571  rosDeclareParam(nh, "time_offset", config_.time_offset);
572  rosGetParam(nh, "time_offset", config_.time_offset);
573 
574  rosDeclareParam(nh, "cloud_output_mode", config_.cloud_output_mode);
575  rosGetParam(nh, "cloud_output_mode", config_.cloud_output_mode);
576 
577  double expected_frequency_tolerance = 0.1; // frequency should be target +- 10%
578  rosDeclareParam(nh, "expected_frequency_tolerance", expected_frequency_tolerance);
579  rosGetParam(nh, "expected_frequency_tolerance", expected_frequency_tolerance);
580 
583 
584  rosDeclareParam(nh, "read_timeout_millisec_default", m_read_timeout_millisec_default);
585  rosGetParam(nh, "read_timeout_millisec_default", m_read_timeout_millisec_default);
586 
587  rosDeclareParam(nh, "read_timeout_millisec_startup", m_read_timeout_millisec_startup);
588  rosGetParam(nh, "read_timeout_millisec_startup", m_read_timeout_millisec_startup);
589 
591  {
592  // NAV-310 only supports min/max angles of -PI to +PI (resp. 0 to 360 degree in sensor coordinates). To avoid unexpected results, min/max angles can not be set by configuration.
593  if(std::abs(config_.min_ang + M_PI) > FLT_EPSILON || std::abs(config_.max_ang - M_PI) > FLT_EPSILON)
594  {
595  ROS_WARN_STREAM("## WARNING: configured min/max_angle = " << config_.min_ang << "," << config_.max_ang << " not supported by NAV-3xx. min/max_angle = -PI,+PI will be used.");
596  config_.min_ang = -M_PI;
597  config_.max_ang = +M_PI;
598  }
599  }
600 
601  publish_nav_pose_data_ = false;
603  nav_tf_parent_frame_id_ = "map";
604  nav_tf_child_frame_id_ = "nav";
605  rosDeclareParam(nh, "nav_tf_parent_frame_id", nav_tf_parent_frame_id_);
606  rosGetParam(nh, "nav_tf_parent_frame_id", nav_tf_parent_frame_id_);
607  rosDeclareParam(nh, "nav_tf_child_frame_id", nav_tf_child_frame_id_);
608  rosGetParam(nh, "nav_tf_child_frame_id", nav_tf_child_frame_id_);
609 
611  {
612  nav_pose_data_pub_ = rosAdvertise<sick_scan_msg::NAVPoseData>(nh, nodename + "/nav_pose", 100);
613  nav_landmark_data_pub_ = rosAdvertise<sick_scan_msg::NAVLandmarkData>(nh, nodename + "/nav_landmark", 100);
614  nav_reflector_pub_ = rosAdvertise<ros_visualization_msgs::MarkerArray>(nh, nodename + "/nav_reflectors", 100);
615  publish_nav_pose_data_ = true;
617 #if defined __ROS_VERSION && __ROS_VERSION == 1
618  nav_tf_broadcaster_ = new tf2_ros::TransformBroadcaster();
619  nav_odom_velocity_subscriber_ = nh->subscribe("nav_odom_velocity", 1, &sick_scan_xd::SickScanCommon::messageCbNavOdomVelocity, this);
620  ros_odom_subscriber_ = nh->subscribe("odom", 1, &sick_scan_xd::SickScanCommon::messageCbRosOdom, this);
621 #elif defined __ROS_VERSION && __ROS_VERSION == 2
622  nav_tf_broadcaster_ = new tf2_ros::TransformBroadcaster(nh);
623  nav_odom_velocity_subscriber_ = nh->create_subscription<sick_scan_msg::NAVOdomVelocity>("nav_odom_velocity", 10, std::bind(&sick_scan_xd::SickScanCommon::messageCbNavOdomVelocityROS2, this, std::placeholders::_1));
624  ros_odom_subscriber_ = nh->create_subscription<ros_nav_msgs::Odometry>("odom", 10, std::bind(&sick_scan_xd::SickScanCommon::messageCbRosOdomROS2, this, std::placeholders::_1));
625 #endif
626  }
627 
628  cloud_marker_ = 0;
629  publish_lferec_ = false;
630  publish_lidinputstate_ = false;
631  publish_lidoutputstate_ = false;
633  {
634  lferec_pub_ = rosAdvertise<sick_scan_msg::LFErecMsg>(nh, nodename + "/lferec", 100);
635  lidinputstate_pub_ = rosAdvertise<sick_scan_msg::LIDinputstateMsg>(nh, nodename + "/lidinputstate", 100);
636  lidoutputstate_pub_ = rosAdvertise<sick_scan_msg::LIDoutputstateMsg>(nh, nodename + "/lidoutputstate", 100);
637  publish_lferec_ = true;
638  publish_lidinputstate_ = true;
640  cloud_marker_ = new sick_scan_xd::SickScanMarker(nh, nodename + "/marker", config_.frame_id); // "cloud");
641  }
642  else if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_RMS_XXXX_NAME) == 0) // RMS2xxx provides LIDoutputstate telegrams
643  {
644  lidoutputstate_pub_ = rosAdvertise<sick_scan_msg::LIDoutputstateMsg>(nh, nodename + "/lidoutputstate", 100);
646  }
647 
648  // Pointcloud2 publisher
651  {
652  ROS_INFO_STREAM("Publishing lidar pointcloud2 to " << cloud_topic_val);
653  cloud_pub_ = rosAdvertise<ros_sensor_msgs::PointCloud2>(nh, cloud_topic_val, 100);
654 
655  imuScan_pub_ = rosAdvertise<ros_sensor_msgs::Imu>(nh, nodename + "/imu", 100);
656 
657  Encoder_pub = rosAdvertise<sick_scan_msg::Encoder>(nh, nodename + "/encoder", 100);
658 
659  // scan publisher
660  pub_ = rosAdvertise<ros_sensor_msgs::LaserScan>(nh, laserscan_topic, 1000);
661  }
662  else
663  {
664 #if defined USE_DIAGNOSTIC_UPDATER
665  diagnostics_ = 0;
666 #endif
667  }
668 
669 #if defined USE_DIAGNOSTIC_UPDATER
670  if(diagnostics_)
671  {
672  int num_active_layers = parser_->getCurrentParamPtr()->getNumberOfLayers();
673  diagnostics_->setHardwareID("none"); // set from device after connection
675  if ( (!m_scan_layer_filter_cfg.scan_layer_filter.empty()) // If an optional ScanLayerFilter is activated,
676  && (m_scan_layer_filter_cfg.num_layers > 1) // and the lidar has more than 1 layer,
677  && (m_scan_layer_filter_cfg.num_active_layers < m_scan_layer_filter_cfg.num_layers)) // and some layers are deactivated, then ...
678  {
679  // reduce expected frequency by factor (num_active_layers / num_layers)
681  num_active_layers = m_scan_layer_filter_cfg.num_active_layers;
682  }
683  double max_timestamp_delay = 1.3 * num_active_layers / expectedFrequency_ - config_.time_offset;
684 #if __ROS_VERSION == 1
686  // frequency should be target +- 10%.
688  // timestamp delta can be from 0.0 to 1.3x what it ideally is.
689  diagnostic_updater::TimeStampStatusParam(-1, max_timestamp_delay));
691 #elif __ROS_VERSION == 2
692  diagnosticPub_ = new DiagnosedPublishAdapter<rosPublisher<ros_sensor_msgs::LaserScan>>(pub_, *diagnostics_,
693  diagnostic_updater::FrequencyStatusParam(&expectedFrequency_, &expectedFrequency_, expected_frequency_tolerance, 10), // frequency should be target +- 10%
694  diagnostic_updater::TimeStampStatusParam(-1, max_timestamp_delay));
695  assert(diagnosticPub_ != NULL);
696 #endif
697  }
698 #else
699  config_.time_offset = 0; // to avoid uninitialized variable
700 #endif
701 
702  // Apply an additional transform to the cartesian pointcloud, default: "0,0,0,0,0,0" (i.e. no transform)
703  // Note: add_transform_xyz_rpy is specified by 6D pose x,y,z,roll,pitch,yaw in [m] resp. [rad]
704  // It transforms a 3D point in cloud coordinates to 3D point in user defined world coordinates:
705  // add_transform_xyz_rpy := T[world,cloud] with parent "world" and child "cloud", i.e. P_world = T[world,cloud] * P_cloud
706  // The additional transform applies to cartesian lidar pointclouds and visualization marker (fields)
707  // It is NOT applied to polar pointclouds, radarscans, ldmrs objects or other messages
709  }
710 
716  {
717  std::string set_access_mode_3 = sopasCmdVec[CMD_SET_ACCESS_MODE_3]; // "sMN SetAccessMode 3 F4724744"
718  if (parser_->getCurrentParamPtr()->getUseSafetyPasWD()) // TIM_7xxS - 1 layer Safety Scanner
719  set_access_mode_3 = sopasCmdVec[CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER]; // "\x02sMN SetAccessMode 3 6FD62C05\x03\0"
720  return set_access_mode_3;
721  }
722 
727  int SickScanCommon::stop_scanner(bool force_immediate_shutdown)
728  {
729  /*
730  * Stop streaming measurements
731  */
732  std::vector<std::string> sopas_stop_scanner_cmd = { "\x02sEN LMDscandata 0\x03\0" };
734  {
735  sopas_stop_scanner_cmd.push_back("\x02sEN LFErec 0\x03"); // TiM781S: deactivate LFErec messages, send "sEN LFErec 0"
736  sopas_stop_scanner_cmd.push_back("\x02sEN LIDoutputstate 0\x03"); // TiM781S: deactivate LIDoutputstate messages, send "sEN LIDoutputstate 0"
737  sopas_stop_scanner_cmd.push_back("\x02sEN LIDinputstate 0\x03"); // TiM781S: deactivate LIDinputstate messages, send "sEN LIDinputstate 0"
738  }
739  sopas_stop_scanner_cmd.push_back(cmdSetAccessMode3()); // "sMN SetAccessMode 3 F4724744"
740  sopas_stop_scanner_cmd.push_back("\x02sMN LMCstopmeas\x03\0");
741  // sopas_stop_scanner_cmd.push_back("\x02sMN Run\x03\0");
743  {
744  sopas_stop_scanner_cmd.clear();
745  sopas_stop_scanner_cmd.push_back(cmdSetAccessMode3()); // "sMN SetAccessMode 3 F4724744"
746  sopas_stop_scanner_cmd.push_back(sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_1]); // "sMN mNEVAChangeState 1", 1 = standby
747  sopas_stop_scanner_cmd.push_back(sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_0]); // "sMN mNEVAChangeState 0", 0 = power down
748  }
749 
750  setReadTimeOutInMs(1000);
751  ROS_INFO_STREAM("sick_scan_common: stopping scanner ...");
752  int result = ExitSuccess, cmd_result = ExitSuccess;
753  for(int cmd_idx = 0; cmd_idx < sopas_stop_scanner_cmd.size(); cmd_idx++)
754  {
755  std::vector<unsigned char> sopas_reply;
756  cmd_result = convertSendSOPASCommand(sopas_stop_scanner_cmd[cmd_idx], &sopas_reply, (force_immediate_shutdown==false));
757  if (force_immediate_shutdown == false)
758  {
759  ROS_INFO_STREAM("sick_scan_common: received sopas reply \"" << replyToString(sopas_reply) << "\"");
760  }
761  if (cmd_result != ExitSuccess)
762  {
763  ROS_WARN_STREAM("## ERROR sick_scan_common: ERROR sending sopas command \"" << sopas_stop_scanner_cmd[cmd_idx] << "\"");
764  result = ExitError;
765  }
766  // std::this_thread::sleep_for(std::chrono::milliseconds((int64_t)100));
767  }
768  return result;
769  }
770 
776  unsigned long SickScanCommon::convertBigEndianCharArrayToUnsignedLong(const unsigned char *vecArr)
777  {
778  unsigned long val = 0;
779  for (int i = 0; i < 4; i++)
780  {
781  val = val << 8;
782  val |= vecArr[i];
783  }
784  return (val);
785  }
786 
787 
793  int sick_scan_xd::SickScanCommon::checkForBinaryAnswer(const std::vector<unsigned char> *reply)
794  {
795  int retVal = -1;
796 
797  if (reply == NULL)
798  {
799  }
800  else
801  {
802  if (reply->size() < 8)
803  {
804  retVal = -1;
805  }
806  else
807  {
808  const unsigned char *ptr = &((*reply)[0]);
809  unsigned binId = convertBigEndianCharArrayToUnsignedLong(ptr);
810  unsigned cmdLen = convertBigEndianCharArrayToUnsignedLong(ptr + 4);
811  if (binId == 0x02020202)
812  {
813  int replyLen = reply->size();
814  if (replyLen == 8 + cmdLen + 1)
815  {
816  retVal = cmdLen;
817  }
818  }
819  }
820  }
821  return (retVal);
822 
823  }
824 
830  int SickScanCommon::convertSendSOPASCommand(const std::string& sopas_ascii_request, std::vector<unsigned char> *sopas_reply, bool wait_for_reply)
831  {
832  int result = ExitError;
833  if (getProtocolType() == CoLa_B)
834  {
835  std::vector<unsigned char> requestBinary;
836  convertAscii2BinaryCmd(sopas_ascii_request.c_str(), &requestBinary);
837  ROS_INFO_STREAM("sick_scan_common: sending sopas command \"" << stripControl(requestBinary) << "\"");
838  result = sendSOPASCommand((const char*)requestBinary.data(), sopas_reply, requestBinary.size(), wait_for_reply);
839  }
840  else
841  {
842  ROS_INFO_STREAM("sick_scan_common: sending sopas command \"" << sopas_ascii_request << "\"");
843  result = sendSOPASCommand(sopas_ascii_request.c_str(), sopas_reply, sopas_ascii_request.size(), wait_for_reply);
844  }
845  return result;
846  }
847 
848 
854  {
855  /*
856  * Set Maintenance access mode to allow reboot to be sent
857  */
858  std::vector<unsigned char> access_reply;
859 
860 
861  // changed from "03" to "3"
862  int result = convertSendSOPASCommand(cmdSetAccessMode3(), &access_reply); // "sMN SetAccessMode 3 F4724744"
863  if (result != 0)
864  {
865  ROS_ERROR("SOPAS - Error setting access mode");
866 #ifdef USE_DIAGNOSTIC_UPDATER
867  if(diagnostics_)
868  diagnostics_->broadcast(getDiagnosticErrorCode(), "SOPAS - Error setting access mode.");
869 #endif
870  return false;
871  }
872  std::string access_reply_str = replyToString(access_reply);
873  if (access_reply_str != "sAN SetAccessMode 1")
874  {
875  ROS_ERROR_STREAM("SOPAS - Error setting access mode, unexpected response : " << access_reply_str);
876 #ifdef USE_DIAGNOSTIC_UPDATER
877  if(diagnostics_)
878  diagnostics_->broadcast(getDiagnosticErrorCode(), "SOPAS - Error setting access mode.");
879 #endif
880  return false;
881  }
882 
883  /*
884  * Send reboot command
885  */
886  std::vector<unsigned char> reboot_reply;
887  result = convertSendSOPASCommand("\x02sMN mSCreboot\x03\0", &reboot_reply);
888  if (result != 0)
889  {
890  ROS_ERROR("SOPAS - Error rebooting scanner");
891 #ifdef USE_DIAGNOSTIC_UPDATER
892  if(diagnostics_)
893  diagnostics_->broadcast(getDiagnosticErrorCode(), "SOPAS - Error rebooting device.");
894 #endif
895  return false;
896  }
897  std::string reboot_reply_str = replyToString(reboot_reply);
898  if (reboot_reply_str != "sAN mSCreboot")
899  {
900  ROS_ERROR_STREAM("SOPAS - Error rebooting scanner, unexpected response : " << reboot_reply_str);
901 #ifdef USE_DIAGNOSTIC_UPDATER
902  if(diagnostics_)
903  diagnostics_->broadcast(getDiagnosticErrorCode(), "SOPAS - Error setting access mode.");
904 #endif
905  return false;
906  }
907 
908  ROS_INFO("SOPAS - Rebooted scanner");
909 
910  // Wait a few seconds after rebooting
911  rosSleep(15.0);
912 
913  return true;
914  }
915 
920  {
921  delete cloud_marker_;
922  delete diagnosticPub_;
923  printf("SickScanCommon closed.\n");
924  }
925 
926 
932  std::vector<std::string> SickScanCommon::generateExpectedAnswerString(const std::vector<unsigned char>& requestStr)
933  {
934  std::string expectedAnswer = "";
935  //int i = 0;
936  char cntWhiteCharacter = 0;
937  int initalTokenCnt = 2; // number of initial token to identify command
938  std::map<std::string, int> specialTokenLen;
939  char firstToken[1024] = {0};
940  specialTokenLen["sRI"] = 1; // for SRi-Command only the first token identify the command
941  std::string tmpStr = "";
942  int cnt0x02 = 0;
943  bool isBinary = false;
944  for (size_t i = 0; i < 4; i++)
945  {
946  if (i < requestStr.size())
947  {
948  if (requestStr[i] == 0x02)
949  {
950  cnt0x02++;
951  }
952 
953  }
954  }
955 
956  int iStop = requestStr.size(); // copy string until end of string
957  if (cnt0x02 == 4)
958  {
959 
960  int cmdLen = 0;
961  for (int i = 0; i < 4; i++)
962  {
963  cmdLen |= cmdLen << 8;
964  cmdLen |= requestStr[i + 4];
965  }
966  iStop = cmdLen + 8;
967  isBinary = true;
968 
969  }
970  int iStart = (isBinary == true) ? 8 : 0;
971  for (int i = iStart; i < iStop; i++)
972  {
973  tmpStr += (char) requestStr[i];
974  }
975  if (isBinary)
976  {
977  tmpStr = "\x2" + tmpStr;
978  }
979  if (sscanf(tmpStr.c_str(), "\x2%s", firstToken) == 1)
980  {
981  if (specialTokenLen.find(firstToken) != specialTokenLen.end())
982  {
983  initalTokenCnt = specialTokenLen[firstToken];
984 
985  }
986  }
987 
988  for (int i = iStart; i < iStop; i++)
989  {
990  if ((requestStr[i] == ' ') || (requestStr[i] == '\x3'))
991  {
992  cntWhiteCharacter++;
993  }
994  if (cntWhiteCharacter >= initalTokenCnt)
995  {
996  break;
997  }
998  if (requestStr[i] == '\x2')
999  {
1000  }
1001  else
1002  {
1003  expectedAnswer += requestStr[i];
1004  }
1005  }
1006 
1010  std::map<std::string, std::vector<std::string>> keyWordMap;
1011  keyWordMap["sWN"] = { "sWA", "sAN" };
1012  keyWordMap["sRN"] = { "sRA", "sAN" };
1013  keyWordMap["sRI"] = { "sRA" };
1014  keyWordMap["sMN"] = { "sAN", "sMA" };
1015  keyWordMap["sEN"] = { "sEA" };
1016 
1017  std::vector<std::string> expectedAnswers;
1018  for (std::map<std::string, std::vector<std::string>>::iterator it = keyWordMap.begin(); it != keyWordMap.end(); it++)
1019  {
1020  const std::string& keyWord = it->first;
1021  const std::vector<std::string>& newKeyWords = it->second;
1022 
1023  size_t pos = expectedAnswer.find(keyWord);
1024  if (pos == 0) // must be 0, if keyword has been found
1025  {
1026  for(int n = 0; n < newKeyWords.size(); n++)
1027  {
1028  expectedAnswers.push_back(expectedAnswer);
1029  expectedAnswers.back().replace(pos, keyWord.length(), newKeyWords[n]);
1030  }
1031  }
1032  else if (pos != std::string::npos) // keyword found at unexpected position
1033  {
1034  ROS_WARN("Unexpected position of key identifier.\n");
1035  }
1036  }
1037 
1038  if(expectedAnswers.empty())
1039  {
1040  expectedAnswers.push_back(expectedAnswer);
1041  }
1042  return (expectedAnswers);
1043  }
1044 
1050  std::vector<std::string> SickScanCommon::generateUnexpectedAnswerString(const std::string& requestStr)
1051  {
1052  std::vector<std::string> unexpected_responses;
1053  if (requestStr.find("SetAccessMode") != std::string::npos)
1054  {
1055  unexpected_responses.push_back(std::string("sAN SetAccessMode 0")); // SetAccessMode failed (Cola-A)
1056  unexpected_responses.push_back(std::string("sAN SetAccessMode \x00", 19)); // SetAccessMode failed (Cola-B)
1057  }
1058  return unexpected_responses;
1059  }
1060 
1068  int SickScanCommon::sendSopasAndCheckAnswer(std::string requestStr, std::vector<unsigned char> *reply, int cmdId = -1)
1069  {
1070  std::vector<unsigned char> requestStringVec;
1071  for (size_t i = 0; i < requestStr.length(); i++)
1072  {
1073  requestStringVec.push_back(requestStr[i]);
1074  }
1075  int retCode = sendSopasAndCheckAnswer(requestStringVec, reply, cmdId);
1076  return (retCode);
1077  }
1078 
1086  int SickScanCommon::sendSopasAndCheckAnswer(std::vector<unsigned char> requestStr, std::vector<unsigned char> *reply,
1087  int cmdId = -1)
1088  {
1089  std::lock_guard<std::mutex> send_lock_guard(sopasSendMutex); // lock send mutex in case of asynchronous service calls
1090 
1091  reply->clear();
1092  std::string cmdStr = "";
1093  int cmdLen = 0;
1094  for (size_t i = 0; i < requestStr.size(); i++)
1095  {
1096  cmdLen++;
1097  cmdStr += (char) requestStr[i];
1098  }
1099  int result = -1;
1100 
1101  std::string errString;
1102  if (cmdId == -1)
1103  {
1104  errString = "Error unexpected Sopas answer for request " + stripControl(requestStr, 64);
1105  }
1106  else
1107  {
1108  errString = this->sopasCmdErrMsg[cmdId];
1109  }
1110 
1111  // std::vector<std::string> expectedAnswers = generateExpectedAnswerString(requestStr);
1112 
1113  // send sopas cmd
1114 
1115  std::string reqStr = replyToString(requestStr);
1116  ROS_INFO_STREAM("Sending : " << stripControl(requestStr));
1117  result = sendSOPASCommand(cmdStr.c_str(), reply, cmdLen);
1118  std::string replyStr = replyToString(*reply);
1119  std::vector<unsigned char> replyVec;
1120  replyStr = "<STX>" + replyStr + "<ETX>";
1121  replyVec = stringToVector(replyStr);
1122  ROS_INFO_STREAM("Receiving: " << stripControl(replyVec, 96));
1123 
1124  if (result != 0)
1125  {
1126  std::string tmpStr = "SOPAS Communication -" + errString;
1127  ROS_INFO_STREAM(tmpStr << "\n");
1128 #ifdef USE_DIAGNOSTIC_UPDATER
1129  if(diagnostics_)
1130  diagnostics_->broadcast(getDiagnosticErrorCode(), tmpStr);
1131 #endif
1132  }
1133  else
1134  {
1135  result = -1;
1136  uint64_t retry_start_timestamp_nsec = rosNanosecTimestampNow();
1137  for(int retry_answer_cnt = 0; result != 0; retry_answer_cnt++)
1138  {
1139  std::string answerStr = replyToString(*reply);
1140  std::string replyStrOrg = std::string((const char*)reply->data(), reply->size());
1141  std::stringstream expectedAnswers;
1142  std::vector<std::string> searchPattern = generateExpectedAnswerString(requestStr);
1143  std::vector<std::string> searchPatternNeg = generateUnexpectedAnswerString(reqStr);
1144 
1145  for(int n = 0; n < searchPatternNeg.size(); n++)
1146  {
1147  ROS_DEBUG_STREAM("Compare lidar response \"" << DataDumper::binDataToAsciiString((const uint8_t*)replyStrOrg.data(), replyStrOrg.size()) << "\" to unexpected pattern \""
1148  << DataDumper::binDataToAsciiString((const uint8_t*)searchPatternNeg[n].data(), searchPatternNeg[n].size()) << "\" ...");
1149  if (replyStrOrg.find(searchPatternNeg[n]) != std::string::npos)
1150  {
1151  ROS_ERROR_STREAM("Unexpected response \"" << DataDumper::binDataToAsciiString((const uint8_t*)replyStrOrg.data(), replyStrOrg.size()) << "\" received from lidar, \""
1152  << DataDumper::binDataToAsciiString((const uint8_t*)searchPatternNeg[n].data(), searchPatternNeg[n].size())
1153  << "\" not expected, SOPAS command failed.");
1154  break;
1155  }
1156  }
1157  for(int n = 0; result != 0 && n < searchPattern.size(); n++)
1158  {
1159  if (answerStr.find(searchPattern[n]) != std::string::npos)
1160  {
1161  result = 0;
1162  }
1163  expectedAnswers << (n > 0 ? "," : "") << "\"" << searchPattern[n] << "\"" ;
1164  }
1165  if(result != 0)
1166  {
1167  if (cmdId == CMD_START_IMU_DATA)
1168  {
1169  ROS_INFO_STREAM("IMU-Data transfer started. No checking of reply to avoid confusing with LMD Scandata\n");
1170  result = 0;
1171  }
1172  else
1173  {
1174  if(answerStr.size() > 64)
1175  {
1176  answerStr.resize(64);
1177  answerStr += "...";
1178  }
1179  std::string tmpMsg = "Error Sopas answer mismatch: " + errString + ", received answer: \"" + answerStr + "\", expected patterns: " + expectedAnswers.str();
1180  ROS_WARN_STREAM(tmpMsg);
1181  #ifdef USE_DIAGNOSTIC_UPDATER
1182  if(diagnostics_)
1183  diagnostics_->broadcast(getDiagnosticErrorCode(), tmpMsg);
1184  #endif
1185  result = -1;
1186  if (strncmp(answerStr.c_str(), "sFA", 3) == 0) // Error code received from lidar -> abort waiting for an expected answer
1187  {
1188  ROS_WARN_STREAM("Sopas error code " << answerStr.substr(4) << " received from lidar");
1189  break;
1190  }
1191  // Problably we received some scan data message. Ignore and try again...
1192  std::vector<std::string> response_keywords = { sick_scan_xd::SickScanMessages::getSopasCmdKeyword((uint8_t*)requestStr.data(), requestStr.size()) };
1193  if(retry_answer_cnt < 100 && (rosNanosecTimestampNow() - retry_start_timestamp_nsec) / 1000000 < m_read_timeout_millisec_default)
1194  {
1195  char buffer[64*1024];
1196  int bytes_read = 0;
1197 
1198  int read_timeout_millisec = getReadTimeOutInMs(); // default timeout: 120 seconds (sensor may be starting up)
1199  if (!reply->empty()) // sensor is up and running (i.e. responded with message), try again with 5 sec timeout
1200  read_timeout_millisec = m_read_timeout_millisec_default;
1201  if (readWithTimeout(read_timeout_millisec, buffer, sizeof(buffer), &bytes_read, response_keywords) == ExitSuccess)
1202  {
1203  reply->resize(bytes_read);
1204  std::copy(buffer, buffer + bytes_read, &(*reply)[0]);
1205  }
1206  else
1207  {
1208  reply->clear();
1209  }
1210  }
1211  else
1212  {
1213  reply->clear();
1214  ROS_ERROR_STREAM(errString << ", giving up after " << retry_answer_cnt << " unexpected answers.");
1215  break;
1216  }
1217 
1218  }
1219  }
1220  }
1221 
1222  }
1223  return result;
1224 
1225  }
1226 
1227  int SickScanCommon::sendSopasAorBgetAnswer(const std::string& sopasCmd, std::vector<unsigned char> *reply, bool useBinaryCmd)
1228  {
1229  int result = -1;
1230  std::vector<unsigned char> replyDummy, reqBinary;
1231  int prev_sopas_type = this->getProtocolType();
1232  this->setProtocolType(useBinaryCmd ? CoLa_B : CoLa_A);
1233  if (useBinaryCmd)
1234  {
1235  this->convertAscii2BinaryCmd(sopasCmd.c_str(), &reqBinary);
1236  result = sendSopasAndCheckAnswer(reqBinary, &replyDummy);
1237  }
1238  else
1239  {
1240  result = sendSopasAndCheckAnswer(sopasCmd.c_str(), &replyDummy);
1241  }
1242  if(reply)
1243  *reply = replyDummy;
1244  this->setProtocolType((SopasProtocol)prev_sopas_type); // restore previous sopas type
1245  if (result != 0) // no answer
1246  {
1247  ROS_WARN_STREAM("## ERROR SickScanCommon: sendSopasAndCheckAnswer(\"" << sopasCmd << "\") failed");
1248  }
1249  return result;
1250  }
1251 
1252  int SickScanCommon::get2ndSopasResponse(std::vector<uint8_t>& sopas_response, const std::string& sopas_keyword)
1253  {
1254  int bytes_read = 0;
1255  sopas_response.clear();
1256  sopas_response.resize(64*1024);
1257  std::vector<std::string> sopas_response_keywords = { sopas_keyword };
1258  if (readWithTimeout(getReadTimeOutInMs(), (char*)sopas_response.data(), (int)sopas_response.size(), &bytes_read, sopas_response_keywords) != ExitSuccess)
1259  {
1260  ROS_WARN_STREAM("## ERROR waiting for 2nd response \"" << sopas_keyword << "\" to request \"" << sopas_keyword << "\"");
1261  return ExitError;
1262  }
1263  sopas_response.resize(bytes_read);
1264  return ExitSuccess;
1265  }
1266 
1267  bool SickScanCommon::switchColaProtocol(bool useBinaryCmd)
1268  {
1269  std::vector<unsigned char> sopas_response;
1270  std::vector<std::string> sopas_change_cola_commands = { cmdSetAccessMode3(), sopasCmdVec[(useBinaryCmd ? CMD_SET_TO_COLA_B_PROTOCOL : CMD_SET_TO_COLA_A_PROTOCOL)] };
1271  for(int n = 0; n < sopas_change_cola_commands.size(); n++)
1272  {
1273  if (sendSopasAorBgetAnswer(sopas_change_cola_commands[n], &sopas_response, !useBinaryCmd) != 0) // no answer
1274  {
1275  ROS_WARN_STREAM("checkColaDialect: no lidar response to sopas requests \"" << sopas_change_cola_commands[n] << "\", aborting");
1276  return false;
1277  }
1278  }
1279  ROS_INFO_STREAM("checkColaDialect: switched to Cola-" << (useBinaryCmd ? "B" : "A"));
1280  return true;
1281  }
1282 
1283  // Check Cola-Configuration of the scanner:
1284  // * Send "sRN DeviceState" with configured cola-dialect (Cola-B = useBinaryCmd)
1285  // * If lidar does not answer:
1286  // * Send "sRN DeviceState" with different cola-dialect (Cola-B = !useBinaryCmd)
1287  // * If lidar sends a response:
1288  // * Switch to configured cola-dialect (Cola-B = useBinaryCmd) using "sWN EIHstCola" and restart
1290  {
1291  static bool tim240_binary_mode = useBinaryCmd;
1292  bool useBinaryCmdCfg = useBinaryCmd;
1294  {
1295  useBinaryCmd = tim240_binary_mode; // TiM240 does not respond to any request if once sent a sopas command in wrong cola dialect. Toggle Cola dialect directly after restart required for TiM240.
1296  ROS_INFO_STREAM("checkColaDialect using Cola-" << (useBinaryCmd ? "B" : "A") << " (TiM-240)");
1297  }
1298  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_DEVICE_STATE], 0, useBinaryCmd) != 0) // no answer
1299  {
1300  ROS_WARN_STREAM("checkColaDialect: lidar response not in configured Cola-dialect Cola-" << (useBinaryCmd ? "B" : "A") << ", trying different Cola configuration");
1301  std::vector<unsigned char> sopas_response;
1302  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_DEVICE_STATE], &sopas_response, !useBinaryCmd) != 0) // no answer
1303  {
1304  ROS_WARN_STREAM("checkColaDialect: no lidar response in any cola configuration, check lidar and network!");
1305  ROS_WARN_STREAM("SickScanCommon::init_scanner() failed, aborting.");
1306  }
1307  else
1308  {
1309  ROS_WARN_STREAM("checkColaDialect: lidar response in configured Cola-dialect Cola-" << (!useBinaryCmd ? "B" : "A") << ", changing Cola configuration and restart!");
1310  switchColaProtocol(useBinaryCmd);
1311  ROS_INFO_STREAM("checkColaDialect: restarting after Cola configuration change.");
1312  }
1314  {
1315  tim240_binary_mode = !tim240_binary_mode; // TiM240 does not respond to any request if once sent a sopas command in wrong cola dialect. Toggle Cola dialect directly after restart required for TiM240.
1316  ROS_INFO_STREAM("checkColaDialect: switching to Cola-" << (useBinaryCmd ? "B" : "A") << " after restart (TiM-240)");
1317  }
1318  return ExitError;
1319  }
1320  else
1321  {
1322  ROS_INFO_STREAM("checkColaDialect: lidar response in configured Cola-dialect Cola-" << (useBinaryCmd ? "B" : "A"));
1324  {
1325  if (useBinaryCmd != useBinaryCmdCfg)
1326  {
1327  ROS_INFO_STREAM("checkColaDialect sucessful using Cola-" << (useBinaryCmd ? "B" : "A") << ", switch to Cola-" << (useBinaryCmdCfg ? "B" : "A") << " (TiM-240)");
1328  switchColaProtocol(useBinaryCmdCfg);
1329  tim240_binary_mode = useBinaryCmdCfg;
1330  return ExitError; // Restart after protocol switch required for TiM240
1331  }
1332  }
1333  }
1334  return ExitSuccess;
1335  }
1336 
1338  {
1339  bool result = true;
1340  if (useBinaryCmd)
1341  {
1342  std::vector<unsigned char> reqBinary;
1343  reqBinary.clear();
1344  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_RUN].c_str(), &reqBinary);
1345  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_RUN]));
1346  reqBinary.clear();
1347  std::string sUserLvlCmd = cmdSetAccessMode3(); // "sMN SetAccessMode 3 F4724744"
1348  this->convertAscii2BinaryCmd(sUserLvlCmd.c_str(), &reqBinary);
1349  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_ACCESS_MODE_3]));
1350  reqBinary.clear();
1351  }
1352  else
1353  {
1354  std::vector<unsigned char> resetReply;
1355  std::string runCmd = sopasCmdVec[CMD_RUN];
1356  std::string sUserLvlCmd = cmdSetAccessMode3(); // "sMN SetAccessMode 3 F4724744"
1357  result &= (0 == sendSopasAndCheckAnswer(runCmd, &resetReply));
1358  result &= (0 == sendSopasAndCheckAnswer(sUserLvlCmd, &resetReply));
1359  }
1360  return result;
1361  }
1362 
1363  // NAV-350 data must be polled by sending sopas command "sMN mNPOSGetData wait mask"
1365  {
1366  // "sMN mNPOSGetData wait mask" (Cola-A "sMN mNPOSGetData 1 2" or Cola-B "sMN mNPOSGetData 0102"): wait for next pose result and send pose+reflectors+scan
1367  std::string sopas_cmd = "\x02sMN mNPOSGetData 1 2\x03"; // wait = 1 (wait for next pose result), mask = 2 (send pose+reflectors+scan)
1368  std::vector<unsigned char> sopas_request;
1369  this->convertAscii2BinaryCmd(sopas_cmd.c_str(), &sopas_request);
1370  // Send "sMN mNPOSGetData 1 2"
1371  ROS_DEBUG_STREAM("NAV350: Sending: " << stripControl(sopas_request, -1));
1372  return sendSOPASCommand((const char*)sopas_request.data(), 0, sopas_request.size(), false);
1373  }
1374 
1375  // Parse NAV-350 pose and scan data and send next "sMN mNPOSGetData" request (NAV-350 polling)
1376  bool SickScanCommon::handleNAV350BinaryPositionData(const uint8_t* receiveBuffer, int receiveBufferLength, short& elevAngleX200, double& elevationAngleInRad, rosTime & recvTimeStamp,
1377  bool config_sw_pll_only_publish, double config_time_offset, SickGenericParser * parser_, int& numEchos, ros_sensor_msgs::LaserScan & msg, NAV350mNPOSData & navdata)
1378  {
1379  // Parse NAV-350 pose and scan data and convert to LaserScan message
1380  sick_scan_msg::NAVPoseData nav_pose_msg;
1381  sick_scan_msg::NAVLandmarkData nav_landmark_msg;
1382  if (!parseNAV350BinaryPositionData(receiveBuffer, receiveBufferLength, elevAngleX200, elevationAngleInRad, recvTimeStamp, config_sw_pll_only_publish, config_time_offset, parser_, numEchos, msg, nav_pose_msg, nav_landmark_msg, navdata))
1383  ROS_ERROR_STREAM("## ERROR NAV350: Error parsing mNPOSGetData response");
1384  // Send next "sMN mNPOSGetData" request (NAV-350 polling)
1385  int result = sendNAV350mNPOSGetData();
1386  if (result != ExitSuccess)
1387  {
1388  ROS_ERROR_STREAM("## ERROR NAV350: Error sending sMN mNPOSGetData request, retrying ...");
1389  return false;
1390  }
1391  // Publish pose and landmark data
1392  if (publish_nav_pose_data_ && navdata.poseDataValid > 0)
1393  {
1394  rosPublish(nav_pose_data_pub_, nav_pose_msg);
1395 #if __ROS_VERSION > 0
1396  if (nav_tf_broadcaster_)
1397  {
1399  nav_tf_broadcaster_->sendTransform(nav_pose_transform);
1400  }
1401 #endif
1402  }
1403  if (publish_nav_landmark_data_ && navdata.landmarkDataValid > 0)
1404  {
1405  rosPublish(nav_landmark_data_pub_, nav_landmark_msg);
1406 #if __ROS_VERSION > 0
1407  if (navdata.landmarkData.reflectors.size() > 0)
1408  {
1410  rosPublish(nav_reflector_pub_, nav_reflector_marker_msg);
1411  }
1412 #endif
1413  }
1414  if (navdata.poseDataValid > 0 || navdata.landmarkDataValid > 0)
1415  {
1417  }
1418 
1419  return true;
1420  }
1421 
1423  {
1424  ROS_DEBUG_STREAM("SickScanCommon::messageCbNavOdomVelocity(): vel_x=" << msg.vel_x << " m/s, vel_y=" << msg.vel_y << " m/s, omega=" << msg.omega << " rad/s, timestamp=" << msg.timestamp << ", coordbase=" << (int)msg.coordbase);
1425  std::vector<unsigned char> sopas_response;
1426  std::vector<uint8_t> setNAVSpeedRequestPayload = createNAV350BinarySetSpeedRequest(msg); // "sMN mNPOSSetSpeed X Y Phi timestamp coordBase"
1427  std::vector<uint8_t> setNAVSpeedRequest = { 0x02, 0x02, 0x02, 0x02, 0, 0, 0, 0 };
1428  setNAVSpeedRequest.insert(setNAVSpeedRequest.end(), setNAVSpeedRequestPayload.begin(), setNAVSpeedRequestPayload.end());
1429  setLengthAndCRCinBinarySopasRequest(&setNAVSpeedRequest);
1430  if (sendSopasAndCheckAnswer(setNAVSpeedRequest, &sopas_response) != 0)
1431  ROS_ERROR_STREAM("SickScanCommon::messageCbNavOdomVelocity(): sendSopasAndCheckAnswer() failed");
1432  }
1433 
1434 #if __ROS_VERSION > 0
1435  void SickScanCommon::messageCbRosOdom(const ros_nav_msgs::Odometry& msg)
1436  {
1437  sick_scan_msg::NAVOdomVelocity nav_odom_vel_msg;
1438  nav_odom_vel_msg.vel_x = msg.twist.twist.linear.x;
1439  nav_odom_vel_msg.vel_y = msg.twist.twist.linear.y;
1440  double angle_shift = -1.0 * parser_->getCurrentParamPtr()->getScanAngleShift();
1441  rotateXYbyAngleOffset(nav_odom_vel_msg.vel_x, nav_odom_vel_msg.vel_y, angle_shift); // Convert to velocity in lidar coordinates in m/s
1442  nav_odom_vel_msg.omega = msg.twist.twist.angular.z; // angular velocity of the NAV350 in radians/s, -2*PI ... +2*PI rad/s
1443  nav_odom_vel_msg.coordbase = 0; // 0 = local coordinate system of the NAV350
1444  nav_odom_vel_msg.timestamp = (uint32_t)(1000.0 * rosTimeToSeconds(msg.header.stamp)); // millisecond timestamp of the Velocity vector related to the NAV350 clock
1446  {
1447  SoftwarePLL::instance().convSystemtimeToLidarTimestamp(sec(msg.header.stamp), nsec(msg.header.stamp), nav_odom_vel_msg.timestamp);
1448  messageCbNavOdomVelocity(nav_odom_vel_msg);
1449  }
1450  else
1451  {
1452  ROS_WARN_STREAM("## ERROR SickScanCommon::messageCbRosOdom(): SoftwarePLL not yet ready, timestamp can not be converted from system time to lidar time, odometry message ignored.");
1453  ROS_WARN_STREAM("## ERROR SickScanCommon::messageCbRosOdom(): Send odometry messages after SoftwarePLL is ready (i.e. has finished initialization phase).");
1454  }
1455  }
1456 #endif
1457 
1464  {
1465  readTimeOutInMs = timeOutInMs;
1466  }
1467 
1474  {
1475  return (readTimeOutInMs);
1476  }
1477 
1484  {
1485  return m_protocolId;
1486  }
1487 
1493  {
1494  /* switch(cola_dialect_id)
1495  {
1496  case CoLa_A:
1497  ROS_INFO_STREAM("SickScanCommon::setProtocolType(CoLa_A)");
1498  break;
1499  case CoLa_B:
1500  ROS_INFO_STREAM("SickScanCommon::setProtocolType(CoLa_B)");
1501  break;
1502  default:
1503  ROS_INFO_STREAM("SickScanCommon::setProtocolType(CoLa_Unknown)");
1504  break;
1505  } */
1506  m_protocolId = cola_dialect_id;
1507  }
1508 
1514  {
1515  m_nh = nh;
1516  int result = init_device();
1517  if (result != 0)
1518  {
1519  ROS_FATAL_STREAM("Failed to init device: " << result);
1520  return result;
1521  }
1522 
1523  result = init_scanner(nh);
1524  if (result != 0)
1525  {
1526  ROS_INFO_STREAM("Failed to init scanner Error Code: " << result << "\nWaiting for timeout...\n"
1527  "If the communication mode set in the scanner memory is different from that used by the driver, the scanner's communication mode is changed.\n"
1528  "This requires a restart of the TCP-IP connection, which can extend the start time by up to 30 seconds. There are two ways to prevent this:\n"
1529  "1. [Recommended] Set the communication mode with the SOPAS ET software to binary and save this setting in the scanner's EEPROM.\n"
1530  "2. Use the parameter \"use_binary_protocol\" to overwrite the default settings of the driver.");
1531  }
1532 
1533  return result;
1534  }
1535 
1536 
1542  {
1544  sopasCmdMaskVec.resize(
1545  SickScanCommon::CMD_END); // you for cmd with variable content. sprintf should print into corresponding sopasCmdVec
1546  sopasCmdErrMsg.resize(
1547  SickScanCommon::CMD_END); // you for cmd with variable content. sprintf should print into corresponding sopasCmdVec
1551 
1552  std::string unknownStr = "Command or Error message not defined";
1553  for (int i = 0; i < SickScanCommon::CMD_END; i++)
1554  {
1555  sopasCmdVec[i] = unknownStr;
1556  sopasCmdMaskVec[i] = unknownStr; // for cmd with variable content. sprintf should print into corresponding sopasCmdVec
1557  sopasCmdErrMsg[i] = unknownStr;
1558  sopasReplyVec[i] = unknownStr;
1559  sopasReplyStrVec[i] = unknownStr;
1560  }
1561 
1562  sopasCmdVec[CMD_DEVICE_IDENT_LEGACY] = "\x02sRI 0\x03\0";
1563  sopasCmdVec[CMD_DEVICE_IDENT] = "\x02sRN DeviceIdent\x03\0";
1564  sopasCmdVec[CMD_REBOOT] = "\x02sMN mSCreboot\x03";
1565  sopasCmdVec[CMD_WRITE_EEPROM] = "\x02sMN mEEwriteall\x03";
1566  sopasCmdVec[CMD_SERIAL_NUMBER] = "\x02sRN SerialNumber\x03\0";
1567  sopasCmdVec[CMD_FIRMWARE_VERSION] = "\x02sRN FirmwareVersion\x03\0";
1568  sopasCmdVec[CMD_DEVICE_STATE] = "\x02sRN SCdevicestate\x03\0";
1569  sopasCmdVec[CMD_OPERATION_HOURS] = "\x02sRN ODoprh\x03\0";
1570  sopasCmdVec[CMD_POWER_ON_COUNT] = "\x02sRN ODpwrc\x03\0";
1571  sopasCmdVec[CMD_LOCATION_NAME] = "\x02sRN LocationName\x03\0";
1572  sopasCmdVec[CMD_ACTIVATE_STANDBY] = "\x02sMN LMCstandby\x03";
1573  sopasCmdVec[CMD_SET_ACCESS_MODE_3] = "\x02sMN SetAccessMode 3 F4724744\x03\0";
1574  sopasCmdVec[CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER] = "\x02sMN SetAccessMode 3 6FD62C05\x03\0";
1575  sopasCmdVec[CMD_GET_OUTPUT_RANGES] = "\x02sRN LMPoutputRange\x03";
1576  sopasCmdVec[CMD_RUN] = "\x02sMN Run\x03\0";
1577  sopasCmdVec[CMD_STOP_SCANDATA] = "\x02sEN LMDscandata 0\x03";
1578  sopasCmdVec[CMD_START_SCANDATA] = "\x02sEN LMDscandata 1\x03";
1579  sopasCmdVec[CMD_START_RADARDATA] = "\x02sEN LMDradardata 1\x03";
1580  sopasCmdVec[CMD_ACTIVATE_NTP_CLIENT] = "\x02sWN TSCRole 1\x03";
1581  sopasCmdVec[CMD_SET_NTP_INTERFACE_ETH] = "\x02sWN TSCTCInterface 0\x03";
1582 
1583  /*
1584  * Overwrite CMD_SET_ACCESS_MODE_3 by customized hash value
1585  */
1586  std::string client_authorization_pw = "F4724744";
1587  rosDeclareParam(nh, "client_authorization_pw", client_authorization_pw);
1588  rosGetParam(nh, "client_authorization_pw", client_authorization_pw);
1589  if (!client_authorization_pw.empty() && client_authorization_pw != "F4724744")
1590  {
1591  std::string s_access_mode_3 = std::string("\x02sMN SetAccessMode 3 ") + client_authorization_pw + std::string("\x03\0");
1592  sopasCmdVec[CMD_SET_ACCESS_MODE_3] = s_access_mode_3;
1594  }
1595 
1596  /*
1597  * Radar specific commands
1598  */
1599  sopasCmdVec[CMD_SET_TRANSMIT_RAWTARGETS_ON] = "\x02sWN TransmitTargets 1\x03"; // transmit raw target for radar
1600  sopasCmdVec[CMD_SET_TRANSMIT_RAWTARGETS_OFF] = "\x02sWN TransmitTargets 0\x03"; // do not transmit raw target for radar
1601  sopasCmdVec[CMD_SET_TRANSMIT_OBJECTS_ON] = "\x02sWN TransmitObjects 1\x03"; // transmit objects from radar tracking
1602  sopasCmdVec[CMD_SET_TRANSMIT_OBJECTS_OFF] = "\x02sWN TransmitObjects 0\x03"; // do not transmit objects from radar tracking
1603  sopasCmdVec[CMD_SET_TRACKING_MODE_0] = "\x02sWN TCTrackingMode 0\x03"; // set object tracking mode to BASIC
1604  sopasCmdVec[CMD_SET_TRACKING_MODE_1] = "\x02sWN TCTrackingMode 1\x03"; // set object tracking mode to TRAFFIC
1605 
1606 
1607  sopasCmdVec[CMD_LOAD_APPLICATION_DEFAULT] = "\x02sMN mSCloadappdef\x03"; // load application default
1608  sopasCmdVec[CMD_DEVICE_TYPE] = "\x02sRN DItype\x03"; // ask for radar device type
1609  sopasCmdVec[CMD_ORDER_NUMBER] = "\x02sRN OrdNum\x03"; // ask for radar order number
1610 
1611 
1612 
1613  sopasCmdVec[CMD_START_MEASUREMENT] = "\x02sMN LMCstartmeas\x03";
1614  sopasCmdVec[CMD_STOP_MEASUREMENT] = "\x02sMN LMCstopmeas\x03";
1615  sopasCmdVec[CMD_APPLICATION_MODE_FIELD_ON] = "\x02sWN SetActiveApplications 1 FEVL 1\x03"; // <STX>sWN{SPC}SetActiveApplications{SPC}1{SPC}FEVL{SPC}1<ETX>
1616  sopasCmdVec[CMD_APPLICATION_MODE_FIELD_OFF] = "\x02sWN SetActiveApplications 1 FEVL 0\x03"; // <STX>sWN{SPC}SetActiveApplications{SPC}1{SPC}FEVL{SPC}0<ETX>
1617  sopasCmdVec[CMD_APPLICATION_MODE_RANGING_ON] = "\x02sWN SetActiveApplications 1 RANG 1\x03";
1618  sopasCmdVec[CMD_READ_ACTIVE_APPLICATIONS] = "\x02sRN SetActiveApplications\x03";
1619  sopasCmdVec[CMD_SET_TO_COLA_A_PROTOCOL] = "\x02sWN EIHstCola 0\x03";
1620  sopasCmdVec[CMD_GET_PARTIAL_SCANDATA_CFG] = "\x02sRN LMDscandatacfg\x03";//<STX>sMN{SPC}mLMPsetscancfg{SPC } +5000{SPC}+1{SPC}+5000{SPC}-450000{SPC}+2250000<ETX>
1621  sopasCmdVec[CMD_GET_PARTIAL_SCAN_CFG] = "\x02sRN LMPscancfg\x03";
1622  sopasCmdVec[CMD_SET_TO_COLA_B_PROTOCOL] = "\x02sWN EIHstCola 1\x03";
1623 
1624  sopasCmdVec[CMD_STOP_IMU_DATA] = "\x02sEN InertialMeasurementUnit 0\x03";
1625  sopasCmdVec[CMD_START_IMU_DATA] = "\x02sEN InertialMeasurementUnit 1\x03";
1626 
1627  // Encoder settings
1628  sopasCmdVec[CMD_SET_ENCODER_MODE_NO] = "\x02sWN LICencset 0\x03"; // Encoder setting: off (LMS1xx, LMS5xx, LMS4000, LRS4000)
1629  sopasCmdVec[CMD_SET_ENCODER_MODE_SI] = "\x02sWN LICencset 1\x03"; // Encoder setting: single increment (LMS1xx, LMS5xx, LMS4000, LRS4000)
1630  sopasCmdVec[CMD_SET_ENCODER_MODE_DP] = "\x02sWN LICencset 2\x03"; // Encoder setting: direction recognition phase (LMS1xx, LMS5xx, LMS4000, LRS4000)
1631  sopasCmdVec[CMD_SET_ENCODER_MODE_DL] = "\x02sWN LICencset 3\x03"; // Encoder setting: direction recognition level (LMS1xx, LMS5xx, LMS4000, LRS4000)
1632  sopasCmdVec[CMD_SET_ENCODER_MODE_FI] = "\x02sWN LICencset 4\x03"; // Encoder setting: fixed increment speed/ticks (LMS4000)
1633  sopasCmdVec[CMD_SET_INCREMENTSOURCE_ENC] = "\x02sWN LICsrc 1\x03"; // LMS1xx, LMS5xx, LRS4000
1634  sopasCmdVec[CMD_SET_3_4_TO_ENCODER] = "\x02sWN DO3And4Fnc 1\x03"; // Input state: encoder (LMS5xx)
1635  sopasCmdVec[CMD_SET_ENOCDER_RES_1] = "\x02sWN LICencres 1\x03"; // LMS1xx, LMS5xx, LRS4000
1636 
1637  sopasCmdVec[CMD_SET_SCANDATACONFIGNAV] = "\x02sMN mLMPsetscancfg +2000 +1 +7500 +3600000 0 +2500 0 0 +2500 0 0 +2500 0 0\x03";
1638  /*
1639  * Special configuration for NAV Scanner
1640  * in hex
1641  * sMN mLMPsetscancfg 0320 01 09C4 0 0036EE80 09C4 0 0 09C4 0 0 09C4 0 0
1642  * | | | | | | | | | | | | | |
1643  * | | | | | | | | | | | | | +->
1644  * | | | | | | | | | | | | +---> 0x0 --> 0 -> 0° start ang for sector 4
1645  * | | | | | | | | | | | +-------> 0x09c4 --> 2500 -> 0.25° deg ang res for Sector 4
1646  * | | | | | | | | | | +----------> 0x0 --> 0 -> 0° start ang for sector 4
1647  * | | | | | | | | | +------------> 0x0 --> 0 -> 0° start ang for sector 3
1648  * | | | | | | | | +----------------> 0x09c4 --> 2500 -> 0.25° deg ang res for Sector 3
1649  * | | | | | | | +-------------------> 0x0 --> 0 -> 0° start ang for sector 2
1650  * | | | | | | +---------------------> 0x0 --> 0 -> 0° start ang for sector 2
1651  * | | | | | +------------------------> 0x09c4 --> 2500 -> 0.25° Deg ang res for Sector 2
1652  * | | | | +-------------------------------> 0x36EE80h--> 3600000-> 360° stop ang for sector 1
1653  * | | | +-------------------------------------> 0x0 --> 0 -> 0° Start ang for sector 1
1654  * | | +----------------------------------------> 0x09c4 --> 2500 -> 0.25° Deg ang res for Sector 1
1655  * | +---------------------------------------------> 0x01 --> 01 -> 1 Number of active sectors
1656  * +------------------------------------------------> 0x0320 --> 0800 -> 8 Hz scanfreq
1657  */
1658  // 0320 01 09C4 0 0036EE80 09C4 0 0 09C4 0 0 09C4 0 0
1659  sopasCmdVec[CMD_GET_SCANDATACONFIGNAV] = "\x02sRN LMPscancfg\x03";
1660  sopasCmdVec[CMD_SEN_SCANDATACONFIGNAV] = "\x02sEN LMPscancfg 1\x03";
1661  sopasCmdVec[CMD_SET_LFEREC_ACTIVE] = "\x02sEN LFErec 1\x03"; // TiM781S: activate LFErec messages, send "sEN LFErec 1"
1662  sopasCmdVec[CMD_SET_LID_OUTPUTSTATE_ACTIVE] = "\x02sEN LIDoutputstate 1\x03"; // TiM781S: activate LIDoutputstate messages, send "sEN LIDoutputstate 1"
1663  sopasCmdVec[CMD_SET_LID_INPUTSTATE_ACTIVE] = "\x02sEN LIDinputstate 1\x03"; // TiM781S: activate LIDinputstate messages, send "sEN LIDinputstate 1"
1664 
1665  // NAV-350 commands
1666  sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_0] = "\x02sMN mNEVAChangeState 0\x03"; // 0 = power down
1667  sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_1] = "\x02sMN mNEVAChangeState 1\x03"; // 1 = standby
1668  sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_2] = "\x02sMN mNEVAChangeState 2\x03"; // 2 = mapping
1669  sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_3] = "\x02sMN mNEVAChangeState 3\x03"; // 3 = landmark detection
1670  sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_4] = "\x02sMN mNEVAChangeState 4\x03"; // 4 = navigation
1671  sopasCmdVec[CMD_SET_NAV_CURR_LAYER] = "\x02sWN NEVACurrLayer 0\x03"; // Set NAV curent layer
1672  sopasCmdVec[CMD_SET_NAV_LANDMARK_DATA_FORMAT] = "\x02sWN NLMDLandmarkDataFormat 0 1 1\x03"; // Set NAV LandmarkDataFormat
1673  sopasCmdVec[CMD_SET_NAV_SCAN_DATA_FORMAT] = "\x02sWN NAVScanDataFormat 1 1\x03"; // Set NAV ScanDataFormat
1674  sopasCmdVec[CMD_SET_NAV_POSE_DATA_FORMAT] = "\x02sWN NPOSPoseDataFormat 1 1\x03"; // Set NAV PoseDataFormat
1675  sopasCmdVec[CMD_SET_NAV_MAP_CFG] = "\x02sWN NMAPMapCfg 50 0 0 0 0\x03";
1676  sopasCmdVec[CMD_SET_NAV_REFL_SIZE] = "\x02sWN NLMDReflSize 80\x03";
1677  sopasCmdVec[CMD_SET_NAV_DO_MAPPING] = "\x02sMN mNMAPDoMapping\x03";
1678  sopasCmdVec[CMD_SET_NAV_ADD_LANDMARK] = "\x02sMN mNLAYAddLandmark 0\x03";
1679  sopasCmdVec[CMD_SET_NAV_ERASE_LAYOUT] = "\x02sMN mNLAYEraseLayout 1\x03";
1680  sopasCmdVec[CMD_SET_NAV_STORE_LAYOUT] = "\x02sMN mNLAYStoreLayout\x03";
1681  sopasCmdVec[CMD_SET_NAV_POSE] = "\x02sMN mNPOSSetPose 0 0 0\x03"; // Set NAV-350 start pose in navigation mode by "sMN mNPOSSetPose X Y Phi"
1682 
1683  // Supported by sick_generic_caller version 2.7.3 and above:
1684  sopasCmdVec[CMD_SET_LFPMEANFILTER] = "\x02sWN LFPmeanfilter 0 0 0\x03"; // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmeanfilter" + { 1 byte 0|1 active/inactive } + { 2 byte 0x02 ... 0x64 number of scans } + { 1 byte 0x00 }
1685  sopasCmdVec[CMD_SET_LFPMEDIANFILTER] = "\x02sWN LFPmedianfilter 0 3\x03"; // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmedianfilter" (3x1 median filter) + { 1 byte 0|1 active/inactive } + { 2 byte 0x03 }
1686  sopasCmdVec[CMD_SET_LMDSCANDATASCALEFACTOR] = "\x02sWN LMDscandatascalefactor 3F800000\x03"; // LRS4xxx: "sWN LMDscandatascalefactor" + { 4 byte float }, e.g. scalefactor 1.0f = 0x3f800000, scalefactor 2.0f = 0x40000000
1687  sopasCmdVec[CMD_SET_GLARE_DETECTION_SENS] = "\x02sWN GlareDetectionSens 0\x03"; // Glare Detection Sensitivity (LRS4xxx only): glare_detection_sens<0: do not apply, glare_detection_sens==0: deactivate glare_detection_filter, glare_detection_sens==5: medium glare detection sensitivity, glare_detection_sens==10: sensitive glare detection filter
1688 
1689  // Supported for MRS-1000 layer activation
1690  // sopasCmdVec[CMD_SET_ALIGNMENT_MODE] = "\x02sWN MMAlignmentMode 0\x03"; // MRS-1000 layer activation (alignment mode): do not overwrite: -1, all Layer: 0 (default), red Layer (-2.5 deg): 1, blue Layer (0 deg): 2, green Layer (+2.5 deg): 3, yellow Layer (+5 deg): 4
1691  sopasCmdVec[CMD_SET_SCAN_LAYER_FILTER] = "\x02sWN ScanLayerFilter 4 1 1 1 1\x03"; // MRS-1000 scan layer activation mask, "sWN ScanLayerFilter <number of layers> <layer 1: on/off> … <layer N: on/off>"", default: all layer activated: "sWN ScanLayerFilter 4 1 1 1 1"
1692 
1693 
1694  /*
1695  * Angle Compensation Command
1696  *
1697  */
1698  sopasCmdVec[CMD_GET_ANGLE_COMPENSATION_PARAM] = "\x02sRN MCAngleCompSin\x03";
1699  // defining cmd mask for cmds with variable input
1700  sopasCmdMaskVec[CMD_SET_PARTIAL_SCAN_CFG] = "\x02sMN mLMPsetscancfg %+d 1 %+d 0 0\x03";//scanfreq [1/100 Hz],angres [1/10000°],
1701  sopasCmdMaskVec[CMD_SET_PARTICLE_FILTER] = "\x02sWN LFPparticle %d %d\x03";
1702  sopasCmdMaskVec[CMD_SET_MEAN_FILTER] = "\x02sWN LFPmeanfilter %d %d 0\x03";
1703  // sopasCmdMaskVec[CMD_ALIGNMENT_MODE] = "\x02sWN MMAlignmentMode %d\x03";
1704  sopasCmdMaskVec[CMD_SCAN_LAYER_FILTER] = "\x02sWN ScanLayerFilter %s\x03";
1705  sopasCmdMaskVec[CMD_APPLICATION_MODE] = "\x02sWN SetActiveApplications 1 %s %d\x03";
1706  sopasCmdMaskVec[CMD_SET_OUTPUT_RANGES] = "\x02sWN LMPoutputRange 1 %X %X %X\x03";
1707  sopasCmdMaskVec[CMD_SET_OUTPUT_RANGES_NAV3] = "\x02sWN LMPoutputRange 1 %X %X %X %X %X %X %X %X %X %X %X %X\x03";
1708  sopasCmdMaskVec[CMD_SET_PARTIAL_SCANDATA_CFG] = "\x02sWN LMDscandatacfg %d 0 %d %d 0 %d 0 0 0 0 %d 1\x03"; // outputChannelFlagId, rssiFlag, rssiResolutionIs16Bit, EncoderSettings, timingflag // "\x02sWN LMDscandatacfg %02d 00 %d %d 0 0 %02d 0 0 0 %d 1\x03"
1709  /*
1710  * configuration in ASCII
1711  * sWN LMDscandatacfg %d 0 %d %d 0 %d 0 0 0 0 %d 1
1712  * | | | | | | | | | | |
1713  * | | | | | | | | | | +----------> Output rate -> All scans: 1--> every 1 scan
1714  * | | | | | | | | | +-------------> Time -> True (unused in Data Processing, TiM240:false)
1715  * | | | | | | | | +----------------> Comment -> False
1716  * | | | | | | | +-------------------> Device Name -> False
1717  * | | | | | | +----------------------> Position -> False
1718  * | | | | | +-------------------------> Encoder MSB -> Always 0
1719  * | | | | +----------------------------> Encoder LSB -> Param set by Mask (0 = no encoder, 1 = activate encoder), default: 0
1720  * | | | +--------------------------------> Unit of Remission -> Always 0
1721  * | | +------------------------------------> RSSi Resolution -> 0 8Bit 1 16 Bit
1722  * | +-----------------------------------------> Remission data -> Param set by Mask 0 False 1 True
1723  * +----------------------------------------------> Data channel -> Param set by Mask
1724  */
1725  sopasCmdMaskVec[CMD_GET_PARTIAL_SCANDATA_CFG] = "\x02sRA LMPscancfg %02d 00 %d %d 0 0 %02d 0 0 0 1 1\x03";
1726  sopasCmdMaskVec[CMD_GET_SAFTY_FIELD_CFG] = "\x02sRN field%03d\x03";
1727  sopasCmdMaskVec[CMD_SET_ECHO_FILTER] = "\x02sWN FREchoFilter %d\x03";
1728  sopasCmdMaskVec[CMD_SET_NTP_UPDATETIME] = "\x02sWN TSCTCupdatetime %d\x03";
1729  sopasCmdMaskVec[CMD_SET_NTP_TIMEZONE] = "sWN TSCTCtimezone %d";
1730  sopasCmdMaskVec[CMD_SET_IP_ADDR] = "\x02sWN EIIpAddr %02X %02X %02X %02X\x03";
1731  sopasCmdMaskVec[CMD_SET_NTP_SERVER_IP_ADDR] = "\x02sWN TSCTCSrvAddr %02X %02X %02X %02X\x03";
1732  sopasCmdMaskVec[CMD_SET_GATEWAY] = "\x02sWN EIgate %02X %02X %02X %02X\x03";
1733  sopasCmdMaskVec[CMD_SET_ENCODER_RES] = "\x02sWN LICencres %f\x03";
1734  sopasCmdMaskVec[CMD_SET_SCAN_CFG_LIST] ="\x02sMN mCLsetscancfglist %d\x03";// set scan config from list for NAX310 LD-OEM15xx LD-LRS36xx
1735 
1736 
1737 /*
1738  |Mode |Inter-laced |Scan freq. | Result. scan freq.| Reso-lution |Total Resol. | Field of view| Sector| LRS 3601 3611 |OEM 1501|NAV 310 |LRS 3600 3610 |OEM 1500|
1739 |---|---|-------|--------|--------|---------|-------|-----------------|---|---|---|---|---|
1740 |1 |0x |8 Hz |8 Hz |0.25° |0.25° |360° |0 ... 360° |x |x |x |(x)|(x)|
1741 |2 |0x |15 Hz |15 Hz |0.5° |0.5° |360° |0 ... 360° |x |x |x |(x)|(x)|
1742 |3 |0x |10 Hz |10 Hz |0.25° |0.25° |300° |30 ... 330° |x |x |x |x |x |
1743 |4 |0x |5 Hz |5 Hz |0.125° |0.125° |300° |30 ... 330° |x |x |x |x |x |
1744 |5 |0x |6 Hz |6 Hz |0.1875° |0.1875° |360° |0 ... 360° |x |x |x |(x)|(x)|
1745 |6 |0x |8 Hz |8 Hz |0.25° |0.25° |359.5° |0.25° ...359.25° | | | |x |X |
1746 |8 |0x |15 Hz |15 Hz |0.375° |0.375° |300° |30...330° |x |X |x |x |x |
1747 |9 |0x |15 Hz |15 Hz |0.5° |0.5° |359° |0.5 ... 359.5° | | | |x |x |
1748 |21 |0x |20 Hz |20 Hz |0.5° |0.5° |300° |30 ... 330° | |X |x | |x |
1749 |22 |0x |20 Hz |20 Hz |0.75° |0.75° |360° |0 ... 360° | |x |x | |(x)|
1750 |44 |4x |10 Hz |2.5 Hz |0.25° |0.0625° |300° |30 ... 330° |x |x | |(x)|(x)|
1751 |46 |4x |16 Hz |4 Hz |0.5° |0.125° |300° |30 ... 330° | |x | | |(x)|
1752  */
1753 
1754  //error Messages
1755  sopasCmdErrMsg[CMD_DEVICE_IDENT_LEGACY] = "Error reading device ident";
1756  sopasCmdErrMsg[CMD_DEVICE_IDENT] = "Error reading device ident for MRS-family";
1757  sopasCmdErrMsg[CMD_SERIAL_NUMBER] = "Error reading SerialNumber";
1758  sopasCmdErrMsg[CMD_FIRMWARE_VERSION] = "Error reading FirmwareVersion";
1759  sopasCmdErrMsg[CMD_DEVICE_STATE] = "Error reading SCdevicestate";
1760  sopasCmdErrMsg[CMD_OPERATION_HOURS] = "Error reading operation hours";
1761  sopasCmdErrMsg[CMD_POWER_ON_COUNT] = "Error reading operation power on counter";
1762  sopasCmdErrMsg[CMD_LOCATION_NAME] = "Error reading Locationname";
1763  sopasCmdErrMsg[CMD_ACTIVATE_STANDBY] = "Error acticvating Standby";
1764  sopasCmdErrMsg[CMD_SET_PARTICLE_FILTER] = "Error setting Particelefilter";
1765  sopasCmdErrMsg[CMD_SET_MEAN_FILTER] = "Error setting Meanfilter";
1766  // sopasCmdErrMsg[CMD_ALIGNMENT_MODE] = "Error setting Alignmentmode";
1767  sopasCmdErrMsg[CMD_SCAN_LAYER_FILTER] = "Error setting ScanLayerFilter";
1768  sopasCmdErrMsg[CMD_APPLICATION_MODE] = "Error setting Meanfilter";
1769  sopasCmdErrMsg[CMD_READ_ACTIVE_APPLICATIONS] = "Error reading active applications by \"sRA SetActiveApplications\"";
1770  sopasCmdErrMsg[CMD_SET_ACCESS_MODE_3] = "Error Access Mode Client";
1771  sopasCmdErrMsg[CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER] = "Error Access Mode Client";
1772  sopasCmdErrMsg[CMD_SET_OUTPUT_RANGES] = "Error setting angular ranges";
1773  sopasCmdErrMsg[CMD_GET_OUTPUT_RANGES] = "Error reading angle range";
1774  sopasCmdErrMsg[CMD_RUN] = "FATAL ERROR unable to start RUN mode!";
1775  sopasCmdErrMsg[CMD_SET_PARTIAL_SCANDATA_CFG] = "Error setting Scandataconfig";
1776  sopasCmdErrMsg[CMD_STOP_SCANDATA] = "Error stopping scandata output";
1777  sopasCmdErrMsg[CMD_START_SCANDATA] = "Error starting Scandata output";
1778  sopasCmdErrMsg[CMD_SET_IP_ADDR] = "Error setting IP address";
1779  sopasCmdErrMsg[CMD_SET_GATEWAY] = "Error setting gateway";
1780  sopasCmdErrMsg[CMD_REBOOT] = "Error rebooting the device";
1781  sopasCmdErrMsg[CMD_WRITE_EEPROM] = "Error writing data to EEPRom";
1782  sopasCmdErrMsg[CMD_ACTIVATE_NTP_CLIENT] = "Error activating NTP client";
1783  sopasCmdErrMsg[CMD_SET_NTP_INTERFACE_ETH] = "Error setting NTP interface to ETH";
1784  sopasCmdErrMsg[CMD_SET_NTP_SERVER_IP_ADDR] = "Error setting NTP server Adress";
1785  sopasCmdErrMsg[CMD_SET_NTP_UPDATETIME] = "Error setting NTP update time";
1786  sopasCmdErrMsg[CMD_SET_NTP_TIMEZONE] = "Error setting NTP timezone";
1787  sopasCmdErrMsg[CMD_SET_ENCODER_MODE] = "Error activating encoder in single incremnt mode";
1788  sopasCmdErrMsg[CMD_SET_INCREMENTSOURCE_ENC] = "Error seting encoder increment source to Encoder";
1789  sopasCmdErrMsg[CMD_SET_SCANDATACONFIGNAV] = "Error setting scandata config";
1790  sopasCmdErrMsg[CMD_GET_SCANDATACONFIGNAV] = "Error getting scandata config";
1791  sopasCmdErrMsg[CMD_SEN_SCANDATACONFIGNAV] = "Error setting sEN LMPscancfg";
1792  sopasCmdErrMsg[CMD_SET_LFEREC_ACTIVE] = "Error activating LFErec messages";
1793  sopasCmdErrMsg[CMD_SET_LID_OUTPUTSTATE_ACTIVE] = "Error activating LIDoutputstate messages";
1794  sopasCmdErrMsg[CMD_SET_LID_INPUTSTATE_ACTIVE] = "Error activating LIDinputstate messages";
1795  sopasCmdErrMsg[CMD_SET_SCAN_CFG_LIST] ="Error setting scan config from list";
1796 
1797  // NAV-350 commands
1798  sopasCmdErrMsg[CMD_SET_NAV_OPERATIONAL_MODE_0] = "Error setting operational mode power down \"sMN mNEVAChangeState 0\"";
1799  sopasCmdErrMsg[CMD_SET_NAV_OPERATIONAL_MODE_1] = "Error setting operational mode standby \"sMN mNEVAChangeState 1\"";
1800  sopasCmdErrMsg[CMD_SET_NAV_OPERATIONAL_MODE_2] = "Error setting operational mode mapping \"sMN mNEVAChangeState 2\"";
1801  sopasCmdErrMsg[CMD_SET_NAV_OPERATIONAL_MODE_3] = "Error setting operational mode landmark detection \"sMN mNEVAChangeState 3\"";
1802  sopasCmdErrMsg[CMD_SET_NAV_OPERATIONAL_MODE_4] = "Error setting operational mode navigation \"sMN mNEVAChangeState 4\"";
1803  sopasCmdErrMsg[CMD_SET_NAV_CURR_LAYER] = "Error setting NAV current layer \"sWN NEVACurrLayer ...\"";
1804  sopasCmdErrMsg[CMD_SET_NAV_LANDMARK_DATA_FORMAT] = "Error setting NAV landmark data format \"sWN NLMDLandmarkDataFormat ...\"";
1805  sopasCmdErrMsg[CMD_SET_NAV_SCAN_DATA_FORMAT] = "Error setting NAV scan data format \"sWN NAVScanDataFormat ...\"";
1806  sopasCmdErrMsg[CMD_SET_NAV_POSE_DATA_FORMAT] = "Error setting NAV pose data format \"sWN NPOSPoseDataFormat ...\"";
1807  sopasCmdErrMsg[CMD_SET_NAV_MAP_CFG] = "Error setting NAV mapping configuration \"sWN NMAPMapCfg ...\"";
1808  sopasCmdErrMsg[CMD_SET_NAV_REFL_SIZE] = "Error setting NAV reflector size \"sWN NLMDReflSize ...\"";
1809  sopasCmdErrMsg[CMD_SET_NAV_DO_MAPPING] = "Error setting NAV mapping \"sMN mNMAPDoMapping\"";
1810  sopasCmdErrMsg[CMD_SET_NAV_ADD_LANDMARK] = "Error adding NAV landmark \"sMN mNLAYAddLandmark\"";
1811  sopasCmdErrMsg[CMD_SET_NAV_ERASE_LAYOUT] = "Error erasing NAV layout \"sMN mNLAYEraseLayout\"";
1812  sopasCmdErrMsg[CMD_SET_NAV_STORE_LAYOUT] = "Error storing NAV layout \"sMN mNLAYStoreLayout\"";
1813  sopasCmdErrMsg[CMD_SET_NAV_POSE] = "Error setting start pose \"sMN mNPOSSetPose ...\"";
1814 
1815  // Supported by sick_generic_caller version 2.7.3 and above:
1816  sopasCmdErrMsg[CMD_SET_LFPMEANFILTER] = "Error setting sopas command \"sWN LFPmeanfilter ...\"";
1817  sopasCmdErrMsg[CMD_SET_LFPMEDIANFILTER] = "Error setting sopas command \"sWN LFPmedianfilter ...\"";
1818  sopasCmdErrMsg[CMD_SET_LMDSCANDATASCALEFACTOR] = "Error setting sopas command\"sWN LMDscandatascalefactor ...\"";
1819  sopasCmdErrMsg[CMD_SET_GLARE_DETECTION_SENS] = "Error setting sopas command \"sWN GlareDetectionSens ...\"";
1820 
1821  // Supported for MRS-1000 layer activation (alignment mode)
1822  // sopasCmdErrMsg[CMD_SET_ALIGNMENT_MODE] = "Error setting sopas command \"sWN MMAlignmentMode ...\"";
1823  sopasCmdErrMsg[CMD_SET_SCAN_LAYER_FILTER] = "Error setting sopas command \"sWN ScanLayerFilter ...\"";
1824 
1825  // ML: Add here more useful cmd and mask entries
1826 
1827  // After definition of command, we specify the command sequence for scanner initalisation
1829  {
1831  }
1832  else
1833  {
1835  }
1836 
1837  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LRS_4XXX_NAME) != 0) // "sWN EIHstCola" currently not supported by LRS-4xxx
1838  {
1840  {
1842  }
1843  else
1844  {
1845  //for binary Mode Testing
1847  }
1848  }
1849 
1850  //TODO add basicParam for this
1855  {
1857  }
1858 
1859 
1860  /*
1861  * NAV2xx supports angle compensation
1862  */
1863  bool isNav2xxOr3xx = false;
1865  {
1866  isNav2xxOr3xx = true;
1867  }
1869  {
1870  isNav2xxOr3xx = true;
1871  }
1872  if (isNav2xxOr3xx)
1873  {
1875  }
1876 
1877  bool tryToStopMeasurement = true;
1879  {
1880  tryToStopMeasurement = false;
1881  // do not stop measurement for TiM571 otherwise the scanner would not start after start measurement
1882  // do not change the application - not supported for TiM5xx
1883  }
1884  if (parser_->getCurrentParamPtr()->getDeviceIsRadar() == true)
1885  {
1886  bool load_application_default = false;
1887  rosDeclareParam(nh, "load_application_default", load_application_default);
1888  rosGetParam(nh, "load_application_default", load_application_default);
1889  if(load_application_default)
1890  {
1891  sopasCmdChain.push_back(CMD_LOAD_APPLICATION_DEFAULT); // load application default for radar
1892  }
1893 
1894  tryToStopMeasurement = false;
1895  // do not stop measurement for RMSxxxx (not supported)
1896  }
1897  if (tryToStopMeasurement)
1898  {
1901  {
1902  sopasCmdChain.push_back(CMD_READ_ACTIVE_APPLICATIONS); // "sRN SetActiveApplications"
1903  }
1904  int numberOfLayers = parser_->getCurrentParamPtr()->getNumberOfLayers();
1905 
1906  switch (numberOfLayers)
1907  {
1908  case 4:
1909  sopasCmdChain.push_back(CMD_DEVICE_IDENT);
1912  sopasCmdChain.push_back(CMD_SERIAL_NUMBER);
1913 
1914  break;
1915  case 24:
1916  // just measuring - Application setting not supported
1917  // "Old" device ident command "SRi 0" not supported
1918  sopasCmdChain.push_back(CMD_DEVICE_IDENT);
1919  break;
1920 
1921  default:
1925 
1926  sopasCmdChain.push_back(CMD_SERIAL_NUMBER);
1927  break;
1928  }
1929 
1930  }
1933  {
1934  sopasCmdChain.push_back(CMD_DEVICE_IDENT);
1935  }
1936 
1937  sopasCmdChain.push_back(CMD_FIRMWARE_VERSION); // read firmware
1938  sopasCmdChain.push_back(CMD_DEVICE_STATE); // read device state
1939  sopasCmdChain.push_back(CMD_OPERATION_HOURS); // read operation hours
1940  sopasCmdChain.push_back(CMD_POWER_ON_COUNT); // read power on count
1941  sopasCmdChain.push_back(CMD_LOCATION_NAME); // read location name
1942 
1943  // Support for "sRN LMPscancfg" and "sMN mLMPsetscancfg" for NAV_31X and LRS_36x1 since version 2.4.4
1944  // TODO: apply and test for LRS_36x0 and OEM_15XX, too
1945  // if (this->parser_->getCurrentParamPtr()->getUseScancfgList() == true) // true for SICK_SCANNER_LRS_36x0_NAME, SICK_SCANNER_LRS_36x1_NAME, SICK_SCANNER_NAV_31X_NAME, SICK_SCANNER_OEM_15XX_NAME
1948  {
1949  sopasCmdChain.push_back(CMD_GET_SCANDATACONFIGNAV); // Read LMPscancfg by "sRN LMPscancfg"
1950  sopasCmdChain.push_back(CMD_SET_SCAN_CFG_LIST); // "sMN mCLsetscancfglist 1", set scan config from list for NAX310 LD-OEM15xx LD-LRS36xx
1951  sopasCmdChain.push_back(CMD_RUN); // Apply changes, note by manual: "the new values will be activated only after log out (from the user level), when re-entering the Run mode"
1952  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3); // re-enter authorized client level
1953  sopasCmdChain.push_back(CMD_GET_SCANDATACONFIGNAV); // Read LMPscancfg by "sRN LMPscancfg"
1954  sopasCmdChain.push_back(CMD_SET_SCANDATACONFIGNAV); // Set configured start/stop angle using "sMN mLMPsetscancfg"
1955  sopasCmdChain.push_back(CMD_RUN); // Apply changes, note by manual: "the new values will be activated only after log out (from the user level), when re-entering the Run mode"
1956  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3); // re-enter authorized client level
1957  sopasCmdChain.push_back(CMD_GET_SCANDATACONFIGNAV); // Read LMPscancfg by "sRN LMPscancfg"
1958  }
1959  /*
1960  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_31X_NAME) == 0)
1961  {
1962  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3); // re-enter authorized client level
1963  sopasCmdChain.push_back("\x02sWN LMDscandatacfg 01 00 1 0 0 0 00 0 0 0 1 1\x03");
1964  sopasCmdChain.push_back(CMD_RUN); // Apply changes
1965  }
1966  */
1967  // Support for "sWN LFPmeanfilter" and "sWN LFPmedianfilter" (MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx)
1972  {
1973  int lfp_meanfilter_arg = -1, lfp_medianfilter_arg = -1;
1974  rosDeclareParam(nh, "lfp_meanfilter", lfp_meanfilter_arg);
1975  rosGetParam(nh, "lfp_meanfilter", lfp_meanfilter_arg);
1976  rosDeclareParam(nh, "lfp_medianfilter", lfp_medianfilter_arg);
1977  rosGetParam(nh, "lfp_medianfilter", lfp_medianfilter_arg);
1978  if (lfp_meanfilter_arg >= 0)
1979  {
1980  // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmeanfilter" + { 1 byte 0|1 active/inactive } + { 2 byte 0x02 ... 0x64 number of scans } + { 1 byte 0x00 }
1981  if (lfp_meanfilter_arg == 0) // deactivate, number of scans must be >= 2
1982  sopasCmdVec[CMD_SET_LFPMEANFILTER] = "\x02sWN LFPmeanfilter 0 2 0\x03";
1983  else
1984  sopasCmdVec[CMD_SET_LFPMEANFILTER] = "\x02sWN LFPmeanfilter 1 " + toString(lfp_meanfilter_arg) + " 0\x03";
1986  }
1987  if (lfp_medianfilter_arg >= 0)
1988  {
1989  // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmedianfilter" (3x1 median filter) + { 1 byte 0|1 active/inactive } + { 2 byte 0x03 }
1990  sopasCmdVec[CMD_SET_LFPMEDIANFILTER] = "\x02sWN LFPmedianfilter "+ toString((lfp_medianfilter_arg > 0) ? 1 : 0) + " 3\x03";
1992  }
1993  }
1994  // Support for "sWN GlareDetectionSens" (Glare Detection Sensitivity, LRS4xxx only): glare_detection_sens<0: do not apply, glare_detection_sens==0: deactivate glare_detection_filter, glare_detection_sens==5: medium glare detection sensitivity, glare_detection_sens==10: sensitive glare detection filter
1996  {
1997  int glare_detection_sens_arg = -1;
1998  rosDeclareParam(nh, "glare_detection_sens", glare_detection_sens_arg);
1999  rosGetParam(nh, "glare_detection_sens", glare_detection_sens_arg);
2000  if (glare_detection_sens_arg >= 0)
2001  {
2002  sopasCmdVec[CMD_SET_GLARE_DETECTION_SENS] = "\x02sWN GlareDetectionSens " + toString(glare_detection_sens_arg) + "\x03";
2004  }
2005  }
2006 
2007  // Support for MRS-1000 scan layer activation mask, "sWN ScanLayerFilter <number of layers> <layer 1: on/off> … <layer N: on/off>"", default: all layer activated: "sWN ScanLayerFilter 4 1 1 1 1"
2009  {
2010  std::string scan_layer_filter = "";
2011  rosDeclareParam(nh, "scan_layer_filter", scan_layer_filter);
2012  rosGetParam(nh, "scan_layer_filter", scan_layer_filter);
2013  if (!scan_layer_filter.empty())
2014  {
2015  m_scan_layer_filter_cfg = ScanLayerFilterCfg(scan_layer_filter);
2016  sopasCmdVec[CMD_SET_SCAN_LAYER_FILTER] = "\x02sWN ScanLayerFilter " + scan_layer_filter + "\x03";
2017  sopasCmdChain.push_back(CMD_SET_SCAN_LAYER_FILTER); // set scan layer activation
2018  sopasCmdChain.push_back(CMD_RUN); // Apply changes
2019  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3); // re-enter authorized client level
2020  }
2021  }
2022 
2023  // Support for "sWN LMDscandatascalefactor" (LRS4xxx)
2025  {
2026  double lmd_scandatascalefactor_arg = 0;
2027  rosDeclareParam(nh, "lmd_scandatascalefactor", lmd_scandatascalefactor_arg);
2028  rosGetParam(nh, "lmd_scandatascalefactor", lmd_scandatascalefactor_arg);
2029  if (lmd_scandatascalefactor_arg > 0)
2030  {
2031  // LRS4xxx: "sWN LMDscandatascalefactor" + { 4 byte float }, e.g. scalefactor 1.0f = 0x3f800000, scalefactor 2.0f = 0x40000000
2032  std::string scalefactor_hex = sick_scan_xd::SickScanServices::convertFloatToHexString((float)lmd_scandatascalefactor_arg, true);
2033  sopasCmdVec[CMD_SET_LMDSCANDATASCALEFACTOR] = "\x02sWN LMDscandatascalefactor " + scalefactor_hex + "\x03";
2035  }
2036  }
2037 
2038  return (0);
2039  }
2040 
2041 
2047  {
2048 
2049 
2050  int maxNumberOfEchos = 1;
2051 
2052 
2053  maxNumberOfEchos = this->parser_->getCurrentParamPtr()->getNumberOfMaximumEchos(); // 1 for TIM 571, 3 for MRS1104, 5 for 6000, 5 for LMS5xx
2054 
2055 
2056  bool rssiFlag = false;
2057  bool rssiResolutionIs16Bit = true; //True=16 bit Flase=8bit
2058  int activeEchos = 0;
2059 
2060  rosDeclareParam(nh, "intensity", rssiFlag);
2061  rosGetParam(nh, "intensity", rssiFlag);
2062  rosDeclareParam(nh, "intensity_resolution_16bit", rssiResolutionIs16Bit);
2063  rosGetParam(nh, "intensity_resolution_16bit", rssiResolutionIs16Bit);
2064  // rosDeclareParam(nh, "min_intensity", m_min_intensity);
2065  rosGetParam(nh, "min_intensity", m_min_intensity); // Set range of LaserScan messages to infinity, if intensity < min_intensity (default: 0)
2066  //check new ip address and add cmds to write ip to comand chain
2067  std::string sNewIPAddr = "";
2068  std::string ipNewIPAddr;
2069  bool setNewIPAddr = false;
2070  rosDeclareParam(nh, "new_IP_address", sNewIPAddr);
2071  if(rosGetParam(nh, "new_IP_address", sNewIPAddr) && !sNewIPAddr.empty())
2072  {
2073  ipNewIPAddr = sNewIPAddr;
2074  sopasCmdChain.clear();
2075  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3); // "sMN SetAccessMode 3 F4724744"
2076  if (this->parser_->getCurrentParamPtr()->getUseSafetyPasWD()) // TIM_7xxS - 1 layer Safety Scanner
2077  sopasCmdChain.push_back(CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER); // "\x02sMN SetAccessMode 3 6FD62C05\x03\0"
2078  }
2079  std::string sNTPIpAdress = "";
2080  std::string NTPIpAdress;
2081  bool setUseNTP = false;
2082  rosDeclareParam(nh, "ntp_server_address", sNTPIpAdress);
2083  setUseNTP = rosGetParam(nh, "ntp_server_address", sNTPIpAdress);
2084  if (sNTPIpAdress.empty())
2085  setUseNTP = false;
2086  if (setUseNTP)
2087  {
2088  NTPIpAdress = sNTPIpAdress;
2089  }
2090 
2091  this->parser_->getCurrentParamPtr()->setIntensityResolutionIs16Bit(rssiResolutionIs16Bit);
2092  // parse active_echos entry and set flag array
2093  rosDeclareParam(nh, "active_echos", activeEchos);
2094  rosGetParam(nh, "active_echos", activeEchos);
2095 
2096  ROS_INFO_STREAM("Parameter setting for <active_echo: " << activeEchos << ">");
2097  std::vector<bool> outputChannelFlag;
2098  outputChannelFlag.resize(maxNumberOfEchos);
2099  //int i;
2100  int numOfFlags = 0;
2101  for (int i = 0; i < outputChannelFlag.size(); i++)
2102  {
2103  /*
2104  After consultation with the company SICK,
2105  all flags are set to true because the firmware currently does not support single selection of targets.
2106  The selection of the echoes takes place via FREchoFilter.
2107  */
2108  /* former implementation
2109  if (activeEchos & (1 << i))
2110  {
2111  outputChannelFlag[i] = true;
2112  numOfFlags++;
2113  }
2114  else
2115  {
2116  outputChannelFlag[i] = false;
2117  }
2118  */
2119  outputChannelFlag[i] = true; // always true (see comment above)
2120  numOfFlags++;
2121  }
2122 
2123  if (numOfFlags == 0) // Fallback-Solution
2124  {
2125  outputChannelFlag[0] = true;
2126  numOfFlags = 1;
2127  ROS_WARN("Activate at least one echo.");
2128  }
2129 
2130  //================== DEFINE ENCODER SETTING ==========================
2131  int EncoderSettings = -1; // Do not use encoder commands as default
2132  rosDeclareParam(nh, "encoder_mode", EncoderSettings);
2133  rosGetParam(nh, "encoder_mode", EncoderSettings);
2134 
2135  this->parser_->getCurrentParamPtr()->setEncoderMode((int8_t)EncoderSettings);
2136  if (this->parser_->getCurrentParamPtr()->getEncoderMode() >= 0) // EncoderSettings supported by LMS1xx, LMS5xx, LMS4000, LRS4000
2137  {
2138  if (this->parser_->getCurrentParamPtr()->isOneOfScannerNames({SICK_SCANNER_LMS_5XX_NAME})) // LMS5xx only
2139  {
2141  }
2142  if (this->parser_->getCurrentParamPtr()->isOneOfScannerNames({SICK_SCANNER_LMS_1XX_NAME, SICK_SCANNER_LMS_5XX_NAME, SICK_SCANNER_LRS_4XXX_NAME})) // for LMS1xx, LMS5xx, LRS4000
2143  {
2145  }
2146  if (this->parser_->getCurrentParamPtr()->isOneOfScannerNames({SICK_SCANNER_LMS_1XX_NAME, SICK_SCANNER_LMS_5XX_NAME, SICK_SCANNER_LMS_4XXX_NAME, SICK_SCANNER_LRS_4XXX_NAME})) // for LMS1xx, LMS5xx, LMS4000, LRS4000
2147  {
2149  {
2150  case 0:
2152  break;
2153  case 1:
2155  break;
2156  case 2:
2158  break;
2159  case 3:
2161  break;
2162  case 4:
2163  if (this->parser_->getCurrentParamPtr()->isOneOfScannerNames({SICK_SCANNER_LRS_4XXX_NAME})) // for LMS4000 only
2164  {
2166  }
2167  break;
2168  default:
2169  break;
2170  }
2171  }
2172  if (this->parser_->getCurrentParamPtr()->isOneOfScannerNames({SICK_SCANNER_LMS_1XX_NAME, SICK_SCANNER_LMS_5XX_NAME, SICK_SCANNER_LRS_4XXX_NAME})) // for LMS1xx, LMS5xx, LRS4000
2173  {
2175  }
2176  }
2177  int result;
2178  //================== DEFINE ANGULAR SETTING ==========================
2179  int angleRes10000th = 0;
2180  int angleStart10000th = 0;
2181  int angleEnd10000th = 0;
2182 
2183 
2184  // Mainloop for initial SOPAS cmd-Handling
2185  //
2186  // To add new commands do the followings:
2187  // 1. Define new enum-entry in the enumumation "enum SOPAS_CMD" in the file sick_scan_common.h
2188  // 2. Define new command sequence in the member function init_cmdTables()
2189  // 3. Add cmd-id in the sopasCmdChain to trigger sending of command.
2190  // 4. Add handling of reply by using for the pattern "REPLY_HANDLING" in this code and adding a new case instruction.
2191  // That's all!
2192 
2193 
2194  volatile bool useBinaryCmd = false;
2195  if (this->parser_->getCurrentParamPtr()->getUseBinaryProtocol()) // hard coded for every scanner type
2196  {
2197  useBinaryCmd = true; // shall we talk ascii or binary with the scanner type??
2198  }
2199 
2200  bool useBinaryCmdNow = false;
2201  int maxCmdLoop = 2; // try binary and ascii during startup
2202 
2203 
2204  int read_timeout_millisec_default = READ_TIMEOUT_MILLISEC_DEFAULT;
2205  int read_timeout_millisec_startup = READ_TIMEOUT_MILLISEC_STARTUP;
2206  rosDeclareParam(nh, "read_timeout_millisec_default", read_timeout_millisec_default);
2207  rosGetParam(nh, "read_timeout_millisec_default", read_timeout_millisec_default);
2208  rosDeclareParam(nh, "read_timeout_millisec_startup", read_timeout_millisec_startup);
2209  rosGetParam(nh, "read_timeout_millisec_startup", read_timeout_millisec_startup);
2210  const int shortTimeOutInMs = read_timeout_millisec_default; // during startup phase to check binary or ascii
2211  const int defaultTimeOutInMs = read_timeout_millisec_startup; // standard time out 120 sec.
2212 
2213  setReadTimeOutInMs(shortTimeOutInMs);
2214 
2215  bool restartDueToProcolChange = false;
2216 
2217  /* NAV310 needs special handling */
2218  /* The NAV310 does not support LMDscandatacfg and rotates clockwise. */
2219  /* The X-axis shows backwards */
2220 
2221  //TODO remove this and use getUseCfgList instead
2222  bool NAV3xxOutputRangeSpecialHandling=false;
2223  if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_31X_NAME) == 0 ||
2224  this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LRS_36x0_NAME) == 0 ||
2225  this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LRS_36x1_NAME) == 0)
2226  {
2227  NAV3xxOutputRangeSpecialHandling = true;
2228  }
2229 
2230  // Check Cola-Configuration of the scanner:
2231  // * Send "sRN DeviceState" with configured cola-dialect (Cola-B = useBinaryCmd)
2232  // * If lidar does not answer:
2233  // * Send "sRN DeviceState" with different cola-dialect (Cola-B = !useBinaryCmd)
2234  // * If lidar sends a response:
2235  // * Switch to configured cola-dialect (Cola-B = useBinaryCmd) using "sWN EIHstCola" and restart
2236  if (checkColaTypeAndSwitchToConfigured(useBinaryCmd) != ExitSuccess)
2237  {
2238  ROS_WARN_STREAM("SickScanCommon::init_scanner(): checkColaDialect failed, restarting.");
2239  ROS_WARN_STREAM("It is recommended to use the binary communication (Cola-B) by:");
2240  ROS_WARN_STREAM("1. setting parameter use_binary_protocol true in the launch file, and");
2241  ROS_WARN_STREAM("2. setting the lidar communication mode with the SOPAS ET software to binary and save this setting in the scanner's EEPROM.");
2242  return ExitError;
2243  }
2244 
2245  int initialize_scanner = 1;
2247  {
2248  rosDeclareParam(nh, "initialize_scanner", initialize_scanner);
2249  rosGetParam(nh, "initialize_scanner", initialize_scanner);
2250  if (initialize_scanner == 0)
2251  {
2252  ROS_WARN_STREAM("SickScanCommon::init_scanner(): initialize_scanner=" << initialize_scanner << " for TiM7xxS configured.\n");
2253  ROS_WARN_STREAM("Note that this mode does not initialize the lidar. The mode assumes that the scanner is in an appropriate state matching the properties otherwise set in the launchfile.");
2254  ROS_WARN_STREAM("DO NOT USE THIS MODE EXCEPT THE LIDAR HAS BEEN CONFIGURED AND INITIALIZED SUCCESSFULLY");
2255  ROS_WARN_STREAM("AND IS IN THE SAME STATE AS AFTER INITIALIZATION BY THE LAUNCHFILE!\n");
2256  ROS_WARN_STREAM("SickScanCommon::init_scanner(): skipping lidar initialization.\n");
2257  sopasCmdChain.clear();
2258  }
2259  }
2260 
2261  for (size_t i = 0; i < this->sopasCmdChain.size(); i++)
2262  {
2263  rosSleep(0.2); // could maybe removed
2264 
2265  int cmdId = sopasCmdChain[i]; // get next command
2266  std::string sopasCmd = sopasCmdVec[cmdId];
2267  if (sopasCmd.empty()) // skip sopas command, i.e. use default values
2268  continue;
2269  std::vector<unsigned char> replyDummy;
2270  std::vector<unsigned char> reqBinary;
2271 
2272  std::vector<unsigned char> sopasCmdVecTmp;
2273  for (size_t j = 0; j < sopasCmd.length(); j++)
2274  {
2275  sopasCmdVecTmp.push_back(sopasCmd[j]);
2276  }
2277  ROS_DEBUG_STREAM("Command: " << stripControl(sopasCmdVecTmp));
2278 
2279  // switch to either binary or ascii after switching the command mode
2280  // via ... command
2281 
2282 
2283  for (int iLoop = 0; iLoop < maxCmdLoop; iLoop++)
2284  {
2285  if (iLoop == 0)
2286  {
2287  useBinaryCmdNow = useBinaryCmd; // start with expected value
2288 
2289  }
2290  else
2291  {
2292  useBinaryCmdNow = !useBinaryCmdNow;// try the other option
2293  useBinaryCmd = useBinaryCmdNow; // and use it from now on as default
2294 
2295  }
2296 
2297  this->setProtocolType(useBinaryCmdNow ? CoLa_B : CoLa_A);
2298 
2299 
2300  if (useBinaryCmdNow)
2301  {
2302  this->convertAscii2BinaryCmd(sopasCmd.c_str(), &reqBinary);
2303  if (reqBinary.size() > 0)
2304  {
2305  result = sendSopasAndCheckAnswer(reqBinary, &replyDummy);
2306  }
2307  else
2308  {
2309  result = 0;
2310  }
2311  sopasReplyBinVec[cmdId] = replyDummy;
2312  }
2313  else
2314  {
2315  result = sendSopasAndCheckAnswer(sopasCmd.c_str(), &replyDummy);
2316  }
2317 
2318  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_MRS_1XXX_NAME) == 0 && sopasCmd == sopasCmdVec[CMD_READ_ACTIVE_APPLICATIONS]) // "sRN SetActiveApplications"
2319  {
2320  std::string sopas_reply = DataDumper::binDataToAsciiString(replyDummy.data(), replyDummy.size());
2321  ROS_INFO_STREAM("response to \"sRN SetActiveApplications\": " << sopas_reply);
2322  if (sopas_reply.find("FEVL\\x01") != std::string::npos)
2323  isFieldEvaluationActive() = true;
2324  if (sopas_reply.find("FEVL\\x00") != std::string::npos)
2325  isFieldEvaluationActive() = false;
2326  ROS_INFO_STREAM("FieldEvaluationActive = " << (isFieldEvaluationActive() ? "true": "false"));
2327  }
2328 
2329  if (result == 0) // command sent successfully
2330  {
2331  // useBinaryCmd holds information about last successful command mode
2332  break;
2333  }
2334  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, replyDummy); // No response, non-recoverable connection error (return error and do not try other commands)
2335  }
2336  if (result != 0)
2337  {
2339 #ifdef USE_DIAGNOSTIC_UPDATER
2340  if(diagnostics_)
2341  diagnostics_->broadcast(getDiagnosticErrorCode(), sopasCmdErrMsg[cmdId]);
2342 #endif
2343  }
2344  else
2345  {
2346  sopasReplyStrVec[cmdId] = replyToString(replyDummy);
2347  }
2348 
2349  //============================================
2350  // Handle reply of specific commands here by inserting new case instruction
2351  // REPLY_HANDLING
2352  //============================================
2353  maxCmdLoop = 1;
2354 
2355 
2356  // handle special configuration commands ...
2357 
2358  switch (cmdId)
2359  {
2360 
2362  {
2363  bool protocolCheck = checkForProtocolChangeAndMaybeReconnect(useBinaryCmdNow);
2364  if (false == protocolCheck)
2365  {
2366  restartDueToProcolChange = true;
2367  }
2368  useBinaryCmd = useBinaryCmdNow;
2369  setReadTimeOutInMs(defaultTimeOutInMs);
2370  }
2371  break;
2373  {
2374  bool protocolCheck = checkForProtocolChangeAndMaybeReconnect(useBinaryCmdNow);
2375  if (false == protocolCheck)
2376  {
2377  restartDueToProcolChange = true;
2378  }
2379 
2380  useBinaryCmd = useBinaryCmdNow;
2381  setReadTimeOutInMs(defaultTimeOutInMs);
2382  }
2383  break;
2384 
2385  /*
2386  SERIAL_NUMBER: Device ident must be read before!
2387  */
2388 
2389  case CMD_DEVICE_IDENT: // FOR MRS6xxx the Device Ident holds all specific information (used instead of CMD_SERIAL_NUMBER)
2390  {
2391  std::string deviceIdent = "";
2392  int cmdLen = this->checkForBinaryAnswer(&replyDummy);
2393  if (cmdLen == -1)
2394  {
2395  int idLen = 0;
2396  int versionLen = 0;
2397  // ASCII-Return
2398  std::string deviceIdentKeyWord = "sRA DeviceIdent";
2399  char *ptr = (char *) (&(replyDummy[0]));
2400  ptr++; // skip 0x02
2401  ptr += deviceIdentKeyWord.length();
2402  ptr++; //skip blank
2403  sscanf(ptr, "%d", &idLen);
2404  char *ptr2 = strchr(ptr, ' ');
2405  if (ptr2 != NULL)
2406  {
2407  ptr2++;
2408  for (int j = 0; j < idLen; j++)
2409  {
2410  deviceIdent += *ptr2;
2411  ptr2++;
2412  }
2413 
2414  }
2415  ptr = ptr2;
2416  ptr++; //skip blank
2417  sscanf(ptr, "%d", &versionLen);
2418  ptr2 = strchr(ptr, ' ');
2419  if (ptr2 != NULL)
2420  {
2421  ptr2++;
2422  deviceIdent += " V";
2423  for (int j = 0; j < versionLen; j++)
2424  {
2425  deviceIdent += *ptr2;
2426  ptr2++;
2427  }
2428  }
2429 #ifdef USE_DIAGNOSTIC_UPDATER
2430  if(diagnostics_)
2431  diagnostics_->setHardwareID(deviceIdent);
2432 #endif
2433  if (!isCompatibleDevice(deviceIdent))
2434  {
2435  return ExitFatal;
2436  }
2437  deviceIdentStr = deviceIdent;
2438 // ROS_ERROR("BINARY REPLY REQUIRED");
2439  }
2440  else
2441  {
2442  long dummy0, dummy1, identLen, versionLen;
2443  dummy0 = 0;
2444  dummy1 = 0;
2445  identLen = 0;
2446  versionLen = 0;
2447 
2448  const char *scanMask0 = "%04y%04ysRA DeviceIdent %02y";
2449  const char *scanMask1 = "%02y";
2450  unsigned char *replyPtr = &(replyDummy[0]);
2451  int scanDataLen0 = binScanfGuessDataLenFromMask(scanMask0);
2452  int scanDataLen1 = binScanfGuessDataLenFromMask(scanMask1); // should be: 2
2453  binScanfVec(&replyDummy, scanMask0, &dummy0, &dummy1, &identLen);
2454 
2455  std::string identStr = binScanfGetStringFromVec(&replyDummy, scanDataLen0, identLen);
2456  int off = scanDataLen0 + identLen; // consuming header + fixed part + ident
2457 
2458  std::vector<unsigned char> restOfReplyDummy = std::vector<unsigned char>(replyDummy.begin() + off,
2459  replyDummy.end());
2460 
2461  versionLen = 0;
2462  binScanfVec(&restOfReplyDummy, "%02y", &versionLen);
2463  std::string versionStr = binScanfGetStringFromVec(&restOfReplyDummy, scanDataLen1, versionLen);
2464  std::string fullIdentVersionInfo = identStr + " V" + versionStr;
2465 #ifdef USE_DIAGNOSTIC_UPDATER
2466  if(diagnostics_)
2467  diagnostics_->setHardwareID(fullIdentVersionInfo);
2468 #endif
2469  if (!isCompatibleDevice(fullIdentVersionInfo))
2470  {
2471  return ExitFatal;
2472  }
2473  deviceIdentStr = fullIdentVersionInfo;
2474 
2475  }
2476  break;
2477  }
2478 
2479 
2480  case CMD_SERIAL_NUMBER:
2481  if (this->parser_->getCurrentParamPtr()->getNumberOfLayers() == 4)
2482  {
2483  // do nothing for MRS1104 here
2484  }
2485  else
2486  {
2487 #ifdef USE_DIAGNOSTIC_UPDATER
2488  if(diagnostics_)
2489  diagnostics_->setHardwareID(sopasReplyStrVec[CMD_DEVICE_IDENT_LEGACY] + " " + sopasReplyStrVec[CMD_SERIAL_NUMBER]);
2490 #endif
2492  {
2493  return ExitFatal;
2494  }
2496  }
2497  break;
2498  /*
2499  DEVICE_STATE
2500  */
2501  case CMD_DEVICE_STATE:
2502  {
2503  int deviceState = -1;
2504  /*
2505  * Process device state, 0=Busy, 1=Ready, 2=Error
2506  * If configuration parameter is set, try resetting device in error state
2507  */
2508 
2509  int iRetVal = 0;
2510  if (useBinaryCmd)
2511  {
2512  long dummy0 = 0x00;
2513  long dummy1 = 0x00;
2514  deviceState = 0x00; // must be set to zero (because only one byte will be copied)
2515  iRetVal = binScanfVec(&(sopasReplyBinVec[CMD_DEVICE_STATE]), "%4y%4ysRA SCdevicestate %1y", &dummy0,
2516  &dummy1, &deviceState);
2517  }
2518  else
2519  {
2520  iRetVal = sscanf(sopasReplyStrVec[CMD_DEVICE_STATE].c_str(), "sRA SCdevicestate %d", &deviceState);
2521  }
2522  if (iRetVal > 0) // 1 or 3 (depending of ASCII or Binary)
2523  {
2524  switch (deviceState)
2525  {
2526  case 0:
2527  ROS_DEBUG("Laser is busy");
2528  break;
2529  case 1:
2530  ROS_DEBUG("Laser is ready");
2531  break;
2532  case 2:
2533  ROS_ERROR_STREAM("Laser reports error state : " << sopasReplyStrVec[CMD_DEVICE_STATE]);
2534  if (config_.auto_reboot)
2535  {
2536  rebootScanner();
2537  };
2538  break;
2539  default:
2540  ROS_WARN_STREAM("Laser reports unknown devicestate : " << sopasReplyStrVec[CMD_DEVICE_STATE]);
2541  break;
2542  }
2543  }
2544  }
2545  break;
2546 
2547  case CMD_OPERATION_HOURS:
2548  {
2549  int operationHours = -1;
2550  int iRetVal = 0;
2551  /*
2552  * Process device state, 0=Busy, 1=Ready, 2=Error
2553  * If configuration parameter is set, try resetting device in error state
2554  */
2555  if (useBinaryCmd)
2556  {
2557  long dummy0, dummy1;
2558  dummy0 = 0;
2559  dummy1 = 0;
2560  operationHours = 0;
2561  iRetVal = binScanfVec(&(sopasReplyBinVec[CMD_OPERATION_HOURS]), "%4y%4ysRA ODoprh %4y", &dummy0, &dummy1,
2562  &operationHours);
2563  }
2564  else
2565  {
2566  operationHours = 0;
2567  iRetVal = sscanf(sopasReplyStrVec[CMD_OPERATION_HOURS].c_str(), "sRA ODoprh %x", &operationHours);
2568  }
2569  if (iRetVal > 0)
2570  {
2571  double hours = 0.1 * operationHours;
2572  rosDeclareParam(nh, "operationHours", hours);
2573  rosSetParam(nh, "operationHours", hours);
2574 
2575  }
2576  }
2577  break;
2578 
2579  case CMD_POWER_ON_COUNT:
2580  {
2581  int powerOnCount = -1;
2582  int iRetVal = -1;
2583  if (useBinaryCmd)
2584  {
2585  long dummy0, dummy1;
2586  powerOnCount = 0;
2587  iRetVal = binScanfVec(&(sopasReplyBinVec[CMD_POWER_ON_COUNT]), "%4y%4ysRA ODpwrc %4y", &dummy0, &dummy1,
2588  &powerOnCount);
2589  }
2590  else
2591  {
2592  iRetVal = sscanf(sopasReplyStrVec[CMD_POWER_ON_COUNT].c_str(), "sRA ODpwrc %x", &powerOnCount);
2593  }
2594  if (iRetVal > 0)
2595  {
2596  rosDeclareParam(nh, "powerOnCount", powerOnCount);
2597  rosSetParam(nh, "powerOnCount", powerOnCount);
2598  }
2599 
2600  }
2601  break;
2602 
2603  case CMD_LOCATION_NAME:
2604  {
2605  char szLocationName[MAX_STR_LEN] = {0};
2606  const char *strPtr = sopasReplyStrVec[CMD_LOCATION_NAME].c_str();
2607  const char *searchPattern = "sRA LocationName "; // Bug fix (leading space) Jan 2018
2608  strcpy(szLocationName, "unknown location");
2609  if (useBinaryCmd)
2610  {
2611  int iRetVal = 0;
2612  long dummy0, dummy1, locationNameLen;
2613  const char *binLocationNameMask = "%4y%4ysRA LocationName %2y";
2614  int prefixLen = binScanfGuessDataLenFromMask(binLocationNameMask);
2615  dummy0 = 0;
2616  dummy1 = 0;
2617  locationNameLen = 0;
2618 
2619  iRetVal = binScanfVec(&(sopasReplyBinVec[CMD_LOCATION_NAME]), binLocationNameMask, &dummy0, &dummy1,
2620  &locationNameLen);
2621  if (iRetVal > 0)
2622  {
2623  std::string s;
2624  std::string locationNameStr = binScanfGetStringFromVec(&(sopasReplyBinVec[CMD_LOCATION_NAME]), prefixLen,
2625  locationNameLen);
2626  strcpy(szLocationName, locationNameStr.c_str());
2627  }
2628  }
2629  else
2630  {
2631  if (strstr(strPtr, searchPattern) == strPtr)
2632  {
2633  const char *ptr = strPtr + strlen(searchPattern);
2634  strcpy(szLocationName, ptr);
2635  }
2636  else
2637  {
2638  ROS_WARN("Location command not supported.\n");
2639  }
2640  }
2641  rosDeclareParam(nh, "locationName", std::string(szLocationName));
2642  rosSetParam(nh, "locationName", std::string(szLocationName));
2643  }
2644  break;
2645 
2646 
2648  {
2649 
2651  }
2652  break;
2653 
2655  {
2656  bool useNegSign = false;
2657  if (NAV3xxOutputRangeSpecialHandling == 0)
2658  {
2659  useNegSign = true; // use negative phase compensation for NAV3xx
2660  }
2661 
2662  this->angleCompensator = new AngleCompensator(useNegSign);
2664  std::vector<unsigned char> tmpVec;
2665 
2666  if (useBinaryCmd == false)
2667  {
2668  for (int j = 0; j < s.length(); j++)
2669  {
2670  tmpVec.push_back((unsigned char)s[j]);
2671  }
2672  }
2673  else
2674  {
2676  }
2677  angleCompensator->parseReply(useBinaryCmd, tmpVec);
2678 
2679  ROS_INFO_STREAM("Angle Comp. Formula used: " << angleCompensator->getHumanReadableFormula());
2680  }
2681  break;
2682  // if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_2XX_NAME) == 0)
2683 
2684  // ML: add here reply handling
2685 
2687  {
2688  // Here we get the reply to "sRN LMPscancfg". We use this reply to set the scan configuration (frequency, start and stop angle)
2689  // for the following "sMN mCLsetscancfglist" and "sMN mLMPsetscancfg ..." commands
2690  int cfgListEntry = 1;
2691  // rosDeclareParam(nh, "scan_cfg_list_entry", cfgListEntry);
2692  rosGetParam(nh, "scan_cfg_list_entry", cfgListEntry);
2693  sopasCmdVec[CMD_SET_SCAN_CFG_LIST] = "\x02sMN mCLsetscancfglist " + std::to_string(cfgListEntry) + "\x03"; // set scan config from list for NAX310 LD - OEM15xx LD - LRS36xx
2694  sopasCmdVec[CMD_SET_SCANDATACONFIGNAV] = ""; // set start and stop angle by LMPscancfgToSopas()
2697  {
2698  // Overwrite start and stop angle with configured values
2699  for (int sector_cnt = 0; sector_cnt < scancfg.sector_cfg.size() && sector_cnt < scancfg.active_sector_cnt; sector_cnt++)
2700  {
2701  // Compensate angle shift (min/max angle from config in ros-coordinate system)
2702  double start_ang_rad = (scancfg.sector_cfg[sector_cnt].start_angle / 10000.0) * (M_PI / 180.0);
2703  double stop_ang_rad = (scancfg.sector_cfg[sector_cnt].stop_angle / 10000.0) * (M_PI / 180.0);
2704  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_31X_NAME) == 0) // map ros start/stop angle to NAV-3xx logic
2705  {
2706  // NAV-310 only supports min/max angles 0 to 360 degree in sensor coordinates. To avoid unexpected results, min/max angles can not be set by configuration.
2707  start_ang_rad = 0; // this->config_.min_ang;
2708  stop_ang_rad = 2 * M_PI; // this->config_.max_ang;
2709  }
2710  else
2711  {
2712  // Compensate angle shift (min/max angle from config in ros-coordinate system)
2713  double angle_offset_rad = this->parser_->getCurrentParamPtr()->getScanAngleShift();
2714  start_ang_rad = this->config_.min_ang - angle_offset_rad;
2715  stop_ang_rad = this->config_.max_ang - angle_offset_rad;
2716  }
2717  scancfg.sector_cfg[sector_cnt].start_angle = (int32_t)(std::round(10000.0 * rad2deg(start_ang_rad)));
2718  scancfg.sector_cfg[sector_cnt].stop_angle = (int32_t)(std::round(10000.0 * rad2deg(stop_ang_rad)));
2719  ROS_INFO_STREAM("Setting LMPscancfg start_angle: " << rad2deg(start_ang_rad) << " deg, stop_angle: " << rad2deg(stop_ang_rad) << " deg (lidar sector " << sector_cnt << ")");
2720  }
2721  ROS_INFO_STREAM("Setting LMPscancfg start_angle: " << rad2deg(this->config_.min_ang) << " deg, stop_angle: " << rad2deg(this->config_.max_ang) << " deg (ROS)");
2723  {
2724  ROS_INFO_STREAM("Setting LMPscancfg sopas command: \"" << sopasCmdVec[CMD_SET_SCANDATACONFIGNAV] << "\"");
2725  }
2726  else
2727  {
2728  ROS_WARN_STREAM("## ERROR in init_scanner(): SickScanParseUtil::LMPscancfgToSopas() failed, start/stop angle not set, default values will be used.");
2729  }
2730  }
2731  else
2732  {
2733  ROS_WARN_STREAM("## ERROR in init_scanner(): SickScanParseUtil::SopasToLMPscancfg() failed, start/stop angle not set, default values will be used.");
2734  }
2735  }
2736  break;
2737 
2738  case CMD_SET_SCANDATACONFIGNAV: // Parse and print the reply to "sMN mLMPsetscancfg"
2739  {
2742  }
2743  break;
2744 
2745  case CMD_GET_PARTIAL_SCAN_CFG: // Parse and print the reply to "sRN LMPscancfg"
2746  {
2749  }
2750  break;
2751 
2752  } // end of switch (cmdId)
2753 
2754 
2755  if (restartDueToProcolChange)
2756  {
2757  return ExitError;
2758 
2759  }
2760 
2761  }
2762 
2763  if (setNewIPAddr)
2764  {
2765 
2766  setNewIpAddress(ipNewIPAddr, useBinaryCmd);
2767  ROS_INFO("IP address changed. Node restart required");
2768  ROS_INFO("Exiting node NOW.");
2769  exit(0);//stopping node hard to avoide further IP-Communication
2770  }
2771 
2772 
2773  if (setUseNTP)
2774  {
2775 
2776  setNTPServerAndStart(NTPIpAdress, useBinaryCmd);
2777  }
2778 
2779  /*
2780  * The NAV310 and of course the radar does not support angular resolution handling
2781  */
2782 
2783  if (initialize_scanner == 0) // skip initialization sequence (optional for TiM781S only)
2784  {
2785  ROS_INFO_STREAM("MIN_ANG from launchfile (initialize_scanner==0): " << config_.min_ang << " [rad] " << rad2deg(this->config_.min_ang) << " [deg]");
2786  ROS_INFO_STREAM("MAX_ANG from launchfile (initialize_scanner==0): " << config_.max_ang << " [rad] " << rad2deg(this->config_.max_ang) << " [deg]");
2787  outputChannelFlagId = 0xFF; // enable output of all scan data on all channels
2788  // SAFTY FIELD PARSING
2789  result = readParseSafetyFields(useBinaryCmd);
2790  if (result != ExitSuccess)
2791  {
2792  ROS_ERROR_STREAM("## ERROR SickScanCommon::readParseSafetyFields() failed");
2793  return result;
2794  }
2795  }
2796  else if (this->parser_->getCurrentParamPtr()->getDeviceIsRadar() )
2797  {
2798  //=====================================================
2799  // Radar specific commands
2800  //=====================================================
2801  }
2802  else
2803  {
2804  //-----------------------------------------------------------------
2805  //
2806  // This is recommended to decide between TiM551 and TiM561/TiM571 capabilities
2807  // The TiM551 has an angular resolution of 1.000 [deg]
2808  // The TiM561 and TiM571 have an angular resolution of 0.333 [deg]
2809  //-----------------------------------------------------------------
2810 
2811  angleRes10000th = (int) (std::round(
2812  10000.0 * this->parser_->getCurrentParamPtr()->getAngularDegreeResolution()));
2813  std::vector<unsigned char> askOutputAngularRangeReply;
2814 
2815  // LMDscandatacfg corresponds to CMD_GET_OUTPUT_RANGES
2816  // LMDscandatacfg is not supported by NAV310
2817 
2819  {
2823  {
2824  // scanconfig handling with list now done above via sopasCmdChain,
2825  // deactivated here, otherwise the scan config will be (re-)set to default values
2826  }
2827  else // i.e. SICK_SCANNER_LRS_36x0_NAME, SICK_SCANNER_OEM_15XX_NAME
2828  {
2829  // scanconfig handling with list
2830  char requestsMNmCLsetscancfglist[MAX_STR_LEN];
2831  int cfgListEntry = 1;
2832  //rosDeclareParam(nh, "scan_cfg_list_entry", cfgListEntry);
2833  rosGetParam(nh, "scan_cfg_list_entry", cfgListEntry);
2834  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
2835  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_SCAN_CFG_LIST].c_str();
2836  sprintf(requestsMNmCLsetscancfglist, pcCmdMask, cfgListEntry);
2837  if (useBinaryCmd)
2838  {
2839  std::vector<unsigned char> reqBinary;
2840  this->convertAscii2BinaryCmd(requestsMNmCLsetscancfglist, &reqBinary);
2841  // FOR MRS6124 this should be
2842  // like this:
2843  // 0000 02 02 02 02 00 00 00 20 73 57 4e 20 4c 4d 44 73 .......sWN LMDs
2844  // 0010 63 61 6e 64 61 74 61 63 66 67 20 1f 00 01 01 00 candatacfg .....
2845  // 0020 00 00 00 00 00 00 00 01 5c
2847  }
2848  else
2849  {
2850  std::vector<unsigned char> lmdScanDataCfgReply;
2851  result = sendSopasAndCheckAnswer(requestsMNmCLsetscancfglist, &lmdScanDataCfgReply);
2852  }
2853  }
2854  }
2855  if (this->parser_->getCurrentParamPtr()->getUseWriteOutputRanges()) // else // CMD_GET_OUTPUT_RANGE
2856  {
2857  if (useBinaryCmd)
2858  {
2859  std::vector<unsigned char> reqBinary;
2860  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_GET_OUTPUT_RANGES].c_str(), &reqBinary);
2862  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_GET_OUTPUT_RANGES]); // No response, non-recoverable connection error (return error and do not try other commands)
2863  }
2864  else
2865  {
2866  result = sendSopasAndCheckAnswer(sopasCmdVec[CMD_GET_OUTPUT_RANGES].c_str(), &askOutputAngularRangeReply);
2867  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, askOutputAngularRangeReply); // No response, non-recoverable connection error (return error and do not try other commands)
2868  }
2869 
2870 
2871  if (0 == result)
2872  {
2873  int askTmpAngleRes10000th = 0;
2874  int askTmpAngleStart10000th = 0;
2875  int askTmpAngleEnd10000th = 0;
2876  char dummy0[MAX_STR_LEN] = {0};
2877  char dummy1[MAX_STR_LEN] = {0};
2878  int dummyInt = 0;
2879 
2880  int iDummy0, iDummy1;
2881  std::string askOutputAngularRangeStr = replyToString(askOutputAngularRangeReply);
2882  // Binary-Reply Tab. 63
2883  // 0x20 Space
2884  // 0x00 0x01 -
2885  // 0x00 0x00 0x05 0x14 // Resolution in 1/10000th degree --> 0.13°
2886  // 0x00 0x04 0x93 0xE0 // Start Angle 300000 -> 30°
2887  // 0x00 0x16 0xE3 0x60 // End Angle 1.500.000 -> 150° // in ROS +/-60°
2888  // 0x83 // Checksum
2889 
2890  int numArgs;
2891 
2892  if (useBinaryCmd)
2893  {
2894  iDummy0 = 0;
2895  iDummy1 = 0;
2896  dummyInt = 0;
2897  askTmpAngleRes10000th = 0;
2898  askTmpAngleStart10000th = 0;
2899  askTmpAngleEnd10000th = 0;
2900 
2901  const char *askOutputAngularRangeBinMask = "%4y%4ysRA LMPoutputRange %2y%4y%4y%4y";
2902  numArgs = binScanfVec(&sopasReplyBinVec[CMD_GET_OUTPUT_RANGES], askOutputAngularRangeBinMask, &iDummy0,
2903  &iDummy1,
2904  &dummyInt,
2905  &askTmpAngleRes10000th,
2906  &askTmpAngleStart10000th,
2907  &askTmpAngleEnd10000th);
2908  //
2909  }
2910  else
2911  {
2912  numArgs = sscanf(askOutputAngularRangeStr.c_str(), "%s %s %d %X %X %X", dummy0, dummy1,
2913  &dummyInt,
2914  &askTmpAngleRes10000th,
2915  &askTmpAngleStart10000th,
2916  &askTmpAngleEnd10000th);
2917  }
2918  if (numArgs >= 6)
2919  {
2920  double askTmpAngleRes = askTmpAngleRes10000th / 10000.0;
2921  double askTmpAngleStart = askTmpAngleStart10000th / 10000.0;
2922  double askTmpAngleEnd = askTmpAngleEnd10000th / 10000.0;
2923 
2924  angleRes10000th = askTmpAngleRes10000th;
2925  ROS_INFO_STREAM("Angle resolution of scanner is " << askTmpAngleRes << " [deg] (in 1/10000th deg: "
2926  << askTmpAngleRes10000th << ")");
2928  "[From:To] " << askTmpAngleStart << " [deg] to " << askTmpAngleEnd << " [deg] (in 1/10000th deg: from "
2929  << askTmpAngleStart10000th << " to " << askTmpAngleEnd10000th << ")");
2930  }
2931  }
2932 
2933  //-----------------------------------------------------------------
2934  //
2935  // Set Min- und Max scanning angle given by config
2936  //
2937  //-----------------------------------------------------------------
2938 
2939  ROS_INFO_STREAM("MIN_ANG: " << config_.min_ang << " [rad] " << rad2deg(this->config_.min_ang) << " [deg]");
2940  ROS_INFO_STREAM("MAX_ANG: " << config_.max_ang << " [rad] " << rad2deg(this->config_.max_ang) << " [deg]");
2941 
2942  // convert to 10000th degree
2943  double minAngSopas = rad2deg(this->config_.min_ang);
2944  double maxAngSopas = rad2deg(this->config_.max_ang);
2945 
2946  minAngSopas -= rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
2947  maxAngSopas -= rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
2948  // if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_TIM_240_NAME) == 0)
2949  // {
2950  // // the TiM240 operates directly in the ros coordinate system
2951  // }
2952  // else
2953  // {
2954  // minAngSopas += 90.0;
2955  // maxAngSopas += 90.0;
2956  // }
2957 
2958  angleStart10000th = (int)(std::round(10000.0 * minAngSopas));
2959  angleEnd10000th = (int)(std::round(10000.0 * maxAngSopas));
2960  }
2961  char requestOutputAngularRange[MAX_STR_LEN];
2962  // special for LMS1000 TODO unify this
2964  {
2965  ROS_INFO("Angular start/stop settings for LMS 1000 not reliable.\n");
2966  double askAngleStart = -137.0;
2967  double askAngleEnd = +137.0;
2968 
2969  this->config_.min_ang = askAngleStart;
2970  this->config_.max_ang = askAngleEnd;
2971  }
2972  else
2973  {
2974  std::vector<unsigned char> outputAngularRangeReply;
2975 
2977  {
2978  // config is set with list entry
2979  }
2980  if (this->parser_->getCurrentParamPtr()->getUseWriteOutputRanges()) // else
2981  {
2982  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_OUTPUT_RANGES].c_str();
2983  sprintf(requestOutputAngularRange, pcCmdMask, angleRes10000th, angleStart10000th, angleEnd10000th);
2984  if (useBinaryCmd)
2985  {
2986  unsigned char tmpBuffer[255] = {0};
2987  unsigned char sendBuffer[255] = {0};
2988  UINT16 sendLen;
2989  std::vector<unsigned char> reqBinary;
2990  int iStatus = 1;
2991  // const char *askOutputAngularRangeBinMask = "%4y%4ysWN LMPoutputRange %2y%4y%4y%4y";
2992  // int askOutputAngularRangeBinLen = binScanfGuessDataLenFromMask(askOutputAngularRangeBinMask);
2993  // askOutputAngularRangeBinLen -= 8; // due to header and length identifier
2994 
2995  strcpy((char *) tmpBuffer, "WN LMPoutputRange ");
2996  unsigned short orgLen = strlen((char *) tmpBuffer);
2997  if (NAV3xxOutputRangeSpecialHandling)
2998  {
2999  colab::addIntegerToBuffer<UINT16>(tmpBuffer, orgLen, iStatus);
3000  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleRes10000th);
3001  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleStart10000th);
3002  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleEnd10000th);
3003  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleRes10000th);
3004  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleStart10000th);
3005  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleEnd10000th);
3006  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleRes10000th);
3007  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleStart10000th);
3008  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleEnd10000th);
3009  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleRes10000th);
3010  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleStart10000th);
3011  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleEnd10000th);
3012  }
3013  else
3014  {
3015  colab::addIntegerToBuffer<UINT16>(tmpBuffer, orgLen, iStatus);
3016  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleRes10000th);
3017  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleStart10000th);
3018  colab::addIntegerToBuffer<UINT32>(tmpBuffer, orgLen, angleEnd10000th);
3019  }
3020  sendLen = orgLen;
3021  colab::addFrameToBuffer(sendBuffer, tmpBuffer, &sendLen);
3022 
3023  // binSprintfVec(&reqBinary, askOutputAngularRangeBinMask, 0x02020202, askOutputAngularRangeBinLen, iStatus, angleRes10000th, angleStart10000th, angleEnd10000th);
3024 
3025  // unsigned char sickCrc = sick_crc8((unsigned char *)(&(reqBinary)[8]), reqBinary.size() - 8);
3026  // reqBinary.push_back(sickCrc);
3027  reqBinary = std::vector<unsigned char>(sendBuffer, sendBuffer + sendLen);
3028  // Here we must build a more complex binaryRequest
3029 
3030  // this->convertAscii2BinaryCmd(requestOutputAngularRange, &reqBinary);
3031  result = sendSopasAndCheckAnswer(reqBinary, &outputAngularRangeReply);
3032  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, outputAngularRangeReply); // No response, non-recoverable connection error (return error and do not try other commands)
3033  }
3034  else
3035  {
3036  result = sendSopasAndCheckAnswer(requestOutputAngularRange, &outputAngularRangeReply);
3037  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, outputAngularRangeReply); // No response, non-recoverable connection error (return error and do not try other commands)
3038  }
3039  }
3040  }
3041 
3042  //-----------------------------------------------------------------
3043  //
3044  // Get Min- und Max scanning angle from the scanner to verify angle setting and correct the config, if something went wrong.
3045  //
3046  // IMPORTANT:
3047  // Axis Orientation in ROS differs from SICK AXIS ORIENTATION!!!
3048  // In relation to a body the standard is:
3049  // x forward
3050  // y left
3051  // z up
3052  // see http://www.ros.org/reps/rep-0103.html#coordinate-frame-conventions for more details
3053  //-----------------------------------------------------------------
3054 
3056  {
3057  askOutputAngularRangeReply.clear();
3058 
3059  if (useBinaryCmd)
3060  {
3061  std::vector<unsigned char> reqBinary;
3062  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_GET_PARTIAL_SCAN_CFG].c_str(), &reqBinary);
3063  //result = sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_GET_PARTIAL_SCAN_CFG]);
3064  result = sendSopasAndCheckAnswer(reqBinary, &askOutputAngularRangeReply);
3065  }
3066  else
3067  {
3068  result = sendSopasAndCheckAnswer(sopasCmdVec[CMD_GET_PARTIAL_SCAN_CFG].c_str(), &askOutputAngularRangeReply);
3069  }
3070  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, askOutputAngularRangeReply); // No response, non-recoverable connection error (return error and do not try other commands)
3071 
3072  if (result == 0)
3073  {
3075  sick_scan_xd::SickScanParseUtil::SopasToLMPscancfg(replyToString(askOutputAngularRangeReply), scancfg);
3076 
3077  char dummy0[MAX_STR_LEN] = {0};
3078  char dummy1[MAX_STR_LEN] = {0};
3079  int dummyInt = 0;
3080  int askAngleRes10000th = 0;
3081  int askAngleStart10000th = 0;
3082  int askAngleEnd10000th = 0;
3083  int iDummy0, iDummy1=0;
3084  int numOfSectors=0;
3085  int scanFreq=0;
3086  iDummy0 = 0;
3087  iDummy1 = 0;
3088  std::string askOutputAngularRangeStr = replyToString(askOutputAngularRangeReply);
3089  int numArgs=0;
3090  // scan values
3091  if (useBinaryCmd)
3092  {
3093  const char *askOutputAngularRangeBinMask = "%4y%4ysRA LMPscancfg %4y%2y%4y%4y%4y";
3094  numArgs = binScanfVec(&askOutputAngularRangeReply, askOutputAngularRangeBinMask,
3095  &iDummy0,
3096  &iDummy1,
3097  &scanFreq,
3098  &numOfSectors,
3099  &askAngleRes10000th,
3100  &askAngleStart10000th,
3101  &askAngleEnd10000th);
3102  }
3103  else
3104  {
3105  numArgs = sscanf(askOutputAngularRangeStr.c_str(), "%s %s %X %X %X %X %X",
3106  dummy0,
3107  dummy1,
3108  &scanFreq,
3109  &numOfSectors,
3110  &askAngleRes10000th,
3111  &askAngleStart10000th,
3112  &askAngleEnd10000th);
3113  }
3114  if (numArgs >= 6)
3115  {
3116  double askTmpAngleRes = askAngleRes10000th / 10000.0;
3117  double askTmpAngleStart = askAngleStart10000th / 10000.0;
3118  double askTmpAngleEnd = askAngleEnd10000th / 10000.0;
3119 
3120  angleRes10000th = askAngleRes10000th;
3121  ROS_INFO_STREAM("Angle resolution of scanner is " << askTmpAngleRes << " [deg] (in 1/10000th deg: "
3122  << askAngleRes10000th << ")");
3123  }
3124  double askAngleRes = askAngleRes10000th / 10000.0;
3125  double askAngleStart = askAngleStart10000th / 10000.0;
3126  double askAngleEnd = askAngleEnd10000th / 10000.0;
3127 
3128  askAngleStart += rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
3129  askAngleEnd += rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
3130 
3131  // if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_TIM_240_NAME) == 0)
3132  // {
3133  // // the TiM240 operates directly in the ros coordinate system
3134  // }
3135  // else
3136  // {
3137  // askAngleStart -= 90; // angle in ROS relative to y-axis
3138  // askAngleEnd -= 90; // angle in ROS relative to y-axis
3139  // }
3140  this->config_.min_ang = askAngleStart / 180.0 * M_PI;
3141  this->config_.max_ang = askAngleEnd / 180.0 * M_PI;
3142 
3143  rosSetParam(nh, "min_ang",
3144  this->config_.min_ang); // update parameter setting with "true" values read from scanner
3145  rosGetParam(nh, "min_ang",
3146  this->config_.min_ang); // update parameter setting with "true" values read from scanner
3147  rosSetParam(nh, "max_ang",
3148  this->config_.max_ang); // update parameter setting with "true" values read from scanner
3149  rosGetParam(nh, "max_ang",
3150  this->config_.max_ang); // update parameter setting with "true" values read from scanner
3151 
3153  "MIN_ANG (after command verification): " << config_.min_ang << " [rad] " << rad2deg(this->config_.min_ang)
3154  << " [deg]");
3156  "MAX_ANG (after command verification): " << config_.max_ang << " [rad] " << rad2deg(this->config_.max_ang)
3157  << " [deg]");
3158  }
3159  }
3160  if (this->parser_->getCurrentParamPtr()->getUseWriteOutputRanges()) // else
3161  {
3162  askOutputAngularRangeReply.clear();
3163 
3164  if (useBinaryCmd)
3165  {
3166  std::vector<unsigned char> reqBinary;
3167  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_GET_OUTPUT_RANGES].c_str(), &reqBinary);
3169  }
3170  else
3171  {
3172  result = sendSopasAndCheckAnswer(sopasCmdVec[CMD_GET_OUTPUT_RANGES].c_str(), &askOutputAngularRangeReply);
3173  }
3174 
3175  if (result == 0)
3176  {
3177  char dummy0[MAX_STR_LEN] = {0};
3178  char dummy1[MAX_STR_LEN] = {0};
3179  int dummyInt = 0;
3180  int askAngleRes10000th = 0;
3181  int askAngleStart10000th = 0;
3182  int askAngleEnd10000th = 0;
3183  int iDummy0, iDummy1;
3184  iDummy0 = 0;
3185  iDummy1 = 0;
3186  std::string askOutputAngularRangeStr = replyToString(askOutputAngularRangeReply);
3187  // Binary-Reply Tab. 63
3188  // 0x20 Space
3189  // 0x00 0x01 -
3190  // 0x00 0x00 0x05 0x14 // Resolution in 1/10000th degree --> 0.13°
3191  // 0x00 0x04 0x93 0xE0 // Start Angle 300000 -> 30°
3192  // 0x00 0x16 0xE3 0x60 // End Angle 1.500.000 -> 150° // in ROS +/-60°
3193  // 0x83 // Checksum
3194 
3195  int numArgs;
3196 
3197  /*
3198  *
3199  * Initialize variables
3200  */
3201 
3202  iDummy0 = 0;
3203  iDummy1 = 0;
3204  dummyInt = 0;
3205  askAngleRes10000th = 0;
3206  askAngleStart10000th = 0;
3207  askAngleEnd10000th = 0;
3208 
3209  /*
3210  * scan values
3211  *
3212  */
3213  if (useBinaryCmd)
3214  {
3215  const char *askOutputAngularRangeBinMask = "%4y%4ysRA LMPoutputRange %2y%4y%4y%4y";
3216  numArgs = binScanfVec(&sopasReplyBinVec[CMD_GET_OUTPUT_RANGES], askOutputAngularRangeBinMask, &iDummy0,
3217  &iDummy1,
3218  &dummyInt,
3219  &askAngleRes10000th,
3220  &askAngleStart10000th,
3221  &askAngleEnd10000th);
3222  //
3223  }
3224  else
3225  {
3226  numArgs = sscanf(askOutputAngularRangeStr.c_str(), "%s %s %d %X %X %X", dummy0, dummy1,
3227  &dummyInt,
3228  &askAngleRes10000th,
3229  &askAngleStart10000th,
3230  &askAngleEnd10000th);
3231  }
3232  if (numArgs >= 6)
3233  {
3234  double askTmpAngleRes = askAngleRes10000th / 10000.0;
3235  double askTmpAngleStart = askAngleStart10000th / 10000.0;
3236  double askTmpAngleEnd = askAngleEnd10000th / 10000.0;
3237 
3238  angleRes10000th = askAngleRes10000th;
3239  ROS_INFO_STREAM("Angle resolution of scanner is " << askTmpAngleRes << " [deg] (in 1/10000th deg: "
3240  << askAngleRes10000th << ")");
3241 
3242  }
3243  double askAngleRes = askAngleRes10000th / 10000.0;
3244  double askAngleStart = askAngleStart10000th / 10000.0;
3245  double askAngleEnd = askAngleEnd10000th / 10000.0;
3246 
3247  askAngleStart += rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
3248  askAngleEnd += rad2deg(this->parser_->getCurrentParamPtr()->getScanAngleShift());
3249 
3250  // if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_TIM_240_NAME) == 0)
3251  // {
3252  // // the TiM240 operates directly in the ros coordinate system
3253  // }
3254  // else
3255  // {
3256  // askAngleStart -= 90; // angle in ROS relative to y-axis
3257  // askAngleEnd -= 90; // angle in ROS relative to y-axis
3258  // }
3259  this->config_.min_ang = askAngleStart / 180.0 * M_PI;
3260  this->config_.max_ang = askAngleEnd / 180.0 * M_PI;
3261 
3262  rosSetParam(nh, "min_ang",
3263  this->config_.min_ang); // update parameter setting with "true" values read from scanner
3264  rosGetParam(nh, "min_ang",
3265  this->config_.min_ang); // update parameter setting with "true" values read from scanner
3266  rosSetParam(nh, "max_ang",
3267  this->config_.max_ang); // update parameter setting with "true" values read from scanner
3268  rosGetParam(nh, "max_ang",
3269  this->config_.max_ang); // update parameter setting with "true" values read from scanner
3270 
3272  "MIN_ANG (after command verification): " << config_.min_ang << " [rad] " << rad2deg(this->config_.min_ang)
3273  << " [deg]");
3275  "MAX_ANG (after command verification): " << config_.max_ang << " [rad] " << rad2deg(this->config_.max_ang)
3276  << " [deg]");
3277  }
3278  }
3279  //-----------------------------------------------------------------
3280  //
3281  // Configure the data content of scan messing regarding to config.
3282  //
3283  //-----------------------------------------------------------------
3284  /*
3285  see 4.3.1 Configure the data content for the scan in the
3286  */
3287  // 1 2 3
3288  // Prepare flag array for echos
3289  // Except for the LMS5xx scanner here the mask is hard 00 see SICK Telegram listing "Telegram structure: sWN LMDscandatacfg" for details
3290 
3291  outputChannelFlagId = 0x00;
3292  for (size_t i = 0; i < outputChannelFlag.size(); i++)
3293  {
3294  outputChannelFlagId |= ((outputChannelFlag[i] == true) << i);
3295  }
3296  if (outputChannelFlagId < 1)
3297  {
3298  outputChannelFlagId = 1; // at least one channel must be set
3299  }
3301  {
3302  int filter_echos = 2;
3303  // rosDeclareParam(nh, "filter_echos", filter_echos);
3304  rosGetParam(nh, "filter_echos", filter_echos);
3305  switch (filter_echos)
3306  {
3307  default:outputChannelFlagId = 0b00000001; break;
3308  case 0: outputChannelFlagId = 0b00000001; break;
3309  case 1: outputChannelFlagId = 0b00011111; break;
3310  case 2: outputChannelFlagId = 0b00000001; break;
3311 
3312  }
3313 
3314  ROS_INFO("LMS 5xx detected overwriting output channel flag ID");
3315 
3316  ROS_INFO("LMS 5xx detected overwriting resolution flag (only 8 bit supported)");
3318  rssiResolutionIs16Bit = this->parser_->getCurrentParamPtr()->getIntensityResolutionIs16Bit();
3319  }
3321  {
3322  ROS_INFO("MRS 1xxx detected overwriting resolution flag (only 8 bit supported)");
3324  rssiResolutionIs16Bit = this->parser_->getCurrentParamPtr()->getIntensityResolutionIs16Bit();
3325 
3326  }
3327 
3328  // SAFTY FIELD PARSING
3329  result = readParseSafetyFields(useBinaryCmd);
3330  if (result != ExitSuccess)
3331  {
3332  ROS_ERROR_STREAM("## ERROR SickScanCommon::readParseSafetyFields() failed");
3333  return result;
3334  }
3335 
3336  // set scanning angle for tim5xx and for mrs1104
3337  if ((this->parser_->getCurrentParamPtr()->getNumberOfLayers() == 1)
3338  || (this->parser_->getCurrentParamPtr()->getNumberOfLayers() == 4)
3339  || (this->parser_->getCurrentParamPtr()->getNumberOfLayers() == 24)
3340  )
3341  {
3342  if (false==this->parser_->getCurrentParamPtr()->getUseScancfgList())
3343  {
3344  // Timing flag LMDscandatacfg (LMS-1XX, LMS-1XXX, LMS-4XXX, LMS-5XX, MRS-1XXX, MRS-6XXX, NAV-2XX, TIM-240, TIM-4XX, TIM-5XX, TIM-7XX, TIM-7XXS):
3345  // -1: use default (i.e. off for TiM-240, otherwise on), 0: do not send time information, 1: send time information
3346  int scandatacfg_timingflag = -1;
3347  rosDeclareParam(nh, "scandatacfg_timingflag", scandatacfg_timingflag);
3348  rosGetParam(nh, "scandatacfg_timingflag", scandatacfg_timingflag);
3349  if (scandatacfg_timingflag < 0)
3350  {
3352  scandatacfg_timingflag = 0; // default for TiM-240: Timing flag LMDscandatacfg off
3353  else
3354  scandatacfg_timingflag = 1; // default: Timing flag LMDscandatacfg on
3355  }
3356  int rssi_flag = rssiFlag ? 1 : 0;
3358  {
3359  int scandatacfg_azimuth_table = 0;
3360  rosDeclareParam(nh, "scandatacfg_azimuth_table", scandatacfg_azimuth_table);
3361  rosGetParam(nh, "scandatacfg_azimuth_table", scandatacfg_azimuth_table);
3362  if (scandatacfg_azimuth_table != 0)
3363  rssi_flag |= 0x2; // set (enable) "transmit angle flag" for MRS-1xxx
3364  ROS_INFO_STREAM("MRS1xxx scandatacfg_azimuth_table=" << scandatacfg_azimuth_table << ", rssi_flag=" << rssi_flag << ", azimuth table " << ((rssi_flag & 0x02) != 0 ? "": "not ") << "activated");
3365  }
3366 
3367  //normal scanconfig handling
3368  char requestLMDscandatacfg[MAX_STR_LEN];
3369  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
3370  // sopasCmdMaskVec[CMD_SET_PARTIAL_SCANDATA_CFG] = "\x02sWN LMDscandatacfg %d 0 %d %d 0 %d 0 0 0 0 %d 1\x03"; // outputChannelFlagId, rssiFlag, rssiResolutionIs16Bit, EncoderSettings, timingflag
3371  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_PARTIAL_SCANDATA_CFG].c_str();
3372  sprintf(requestLMDscandatacfg, pcCmdMask, outputChannelFlagId, rssi_flag,
3373  rssiResolutionIs16Bit ? 1 : 0,
3374  EncoderSettings > 0 ? 1 : 0,
3375  scandatacfg_timingflag);
3376  if (useBinaryCmd)
3377  {
3378  std::vector<unsigned char> reqBinary;
3379  this->convertAscii2BinaryCmd(requestLMDscandatacfg, &reqBinary);
3380  // FOR MRS6124 this should be
3381  // like this:
3382  // 0000 02 02 02 02 00 00 00 20 73 57 4e 20 4c 4d 44 73 .......sWN LMDs
3383  // 0010 63 61 6e 64 61 74 61 63 66 67 20 1f 00 01 01 00 candatacfg .....
3384  // 0020 00 00 00 00 00 00 00 01 5c
3386  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_SET_PARTIAL_SCANDATA_CFG]); // No response, non-recoverable connection error (return error and do not try other commands)
3387  }
3388  else
3389  {
3390  std::vector<unsigned char> lmdScanDataCfgReply;
3391  result = sendSopasAndCheckAnswer(requestLMDscandatacfg, &lmdScanDataCfgReply);
3392  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmdScanDataCfgReply); // No response, non-recoverable connection error (return error and do not try other commands)
3393  }
3394  }
3395  else
3396  {
3397 
3398  }
3399 
3400  // check setting
3401  char requestLMDscandatacfgRead[MAX_STR_LEN];
3402  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
3403 
3404  strcpy(requestLMDscandatacfgRead, sopasCmdVec[CMD_GET_PARTIAL_SCANDATA_CFG].c_str());
3405  if (useBinaryCmd)
3406  {
3407  std::vector<unsigned char> reqBinary;
3408  this->convertAscii2BinaryCmd(requestLMDscandatacfgRead, &reqBinary);
3410  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_GET_PARTIAL_SCANDATA_CFG]); // No response, non-recoverable connection error (return error and do not try other commands)
3411  }
3412  else
3413  {
3414  std::vector<unsigned char> lmdScanDataCfgReadReply;
3415  result = sendSopasAndCheckAnswer(requestLMDscandatacfgRead, &lmdScanDataCfgReadReply);
3416  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmdScanDataCfgReadReply); // No response, non-recoverable connection error (return error and do not try other commands)
3417  }
3418 
3419 
3420  }
3421  //BBB
3422  // set scanning angle for lms1xx and lms5xx
3423  double scan_freq = 0;
3424  double ang_res = 0;
3425  rosDeclareParam(nh, "scan_freq", scan_freq);
3426  rosGetParam(nh, "scan_freq", scan_freq);
3427  rosDeclareParam(nh, "ang_res", ang_res);
3428  rosGetParam(nh, "ang_res", ang_res);
3429  if (scan_freq != 0 || ang_res != 0)
3430  {
3431  if (scan_freq != 0 && ang_res != 0)
3432  {
3433  if (this->parser_->getCurrentParamPtr()->getUseScancfgList() == true)
3434  {
3435  ROS_INFO("variable ang_res and scan_freq setings for OEM15xx NAV 3xx or LRD-36XX has not been implemented");
3436  }
3437  else
3438  {
3440  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_1XXX_NAME) == 0
3441  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_5XX_NAME) == 0
3442  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_MRS_1XXX_NAME) == 0)
3443  {
3444  // "sMN mLMPsetscancfg" for lms1xx and lms5xx:
3445  // scan frequencies lms1xx: 25 or 50 Hz, lms5xx: 25, 35, 50, 75 or 100 Hz
3446  // Number of active sectors: 1 for lms1xx and lms5xx
3447  // Angular resolution: 0.25 or 0.5 deg for lms1xx, 0.0417, 0.083, 0.1667, 0.25, 0.333, 0.5, 0.667 or 1.0 deg for lms5xx, angular resolution in 1/10000 deg
3448  // Start angle: -45 deg for lms1xx, -5 deg for lms5xx in lidar coordinates
3449  // Stop angle: +225 deg for lms1xx, +185 deg for lms5xx in lidar coordinates
3452  lmp_scancfg.scan_frequency = std::lround(100.0 * scan_freq);
3453  lmp_scancfg.active_sector_cnt = 1; // this->parser_->getCurrentParamPtr()->getNumberOfLayers();
3454  lmp_scancfg_sector.angular_resolution = std::lround(10000.0 * ang_res);
3455 
3457  {
3458  lmp_scancfg_sector.start_angle = -475000;
3459  lmp_scancfg_sector.stop_angle = +2275000;
3460  }
3461  else if(this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_1XX_NAME) == 0)
3462  {
3463  lmp_scancfg_sector.start_angle = -450000;
3464  lmp_scancfg_sector.stop_angle = +2250000;
3465  }
3466  else if(this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_1XXX_NAME) == 0)
3467  {
3468  lmp_scancfg_sector.start_angle = -480000;
3469  lmp_scancfg_sector.stop_angle = +2280000;
3470  }
3471  else if(this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_5XX_NAME) == 0)
3472  {
3473  double angle_offset_rad = this->parser_->getCurrentParamPtr()->getScanAngleShift();
3474  double start_ang_rad = this->config_.min_ang - angle_offset_rad;
3475  double stop_ang_rad = this->config_.max_ang - angle_offset_rad;
3476 
3477  int32_t start_angle_in_10000th = (int32_t)(std::round(10000.0 * rad2deg(start_ang_rad)));
3478  int32_t stop_angle_in_10000th = (int32_t)(std::round(10000.0 * rad2deg(stop_ang_rad)));
3479 
3480  ROS_INFO_STREAM("Prepare mLMPsetscancfg: Start Angle in 10000th deg in lidar notation: " << start_angle_in_10000th);
3481  ROS_INFO_STREAM("Prepare mLMPsetscancfg: Stop Angle in 10000th deg in lidar notation : " << stop_angle_in_10000th);
3482 
3483  lmp_scancfg_sector.start_angle = start_angle_in_10000th; // -5 [deg]
3484  lmp_scancfg_sector.stop_angle = stop_angle_in_10000th; // +185 [deg]
3485  }
3487  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_5XX_NAME) == 0)
3488  {
3489  result = sendSopasRunSetAccessMode(useBinaryCmd);
3490  }
3491  lmp_scancfg.sector_cfg.push_back(lmp_scancfg_sector);
3492  std::string lmp_scancfg_sopas;
3493  if (sick_scan_xd::SickScanParseUtil::LMPscancfgToSopas(lmp_scancfg, lmp_scancfg_sopas))
3494  {
3495  ROS_INFO_STREAM("Sending mLMPsetscancfg request: { " << lmp_scancfg.print() << " }");
3496  std::vector<unsigned char> reqBinary, lmp_scancfg_reply;
3497  if (useBinaryCmd)
3498  {
3499  this->convertAscii2BinaryCmd(lmp_scancfg_sopas.c_str(), &reqBinary);
3500  result = sendSopasAndCheckAnswer(reqBinary, &lmp_scancfg_reply);
3501  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmp_scancfg_reply); // No response, non-recoverable connection error (return error and do not try other commands)
3502  }
3503  else
3504  {
3505  result = sendSopasAndCheckAnswer(lmp_scancfg_sopas, &lmp_scancfg_reply);
3506  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmp_scancfg_reply); // No response, non-recoverable connection error (return error and do not try other commands)
3507  }
3508  std::string sopasReplyString = replyToString(lmp_scancfg_reply);
3509  if (strncmp(sopasReplyString.c_str(), "sAN mLMPsetscancfg ", 19) == 0)
3510  {
3512  sick_scan_xd::SickScanParseUtil::SopasToLMPscancfg(sopasReplyString, scancfg_response);
3513  ROS_INFO_STREAM("sAN mLMPsetscancfg: scan frequency = " << (scancfg_response.scan_frequency/100.0) << " Hz, angular resolution = "
3514  << (scancfg_response.sector_cfg.size() > 0 ? (scancfg_response.sector_cfg[0].angular_resolution / 10000.0) : -1.0) << " deg.");
3515  }
3516  }
3517  else
3518  {
3519  ROS_WARN_STREAM("sick_scan_xd::init_scanner: sick_scan_xd::SickScanParseUtil::LMPscancfgToSopas() failed");
3520  }
3521  }
3522  else
3523  {
3524  ROS_WARN_STREAM("sick_scan_xd::init_scanner: \"sMN mLMPsetscancfg\" currently not supported for "
3526  << ", scan frequency and angular resolution not set, using default values ("
3527  << __FILE__ << ":" << __LINE__ << ")");
3528  }
3529  /* previous version:
3530  char requestLMDscancfg[MAX_STR_LEN];
3531  // sopasCmdMaskVec[CMD_SET_PARTIAL_SCAN_CFG] = "\x02sMN mLMPsetscancfg %d 1 %d 0 0\x03";//scanfreq [1/100 Hz],angres [1/10000°],
3532  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_PARTIAL_SCAN_CFG].c_str();
3533  sprintf(requestLMDscancfg, pcCmdMask, (long) (scan_freq * 100 + 1e-9), (long) (ang_res * 10000 + 1e-9));
3534  if (useBinaryCmd)
3535  {
3536  std::vector<unsigned char> reqBinary;
3537  this->convertAscii2BinaryCmd(requestLMDscancfg, &reqBinary);
3538  result = sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_PARTIAL_SCAN_CFG]);
3539  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_SET_PARTIAL_SCAN_CFG]); // No response, non-recoverable connection error (return error and do not try other commands)
3540  }
3541  else
3542  {
3543  std::vector<unsigned char> lmdScanCfgReply;
3544  result = sendSopasAndCheckAnswer(requestLMDscancfg, &lmdScanCfgReply);
3545  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmdScanCfgReply); // No response, non-recoverable connection error (return error and do not try other commands)
3546  }
3547  */
3548 
3549  // check setting
3550  char requestLMDscancfgRead[MAX_STR_LEN];
3551  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
3552 
3553  strcpy(requestLMDscancfgRead, sopasCmdVec[CMD_GET_PARTIAL_SCAN_CFG].c_str());
3554  if (useBinaryCmd)
3555  {
3556  std::vector<unsigned char> reqBinary;
3557  this->convertAscii2BinaryCmd(requestLMDscancfgRead, &reqBinary);
3559  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_GET_PARTIAL_SCAN_CFG]); // No response, non-recoverable connection error (return error and do not try other commands)
3560  std::string sopasReplyString = replyToString(sopasReplyBinVec[CMD_GET_PARTIAL_SCAN_CFG]);
3561  if (strncmp(sopasReplyString.c_str(), "sRA LMPscancfg ", 15) == 0)
3562  {
3564  sick_scan_xd::SickScanParseUtil::SopasToLMPscancfg(sopasReplyString, scancfg_response);
3565  ROS_INFO_STREAM("sRA LMPscancfg: scan frequency = " << (scancfg_response.scan_frequency/100.0) << " Hz, angular resolution = "
3566  << (scancfg_response.sector_cfg.size() > 0 ? (scancfg_response.sector_cfg[0].angular_resolution / 10000.0) : -1.0) << " deg.");
3567  }
3568  }
3569  else
3570  {
3571  std::vector<unsigned char> lmdScanDataCfgReadReply;
3572  result = sendSopasAndCheckAnswer(requestLMDscancfgRead, &lmdScanDataCfgReadReply);
3573  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, lmdScanDataCfgReadReply); // No response, non-recoverable connection error (return error and do not try other commands)
3574  }
3575 
3576  }
3577  }
3578  else
3579  {
3580  ROS_ERROR("ang_res and scan_freq have to be set, only one param is set skiping scan_fre/ang_res config");
3581  }
3582  }
3583  // Config Mean filter
3584  /*
3585  char requestMeanSetting[MAX_STR_LEN];
3586  int meanFilterSetting = 0;
3587  int MeanFilterActive=0;
3588  pn.getParam("mean_filter", meanFilterSetting); // filter_echos
3589  if(meanFilterSetting>2)
3590  {
3591  MeanFilterActive=1;
3592  }
3593  else{
3594  //needs to be at leas 2 even if filter is disabled
3595  meanFilterSetting = 2;
3596  }
3597  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
3598  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_MEAN_FILTER].c_str();
3599 
3600  //First echo : 0
3601  //All echos : 1
3602  //Last echo : 2
3603 
3604  sprintf(requestMeanSetting, pcCmdMask, MeanFilterActive, meanFilterSetting);
3605  std::vector<unsigned char> outputFilterMeanReply;
3606 
3607 
3608  if (useBinaryCmd)
3609  {
3610  std::vector<unsigned char> reqBinary;
3611  this->convertAscii2BinaryCmd(requestMeanSetting, &reqBinary);
3612  result = sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_ECHO_FILTER]);
3613  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_SET_ECHO_FILTER]); // No response, non-recoverable connection error (return error and do not try other commands)
3614  }
3615  else
3616  {
3617  result = sendSopasAndCheckAnswer(requestMeanSetting, &outputFilterMeanReply);
3618  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, outputFilterMeanReply); // No response, non-recoverable connection error (return error and do not try other commands)
3619  }
3620  */
3621 
3622  // CONFIG ECHO-Filter (only for MRS1xxx and MRS6xxx, not available for TiM5xx)
3623  //if (this->parser_->getCurrentParamPtr()->getNumberOfLayers() >= 4)
3625  {
3626  char requestEchoSetting[MAX_STR_LEN];
3627  int filterEchoSetting = 2;
3628  rosDeclareParam(nh, "filter_echos", filterEchoSetting); // filter_echos
3629  rosGetParam(nh, "filter_echos", filterEchoSetting); // filter_echos
3630 
3631  if (filterEchoSetting < 0)
3632  { filterEchoSetting = 0; }
3633  if (filterEchoSetting > 2)
3634  { filterEchoSetting = 2; }
3635  // Uses sprintf-Mask to set bitencoded echos and rssi enable flag
3636  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_ECHO_FILTER].c_str();
3637  /*
3638  First echo : 0
3639  All echos : 1
3640  Last echo : 2
3641  */
3642  sprintf(requestEchoSetting, pcCmdMask, filterEchoSetting);
3643  std::vector<unsigned char> outputFilterEchoRangeReply;
3644 
3645 
3646  if (useBinaryCmd)
3647  {
3648  std::vector<unsigned char> reqBinary;
3649  this->convertAscii2BinaryCmd(requestEchoSetting, &reqBinary);
3651  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReplyBinVec[CMD_SET_ECHO_FILTER]); // No response, non-recoverable connection error (return error and do not try other commands)
3652  }
3653  else
3654  {
3655  result = sendSopasAndCheckAnswer(requestEchoSetting, &outputFilterEchoRangeReply);
3656  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, outputFilterEchoRangeReply); // No response, non-recoverable connection error (return error and do not try other commands)
3657  }
3658 
3659  }
3660 
3661 
3662  }
3664 
3665 
3666  //-----------------------------------------------------------------
3667  //
3668  // Start sending LMD-Scandata messages
3669  //
3670  //-----------------------------------------------------------------
3671  std::vector<int> startProtocolSequence;
3672  if (initialize_scanner == 0) // skip initialization sequence (optional for TiM781S only)
3673  {
3674  startProtocolSequence.push_back(CMD_START_SCANDATA); // otherwise the lidar does not send LMDscandata
3675  }
3676  else if (this->parser_->getCurrentParamPtr()->getDeviceIsRadar())
3677  {
3678  bool activate_lidoutputstate = false;
3679  bool transmitRawTargets = true;
3680  bool transmitObjects = true;
3681  int trackingMode = 0;
3682  std::string trackingModeDescription[] = {"BASIC", "VEHICLE"};
3683 
3684  int numTrackingModes = sizeof(trackingModeDescription) / sizeof(trackingModeDescription[0]);
3685 
3686  rosDeclareParam(nh, "transmit_raw_targets", transmitRawTargets);
3687  rosGetParam(nh, "transmit_raw_targets", transmitRawTargets);
3688 
3689  rosDeclareParam(nh, "transmit_objects", transmitObjects);
3690  rosGetParam(nh, "transmit_objects", transmitObjects);
3691 
3692  rosDeclareParam(nh, "tracking_mode", trackingMode);
3693  rosGetParam(nh, "tracking_mode", trackingMode);
3694 
3695  rosDeclareParam(nh, "activate_lidoutputstate", activate_lidoutputstate);
3696  rosGetParam(nh, "activate_lidoutputstate", activate_lidoutputstate);
3697 
3698  ROS_INFO_STREAM("LIDoutputstate messages are switched [" << (activate_lidoutputstate ? "ON" : "OFF") << "]");
3699  ROS_INFO_STREAM("Raw target transmission is switched [" << (transmitRawTargets ? "ON" : "OFF") << "]");
3700  ROS_INFO_STREAM("Object transmission is switched [" << (transmitObjects ? "ON" : "OFF") << "]");
3701  ROS_INFO_STREAM("Tracking mode [" << trackingMode << "] [" << ((trackingMode >= 0 && trackingMode < numTrackingModes) ? trackingModeDescription[trackingMode] : "unknown") << "]");
3702 
3703  // Asking some informational from the radar
3704  startProtocolSequence.push_back(CMD_DEVICE_TYPE);
3705  startProtocolSequence.push_back(CMD_SERIAL_NUMBER);
3706  startProtocolSequence.push_back(CMD_ORDER_NUMBER);
3707 
3708  // Optionally activate LIDoutputstate messages
3709  if (activate_lidoutputstate)
3710  {
3711  startProtocolSequence.push_back(CMD_SET_LID_OUTPUTSTATE_ACTIVE);
3712  }
3713 
3714  /*
3715  * With "sWN TCTrackingMode 0" BASIC-Tracking activated
3716  * With "sWN TCTrackingMode 1" TRAFFIC-Tracking activated
3717  *
3718  */
3719  if (transmitRawTargets)
3720  {
3721  startProtocolSequence.push_back(CMD_SET_TRANSMIT_RAWTARGETS_ON); // raw targets will be transmitted
3722  }
3723  else
3724  {
3725  startProtocolSequence.push_back(CMD_SET_TRANSMIT_RAWTARGETS_OFF); // NO raw targets will be transmitted
3726  }
3727 
3728  if (transmitObjects)
3729  {
3730  startProtocolSequence.push_back(CMD_SET_TRANSMIT_OBJECTS_ON); // tracking objects will be transmitted
3731  }
3732  else
3733  {
3734  startProtocolSequence.push_back(CMD_SET_TRANSMIT_OBJECTS_OFF); // NO tracking objects will be transmitted
3735  }
3736  if (!transmitRawTargets && !transmitObjects)
3737  {
3738  ROS_WARN("Both ObjectData and TargetData are disabled. Check launchfile, parameter \"transmit_raw_targets\" or \"transmit_objects\" or both should be activated.");
3739  }
3740 
3741  switch (trackingMode)
3742  {
3743  case 0:
3744  startProtocolSequence.push_back(CMD_SET_TRACKING_MODE_0);
3745  break;
3746  case 1:
3747  startProtocolSequence.push_back(CMD_SET_TRACKING_MODE_1);
3748  break;
3749  default:
3750  ROS_DEBUG("Tracking mode switching sequence unknown\n");
3751  break;
3752  }
3753  // leave user level
3754 
3755  // sWN TransmitTargets 1
3756  // initializing sequence for radar based devices
3757  startProtocolSequence.push_back(CMD_RUN); // leave user level
3758  startProtocolSequence.push_back(CMD_START_RADARDATA);
3759  }
3760  else
3761  {
3763  {
3764 
3765  // Activate LFErec, LIDoutputstate and LIDinputstate messages
3766  bool activate_lferec = true, activate_lidoutputstate = true, activate_lidinputstate = true;
3767  rosDeclareParam(nh, "activate_lferec", activate_lferec);
3768  rosDeclareParam(nh, "activate_lidoutputstate", activate_lidoutputstate);
3769  rosDeclareParam(nh, "activate_lidinputstate", activate_lidinputstate);
3770  if (true == rosGetParam(nh, "activate_lferec", activate_lferec) && true == activate_lferec)
3771  {
3772  startProtocolSequence.push_back(CMD_SET_LFEREC_ACTIVE); // TiM781S: activate LFErec messages, send "sEN LFErec 1"
3773  ROS_INFO_STREAM(parser_->getCurrentParamPtr()->getScannerName() << ": activating field monitoring by lferec messages");
3774  }
3775  if (true == rosGetParam(nh, "activate_lidoutputstate", activate_lidoutputstate) && true == activate_lidoutputstate)
3776  {
3777  startProtocolSequence.push_back(CMD_SET_LID_OUTPUTSTATE_ACTIVE); // TiM781S: activate LIDoutputstate messages, send "sEN LIDoutputstate 1"
3778  ROS_INFO_STREAM(parser_->getCurrentParamPtr()->getScannerName() << ": activating field monitoring by lidoutputstate messages");
3779  }
3780  if (true == rosGetParam(nh, "activate_lidinputstate", activate_lidinputstate) && true == activate_lidinputstate)
3781  {
3782  startProtocolSequence.push_back(CMD_SET_LID_INPUTSTATE_ACTIVE); // TiM781S: activate LIDinputstate messages, send "sEN LIDinputstate 1"
3783  ROS_INFO_STREAM(parser_->getCurrentParamPtr()->getScannerName() << ": activating field monitoring by lidinputstate messages");
3784  }
3785  }
3786 
3787  // initializing sequence for laserscanner
3788  // is this device a TiM240????
3789  // The TiM240 can not interpret CMD_START_MEASUREMENT
3791  {
3792  // the TiM240 operates directly in the ros coordinate system
3793  // do nothing for a TiM240
3794  startProtocolSequence.push_back(CMD_RUN); // leave user level
3795  startProtocolSequence.push_back(CMD_START_SCANDATA);
3796  }
3797  else if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_350_NAME) == 0)
3798  {
3799  // The NAV-350 does not support CMD_START_MEASUREMENT
3800  }
3801  else
3802  {
3803  startProtocolSequence.push_back(CMD_START_MEASUREMENT);
3804  startProtocolSequence.push_back(CMD_RUN); // leave user level
3805  startProtocolSequence.push_back(CMD_START_SCANDATA);
3806  }
3807 
3808  if (this->parser_->getCurrentParamPtr()->getNumberOfLayers() == 4) // MRS1104: start IMU-Transfer
3809  {
3810  bool imu_enable = false;
3811  rosDeclareParam(nh, "imu_enable", imu_enable);
3812  rosGetParam(nh, "imu_enable", imu_enable);
3813  if (imu_enable)
3814  {
3815  if (useBinaryCmdNow == true)
3816  {
3817  ROS_INFO("Enable IMU data transfer");
3818  // TODO Flag to decide between IMU on or off
3819  startProtocolSequence.push_back(CMD_START_IMU_DATA);
3820  }
3821  else
3822  {
3823  ROS_FATAL(
3824  "IMU USAGE NOT POSSIBLE IN ASCII COMMUNICATION MODE.\nTo use the IMU the communication with the scanner must be set to binary mode.\n This can be done by inserting the line:\n<param name=\"use_binary_protocol\" type=\"bool\" value=\"True\" />\n into the launchfile.\n See also https://github.com/SICKAG/sick_scan_xd/blob/master/doc/IMU.md");
3825  exit(0);
3826  }
3827  }
3828  else
3829  {
3830  ROS_INFO("IMU data transfer not enabled");
3831  }
3832  }
3833  }
3834 
3835  std::vector<int>::iterator it;
3836  for (it = startProtocolSequence.begin(); it != startProtocolSequence.end(); it++)
3837  {
3838  int cmdId = *it;
3839 
3840  std::string sopasCmd = sopasCmdVec[cmdId];
3841  std::vector<unsigned char> replyDummy;
3842  std::vector<unsigned char> reqBinary;
3843  std::vector<unsigned char> sopasVec;
3844  sopasVec = stringToVector(sopasCmd);
3845  ROS_DEBUG_STREAM("Command: " << stripControl(sopasVec));
3846  if (useBinaryCmd)
3847  {
3848  this->convertAscii2BinaryCmd(sopasCmd.c_str(), &reqBinary);
3849  result = sendSopasAndCheckAnswer(reqBinary, &replyDummy, cmdId);
3850  sopasReplyBinVec[cmdId] = replyDummy;
3851  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, replyDummy); // No response, non-recoverable connection error (return error and do not try other commands)
3852  }
3853  else
3854  {
3855  result = sendSopasAndCheckAnswer(sopasCmd.c_str(), &replyDummy, cmdId);
3856  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, replyDummy); // No response, non-recoverable connection error (return error and do not try other commands)
3857  }
3858 
3859  if (result != 0)
3860  {
3862 #ifdef USE_DIAGNOSTIC_UPDATER
3863  if(diagnostics_)
3864  diagnostics_->broadcast(getDiagnosticErrorCode(), sopasCmdErrMsg[cmdId]);
3865 #endif
3866  }
3867  else
3868  {
3869  sopasReplyStrVec[cmdId] = replyToString(replyDummy);
3870  }
3871 
3872 
3873  if (cmdId == CMD_START_RADARDATA)
3874  {
3875  ROS_DEBUG("Starting radar data ....\n");
3876  }
3877 
3878 
3879  if (cmdId == CMD_START_SCANDATA)
3880  {
3881  ROS_DEBUG("Starting scan data ....\n");
3882  }
3883 
3884  if (cmdId == CMD_RUN)
3885  {
3886  bool waitForDeviceState = this->parser_->getCurrentParamPtr()->getWaitForReady();
3887 
3888  if (waitForDeviceState)
3889  {
3890  int maxWaitForDeviceStateReady = 45; // max. 30 sec. (see manual)
3891  bool scannerReady = false;
3892  for (int i = 0; i < maxWaitForDeviceStateReady; i++)
3893  {
3894  double shortSleepTime = 1.0;
3895  std::string sopasDeviceStateCmd = sopasCmdVec[CMD_DEVICE_STATE];
3896  std::vector<unsigned char> replyDummyDeviceState;
3897 
3898  int iRetVal = 0;
3899  int deviceState = 0;
3900 
3901  std::vector<unsigned char> reqBinaryDeviceState;
3902  std::vector<unsigned char> sopasVecDeviceState;
3903  sopasVecDeviceState = stringToVector(sopasDeviceStateCmd);
3904  ROS_DEBUG_STREAM("Command: " << stripControl(sopasVecDeviceState));
3905  if (useBinaryCmd)
3906  {
3907  this->convertAscii2BinaryCmd(sopasDeviceStateCmd.c_str(), &reqBinaryDeviceState);
3908  result = sendSopasAndCheckAnswer(reqBinaryDeviceState, &replyDummyDeviceState);
3909  sopasReplyBinVec[CMD_DEVICE_STATE] = replyDummyDeviceState;
3910  }
3911  else
3912  {
3913  result = sendSopasAndCheckAnswer(sopasDeviceStateCmd.c_str(), &replyDummyDeviceState);
3914  sopasReplyStrVec[CMD_DEVICE_STATE] = replyToString(replyDummyDeviceState);
3915  }
3916  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, replyDummyDeviceState); // No response, non-recoverable connection error (return error and do not try other commands)
3917 
3918 
3919  if (useBinaryCmd)
3920  {
3921  long dummy0, dummy1;
3922  dummy0 = 0;
3923  dummy1 = 0;
3924  deviceState = 0;
3925  iRetVal = binScanfVec(&(sopasReplyBinVec[CMD_DEVICE_STATE]), "%4y%4ysRA SCdevicestate %1y", &dummy0,
3926  &dummy1, &deviceState);
3927  }
3928  else
3929  {
3930  iRetVal = sscanf(sopasReplyStrVec[CMD_DEVICE_STATE].c_str(), "sRA SCdevicestate %d", &deviceState);
3931  }
3932  if (iRetVal > 0) // 1 or 3 (depending of ASCII or Binary)
3933  {
3934  if (deviceState == 1) // scanner is ready
3935  {
3936  scannerReady = true; // interrupt waiting for scanner ready
3937  ROS_INFO_STREAM("Scanner ready for measurement after " << i << " [sec]");
3938  break;
3939  }
3940  }
3941  ROS_INFO_STREAM("Waiting for scanner ready state since " << i << " secs");
3942  rosSleep(shortSleepTime);
3943 
3944  if (scannerReady)
3945  {
3946  break;
3947  }
3948  if(i==(maxWaitForDeviceStateReady-1))
3949  {
3950  ROS_INFO_STREAM("TIMEOUT WHILE STARTING SCANNER " << i);
3951  return ExitError;
3952  }
3953  }
3954  }
3955  }
3956 
3957  if (this->parser_->getCurrentParamPtr()->getDeviceIsRadar() && cmdId == CMD_DEVICE_TYPE)
3958  {
3959  std::string device_type_response = sopasReplyStrVec[cmdId], rms_type_str = "";
3960  size_t cmd_type_idx = device_type_response.find("DItype ");
3961  if (cmd_type_idx != std::string::npos)
3962  device_type_response = device_type_response.substr(cmd_type_idx + 7);
3963  size_t radar_type_idx = device_type_response.find("RMS");
3964  if (radar_type_idx != std::string::npos)
3965  rms_type_str = device_type_response.substr(radar_type_idx, 4);
3966  ROS_INFO_STREAM("Radar device type response: \"" << sopasReplyStrVec[cmdId] << "\" -> device type = \"" << device_type_response << "\" (" << rms_type_str << ")");
3967  // Detect and switch between RMS-1xxx and RMS-2xxx
3968  if (rms_type_str == "RMS1")
3969  {
3970  this->parser_->getCurrentParamPtr()->setDeviceIsRadar(RADAR_1D); // Device is a 1D radar
3971  this->parser_->getCurrentParamPtr()->setTrackingModeSupported(false); // RMS 1xxx does not support selection of tracking modes
3972  for (std::vector<int>::iterator start_cmd_iter = startProtocolSequence.begin(); start_cmd_iter != startProtocolSequence.end(); start_cmd_iter++)
3973  {
3974  if (*start_cmd_iter == CMD_SET_TRACKING_MODE_0 || *start_cmd_iter == CMD_SET_TRACKING_MODE_1)
3975  *start_cmd_iter = CMD_DEVICE_STATE; // Disable unsupported set tracking mode commands by overwriting with "sRN SCdevicestate"
3976  }
3977  ROS_INFO_STREAM("1D radar \"" << rms_type_str << "\" device detected, tracking mode disabled");
3978  }
3979  else if (rms_type_str == "RMS2")
3980  {
3981  this->parser_->getCurrentParamPtr()->setDeviceIsRadar(RADAR_3D); // Device is a 3D radar
3982  this->parser_->getCurrentParamPtr()->setTrackingModeSupported(true); // Default
3983  ROS_INFO_STREAM("3D radar \"" << rms_type_str << "\" device detected, tracking mode enabled");
3984  }
3985  else
3986  {
3987  ROS_ERROR_STREAM("## ERROR init_scanner(): Unexpected device type \"" << device_type_response << "\", expected device type starting with \"RMS1\" or \"RMS2\"");
3988  }
3989  }
3990  }
3991 
3992  if (this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_350_NAME) == 0) // TODO: move to sick_nav_init.cpp...
3993  {
3995  ROS_ERROR_STREAM("## ERROR NAV350: parseNAV350BinaryUnittest() failed.");
3996  std::vector<unsigned char> sopas_response;
3997  // NAV-350 initialization sequence
3998  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_ACCESS_MODE_3], &sopas_response, useBinaryCmd) != 0) // re-enter authorized client level
3999  return ExitError;
4000  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_1], &sopas_response, useBinaryCmd) != 0) // "sMN mNEVAChangeState 1", 1 = standby
4001  return ExitError;
4002  if (get2ndSopasResponse(sopas_response, "mNEVAChangeState") != ExitSuccess)
4003  return ExitError;
4004  // Set the current NAV Layer for Positioning and Mapping
4005  int nav_curr_layer = 0;
4006  rosDeclareParam(nh, "nav_curr_layer", nav_curr_layer);
4007  rosGetParam(nh, "nav_curr_layer", nav_curr_layer);
4008  sopasCmdVec[CMD_SET_NAV_CURR_LAYER] = std::string("\x02sWN NEVACurrLayer ") + std::to_string(nav_curr_layer) + "\x03"; // Set the current NAV Layer for Positioning and Mapping
4010  return ExitError;
4011  // Set NAV LandmarkDataFormat, ScanDataFormat and PoseDataFormat
4013  return ExitError;
4015  return ExitError;
4016  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_POSE_DATA_FORMAT], &sopas_response, useBinaryCmd) != 0) // Set PoseDataFormat: "sWN NPOSPoseDataFormat 1 1"
4017  return ExitError;
4018  // Optionally do Mapping
4019  bool nav_do_initial_mapping = false;
4020  rosDeclareParam(nh, "nav_do_initial_mapping", nav_do_initial_mapping);
4021  rosGetParam(nh, "nav_do_initial_mapping", nav_do_initial_mapping);
4022  if (nav_do_initial_mapping)
4023  {
4024  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_ERASE_LAYOUT], &sopas_response, useBinaryCmd) != 0) // Erase mapping layout: "sMN mNLAYEraseLayout 1"
4025  return ExitError;
4026  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_2], &sopas_response, useBinaryCmd) != 0) // set operational mode mapping
4027  return ExitError;
4028  if (get2ndSopasResponse(sopas_response, "mNEVAChangeState") != ExitSuccess)
4029  return ExitError;
4030  // Configure Mapping: "sWN NMAPMapCfg mean negative x y phi"
4031  int nav_map_cfg_mean = 50, nav_map_cfg_neg = 0, nav_map_cfg_x = 0, nav_map_cfg_y = 0, nav_map_cfg_phi = 0, nav_map_cfg_reflector_size = 80;
4032  rosDeclareParam(nh, "nav_map_cfg_mean", nav_map_cfg_mean);
4033  rosGetParam(nh, "nav_map_cfg_mean", nav_map_cfg_mean);
4034  rosDeclareParam(nh, "nav_map_cfg_neg", nav_map_cfg_neg);
4035  rosGetParam(nh, "nav_map_cfg_neg", nav_map_cfg_neg);
4036  rosDeclareParam(nh, "nav_map_cfg_x", nav_map_cfg_x);
4037  rosGetParam(nh, "nav_map_cfg_x", nav_map_cfg_x);
4038  rosDeclareParam(nh, "nav_map_cfg_y", nav_map_cfg_y);
4039  rosGetParam(nh, "nav_map_cfg_y", nav_map_cfg_y);
4040  rosDeclareParam(nh, "nav_map_cfg_phi", nav_map_cfg_phi);
4041  rosGetParam(nh, "nav_map_cfg_phi", nav_map_cfg_phi);
4042  rosDeclareParam(nh, "nav_map_cfg_reflector_size", nav_map_cfg_reflector_size);
4043  rosGetParam(nh, "nav_map_cfg_reflector_size", nav_map_cfg_reflector_size);
4044  sopasCmdVec[CMD_SET_NAV_MAP_CFG] = std::string("\x02sWN NMAPMapCfg ") + std::to_string(nav_map_cfg_mean) + " " + std::to_string(nav_map_cfg_neg) + " " + std::to_string(nav_map_cfg_x) + " " + std::to_string(nav_map_cfg_y) + " " + std::to_string(nav_map_cfg_phi) + " " + "\x03"; // Configure Mapping
4046  return ExitError;
4047  // Set reflector size: "sWN NLMDReflSize size"
4048  sopasCmdVec[CMD_SET_NAV_REFL_SIZE] = std::string("\x02sWN NLMDReflSize ") + std::to_string(nav_map_cfg_reflector_size) + "\x03"; // Set reflector size
4050  return ExitError;
4051  // Do Mapping: "sMN mNMAPDoMapping"
4053  return ExitError;
4054  // Wait for response "sAN mNMAPDoMapping errorCode landmarkData[...]" (which is sent after the request acknowledge "sMA mNMAPDoMapping")
4055  ROS_INFO_STREAM("1. response to mNMAPDoMapping request: " << stripControl(sopas_response, -1));
4056  if (get2ndSopasResponse(sopas_response, "mNMAPDoMapping") != ExitSuccess)
4057  return ExitError;
4058  ROS_INFO_STREAM("2. response to mNMAPDoMapping request: " << stripControl(sopas_response, -1));
4059  // Parse LandmarkData
4061  if (!parseNAV350BinaryLandmarkDataDoMappingResponse(sopas_response.data(), (int)sopas_response.size(), landmarkData))
4062  {
4063  ROS_WARN_STREAM("## ERROR parseNAV350BinaryLandmarkDataDoMappingResponse() failed");
4064  return ExitError;
4065  }
4066  landmarkData.print();
4067  // Add LandmarkData for mapping // "sMN mNLAYAddLandmark landmarkData {x y type subtype size layerID {ID}}"
4068  if (landmarkData.landmarkDataValid > 0 && landmarkData.landmarkData.reflectors.size())
4069  {
4070  std::vector<uint8_t> addLandmarkRequestPayload = createNAV350BinaryAddLandmarkRequest(landmarkData.landmarkData, nav_curr_layer);
4071  std::vector<uint8_t> addLandmarkRequest = { 0x02, 0x02, 0x02, 0x02, 0, 0, 0, 0 };
4072  addLandmarkRequest.insert(addLandmarkRequest.end(), addLandmarkRequestPayload.begin(), addLandmarkRequestPayload.end());
4073  setLengthAndCRCinBinarySopasRequest(&addLandmarkRequest);
4074  if (sendSopasAndCheckAnswer(addLandmarkRequest, &sopas_response) != 0)
4075  return ExitError;
4076  }
4077  else
4078  {
4079  ROS_ERROR_STREAM("## ERROR parseNAV350BinaryLandmarkDataDoMappingResponse(): Not enough landmarks detected for initial mapping.");
4080  return ExitError;
4081  }
4082  // Store mapping layout: "sMN mNLAYStoreLayout"
4084  return ExitError;
4085  }
4086  // Optionally import landmark layout from imk file
4087  std::string nav_set_landmark_layout_by_imk_file = "";
4088  rosDeclareParam(nh, "nav_set_landmark_layout_by_imk_file", nav_set_landmark_layout_by_imk_file);
4089  rosGetParam(nh, "nav_set_landmark_layout_by_imk_file", nav_set_landmark_layout_by_imk_file);
4090  if (!nav_set_landmark_layout_by_imk_file.empty())
4091  {
4092  std::vector<sick_scan_xd::NAV350ImkLandmark> navImkLandmarks = readNAVIMKfile(nav_set_landmark_layout_by_imk_file);
4093  if (navImkLandmarks.size() >= 3) // at least 3 reflectors required
4094  {
4095  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_ERASE_LAYOUT], &sopas_response, useBinaryCmd) != 0) // Erase mapping layout: "sMN mNLAYEraseLayout 1"
4096  return ExitError;
4097  if (sendSopasAorBgetAnswer(sopasCmdVec[CMD_SET_NAV_OPERATIONAL_MODE_2], &sopas_response, useBinaryCmd) != 0) // set operational mode mapping
4098  return ExitError;
4099  if (get2ndSopasResponse(sopas_response, "mNEVAChangeState") != ExitSuccess)
4100  return ExitError;
4101  for (size_t i = 0; i < navImkLandmarks.size(); i += 50) // Upload chunks of max. 50 landmarks to NAV350
4102  {
4103  size_t limit = std::min<size_t>(navImkLandmarks.size(), i + 50);
4104  std::vector<uint8_t> addLandmarkRequestPayload = createNAV350BinaryAddLandmarkRequest({ navImkLandmarks.begin() + i, navImkLandmarks.begin() + limit });
4105  std::vector<uint8_t> addLandmarkRequest = { 0x02, 0x02, 0x02, 0x02, 0, 0, 0, 0 };
4106  addLandmarkRequest.insert(addLandmarkRequest.end(), addLandmarkRequestPayload.begin(), addLandmarkRequestPayload.end());
4107  setLengthAndCRCinBinarySopasRequest(&addLandmarkRequest);
4108  ROS_DEBUG_STREAM("Sending landmarks, " << addLandmarkRequest.size() << " byte sopas request (" << addLandmarkRequestPayload.size()
4109  << " byte payload): \"" << DataDumper::binDataToAsciiString(addLandmarkRequest.data(), addLandmarkRequest.size()) << "\"");
4110  if (sendSopasAndCheckAnswer(addLandmarkRequest, &sopas_response) != 0)
4111  return ExitError;
4112  }
4113  // Store mapping layout: "sMN mNLAYStoreLayout"
4115  return ExitError;
4116  }
4117  else if (navImkLandmarks.size() > 0)
4118  {
4119  ROS_ERROR_STREAM("## ERROR readNAVIMKfile(): Less than 3 landmarks configured in \"" << nav_set_landmark_layout_by_imk_file << "\". At least 3 landmarks required.");
4120  return ExitError;
4121  }
4122  else
4123  {
4124  ROS_ERROR_STREAM("## ERROR readNAVIMKfile(): Can't read or parse imk file \"" << nav_set_landmark_layout_by_imk_file << "\".");
4125  return ExitError;
4126  }
4127  }
4128  // Switch to final operation mode (navigation by default)
4129  int nav_operation_mode = 4;
4130  rosDeclareParam(nh, "nav_operation_mode", nav_operation_mode);
4131  rosGetParam(nh, "nav_operation_mode", nav_operation_mode);
4132  enum SOPAS_CMD sopas_op_mode_cmd = CMD_SET_NAV_OPERATIONAL_MODE_4;
4133  switch(nav_operation_mode)
4134  {
4135  case 0:
4136  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_0);
4137  break;
4138  case 1:
4139  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_1);
4140  break;
4141  case 2:
4142  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_2);
4143  break;
4144  case 3:
4145  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_3);
4146  break;
4147  case 4:
4148  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_4);
4149  break;
4150  default:
4151  ROS_WARN_STREAM("Invalid parameter nav_operation_mode = " << nav_operation_mode << ", expected 0, 1, 2, 3 or 4, using default mode 4 (navigation)");
4152  nav_operation_mode = 4;
4153  sopas_op_mode_cmd = (CMD_SET_NAV_OPERATIONAL_MODE_4);
4154  break;
4155  }
4156  if (sendSopasAorBgetAnswer(sopasCmdVec[sopas_op_mode_cmd], &sopas_response, useBinaryCmd) != 0)
4157  return ExitError;
4158  if (get2ndSopasResponse(sopas_response, "mNEVAChangeState") != ExitSuccess)
4159  return ExitError;
4160  // NAV-350 data must be polled by sending sopas command "sMN mNPOSGetData wait mask"
4161  bool nav_start_polling = true;
4162  rosDeclareParam(nh, "nav_start_polling", nav_start_polling);
4163  rosGetParam(nh, "nav_start_polling", nav_start_polling);
4164  if (!nav_start_polling)
4165  ROS_WARN_STREAM("NAV350 Warning: start polling deactivated by configuration, no data will be received unless data polling started externally by sopas command \"sMN mNPOSGetData 1 2\"");
4166  for (int retry_cnt = 0; nav_start_polling == true && retry_cnt < 10 && rosOk(); retry_cnt++)
4167  {
4168  ROS_INFO_STREAM("NAV350: Sending: \"sMN mNPOSGetData 1 2\"");
4170  {
4171  ROS_ERROR_STREAM("## ERROR NAV350: Error sending sMN mNPOSGetData request, retrying ...");
4172  rosSleep(1.0);
4173  }
4174  else
4175  {
4176  ROS_INFO_STREAM("NAV350: sMN mNPOSGetData request was sent");
4177  break;
4178  }
4179  }
4180  }
4181  return ExitSuccess;
4182  }
4183 
4184 
4190  std::string sick_scan_xd::SickScanCommon::replyToString(const std::vector<unsigned char> &reply)
4191  {
4192  std::string reply_str;
4193  std::vector<unsigned char>::const_iterator it_start, it_end;
4194  int binLen = this->checkForBinaryAnswer(&reply);
4195  if (binLen == -1) // ASCII-Cmd
4196  {
4197  it_start = reply.begin();
4198  it_end = reply.end();
4199  }
4200  else
4201  {
4202  it_start = reply.begin() + 8; // skip header and length id
4203  it_end = reply.end() - 1; // skip CRC
4204  }
4205  bool inHexPrintMode = false;
4206  for (std::vector<unsigned char>::const_iterator it = it_start; it != it_end; it++)
4207  {
4208  // inHexPrintMode means that we should continue printing hex value after we started with hex-Printing
4209  // That is easier to debug for a human instead of switching between ascii binary and then back to ascii
4210  if (*it >= 0x20 && (inHexPrintMode == false)) // filter control characters for display
4211  {
4212  reply_str.push_back(*it);
4213  }
4214  else
4215  {
4216  if (binLen != -1) // binary
4217  {
4218  char szTmp[255] = {0};
4219  unsigned char val = *it;
4220  inHexPrintMode = true;
4221  sprintf(szTmp, "\\x%02x", val);
4222  for (size_t ii = 0; ii < strlen(szTmp); ii++)
4223  {
4224  reply_str.push_back(szTmp[ii]);
4225  }
4226  }
4227  }
4228 
4229  }
4230  return reply_str;
4231  }
4232 
4233  bool sick_scan_xd::SickScanCommon::dumpDatagramForDebugging(unsigned char *buffer, int bufLen, bool isBinary)
4234  {
4235  static size_t max_dump_size = 64 * 1024 * 1024;
4236  static size_t datasize_cnt = 0;
4237  static int file_cnt = 0;
4238  bool ret = true;
4239  char szDumpFileName[511] = {0};
4240  char szDir[255] = {0};
4241 
4242  if (datasize_cnt > max_dump_size)
4243  {
4244  ROS_WARN_STREAM("Attention: verboseLevel is set to 1 (debugging only). Total dump size of " << (max_dump_size / (1024 * 1024)) << " MByte in /tmp folder exceeded, data NOT dumped to file.");
4245  return false;
4246  }
4247  ROS_WARN("Attention: verboseLevel is set to 1 (debugging only). Datagrams are stored in the /tmp folder.");
4248 #ifdef _MSC_VER
4249  strcpy(szDir, "C:\\temp\\");
4250 #else
4251  strcpy(szDir, "/tmp/");
4252 #endif
4253  sprintf(szDumpFileName, "%ssick_datagram_%06d.bin", szDir, file_cnt);
4254  if (isBinary)
4255  {
4256  FILE *ftmp;
4257  ftmp = fopen(szDumpFileName, "wb");
4258  if (ftmp != NULL)
4259  {
4260  fwrite(buffer, bufLen, 1, ftmp);
4261  fclose(ftmp);
4262  }
4263  }
4264  file_cnt++;
4265  datasize_cnt += bufLen;
4266 
4267  return (true);
4268 
4269  }
4270 
4271 
4277  bool sick_scan_xd::SickScanCommon::isCompatibleDevice(const std::string identStr) const
4278  {
4279  char device_string[7];
4280  int version_major = -1;
4281  int version_minor = -1;
4282 
4283  strcpy(device_string, "???");
4284  // special for TiM3-Firmware
4285  if (sscanf(identStr.c_str(), "sRA 0 6 %6s E V%d.%d", device_string,
4286  &version_major, &version_minor) == 3
4287  && strncmp("TiM3", device_string, 4) == 0
4288  && version_major >= 2 && version_minor >= 50)
4289  {
4290  ROS_ERROR("This scanner model/firmware combination does not support ranging output!");
4291  ROS_ERROR("Supported scanners: TiM5xx: all firmware versions; TiM3xx: firmware versions < V2.50.");
4292  ROS_ERROR_STREAM("This is a " << device_string << ", firmware version " << version_major << "." << version_minor);
4293 
4294  return false;
4295  }
4296 
4297  bool supported = false;
4298 
4299  // DeviceIdent 8 MRS1xxxx 8 1.3.0.0R.
4300  if (sscanf(identStr.c_str(), "sRA 0 6 %6s E V%d.%d", device_string, &version_major, &version_minor) == 3)
4301  {
4302  std::string devStr = device_string;
4303 
4304 
4305  if (devStr.compare(0, 4, "TiM5") == 0)
4306  {
4307  supported = true;
4308  }
4309 
4310  if (supported == true)
4311  {
4312  ROS_INFO_STREAM("Device " << identStr << " V" << version_major << "." << version_minor << " found and supported by this driver.");
4313  }
4314 
4315  }
4316 
4317  if ((identStr.find("MRS1xxx") !=
4318  std::string::npos) // received pattern contains 4 'x' but we check only for 3 'x' (MRS1104 should be MRS1xxx)
4319  || (identStr.find("LMS1xxx") != std::string::npos)
4320  )
4321  {
4322  ROS_INFO_STREAM("Deviceinfo " << identStr << " found and supported by this driver.");
4323  supported = true;
4324  }
4325 
4326 
4327  if (identStr.find("MRS6") !=
4328  std::string::npos) // received pattern contains 4 'x' but we check only for 3 'x' (MRS1104 should be MRS1xxx)
4329  {
4330  ROS_INFO_STREAM("Deviceinfo " << identStr << " found and supported by this driver.");
4331  supported = true;
4332  }
4333 
4334  if (identStr.find("RMS1") != std::string::npos || identStr.find("RMS2") != std::string::npos)
4335  {
4336  ROS_INFO_STREAM("Deviceinfo " << identStr << " found and supported by this driver.");
4337  supported = true;
4338  }
4339 
4340 
4341  if (identStr.find("LD-LRSxx") != std::string::npos)
4342  {
4343  ROS_INFO_STREAM("Deviceinfo " << identStr << " found and supported by this driver.");
4344  supported = true;
4345  }
4346 
4347  if (supported == false)
4348  {
4349  ROS_WARN_STREAM("Device " << device_string << "s V" << version_major << "." << version_minor << " found and maybe unsupported by this driver.");
4350  ROS_WARN_STREAM("Full SOPAS answer: " << identStr);
4351  }
4352  return true;
4353  }
4354 
4355 
4361  {
4362  //static int cnt = 0;
4363 #ifdef USE_DIAGNOSTIC_UPDATER
4364  if(diagnostics_)
4365  {
4366 #if __ROS_VERSION == 2 // ROS 2
4367  diagnostics_->force_update();
4368 #else
4369  diagnostics_->update();
4370 #endif
4371  }
4372 #endif
4373 
4374  unsigned char receiveBuffer[65536];
4375  int actual_length = 0;
4376  static unsigned int iteration_count = 0;
4377  bool useBinaryProtocol = this->parser_->getCurrentParamPtr()->getUseBinaryProtocol();
4378 
4379  rosTime recvTimeStamp = rosTimeNow(); // timestamp incoming package, will be overwritten by get_datagram
4380  // tcp packet
4381  rosTime recvTimeStampPush = recvTimeStamp; // timestamp
4382 
4383  /*
4384  * Try to get datagram
4385  *
4386  *
4387  */
4388 
4389 
4390  int packetsInLoop = 0;
4391 
4392  const int maxNumAllowedPacketsToProcess = 25; // maximum number of packets, which will be processed in this loop.
4393 
4394  int numPacketsProcessed = 0; // count number of processed datagrams
4395 
4396  static bool firstTimeCalled = true;
4397  static bool dumpData = false;
4398  static int verboseLevel = 0; // for low level debugging only
4399  static bool slamBundle = false;
4400  float timeIncrement;
4401  static std::string echoForSlam = "";
4402  if (firstTimeCalled == true)
4403  {
4404 
4405  /* Dump Binary Protocol */
4406  rosDeclareParam(nh, "slam_echo", echoForSlam);
4407  rosGetParam(nh, "slam_echo", echoForSlam);
4408 
4409  rosDeclareParam(nh, "slam_bundle", slamBundle);
4410  rosGetParam(nh, "slam_bundle", slamBundle);
4411 
4412  firstTimeCalled = false;
4413  }
4414  do
4415  {
4416  const std::vector<std::string> datagram_keywords = { // keyword list of datagrams handled here in loopOnce
4417  "LMDscandata", "LMDscandatamon", "mNPOSGetData",
4418  "LMDradardata", "InertialMeasurementUnit", "LIDoutputstate", "LIDinputstate", "LFErec" };
4419 
4420  int result = get_datagram(nh, recvTimeStamp, receiveBuffer, 65536, &actual_length, useBinaryProtocol, &packetsInLoop, datagram_keywords);
4421  numPacketsProcessed++;
4422 
4423  rosDuration dur = recvTimeStampPush - recvTimeStamp;
4424 
4425  if (result != 0)
4426  {
4427  ROS_ERROR_STREAM("Read Error when getting datagram: " << result);
4428 #ifdef USE_DIAGNOSTIC_UPDATER
4429  if(diagnostics_)
4430  diagnostics_->broadcast(getDiagnosticErrorCode(), "Read Error when getting datagram.");
4431 #endif
4432  return ExitError; // return failure to exit node
4433  }
4434  if (actual_length <= 0)
4435  {
4436  return ExitSuccess;
4437  } // return success to continue looping
4438 
4439  // ----- if requested, skip frames
4440  if (iteration_count++ % (config_.skip + 1) != 0)
4441  {
4442  return ExitSuccess;
4443  }
4444  ROS_DEBUG_STREAM("SickScanCommon::loopOnce: received " << actual_length << " byte data " << DataDumper::binDataToAsciiString(&receiveBuffer[0], std::min<int>(32, actual_length)) << " ... ");
4445 
4446  if (publish_datagram_)
4447  {
4448  ros_std_msgs::String datagram_msg;
4449  datagram_msg.data = std::string(reinterpret_cast<char *>(receiveBuffer));
4450  rosPublish(datagram_pub_, datagram_msg);
4451  }
4452 
4453 
4454  if (verboseLevel > 0)
4455  {
4456  dumpDatagramForDebugging(receiveBuffer, actual_length, this->parser_->getCurrentParamPtr()->getUseBinaryProtocol());
4457  }
4458 
4459 
4460  if (true == this->parser_->getCurrentParamPtr()->getDeviceIsRadar())
4461  {
4463  std::string scanner_name = this->parser_->getCurrentParamPtr()->getScannerName();
4464  radar->setNameOfRadar(scanner_name, this->parser_->getCurrentParamPtr()->getDeviceRadarType());
4465  int errorCode = ExitSuccess;
4466  if ( (useBinaryProtocol && memcmp(&receiveBuffer[8], "sSN LIDoutputstate", strlen("sSN LIDoutputstate")) == 0)
4467  || (!useBinaryProtocol && memcmp(&receiveBuffer[1], "sSN LIDoutputstate", strlen("sSN LIDoutputstate")) == 0))
4468  {
4469  // parse optional LIDoutputstate telegram
4470  sick_scan_msg::LIDoutputstateMsg outputstate_msg;
4471  if (sick_scan_xd::SickScanMessages::parseLIDoutputstateMsg(recvTimeStamp, receiveBuffer, actual_length, useBinaryProtocol, scanner_name, outputstate_msg))
4472  {
4473  // Publish LIDoutputstate message
4474  notifyLIDoutputstateListener(nh, &outputstate_msg);
4476  {
4477  rosPublish(lidoutputstate_pub_, outputstate_msg);
4478  }
4479  }
4480  else
4481  {
4482  ROS_WARN_STREAM("## ERROR SickScanCommon: parseLIDoutputstateMsg failed, received " << actual_length << " byte LIDoutputstate " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4483  }
4484  }
4485  else
4486  {
4487  // parse radar telegram and send pointcloud2-debug messages
4488  errorCode = radar->parseDatagram(recvTimeStamp, (unsigned char *) receiveBuffer, actual_length, useBinaryProtocol);
4489  }
4490  return errorCode; // return success to continue looping
4491  }
4492 
4493  static SickScanImu scanImu(this, nh); // todo remove static
4494  if (scanImu.isImuDatagram((char *) receiveBuffer, actual_length))
4495  {
4496  int errorCode = ExitSuccess;
4497  if (scanImu.isImuAckDatagram((char *) receiveBuffer, actual_length))
4498  {
4499 
4500  }
4501  else
4502  {
4503  // parse radar telegram and send pointcloud2-debug messages
4504  errorCode = scanImu.parseDatagram(recvTimeStamp, (unsigned char *) receiveBuffer, actual_length,
4505  useBinaryProtocol);
4506 
4507  }
4508  return errorCode; // return success to continue looping
4509  }
4510  else if(memcmp(&receiveBuffer[8], "sSN LIDoutputstate", strlen("sSN LIDoutputstate")) == 0)
4511  {
4512  int errorCode = ExitSuccess;
4513  ROS_DEBUG_STREAM("SickScanCommon: received " << actual_length << " byte LIDoutputstate " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4514  // Parse and convert LIDoutputstate message
4515  std::string scanner_name = parser_->getCurrentParamPtr()->getScannerName();
4516  EVAL_FIELD_SUPPORT eval_field_logic = this->parser_->getCurrentParamPtr()->getUseEvalFields();
4517  sick_scan_msg::LIDoutputstateMsg outputstate_msg;
4518  if (sick_scan_xd::SickScanMessages::parseLIDoutputstateMsg(recvTimeStamp, receiveBuffer, actual_length, useBinaryProtocol, scanner_name, outputstate_msg))
4519  {
4520  // Publish LIDoutputstate message
4521  notifyLIDoutputstateListener(nh, &outputstate_msg);
4523  {
4524  rosPublish(lidoutputstate_pub_, outputstate_msg);
4525  }
4526  if(cloud_marker_)
4527  {
4528  cloud_marker_->updateMarker(outputstate_msg, eval_field_logic);
4529  }
4530  }
4531  else
4532  {
4533  ROS_WARN_STREAM("## ERROR SickScanCommon: parseLIDoutputstateMsg failed, received " << actual_length << " byte LIDoutputstate " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4534  }
4535  return errorCode; // return success to continue looping
4536  }
4537  else if(memcmp(&receiveBuffer[8], "sSN LIDinputstate", strlen("sSN LIDinputstate")) == 0)
4538  {
4539  int errorCode = ExitSuccess;
4540  // Parse active_fieldsetfrom LIDinputstate message
4542  if(fieldMon && useBinaryProtocol && actual_length > 32)
4543  {
4544  sick_scan_msg::LIDinputstateMsg inputstate_msg;
4545  fieldMon->parseBinaryLIDinputstateMsg(receiveBuffer, actual_length, inputstate_msg);
4546  // notifyLIDinputstateListener(nh, &inputstate_msg); // TODO: notify LIDinputstateMsg listener (API)
4548  {
4549  rosPublish(lidinputstate_pub_, inputstate_msg);
4550  }
4551  if(cloud_marker_)
4552  {
4554  }
4555  ROS_DEBUG_STREAM("SickScanCommon: received " << actual_length << " byte LIDinputstate " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length)
4556  << ", active fieldset = " << fieldMon->getActiveFieldset() << ", " << fieldMon->LIDinputstateMsgToString(inputstate_msg));
4557  }
4558  return errorCode; // return success to continue looping
4559  }
4560  else if(memcmp(&receiveBuffer[8], "sSN LFErec", strlen("sSN LFErec")) == 0)
4561  {
4562  int errorCode = ExitSuccess;
4563  ROS_DEBUG_STREAM("SickScanCommon: received " << actual_length << " byte LFErec " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4564  // Parse and convert LFErec message
4565  sick_scan_msg::LFErecMsg lferec_msg;
4566  std::string scanner_name = parser_->getCurrentParamPtr()->getScannerName();
4567  EVAL_FIELD_SUPPORT eval_field_logic = parser_->getCurrentParamPtr()->getUseEvalFields(); // == USE_EVAL_FIELD_LMS5XX_LOGIC
4568  if (sick_scan_xd::SickScanMessages::parseLFErecMsg(recvTimeStamp, receiveBuffer, actual_length, useBinaryProtocol, eval_field_logic, scanner_name, lferec_msg))
4569  {
4570  // Publish LFErec message
4571  notifyLFErecListener(nh, &lferec_msg);
4572  if(publish_lferec_)
4573  {
4574  rosPublish(lferec_pub_, lferec_msg);
4575  }
4576  if(cloud_marker_)
4577  {
4578  cloud_marker_->updateMarker(lferec_msg, eval_field_logic);
4579  }
4580  }
4581  else
4582  {
4583  ROS_WARN_STREAM("## ERROR SickScanCommon: parseLFErecMsg failed, received " << actual_length << " byte LFErec " << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4584  }
4585  return errorCode; // return success to continue looping
4586  }
4587  else if(memcmp(&receiveBuffer[8], "sSN LMDscandatamon", strlen("sSN LMDscandatamon")) == 0)
4588  {
4589  int errorCode = ExitSuccess;
4590  ROS_DEBUG_STREAM("SickScanCommon: received " << actual_length << " byte LMDscandatamon (ignored) ..."); // << DataDumper::binDataToAsciiString(&receiveBuffer[0], actual_length));
4591  return errorCode; // return success to continue looping
4592  }
4593  else if(memcmp(&receiveBuffer[8], "sMA mNPOSGetData", strlen("sMA mNPOSGetData")) == 0) // NAV-350: method acknowledge, indicates mNPOSGetData has started => wait for the "sAN mNPOSGetData" response
4594  {
4595  int errorCode = ExitSuccess;
4596  ROS_DEBUG_STREAM("NAV350: received " << actual_length << " byte \"sMA mNPOSGetData\", waiting for \"sAN mNPOSGetData\" ...");
4597  return errorCode; // return success to continue looping
4598  }
4599  else
4600  {
4602  sick_scan_msg::Encoder EncoderMsg;
4603  EncoderMsg.header.stamp = recvTimeStamp + rosDurationFromSec(config_.time_offset);
4604  //TODO remove this hardcoded variable
4605  bool FireEncoder = false;
4606  EncoderMsg.header.frame_id = "Encoder";
4607  ROS_HEADER_SEQ(EncoderMsg.header, numPacketsProcessed);
4608  msg.header.stamp = recvTimeStamp + rosDurationFromSec(config_.time_offset); // default: ros-timestamp at message received, will be updated by software-pll
4609  msg.header.frame_id = config_.frame_id; // Use configured frame_id for both laser scan and pointcloud messages
4610  double elevationAngleInRad = 0.0;
4611  short elevAngleX200 = 0; // signed short (F5 B2 -> Layer 24
4612  // F5B2h -> -2638/200= -13.19°
4613  double elevAngleTelegramValToDeg = 1.0 / 200.0; // MRS-6000: elevation angle in deg := (layer angle value from lidar scan telegram) / 200
4615  {
4616  elevAngleTelegramValToDeg = 1.0 / 100.0; // MRS-1000: elevation angle in deg := (layer angle value from lidar scan telegram) / 100
4617  }
4618  /*
4619  * datagrams are enclosed in <STX> (0x02), <ETX> (0x03) pairs
4620  */
4621  char *buffer_pos = (char *) receiveBuffer;
4622  char *dstart, *dend;
4623  bool dumpDbg = false;
4624  bool dataToProcess = true;
4625  std::vector<float> vang_vec, azimuth_vec;
4626  vang_vec.clear();
4627  dstart = NULL;
4628  dend = NULL;
4629 
4630  while (dataToProcess)
4631  {
4632  const int maxAllowedEchos = 5;
4633 
4634  int numValidEchos = 0;
4635  int aiValidEchoIdx[maxAllowedEchos] = {0};
4636  size_t dlength;
4637  int success = -1;
4638  int numEchos = 0;
4639  int echoMask = 0;
4640  bool publishPointCloud = true;
4641 
4642  if (useBinaryProtocol)
4643  {
4644  // if binary protocol used then parse binary message
4645  std::vector<unsigned char> receiveBufferVec = std::vector<unsigned char>(receiveBuffer,
4646  receiveBuffer + actual_length);
4647 #ifdef DEBUG_DUMP_TO_CONSOLE_ENABLED
4648  if (actual_length > 1000)
4649  {
4650  DataDumper::instance().dumpUcharBufferToConsole(receiveBuffer, actual_length);
4651 
4652  }
4653 
4654  DataDumper::instance().dumpUcharBufferToConsole(receiveBuffer, actual_length);
4655 #endif
4656  if (receiveBufferVec.size() > 8)
4657  {
4658  long idVal = 0;
4659  long lenVal = 0;
4660  memcpy(&idVal, receiveBuffer + 0, 4); // read identifier
4661  memcpy(&lenVal, receiveBuffer + 4, 4); // read length indicator
4662  swap_endian((unsigned char *) &lenVal, 4);
4663 
4664  if (idVal == 0x02020202) // id for binary message
4665  {
4666 #if 0
4667  {
4668  static int cnt = 0;
4669  char szFileName[255];
4670 
4671 #ifdef _MSC_VER
4672  sprintf(szFileName, "c:\\temp\\dump%05d.bin", cnt);
4673 #else
4674  sprintf(szFileName, "/tmp/dump%05d.txt", cnt);
4675 #endif
4676  FILE *fout;
4677  fout = fopen(szFileName, "wb");
4678  fwrite(receiveBuffer, actual_length, 1, fout);
4679  fclose(fout);
4680  cnt++;
4681  }
4682 #endif
4683  // binary message
4684  if (actual_length > 17 && memcmp(&receiveBuffer[8], "sAN mNPOSGetData ", 17) == 0) // NAV-350 pose and scan data
4685  {
4686  NAV350mNPOSData navdata; // NAV-350 pose and scan data
4687  success = handleNAV350BinaryPositionData(receiveBuffer, actual_length, elevAngleX200, elevationAngleInRad, recvTimeStamp, config_.sw_pll_only_publish, config_.time_offset, parser_, numEchos, msg, navdata);
4688  if (!success)
4689  ROS_ERROR_STREAM("## ERROR SickScanCommon::loopOnce(): handleNAV350BinaryPositionData() failed");
4690  }
4691  else if (lenVal >= actual_length || actual_length < 64) // scan data message requires at least 64 byte, otherwise this can't be a scan data message
4692  {
4693  success = false;
4694  // warn about unexpected message and ignore all non-scandata messages
4695  if (rosOk())
4696  ROS_WARN_STREAM("## WARNING in SickScanCommon::loopOnce(): " << actual_length << " byte message ignored ("
4697  << DataDumper::binDataToAsciiString(&receiveBuffer[0], std::min<int>(actual_length, 64)) << (actual_length>64?"...":"") << ")");
4698  else
4699  ROS_INFO_STREAM("SickScanCommon::loopOnce(): " << actual_length << " byte message ignored");
4700  }
4701  else
4702  {
4703  success = parseCommonBinaryResultTelegram(receiveBuffer, actual_length, elevAngleX200, elevAngleTelegramValToDeg, elevationAngleInRad, recvTimeStamp,
4704  config_.sw_pll_only_publish, config_.use_generation_timestamp, parser_, FireEncoder, EncoderMsg, numEchos, vang_vec, azimuth_vec, msg);
4705  if (!success)
4706  ROS_ERROR_STREAM("## ERROR SickScanCommon::loopOnce(): parseCommonBinaryResultTelegram() failed");
4707  }
4708  if (!success)
4709  {
4710  dataToProcess = false;
4711  break;
4712  }
4713  msg.header.stamp = recvTimeStamp + rosDurationFromSec(config_.time_offset); // recvTimeStamp updated by software-pll
4714  timeIncrement = msg.time_increment;
4715  echoMask = (1 << numEchos) - 1;
4716  }
4717  }
4718 
4719  //perform time consistency test
4720  parser_->checkScanTiming(msg.time_increment, msg.scan_time, msg.angle_increment, 0.00001f);
4721 
4722  success = ExitSuccess;
4723  // change Parsing Mode
4724  dataToProcess = false; // only one package allowed - no chaining
4725  }
4726  else // Parsing of Ascii-Encoding of datagram, xxx
4727  {
4728  dstart = strchr(buffer_pos, 0x02);
4729  if (dstart != NULL)
4730  {
4731  dend = strchr(dstart + 1, 0x03);
4732  }
4733  if ((dstart != NULL) && (dend != NULL))
4734  {
4735  dataToProcess = true; // continue parasing
4736  dlength = dend - dstart;
4737  *dend = '\0';
4738  dstart++;
4739  }
4740  else
4741  {
4742  dataToProcess = false;
4743  break; //
4744  }
4745 
4746  if (dumpDbg)
4747  {
4748  {
4749  static int cnt = 0;
4750  char szFileName[255];
4751 
4752 #ifdef _MSC_VER
4753  sprintf(szFileName, "c:\\temp\\dump%05d.txt", cnt);
4754 #else
4755  sprintf(szFileName, "/tmp/dump%05d.txt", cnt);
4756 #endif
4757 #if 0
4758  FILE *fout;
4759  fout = fopen(szFileName, "wb");
4760  fwrite(dstart, dlength, 1, fout);
4761  fclose(fout);
4762 #endif
4763  cnt++;
4764  }
4765  }
4766 
4767  // HEADER of data followed by DIST1 ... DIST2 ... DIST3 .... RSSI1 ... RSSI2.... RSSI3...
4768 
4769  // <frameid>_<sign>00500_DIST[1|2|3]
4770  numEchos = 1;
4771  // numEchos contains number of echos (1..5)
4772  // _msg holds ALL data of all echos
4773  success = parser_->parse_datagram(dstart, dlength, config_, msg, numEchos, echoMask);
4774  publishPointCloud = true; // for MRS1000
4775 
4776  numValidEchos = 0;
4777  for (int i = 0; i < maxAllowedEchos; i++)
4778  {
4779  aiValidEchoIdx[i] = 0;
4780  }
4781  }
4782 
4783 
4784  int numOfLayers = parser_->getCurrentParamPtr()->getNumberOfLayers();
4785 
4786  if (success == ExitSuccess)
4787  {
4788  bool elevationPreCalculated = false;
4789  double elevationAngleDegree = 0.0;
4790 
4791 
4792  std::vector<float> rangeTmp = msg.ranges; // copy all range value
4793  std::vector<float> intensityTmp = msg.intensities; // copy all intensity value
4794 
4795  int intensityTmpNum = intensityTmp.size();
4796  float *intensityTmpPtr = NULL;
4797  if (intensityTmpNum > 0)
4798  {
4799  intensityTmpPtr = &intensityTmp[0];
4800  }
4801 
4802  // Helpful: https://answers.ros.org/question/191265/pointcloud2-access-data/
4803  // https://gist.github.com/yuma-m/b5dcce1b515335c93ce8
4804  // Copy to pointcloud
4805  int layer = 0;
4806  int baseLayer = 0;
4807 
4808  switch (numOfLayers)
4809  {
4810  case 1: // TIM571 etc.
4811  baseLayer = 0;
4812  break;
4813  case 4:
4814 
4815  baseLayer = -1;
4816  if (elevAngleX200 == 250) // if (msg.header.seq == 250) // msg.header.seq := elevAngleX200
4817  { layer = -1; }
4818  else if (elevAngleX200 == 0) // else if (msg.header.seq == 0) // msg.header.seq := elevAngleX200
4819  { layer = 0; }
4820  else if (elevAngleX200 == -250) // else if (msg.header.seq == -250) // msg.header.seq := elevAngleX200
4821  { layer = 1; }
4822  else if (elevAngleX200 == -500) // else if (msg.header.seq == -500) // msg.header.seq := elevAngleX200
4823  { layer = 2; }
4824  elevationAngleDegree = this->parser_->getCurrentParamPtr()->getElevationDegreeResolution();
4825  elevationAngleDegree = elevationAngleDegree / 180.0 * M_PI;
4826  // 0.0436332 /*2.5 degrees*/;
4827  break;
4828  case 24: // Preparation for MRS6000
4829  baseLayer = -1;
4830  layer = (elevAngleX200 - (-2638)) / 125; // (msg.header.seq - (-2638)) / 125; // msg.header.seq := elevAngleX200
4831  layer = (23 - layer) - 1;
4832 #if 0
4833  elevationAngleDegree = this->parser_->getCurrentParamPtr()->getElevationDegreeResolution();
4834  elevationAngleDegree = elevationAngleDegree / 180.0 * M_PI;
4835 #endif
4836 
4837  elevationPreCalculated = true;
4838  break;
4839  default:
4840  assert(0);
4841  break; // unsupported
4842 
4843  }
4844 
4845 
4846 
4847 
4848 
4849  // XXX - HIER MEHRERE SCANS publish, falls Mehrzielszenario läuft
4850  // numEchos = 0; // temporary test for issue #17 (core dump with numEchos = 0 after unexpected message, see https://github.com/michael1309/sick_scan_xd/issues/17)
4851  if (numEchos > 5)
4852  {
4853  ROS_WARN("Too much echos");
4854  }
4855  else if (numEchos > 0)
4856  {
4857 
4858  size_t startOffset = 0;
4859  size_t endOffset = 0;
4860  int echoPartNum = msg.ranges.size() / numEchos;
4861  for (int i = 0; i < numEchos; i++)
4862  {
4863 
4864  bool sendMsg = false;
4865  if ((echoMask & (1 << i)) & outputChannelFlagId)
4866  {
4867  aiValidEchoIdx[numValidEchos] = i; // save index
4868  numValidEchos++;
4869  sendMsg = true;
4870  }
4871  startOffset = i * echoPartNum;
4872  endOffset = (i + 1) * echoPartNum;
4873 
4874  msg.ranges.clear();
4875  msg.intensities.clear();
4876  msg.ranges = std::vector<float>(
4877  rangeTmp.begin() + startOffset,
4878  rangeTmp.begin() + endOffset);
4879  // check also for MRS1104
4880  if (endOffset <= intensityTmp.size() && (intensityTmp.size() > 0))
4881  {
4882  msg.intensities = std::vector<float>(
4883  intensityTmp.begin() + startOffset,
4884  intensityTmp.begin() + endOffset);
4885  }
4886  else
4887  {
4888  msg.intensities.resize(echoPartNum); // fill with zeros
4889  }
4890  {
4891  // numEchos
4892  char szTmp[255] = {0};
4893  if (this->parser_->getCurrentParamPtr()->getNumberOfLayers() > 1)
4894  {
4895  const char *cpFrameId = config_.frame_id.c_str();
4896 #if 0
4897  sprintf(szTmp, "%s_%+04d_DIST%d", cpFrameId, msg.header.seq, i + 1);
4898 #else // experimental
4899  char szSignName[10] = {0};
4900  int seqDummy = elevAngleX200; // msg.header.seq := elevAngleX200
4901  if (seqDummy < 0)
4902  {
4903  strcpy(szSignName, "NEG");
4904  }
4905  else
4906  {
4907  strcpy(szSignName, "POS");
4908 
4909  }
4910 
4911  sprintf(szTmp, "%s_%s_%03d_DIST%d", cpFrameId, szSignName, abs((int)seqDummy), i + 1);
4912 #endif
4913  }
4914  else
4915  {
4916  strcpy(szTmp, config_.frame_id.c_str()); // Use configured frame_id for both laser scan and pointcloud messages
4917  }
4918 
4919  msg.header.frame_id = std::string(szTmp);
4920  // Hector slam can only process ONE valid frame id.
4921  if (echoForSlam.length() > 0)
4922  {
4923  if (slamBundle)
4924  {
4925  // try to map first echos to horizontal layers.
4926  if (i == 0)
4927  {
4928  // first echo
4929  msg.header.frame_id = echoForSlam;
4930  strcpy(szTmp, echoForSlam.c_str()); //
4931  if (elevationAngleInRad != 0.0)
4932  {
4933  float cosVal = (float)cos(elevationAngleInRad);
4934  int rangeNum = msg.ranges.size();
4935  for (int j = 0; j < rangeNum; j++)
4936  {
4937  msg.ranges[j] *= cosVal;
4938  }
4939  }
4940  }
4941  }
4942 
4943  if (echoForSlam.compare(szTmp) == 0)
4944  {
4945  sendMsg = true;
4946  }
4947  else
4948  {
4949  sendMsg = false;
4950  }
4951  }
4952  // If msg.intensities[j] < min_intensity, then set msg.ranges[j] to inf according to https://github.com/SICKAG/sick_scan/issues/131
4953  if(m_min_intensity > 0) // Set range of LaserScan messages to infinity, if intensity < min_intensity (default: 0)
4954  {
4955  for (int j = 0, j_max = (int)std::min<size_t>(msg.ranges.size(), msg.intensities.size()); j < j_max; j++)
4956  {
4957  if(msg.intensities[j] < m_min_intensity)
4958  {
4959  msg.ranges[j] = std::numeric_limits<float>::infinity();
4960  // ROS_DEBUG_STREAM("msg.intensities[" << j << "]=" << msg.intensities[j] << " < " << m_min_intensity << ", set msg.ranges[" << j << "]=" << msg.ranges[j] << " to infinity.");
4961  }
4962  }
4963  }
4964 
4965  }
4966 #ifndef _MSC_VER
4967  if (parser_->getCurrentParamPtr()->getEncoderMode() >= 0 && FireEncoder == true)//
4968  {
4969  rosPublish(Encoder_pub, EncoderMsg);
4970  }
4971  if (numOfLayers > 4)
4972  {
4973  sendMsg = false; // too many layers for publish as scan message. Only pointcloud messages will be pub.
4974  }
4975  if (sendMsg & outputChannelFlagId) // publish only configured channels - workaround for cfg-bug MRS1104
4976  {
4977 
4978  // rosPublish(pub_, msg);
4979 #if defined USE_DIAGNOSTIC_UPDATER // && __ROS_VERSION == 1
4980  // if(diagnostics_)
4981  // diagnostics_->broadcast(diagnostic_msgs_DiagnosticStatus_OK, "SickScanCommon running, no error");
4982  if(diagnosticPub_)
4983  diagnosticPub_->publish(msg);
4984  else
4985  rosPublish(pub_, msg);
4986 #else
4987  rosPublish(pub_, msg);
4988 #endif
4989 
4990  }
4991 #else
4992  ROS_DEBUG_STREAM("MSG received...");
4993 #endif
4994  }
4995  }
4996  else // i.e. (numEchos <= 0)
4997  {
4998  if (rosOk())
4999  ROS_WARN_STREAM("## WARNING in SickScanCommon::loopOnce(): no echos in measurement message (numEchos=" << numEchos
5000  << ", msg.ranges.size()=" << msg.ranges.size() << ", msg.intensities.size()=" << msg.intensities.size() << ")");
5001  else
5002  ROS_INFO_STREAM("SickScanCommon::loopOnce(): no echos in measurement message");
5003  }
5004 
5005  if (publishPointCloud == true && numValidEchos > 0 && msg.ranges.size() > 0)
5006  {
5007 
5008 
5009  const int numChannels = 4; // x y z i (for intensity)
5010 
5011  int numTmpLayer = numOfLayers;
5013  {
5014  numTmpLayer = 1; // LMS_1XXX has 4 interlaced layer, each layer published in one pointcloud message
5015  baseLayer = 0;
5016  layer = 0;
5017  msg.header.frame_id = config_.frame_id; // Use configured frame_id for both laser scan and pointcloud messages
5018  }
5019 
5020  cloud_.header.stamp = recvTimeStamp + rosDurationFromSec(config_.time_offset);
5021  // ROS_DEBUG_STREAM("laser_scan timestamp: " << msg.header.stamp << ", pointclound timestamp: " << cloud_.header.stamp);
5022  cloud_.header.frame_id = config_.frame_id;
5023  ROS_HEADER_SEQ(cloud_.header, 0);
5024  cloud_.height = numTmpLayer * numValidEchos; // due to multi echo multiplied by num. of layers
5025  cloud_.width = msg.ranges.size();
5026  cloud_.is_bigendian = false;
5027  cloud_.is_dense = true;
5028  cloud_.point_step = numChannels * sizeof(float);
5029  cloud_.row_step = cloud_.point_step * cloud_.width;
5030  cloud_.fields.resize(numChannels);
5031  for (int i = 0; i < numChannels; i++)
5032  {
5033  std::string channelId[] = {"x", "y", "z", "intensity"};
5034  cloud_.fields[i].name = channelId[i];
5035  cloud_.fields[i].offset = i * sizeof(float);
5036  cloud_.fields[i].count = 1;
5037  cloud_.fields[i].datatype = ros_sensor_msgs::PointField::FLOAT32;
5038  }
5039  cloud_.data.resize(cloud_.row_step * cloud_.height, 0);
5040 
5041  cloud_polar_.header = cloud_.header;
5042  cloud_polar_.height = cloud_.height;
5043  cloud_polar_.width = cloud_.width;
5044  cloud_polar_.is_bigendian = cloud_.is_bigendian;
5045  cloud_polar_.is_dense = cloud_.is_dense;
5046  cloud_polar_.point_step = cloud_.point_step;
5047  cloud_polar_.row_step = cloud_.row_step;
5048  cloud_polar_.fields = cloud_.fields;
5049  cloud_polar_.fields[0].name = "range";
5050  cloud_polar_.fields[1].name = "azimuth";
5051  cloud_polar_.fields[2].name = "elevation";
5052  cloud_polar_.data.resize(cloud_.data.size(), 0);
5053 
5054  unsigned char *cloudDataPtr = &(cloud_.data[0]);
5055  unsigned char *cloudDataPtr_polar = &(cloud_polar_.data[0]);
5056 
5057  // prepare lookup for elevation angle table
5058 
5059  std::vector<float> cosAlphaTable; // Lookup table for cos
5060  std::vector<float> sinAlphaTable; // Lookup table for sin
5061 
5062  size_t rangeNumAllEchos = rangeTmp.size(); // rangeTmp.size() := number of range values in all echos (max. 5 echos)
5063  size_t rangeNumAllEchosCloud = cloud_.height * cloud_.width; // number of points allocated in the point cloud
5064  rangeNumAllEchos = std::min<size_t>(rangeNumAllEchos, rangeNumAllEchosCloud); // limit number of range values (issue #49): if no echofilter was set, the number of echos can exceed the expected echos
5065  size_t rangeNum = rangeNumAllEchos / numValidEchos;
5066  // ROS_INFO_STREAM("numValidEchos=" << numValidEchos << ", numEchos=" << numEchos << ", cloud_.height * cloud_.width=" << cloud_.height * cloud_.width << ", rangeNum=" << rangeNum);
5067 
5068  cosAlphaTable.resize(rangeNum);
5069  sinAlphaTable.resize(rangeNum);
5070  float mirror_factor = 1.0;
5071  float angleShift=0;
5072  if (this->parser_->getCurrentParamPtr()->getScanMirroredAndShifted()) // i.e. NAV3xx-series
5073  {
5074  mirror_factor = -1.0;
5075  }
5076 
5077  size_t rangeNumPointcloudAllEchos = 0;
5078  SickRangeFilter range_filter(this->parser_->get_range_min(), this->parser_->get_range_max(), this->parser_->get_range_filter_config());
5079  for (size_t iEcho = 0; iEcho < numValidEchos; iEcho++)
5080  {
5081 
5082  float angle = (float)config_.min_ang;
5083  if(this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_TIM_240_NAME) == 0
5084  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LMS_1XXX_NAME) == 0 // Check and todo: Can we use msg.angle_min for all lidars?
5085  || this->parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_MRS_1XXX_NAME) == 0) // Can we use this for all lidars where msg.angle_min is not 0?
5086  {
5087  angle = msg.angle_min - angleShift; // LMS-1xxx and MRS-1xxx have 4 interlaced layer with different start angle in each layer, start angle parsed from LMDscandata and set in msg.angle_min
5088  }
5089 
5090  float *cosAlphaTablePtr = &cosAlphaTable[0];
5091  float *sinAlphaTablePtr = &sinAlphaTable[0];
5092 
5093  float *vangPtr = NULL;
5094  float *azimuthPtr = NULL;
5095  float *rangeTmpPtr = &rangeTmp[0];
5096  if (vang_vec.size() > 0)
5097  {
5098  vangPtr = &vang_vec[0];
5099  }
5100  if (azimuth_vec.size() > 0)
5101  {
5102  azimuthPtr = &azimuth_vec[0];
5103  }
5104 
5105  size_t rangeNumPointcloudCurEcho = 0;
5106  for (size_t rangeIdxScan = 0; rangeIdxScan < rangeNum; rangeIdxScan++)
5107  {
5108  enum enum_index_descr
5109  {
5110  idx_x,
5111  idx_y,
5112  idx_z,
5113  idx_intensity,
5114  idx_num
5115  };
5116  long pointcloud_adroff = rangeNumPointcloudCurEcho * (numChannels * (int) sizeof(float));
5117  pointcloud_adroff += (layer - baseLayer) * cloud_.row_step;
5118  pointcloud_adroff += iEcho * cloud_.row_step * numTmpLayer;
5119  assert(pointcloud_adroff < cloud_.data.size()); // issue #49
5120 
5121  unsigned char *ptr = cloudDataPtr + pointcloud_adroff;
5122  float *fptr = (float *) (cloudDataPtr + pointcloud_adroff);
5123  float *fptr_polar = (float *) (cloudDataPtr_polar + pointcloud_adroff);
5124 
5125  float phi = angle; // azimuth angle
5126  float alpha = 0.0; // elevation angle
5127 
5128  if (vangPtr) // use elevation table for MRS6124
5129  {
5130  alpha = -vangPtr[rangeIdxScan] * deg2rad_const;
5131  }
5132  else
5133  {
5134  if (elevationPreCalculated) // FOR MRS6124 without VANGL
5135  {
5136  alpha = (float)elevationAngleInRad;
5137  }
5138  else
5139  {
5140  alpha = (float)(layer * elevationAngleDegree); // for MRS1104
5141  }
5142  }
5143 
5144  if (iEcho == 0)
5145  {
5146  cosAlphaTablePtr[rangeIdxScan] = cos(alpha); // for z-value (elevation)
5147  sinAlphaTablePtr[rangeIdxScan] = sin(alpha);
5148  }
5149  else
5150  {
5151  // Just for Debugging: printf("%3d %8.3lf %8.3lf\n", (int)rangeIdxScan, cosAlphaTablePtr[rangeIdxScan], sinAlphaTablePtr[rangeIdxScan]);
5152  }
5153 
5154  // Apply range filter
5155  float range_meter = rangeTmpPtr[iEcho * rangeNum + rangeIdxScan];
5156  bool range_modified = false;
5157  if (range_filter.apply(range_meter, range_modified)) // otherwise point dropped by range filter
5158  {
5159  // ROS_DEBUG_STREAM("alpha:" << alpha << " elevPreCalc:" << std::to_string(elevationPreCalculated) << " layer:" << layer << " elevDeg:" << elevationAngleDegree
5160  // << " numOfLayers:" << numOfLayers << " elevAngleX200:" << elevAngleX200);
5161 
5162  // Thanks to Sebastian Pütz <spuetz@uos.de> for his hint
5163  float rangeCos = range_meter * cosAlphaTablePtr[rangeIdxScan];
5164 
5165  double phi_used = phi + angleShift;
5166  if (this->angleCompensator != NULL)
5167  {
5168  phi_used = angleCompensator->compensateAngleInRadFromRos(phi_used);
5169  }
5170  if (azimuthPtr)
5171  {
5172  // ROS_DEBUG_STREAM("azimuth[" << rangeIdxScan << "] = " << std::fixed << std::setprecision(3) << (azimuthPtr[rangeIdxScan] * 180 / M_PI) << ", angle diff = " << ((azimuthPtr[rangeIdxScan] - phi_used) * 180 / M_PI));
5173  phi_used = azimuthPtr[rangeIdxScan]; // use azimuth table for MRS1xxx
5174  }
5175 
5176  // Cartesian pointcloud
5177  float phi2_used = phi_used + m_add_transform_xyz_rpy.azimuthOffset();
5178  fptr[idx_x] = rangeCos * (float)cos(phi2_used) * mirror_factor; // copy x value in pointcloud
5179  fptr[idx_y] = rangeCos * (float)sin(phi2_used) * mirror_factor; // copy y value in pointcloud
5180  fptr[idx_z] = range_meter * sinAlphaTablePtr[rangeIdxScan] * mirror_factor; // copy z value in pointcloud
5181 
5182  m_add_transform_xyz_rpy.applyTransform(fptr[idx_x], fptr[idx_y], fptr[idx_z]);
5183 
5184  // Polar pointcloud (sick_scan_xd API)
5185  fptr_polar[idx_x] = range_meter; // range in meter
5186  fptr_polar[idx_y] = phi_used; // azimuth in radians
5187  fptr_polar[idx_z] = alpha; // elevation in radians
5188 
5189  fptr[idx_intensity] = 0.0;
5190  if (config_.intensity)
5191  {
5192  int intensityIndex = aiValidEchoIdx[iEcho] * rangeNum + rangeIdxScan;
5193  // intensity values available??
5194  if (intensityIndex < intensityTmpNum)
5195  {
5196  fptr[idx_intensity] = intensityTmpPtr[intensityIndex]; // copy intensity value in pointcloud
5197  }
5198  }
5199  fptr_polar[idx_intensity] = fptr[idx_intensity];
5200  rangeNumPointcloudCurEcho++;
5201  }
5202  angle += msg.angle_increment;
5203  }
5204  rangeNumPointcloudAllEchos = std::max<size_t>(rangeNumPointcloudAllEchos, rangeNumPointcloudCurEcho);
5205 
5206  // Publish
5207  //static int cnt = 0;
5208  int layerOff = (layer - baseLayer);
5209 
5210  }
5211 
5212  bool shallIFire = false;
5213  if ((elevAngleX200 == 0) || (elevAngleX200 == 237))
5214  {
5215  shallIFire = true;
5216  }
5217  if(!m_scan_layer_filter_cfg.scan_layer_filter.empty()) // If scan_layer_filter is activated: fire when last activated layer received
5218  {
5219  int cur_layer = (layer - baseLayer);
5220  shallIFire = (cur_layer == m_scan_layer_filter_cfg.last_active_layer);
5221  // ROS_INFO_STREAM("scan_layer_filter activated: scan_layer_filter = " << m_scan_layer_filter_cfg.scan_layer_filter << ", elevAngleX200 = " << elevAngleX200 << ", layer = " << cur_layer << ", last_active_layer = " << m_scan_layer_filter_cfg.last_active_layer << ", shallIFire = " << shallIFire);
5222  }
5223 
5224  static int layerCnt = 0;
5225  static int layerSeq[4];
5226 
5227  if (config_.cloud_output_mode > 0)
5228  {
5229 
5230  layerSeq[layerCnt % 4] = layer;
5231  if (layerCnt >= 4) // mind. erst einmal vier Layer zusammensuchen
5232  {
5233  shallIFire = true; // here are at least 4 layers available
5234  }
5235  else
5236  {
5237  shallIFire = false;
5238  }
5239 
5240  layerCnt++;
5241  }
5242 
5243  if (shallIFire) // shall i fire the signal???
5244  {
5245  if (this->parser_->get_range_filter_config() == RangeFilterResultHandling::RANGE_FILTER_DROP && rangeNumPointcloudAllEchos < rangeNum)
5246  {
5247  // Points have been dropped, resize point cloud to number of points after applying the range filter
5248  range_filter.resizePointCloud(rangeNumPointcloudAllEchos, cloud_);
5249  range_filter.resizePointCloud(rangeNumPointcloudAllEchos, cloud_polar_);
5250  }
5251 
5252  sick_scan_xd::PointCloud2withEcho cloud_msg(&cloud_, numValidEchos, 0, cloud_topic_val);
5253  sick_scan_xd::PointCloud2withEcho cloud_msg_polar(&cloud_polar_, numValidEchos, 0, cloud_topic_val);
5254 #ifdef ROSSIMU
5255  notifyPolarPointcloudListener(nh, &cloud_msg_polar);
5256  notifyCartesianPointcloudListener(nh, &cloud_msg);
5257  // plotPointCloud(cloud_);
5258 #else
5259  // ROS_DEBUG_STREAM("publishing cloud " << cloud_.height << " x " << cloud_.width << " data, cloud_output_mode=" << config_.cloud_output_mode);
5260  if (config_.cloud_output_mode==0)
5261  {
5262  // standard handling of scans
5263  notifyPolarPointcloudListener(nh, &cloud_msg_polar);
5264  notifyCartesianPointcloudListener(nh, &cloud_msg);
5266  }
5267  else if (config_.cloud_output_mode == 2)
5268  {
5269  // Following cases are interesting:
5270  // LMS5xx: seq is always 0 -> publish every scan
5271  // MRS1104: Every 4th-Layer is 0 -> publish pointcloud every 4th layer message
5272  // LMS1104: Publish every layer. The timing for the LMS1104 seems to be:
5273  // Every 67 ms receiving of a new scan message
5274  // Scan message contains 367 measurements
5275  // angle increment is 0.75° (yields 274,5° covery -> OK)
5276  // MRS6124: Publish very 24th layer at the layer = 237 , MRS6124 contains no sequence with seq 0
5277  //BBB
5278  int numTotalShots = 360; //TODO where is this from ?
5279  int numPartialShots = 40; //
5280 
5281  for (int i = 0; i < numTotalShots; i += numPartialShots)
5282  {
5283  ros_sensor_msgs::PointCloud2 partialCloud;
5284  partialCloud = cloud_;
5285  rosTime partialTimeStamp = cloud_.header.stamp;
5286 
5287  partialTimeStamp = partialTimeStamp + rosDurationFromSec((i + 0.5 * (numPartialShots - 1)) * timeIncrement);
5288  partialTimeStamp = partialTimeStamp + rosDurationFromSec((3 * numTotalShots) * timeIncrement);
5289  partialCloud.header.stamp = partialTimeStamp;
5290  partialCloud.width = numPartialShots * 3; // die sind sicher in diesem Bereich
5291 
5292  int diffTo1100 = cloud_.width - 3 * (numTotalShots + i);
5293  if (diffTo1100 > numPartialShots)
5294  {
5295  diffTo1100 = numPartialShots;
5296  }
5297  if (diffTo1100 < 0)
5298  {
5299  diffTo1100 = 0;
5300  }
5301  partialCloud.width += diffTo1100;
5302  // printf("Offset: %4d Breite: %4d\n", i, partialCloud.width);
5303  partialCloud.height = 1;
5304 
5305 
5306  partialCloud.is_bigendian = false;
5307  partialCloud.is_dense = true;
5308  partialCloud.point_step = numChannels * sizeof(float);
5309  partialCloud.row_step = partialCloud.point_step * partialCloud.width;
5310  partialCloud.fields.resize(numChannels);
5311  for (int ii = 0; ii < numChannels; ii++)
5312  {
5313  std::string channelId[] = {"x", "y", "z", "intensity"};
5314  partialCloud.fields[ii].name = channelId[ii];
5315  partialCloud.fields[ii].offset = ii * sizeof(float);
5316  partialCloud.fields[ii].count = 1;
5317  partialCloud.fields[ii].datatype = ros_sensor_msgs::PointField::FLOAT32;
5318  }
5319 
5320  partialCloud.data.resize(partialCloud.row_step, 0);
5321 
5322  int partOff = 0;
5323  for (int j = 0; j < 4; j++)
5324  {
5325  int layerIdx = (j + (layerCnt)) % 4; // j = 0 -> oldest
5326  int rowIdx = 1 + layerSeq[layerIdx % 4]; // +1, da es bei -1 beginnt
5327  int colIdx = j * numTotalShots + i;
5328  int maxAvail = cloud_.width - colIdx; //
5329  if (maxAvail < 0)
5330  {
5331  maxAvail = 0;
5332  }
5333 
5334  if (maxAvail > numPartialShots)
5335  {
5336  maxAvail = numPartialShots;
5337  }
5338 
5339  // printf("Most recent LayerIdx: %2d RowIdx: %4d ColIdx: %4d\n", layer, rowIdx, colIdx);
5340  if (maxAvail > 0)
5341  {
5342  memcpy(&(partialCloud.data[partOff]),
5343  &(cloud_.data[(rowIdx * cloud_.width + colIdx + i) * cloud_.point_step]),
5344  cloud_.point_step * maxAvail);
5345 
5346  }
5347 
5348  partOff += maxAvail * partialCloud.point_step;
5349  }
5350  assert(partialCloud.data.size() == partialCloud.width * partialCloud.point_step);
5351 
5352 
5353  sick_scan_xd::PointCloud2withEcho partial_cloud_msg(&partialCloud, numValidEchos, 0, cloud_topic_val);
5354  notifyCartesianPointcloudListener(nh, &partial_cloud_msg);
5355  rosPublish(cloud_pub_, partialCloud);
5356  //memcpy(&(partialCloud.data[0]), &(cloud_.data[0]) + i * cloud_.point_step, cloud_.point_step * numPartialShots);
5357  //cloud_pub_.publish(partialCloud);
5358  }
5359  }
5360  // cloud_pub_.publish(cloud_);
5361 
5362 #endif
5363  }
5364  }
5365  }
5366  // Start Point
5367  if (dend != NULL)
5368  {
5369  buffer_pos = dend + 1;
5370  }
5371  } // end of while loop
5372  }
5373 
5374  // shall we process more data? I.e. are there more packets to process in the input queue???
5375 
5376  } while ((packetsInLoop > 0) && (numPacketsProcessed < maxNumAllowedPacketsToProcess));
5377  return ExitSuccess; // return success to continue looping
5378  }
5379 
5380 
5385  {
5386  if (conf.min_ang > conf.max_ang)
5387  {
5388  ROS_WARN("Maximum angle must be greater than minimum angle. Adjusting >min_ang<.");
5389  conf.min_ang = conf.max_ang;
5390  }
5391  }
5392 
5399  {
5400  check_angle_range(new_config);
5401  config_ = new_config;
5402  }
5403 
5404 #if defined USE_DYNAMIC_RECONFIGURE && __ROS_VERSION == 2
5405  rcl_interfaces::msg::SetParametersResult SickScanCommon::update_config_cb(const std::vector<rclcpp::Parameter> &parameters)
5406  {
5407  rcl_interfaces::msg::SetParametersResult result;
5408  result.successful = true;
5409  result.reason = "success";
5410  if(!parameters.empty())
5411  {
5412  SickScanConfig new_config = config_;
5413  // setConfigUpdateParam(new_config);
5414  for (const auto &parameter : parameters)
5415  {
5416  ROS_DEBUG_STREAM("SickScanCommon::update_config_cb(): parameter " << parameter.get_name());
5417  if(parameter.get_name() == "frame_id" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_STRING)
5418  new_config.frame_id = parameter.as_string();
5419  else if(parameter.get_name() == "imu_frame_id" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_STRING)
5420  new_config.imu_frame_id = parameter.as_string();
5421  else if(parameter.get_name() == "intensity" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
5422  new_config.intensity = parameter.as_bool();
5423  else if(parameter.get_name() == "auto_reboot" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
5424  new_config.auto_reboot = parameter.as_bool();
5425  else if(parameter.get_name() == "min_ang" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
5426  new_config.min_ang = parameter.as_double();
5427  else if(parameter.get_name() == "max_ang" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
5428  new_config.max_ang = parameter.as_double();
5429  else if(parameter.get_name() == "ang_res" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
5430  new_config.ang_res = parameter.as_double();
5431  else if(parameter.get_name() == "skip" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_INTEGER)
5432  new_config.skip = parameter.as_int();
5433  else if(parameter.get_name() == "sw_pll_only_publish" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
5434  new_config.sw_pll_only_publish = parameter.as_bool();
5435  else if(parameter.get_name() == "use_generation_timestamp" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
5436  new_config.use_generation_timestamp = parameter.as_bool();
5437  else if(parameter.get_name() == "time_offset" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
5438  new_config.time_offset = parameter.as_double();
5439  else if(parameter.get_name() == "cloud_output_mode" && parameter.get_type() == rclcpp::ParameterType::PARAMETER_INTEGER)
5440  new_config.cloud_output_mode = parameter.as_int();
5441  }
5442  // getConfigUpdateParam(new_config);
5443  update_config(new_config, 0);
5444  }
5445  return result;
5446  }
5447 #endif
5448 
5455  int SickScanCommon::convertAscii2BinaryCmd(const char *requestAscii, std::vector<unsigned char> *requestBinary)
5456  {
5457  requestBinary->clear();
5458  if (requestAscii == NULL)
5459  {
5460  return (-1);
5461  }
5462  int cmdLen = strlen(requestAscii);
5463  if (cmdLen == 0)
5464  {
5465  return (-1);
5466  }
5467  if (requestAscii[0] != 0x02)
5468  {
5469  return (-1);
5470  }
5471  if (requestAscii[cmdLen - 1] != 0x03)
5472  {
5473  return (-1);
5474  }
5475  // Here we know, that the ascii format is correctly framed with <stx> .. <etx>
5476  for (int i = 0; i < 4; i++)
5477  {
5478  requestBinary->push_back(0x02);
5479  }
5480 
5481  for (int i = 0; i < 4; i++) // Puffer for Length identifier
5482  {
5483  requestBinary->push_back(0x00);
5484  }
5485 
5486  unsigned long msgLen = cmdLen - 2; // without stx and etx
5487 
5488  // special handling for the following commands
5489  // due to higher number of command arguments
5490  std::string keyWord0 = "sMN SetAccessMode";
5491  std::string keyWord1 = "sWN FREchoFilter";
5492  std::string keyWord2 = "sEN LMDscandata";
5493  std::string keyWord3 = "sWN LMDscandatacfg";
5494  std::string keyWord4 = "sWN SetActiveApplications"; // "sWN SetActiveApplications 2 FEVL <0|1> RANG 1" for MRS-1xxx with firmware >= 2.x
5495  std::string keyWord5 = "sEN IMUData";
5496  std::string keyWord6 = "sWN EIIpAddr";
5497  std::string keyWord7 = "sMN mLMPsetscancfg";
5498  std::string keyWord8 = "sWN TSCTCupdatetime";
5499  std::string keyWord9 = "sWN TSCTCSrvAddr";
5500  std::string keyWord10 = "sWN LICencres";
5501  std::string keyWord11 = "sWN LFPmeanfilter";
5502  std::string KeyWord12 = "sRN field";
5503  std::string KeyWord13 = "sMN mCLsetscancfglist";
5504  std::string KeyWord14 = "sWN LFPmeanfilter"; // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmeanfilter" + { 1 byte 0|1 active/inactive } + { 2 byte 0x02 ... 0x64 number of scans } + { 1 byte 0x00 }
5505  std::string KeyWord15 = "sWN LFPmedianfilter"; // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmedianfilter" (3x1 median filter) + { 1 byte 0|1 active/inactive } + { 2 byte 0x03 }
5506  std::string KeyWord16 = "sWN LMDscandatascalefactor"; // LRS4xxx: "sWN LMDscandatascalefactor" + { 4 byte float }, e.g. scalefactor 1.0f = 0x3f800000, scalefactor 2.0f = 0x40000000
5507  std::string KeyWord17 = "sWN GlareDetectionSens"; // LRS4xxx: "sWN GlareDetectionSens" + { 1 byte sensitivity } + { 2 byte 0x03 }
5508  std::string KeyWord18 = "sWN ScanLayerFilter"; // MRS-1000 scan layer activation mask, "sWN ScanLayerFilter <number of layers> <layer 1: on/off> … <layer N: on/off>", default: all layer activated: "sWN ScanLayerFilter 4 1 1 1 1"
5509  std::string KeyWord19 = "sMN mNPOSGetData"; // NAV-350 poll data: "sMN mNPOSGetData 1 2" (sopas arguments: wait = 1, i.e. wait for next pose result), mask = 2, i.e. send pose+reflectors+scan)
5510  std::string KeyWord20 = "sMN mNPOSSetPose"; // Set NAV-350 start pose in navigation mode by "sMN mNPOSSetPose X Y Phi"
5511  std::string KeyWord21 = "sWN NEVACurrLayer";
5512  std::string KeyWord22 = "sWN NMAPMapCfg";
5513  std::string KeyWord23 = "sWN NLMDReflSize";
5514  std::string KeyWord24 = "sWN NPOSPoseDataFormat";
5515  std::string KeyWord25 = "sWN NLMDLandmarkDataFormat";
5516  std::string KeyWord26 = "sWN NAVScanDataFormat";
5517  std::string KeyWord27 = "sMN mNLAYEraseLayout";
5518  std::string KeyWord28 = "sWN ActiveFieldSet"; // "\x02sWN ActiveFieldSet %02d\x03"
5519 
5520  //BBB
5521 
5522  std::string cmdAscii = requestAscii;
5523 
5524 
5525  int copyUntilSpaceCnt = 2;
5526  int spaceCnt = 0;
5527  char hexStr[255] = {0};
5528  int level = 0;
5529  unsigned char buffer[255];
5530  int bufferLen = 0;
5531  if (cmdAscii.find(keyWord0) != std::string::npos) // SetAccessMode
5532  {
5533  copyUntilSpaceCnt = 2;
5534  int keyWord0Len = keyWord0.length();
5535  sscanf(requestAscii + keyWord0Len + 1, " %d %s", &level, hexStr);
5536  buffer[0] = (unsigned char) (0xFF & level);
5537  bufferLen = 1;
5538  char hexTmp[3] = {0};
5539  for (int i = 0; i < 4; i++)
5540  {
5541  int val;
5542  hexTmp[0] = hexStr[i * 2];
5543  hexTmp[1] = hexStr[i * 2 + 1];
5544  hexTmp[2] = 0x00;
5545  sscanf(hexTmp, "%x", &val);
5546  buffer[i + 1] = (unsigned char) (0xFF & val);
5547  bufferLen++;
5548  }
5549  }
5550  if (cmdAscii.find(keyWord1) != std::string::npos)
5551  {
5552  int echoCodeNumber = 0;
5553  int keyWord1Len = keyWord1.length();
5554  sscanf(requestAscii + keyWord1Len + 1, " %d", &echoCodeNumber);
5555  buffer[0] = (unsigned char) (0xFF & echoCodeNumber);
5556  bufferLen = 1;
5557  }
5558  if (cmdAscii.find(keyWord2) != std::string::npos)
5559  {
5560  int scanDataStatus = 0;
5561  int keyWord2Len = keyWord2.length();
5562  sscanf(requestAscii + keyWord2Len + 1, " %d", &scanDataStatus);
5563  buffer[0] = (unsigned char) (0xFF & scanDataStatus);
5564  bufferLen = 1;
5565  }
5566 
5567  if (cmdAscii.find(keyWord3) != std::string::npos)
5568  {
5569  int scanDataStatus = 0;
5570  int keyWord3Len = keyWord3.length();
5571  int dummyArr[12] = {0};
5572  // sWN LMDscandatacfg %d 0 %d %d 0 %d 0 0 0 0 %d 1
5573  int sscanfresult = sscanf(requestAscii + keyWord3Len + 1, " %d %d %d %d %d %d %d %d %d %d %d %d",
5574  &dummyArr[0], // Data Channel Idx LSB
5575  &dummyArr[1], // Data Channel Idx MSB
5576  &dummyArr[2], // Remission
5577  &dummyArr[3], // Remission data format
5578  &dummyArr[4], // Unit
5579  &dummyArr[5], // Encoder Setting LSB
5580  &dummyArr[6], // Encoder Setting MSB
5581  &dummyArr[7], // Position
5582  &dummyArr[8], // Send Name
5583  &dummyArr[9], // Send Comment
5584  &dummyArr[10], // Time information
5585  &dummyArr[11]); // n-th Scan (packed - not sent as single byte sequence) !!!
5586  if (1 < sscanfresult)
5587  {
5588 
5589  for (int i = 0; i < 13; i++)
5590  {
5591  buffer[i] = 0x00;
5592  }
5593  buffer[0] = (unsigned char) (0xFF & dummyArr[0]); //Data Channel 2 Bytes
5594  buffer[1] = (unsigned char) (0xFF & dummyArr[1]);; // MSB of Data Channel (here Little Endian!!)
5595  buffer[2] = (unsigned char) (0xFF & dummyArr[2]); // Remission
5596  buffer[3] = (unsigned char) (0xFF & dummyArr[3]); // Remission data format 0=8 bit 1= 16 bit
5597  buffer[4] = (unsigned char) (0xFF & dummyArr[4]); //Unit of remission data
5598  buffer[5] = (unsigned char) (0xFF & dummyArr[5]); //encoder Data LSB
5599  buffer[6] = (unsigned char) (0xFF & dummyArr[6]); //encoder Data MSB
5600  buffer[7] = (unsigned char) (0xFF & dummyArr[7]); // Position
5601  buffer[8] = (unsigned char) (0xFF & dummyArr[8]); // Send Scanner Name
5602  buffer[9] = (unsigned char) (0xFF & dummyArr[9]); // Comment
5603  buffer[10] = (unsigned char) (0xFF & dummyArr[10]); // Time information
5604  buffer[11] = (unsigned char) (0xFF & (dummyArr[11] >> 8)); // BIG Endian High Byte nth-Scan
5605  buffer[12] = (unsigned char) (0xFF & (dummyArr[11] >> 0)); // BIG Endian Low Byte nth-Scan
5606  bufferLen = 13;
5607 
5608  }
5609 
5610  }
5611 
5612  if (cmdAscii.find(keyWord4) != std::string::npos) // "sWN SetActiveApplications 1 FEVL 1" or "sWN SetActiveApplications 1 RANG 1"
5613  {
5614  char tmpStr[1024] = {0};
5615  char szApplStr[255] = {0};
5616  int keyWord4Len = keyWord4.length();
5617  int scanDataStatus = 0;
5618  int dummy0, dummy1;
5619  strcpy(tmpStr, requestAscii + keyWord4Len + 2);
5620  sscanf(tmpStr, "%d %s %d", &dummy0, szApplStr, &dummy1);
5621  // rebuild string
5622  buffer[0] = 0x00;
5623  buffer[1] = dummy0 ? 0x01 : 0x00;
5624  for (int ii = 0; ii < 4; ii++)
5625  {
5626  buffer[2 + ii] = szApplStr[ii]; // idx: 1,2,3,4
5627  }
5628  buffer[6] = dummy1 ? 0x01 : 0x00;
5629  bufferLen = 7;
5630  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_MRS_1XXX_NAME) == 0) // activate FEVL and RANG in case of MRS1xxx with firmware version > 1
5631  {
5632  std::vector<int> version_id = parseFirmwareVersion("MRS1xxx", deviceIdentStr); // Get MRS1xxx version from device ident string
5633  if (version_id[0] > 1)
5634  {
5635  // buffer[6] = 0x01; // MRS1xxx with firmware version > 1 supports RANG+FEVL -> overwrite with "<STX>sWN{SPC}SetActiveApplications{SPC}1{SPC}FEVL{SPC}1<ETX>"
5636  // MRS1xxx with firmware version > 1 supports RANG+FEVL -> overwrite with "<STX>sWN{SPC}SetActiveApplications{SPC}2{SPC}FEVL{SPC}1{SPC}RANG{SPC}1<ETX>"
5637  // resp. binary "sWN SetActiveApplications \00\02\46\45\56\4C\01\52\41\4e\47\01"
5638  uint8_t field_evaluation_status = isFieldEvaluationActive() ? 0x01: 0x00;
5639  std::vector<uint8_t> binary_parameter = {0x00, 0x02, 0x46, 0x45, 0x56, 0x4C, field_evaluation_status, 0x52, 0x41, 0x4e, 0x47, 0x01};
5640  for (int ii = 0; ii < binary_parameter.size(); ii++)
5641  buffer[ii] = binary_parameter[ii];
5642  bufferLen = binary_parameter.size();
5643  }
5644  // Disable scandatacfg_azimuth_table if firmware version is < 2.2
5645  if (version_id[0] < 2)
5647  if (version_id[0] == 2 && version_id[1] < 2)
5649  }
5651  {
5652  std::vector<int> version_id = parseFirmwareVersion("LMS1xxx", deviceIdentStr); // Get LMS1xxx version from device ident string
5653  // Disable scandatacfg_azimuth_table if firmware version is < 2.2
5654  if (version_id[0] < 2)
5656  if (version_id[0] == 2 && version_id[1] < 2)
5658  }
5659  }
5660 
5661  if (cmdAscii.find(keyWord5) != std::string::npos)
5662  {
5663  int imuSetStatus = 0;
5664  int keyWord5Len = keyWord5.length();
5665  sscanf(requestAscii + keyWord5Len + 1, " %d", &imuSetStatus);
5666  buffer[0] = (unsigned char) (0xFF & imuSetStatus);
5667  bufferLen = 1;
5668  }
5669 
5670  if (cmdAscii.find(keyWord6) != std::string::npos)
5671  {
5672  int adrPartArr[4];
5673  int imuSetStatus = 0;
5674  int keyWord6Len = keyWord6.length();
5675  sscanf(requestAscii + keyWord6Len + 1, " %x %x %x %x", &(adrPartArr[0]), &(adrPartArr[1]), &(adrPartArr[2]),
5676  &(adrPartArr[3]));
5677  buffer[0] = (unsigned char) (0xFF & adrPartArr[0]);
5678  buffer[1] = (unsigned char) (0xFF & adrPartArr[1]);
5679  buffer[2] = (unsigned char) (0xFF & adrPartArr[2]);
5680  buffer[3] = (unsigned char) (0xFF & adrPartArr[3]);
5681  bufferLen = 4;
5682  }
5683  //\x02sMN mLMPsetscancfg %d 1 %d 0 0\x03";
5684  //02 02 02 02 00 00 00 25 73 4D 4E 20 6D 4C 4D 50 73 65 74 73 63 61 6E 63 66 67 20
5685  // 00 00 13 88 4byte freq
5686  // 00 01 2 byte sectors always 1
5687  // 00 00 13 88 ang_res
5688  // FF F9 22 30 sector start always 0
5689  // 00 22 55 10 sector stop always 0
5690  // 21
5691  if (cmdAscii.find(keyWord7) != std::string::npos)
5692  {
5693 #if 1
5694  bufferLen = 0;
5695  for (int i = keyWord7.length() + 2, i_max = strlen(requestAscii) - 1; i + 3 < i_max && bufferLen < sizeof(buffer); i += 4, bufferLen++)
5696  {
5697  char hex_str[] = { requestAscii[i + 2], requestAscii[i + 3], '\0' };
5698  buffer[bufferLen] = (std::stoul(hex_str, nullptr, 16) & 0xFF);
5699  }
5700 #else
5702  {
5703  {
5704  bufferLen = 18;
5705  for (int i = 0; i < bufferLen; i++)
5706  {
5707  unsigned char uch = 0x00;
5708  switch (i)
5709  {
5710  case 5:
5711  uch = 0x01;
5712  break;
5713  }
5714  buffer[i] = uch;
5715  }
5716  char tmpStr[1024] = {0};
5717  char szApplStr[255] = {0};
5718  int keyWord7Len = keyWord7.length();
5719  int scanDataStatus = 0;
5720  int dummy0, dummy1;
5721  strcpy(tmpStr, requestAscii + keyWord7Len + 2);
5722  sscanf(tmpStr, "%d 1 %d", &dummy0, &dummy1);
5723 
5724  buffer[0] = (unsigned char) (0xFF & (dummy0 >> 24));
5725  buffer[1] = (unsigned char) (0xFF & (dummy0 >> 16));
5726  buffer[2] = (unsigned char) (0xFF & (dummy0 >> 8));
5727  buffer[3] = (unsigned char) (0xFF & (dummy0 >> 0));
5728 
5729 
5730  buffer[6] = (unsigned char) (0xFF & (dummy1 >> 24));
5731  buffer[7] = (unsigned char) (0xFF & (dummy1 >> 16));
5732  buffer[8] = (unsigned char) (0xFF & (dummy1 >> 8));
5733  buffer[9] = (unsigned char) (0xFF & (dummy1 >> 0));
5734  }
5735  }
5736  else
5737  {
5738  int keyWord7Len = keyWord7.length();
5739  int dummyArr[14] = {0};
5740  if (14 == sscanf(requestAscii + keyWord7Len + 1, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
5741  &dummyArr[0], &dummyArr[1], &dummyArr[2],
5742  &dummyArr[3], &dummyArr[4], &dummyArr[5],
5743  &dummyArr[6], &dummyArr[7], &dummyArr[8],
5744  &dummyArr[9], &dummyArr[10], &dummyArr[11], &dummyArr[12], &dummyArr[13]))
5745  {
5746  for (int i = 0; i < 54; i++)
5747  {
5748  buffer[i] = 0x00;
5749  }
5750  int targetPosArr[] = {0, 4, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54};
5751  int numElem = (sizeof(targetPosArr) / sizeof(targetPosArr[0])) - 1;
5752  for (int i = 0; i < numElem; i++)
5753  {
5754  int lenOfBytesToRead = targetPosArr[i + 1] - targetPosArr[i];
5755  int adrPos = targetPosArr[i];
5756  unsigned char *destPtr = buffer + adrPos;
5757  memcpy(destPtr, &(dummyArr[i]), lenOfBytesToRead);
5758  swap_endian(destPtr, lenOfBytesToRead);
5759  }
5760  bufferLen = targetPosArr[numElem];
5761  /*
5762  * 00 00 03 20 00 01
5763  00 00 09 C4 00 00 00 00 00 36 EE 80 00 00 09 C4 00 00 00 00 00 00 00 00 00 00 09 C4 00 00 00 00 00
5764  00 00 00 00 00 09 C4 00 00 00 00 00 00 00 00 00 00 09 C4 00 00 00 00 00 00 00 00 E4
5765  */
5766 
5767  }
5768  }
5769 #endif
5770 
5771  }
5772  if (cmdAscii.find(keyWord8) != std::string::npos)
5773  {
5774  uint32_t updatetime = 0;
5775  int keyWord8Len = keyWord8.length();
5776  sscanf(requestAscii + keyWord8Len + 1, " %d", &updatetime);
5777  buffer[0] = (unsigned char) (0xFF & (updatetime >> 24));
5778  buffer[1] = (unsigned char) (0xFF & (updatetime >> 16));
5779  buffer[2] = (unsigned char) (0xFF & (updatetime >> 8));
5780  buffer[3] = (unsigned char) (0xFF & (updatetime >> 0));
5781  bufferLen = 4;
5782  }
5783  if (cmdAscii.find(keyWord9) != std::string::npos)
5784  {
5785  int adrPartArr[4];
5786  int imuSetStatus = 0;
5787  int keyWord9Len = keyWord9.length();
5788  sscanf(requestAscii + keyWord9Len + 1, " %x %x %x %x", &(adrPartArr[0]), &(adrPartArr[1]), &(adrPartArr[2]),
5789  &(adrPartArr[3]));
5790  buffer[0] = (unsigned char) (0xFF & adrPartArr[0]);
5791  buffer[1] = (unsigned char) (0xFF & adrPartArr[1]);
5792  buffer[2] = (unsigned char) (0xFF & adrPartArr[2]);
5793  buffer[3] = (unsigned char) (0xFF & adrPartArr[3]);
5794  bufferLen = 4;
5795  }
5796  if (cmdAscii.find(keyWord10) != std::string::npos)
5797  {
5798  float EncResolution = 0;
5799  bufferLen = 4;
5800  int keyWord10Len = keyWord10.length();
5801  sscanf(requestAscii + keyWord10Len + 1, " %f", &EncResolution);
5802  memcpy(buffer, &EncResolution, bufferLen);
5803  swap_endian(buffer, bufferLen);
5804 
5805  }
5806  if (cmdAscii.find(keyWord11) != std::string::npos)
5807  {
5808  char tmpStr[1024] = {0};
5809  char szApplStr[255] = {0};
5810  int keyWord11Len = keyWord11.length();
5811  int dummy0, dummy1,dummy2;
5812  strcpy(tmpStr, requestAscii + keyWord11Len + 2);
5813  sscanf(tmpStr, "%d %d %d", &dummy0, &dummy1, &dummy2);
5814  // rebuild string
5815  buffer[0] = dummy0 ? 0x01 : 0x00;
5816  buffer[1] =dummy1/256;//
5817  buffer[2] =dummy1%256;//
5818  buffer[3] =dummy2;
5819  bufferLen = 4;
5820  }
5821  if (cmdAscii.find(KeyWord12) != std::string::npos)
5822  {
5823  uint32_t fieldID = 0;
5824  int keyWord12Len = KeyWord12.length();
5825  sscanf(requestAscii + keyWord12Len + 1, "%d", &fieldID);
5826  bufferLen = 0;
5827  }
5828  if (cmdAscii.find(KeyWord13) != std::string::npos)
5829  {
5830  int scanCfgListEntry = 0;
5831  int keyWord13Len = KeyWord13.length();
5832  sscanf(requestAscii + keyWord13Len + 1, " %d", &scanCfgListEntry);
5833  buffer[0] = (unsigned char) (0xFF & scanCfgListEntry);
5834  bufferLen = 1;
5835  }
5836  if (cmdAscii.find(KeyWord14) != std::string::npos) // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmeanfilter" + { 1 byte 0|1 active/inactive } + { 2 byte 0x02 ... 0x64 number of scans } + { 1 byte 0x00 }
5837  {
5838  // ROS_INFO_STREAM("convertAscii2BinaryCmd: requestAscii=" << requestAscii);
5839  int args[3] = { 0, 0, 0 };
5840  sscanf(requestAscii + KeyWord14.length() + 1, " %d %d %d", &(args[0]), &(args[1]), &(args[2]));
5841  buffer[0] = (unsigned char) (0xFF & args[0]);
5842  buffer[1] = (unsigned char) (0xFF & (args[1] >> 8));
5843  buffer[2] = (unsigned char) (0xFF & (args[1] >> 0));
5844  buffer[3] = (unsigned char) (0xFF & args[2]);
5845  bufferLen = 4;
5846  }
5847  if (cmdAscii.find(KeyWord15) != std::string::npos) // MRS1xxx, LMS1xxx, LMS4xxx, LRS4xxx: "sWN LFPmedianfilter" (3x1 median filter) + { 1 byte 0|1 active/inactive } + { 2 byte 0x03 }
5848  {
5849  // ROS_INFO_STREAM("convertAscii2BinaryCmd: requestAscii=" << requestAscii);
5850  int args[2] = { 0, 0 };
5851  sscanf(requestAscii + KeyWord15.length() + 1, " %d %d", &(args[0]), &(args[1]));
5852  buffer[0] = (unsigned char) (0xFF & args[0]);
5853  buffer[1] = (unsigned char) (0xFF & (args[1] >> 8));
5854  buffer[2] = (unsigned char) (0xFF & (args[1] >> 0));
5855  bufferLen = 3;
5856  }
5857  if (cmdAscii.find(KeyWord16) != std::string::npos) // LRS4xxx: "sWN LMDscandatascalefactor" + { 4 byte float }, e.g. scalefactor 1.0f = 0x3f800000, scalefactor 2.0f = 0x40000000
5858  {
5859  // ROS_INFO_STREAM("convertAscii2BinaryCmd: requestAscii=" << requestAscii);
5860  uint32_t args = 0;
5861  sscanf(requestAscii + KeyWord16.length() + 1, " %x", &args);
5862  buffer[0] = (unsigned char) (0xFF & (args >> 24));
5863  buffer[1] = (unsigned char) (0xFF & (args >> 16));
5864  buffer[2] = (unsigned char) (0xFF & (args >> 8));
5865  buffer[3] = (unsigned char) (0xFF & (args >> 0));
5866  bufferLen = 4;
5867  }
5868  if (cmdAscii.find(KeyWord17) != std::string::npos) // LRS4xxx: "sWN GlareDetectionSens" + { 1 byte sensitivity } + { 2 byte 0x03 }
5869  {
5870  // ROS_INFO_STREAM("convertAscii2BinaryCmd: requestAscii=" << requestAscii);
5871  int args[1] = { 0 };
5872  sscanf(requestAscii + KeyWord17.length() + 1, " %d", &(args[0]));
5873  buffer[0] = (unsigned char) (0xFF & args[0]);
5874  bufferLen = 1;
5875  }
5876  if (cmdAscii.find(KeyWord18) != std::string::npos && strlen(requestAscii) > KeyWord18.length() + 1) // MRS-1000 scan layer activation mask, "sWN ScanLayerFilter <number of layers> <layer 1: on/off> … <layer N: on/off>", default: all layer activated: "sWN ScanLayerFilter 4 1 1 1 1"
5877  {
5878  // Convert ascii integer args to binary, e.g. "4 1 1 1 1" to 0x000401010101
5879  ScanLayerFilterCfg scan_filter_cfg(requestAscii + KeyWord18.length() + 1);
5880  int num_layers = scan_filter_cfg.scan_layer_activated.size();
5881  if (num_layers > 0)
5882  {
5883  bufferLen = 0;
5884  // 2 byte <number of layers>
5885  buffer[bufferLen++] = (unsigned char) (0xFF & (num_layers >> 8));
5886  buffer[bufferLen++] = (unsigned char) (0xFF & num_layers);
5887  for(int n = 0; n < num_layers; n++)
5888  buffer[bufferLen++] = (unsigned char) (0xFF & (scan_filter_cfg.scan_layer_activated[n])); // 1 byte <layer on/off>
5889  }
5890  }
5891  if (cmdAscii.find(KeyWord19) != std::string::npos && strlen(requestAscii) > KeyWord19.length() + 1)// NAV-350 poll data: "sMN mNPOSGetData 1 2" (sopas arguments: wait = 1, i.e. wait for next pose result), mask = 2, i.e. send pose+reflectors+scan)
5892  {
5893  // Convert ascii integer args to binary, e.g. "1 2" to 0x0102
5894  int args[2] = { 0, 0 };
5895  sscanf(requestAscii + KeyWord19.length() + 1, " %d %d", &(args[0]), &(args[1]));
5896  buffer[0] = (unsigned char) (0xFF & args[0]);
5897  buffer[1] = (unsigned char) (0xFF & args[1]);
5898  bufferLen = 2;
5899  }
5900  if (cmdAscii.find(KeyWord20) != std::string::npos && strlen(requestAscii) > KeyWord20.length() + 1) // Set NAV-350 start pose in navigation mode by "sMN mNPOSSetPose X Y Phi", 3 arguments, each int32_t
5901  {
5902  int32_t args[3] = { 0, 0, 0 };
5903  sscanf(requestAscii + KeyWord20.length() + 1, " %d %d %d", &(args[0]), &(args[1]), &(args[2]));
5904  bufferLen = 0;
5905  for(int arg_cnt = 0; arg_cnt < 3; arg_cnt++)
5906  {
5907  buffer[bufferLen + 0] = (unsigned char) (0xFF & (args[arg_cnt] >> 24));
5908  buffer[bufferLen + 1] = (unsigned char) (0xFF & (args[arg_cnt] >> 16));
5909  buffer[bufferLen + 2] = (unsigned char) (0xFF & (args[arg_cnt] >> 8));
5910  buffer[bufferLen + 3] = (unsigned char) (0xFF & (args[arg_cnt] >> 0));
5911  bufferLen += 4;
5912  }
5913  }
5914  if (cmdAscii.find(KeyWord21) != std::string::npos && strlen(requestAscii) > KeyWord21.length() + 1) // "sWN NEVACurrLayer 0"
5915  {
5916  int args[1] = { 0 };
5917  sscanf(requestAscii + KeyWord21.length() + 1, " %d", &(args[0]));
5918  buffer[0] = (unsigned char) (0xFF & (args[0] >> 8));
5919  buffer[1] = (unsigned char) (0xFF & (args[0] >> 0));
5920  bufferLen = 2;
5921  }
5922  if (cmdAscii.find(KeyWord22) != std::string::npos && strlen(requestAscii) > KeyWord22.length() + 1) // "sWN NMAPMapCfg 50 0 0 0 0";
5923  {
5924  int args[5] = { 0, 0, 0, 0, 0 };
5925  sscanf(requestAscii + KeyWord22.length() + 1, " %d %d %d %d %d", &(args[0]), &(args[1]), &(args[2]), &(args[3]), &(args[4]));
5926  buffer[0] = (unsigned char) (0xFF & args[0]);
5927  buffer[1] = (unsigned char) (0xFF & args[1]);
5928  bufferLen = 2;
5929  for(int arg_cnt = 2; arg_cnt < 5; arg_cnt++)
5930  {
5931  buffer[bufferLen + 0] = (unsigned char) (0xFF & (args[arg_cnt] >> 24));
5932  buffer[bufferLen + 1] = (unsigned char) (0xFF & (args[arg_cnt] >> 16));
5933  buffer[bufferLen + 2] = (unsigned char) (0xFF & (args[arg_cnt] >> 8));
5934  buffer[bufferLen + 3] = (unsigned char) (0xFF & (args[arg_cnt] >> 0));
5935  bufferLen += 4;
5936  }
5937  }
5938  if (cmdAscii.find(KeyWord23) != std::string::npos && strlen(requestAscii) > KeyWord23.length() + 1) // "sWN NLMDReflSize 80";
5939  {
5940  int args[1] = { 0 };
5941  sscanf(requestAscii + KeyWord23.length() + 1, " %d", &(args[0]));
5942  buffer[0] = (unsigned char) (0xFF & (args[0] >> 8));
5943  buffer[1] = (unsigned char) (0xFF & (args[0] >> 0));
5944  bufferLen = 2;
5945  }
5946  if (cmdAscii.find(KeyWord24) != std::string::npos && strlen(requestAscii) > KeyWord24.length() + 1) // "NPOSPoseDataFormat 1 1";
5947  {
5948  int args[2] = { 0, 0 };
5949  sscanf(requestAscii + KeyWord24.length() + 1, " %d %d", &(args[0]), &(args[1]));
5950  buffer[0] = (unsigned char) (0xFF & (args[0]));
5951  buffer[1] = (unsigned char) (0xFF & (args[1]));
5952  bufferLen = 2;
5953  }
5954 
5955  if (cmdAscii.find(KeyWord25) != std::string::npos && strlen(requestAscii) > KeyWord25.length() + 1) // "sWN NLMDLandmarkDataFormat 0 1 1"
5956  {
5957  int args[3] = { 0, 0, 0 };
5958  sscanf(requestAscii + KeyWord25.length() + 1, " %d %d %d", &(args[0]), &(args[1]), &(args[2]));
5959  buffer[0] = (unsigned char) (0xFF & (args[0]));
5960  buffer[1] = (unsigned char) (0xFF & (args[1]));
5961  buffer[2] = (unsigned char) (0xFF & (args[2]));
5962  bufferLen = 3;
5963  }
5964  if (cmdAscii.find(KeyWord26) != std::string::npos && strlen(requestAscii) > KeyWord26.length() + 1) // "sWN NAVScanDataFormat 1 1"
5965  {
5966  int args[2] = { 0, 0 };
5967  sscanf(requestAscii + KeyWord26.length() + 1, " %d %d", &(args[0]), &(args[1]));
5968  buffer[0] = (unsigned char) (0xFF & (args[0]));
5969  buffer[1] = (unsigned char) (0xFF & (args[1]));
5970  bufferLen = 2;
5971  }
5972  if (cmdAscii.find(KeyWord27) != std::string::npos && strlen(requestAscii) > KeyWord27.length() + 1) // "sMN mNLAYEraseLayout 1"
5973  {
5974  int args[1] = { 0 };
5975  sscanf(requestAscii + KeyWord27.length() + 1, " %d", &(args[0]));
5976  buffer[0] = (unsigned char) (0xFF & (args[0]));
5977  bufferLen = 1;
5978  }
5979  if (cmdAscii.find(KeyWord28) != std::string::npos && strlen(requestAscii) > KeyWord28.length() + 1) // "sWN ActiveFieldSet %02d"
5980  {
5981  int args[1] = { 0 };
5982  sscanf(requestAscii + KeyWord28.length() + 1, " %d", &(args[0]));
5983  buffer[0] = (unsigned char) (0xFF & (args[0] >> 8));
5984  buffer[1] = (unsigned char) (0xFF & (args[0] >> 0));
5985  bufferLen = 2;
5986  }
5987 
5988  // copy base command string to buffer
5989  bool switchDoBinaryData = false;
5990  for (int i = 1; i <= (int) (msgLen); i++) // STX DATA ETX --> 0 1 2
5991  {
5992  char c = requestAscii[i];
5993  if (switchDoBinaryData == true)
5994  {
5995  if (0 == bufferLen) // no keyword handling before this point
5996  {
5997  if (c >= '0' && c <= '9') // standard data format expected - only one digit
5998  {
5999  c -= '0'; // convert ASCII-digit to hex-digit
6000  } // 48dez to 00dez and so on.
6001  }
6002  else
6003  {
6004  break;
6005  }
6006  }
6007  requestBinary->push_back(c);
6008  if (requestAscii[i] == 0x20) // space
6009  {
6010  spaceCnt++;
6011  if (spaceCnt >= copyUntilSpaceCnt)
6012  {
6013  switchDoBinaryData = true;
6014  }
6015  }
6016 
6017  }
6018  // if there are already process bytes (due to special key word handling)
6019  // copy these data bytes to the buffer
6020  for (int i = 0; i < bufferLen; i++) // append variable data
6021  {
6022  requestBinary->push_back(buffer[i]);
6023  }
6024 
6025  setLengthAndCRCinBinarySopasRequest(requestBinary);
6026 
6027  return (0);
6028 
6029  };
6030 
6031  void SickScanCommon::setLengthAndCRCinBinarySopasRequest(std::vector<uint8_t>* requestBinary)
6032  {
6033 
6034  int msgLen = (int)requestBinary->size(); // requestBinary = { 4 byte 0x02020202 } + { 4 byte placeholder for payload length } + { payload }
6035  msgLen -= 8;
6036  for (int i = 0; i < 4; i++)
6037  {
6038  (*requestBinary)[4 + i] = (uint8_t)((msgLen >> (3 - i) * 8) & 0xFF); // payload length is always 4 byte big endian encoded
6039  }
6040  unsigned char xorVal = 0x00;
6041  xorVal = sick_crc8((unsigned char *) (&((*requestBinary)[8])), requestBinary->size() - 8);
6042  requestBinary->push_back(xorVal);
6043 #if 0
6044  for (int i = 0; i < requestBinary->size(); i++)
6045  {
6046  unsigned char c = (*requestBinary)[i];
6047  printf("[%c]%02x ", (c < ' ') ? '.' : c, c) ;
6048  }
6049  printf("\n");
6050 #endif
6051  }
6052 
6060  {
6061  bool retValue = true;
6062  bool shouldUseBinary = this->parser_->getCurrentParamPtr()->getUseBinaryProtocol();
6063  if (shouldUseBinary == useBinaryCmdNow)
6064  {
6065  retValue = true; // !!!! CHANGE ONLY FOR TEST!!!!!
6066  }
6067  else
6068  {
6069  /*
6070  // we must reconnect and set the new protocoltype
6071  int iRet = this->close_device();
6072  ROS_INFO("SOPAS - Close and reconnected to scanner due to protocol change and wait 15 sec. ");
6073  ROS_INFO_STREAM("SOPAS - Changing from " << shouldUseBinary ? "ASCII" : "BINARY" << " to " << shouldUseBinary ? "BINARY" : "ASCII" << "\n");
6074  // Wait a few seconds after rebooting
6075  rosSleep(15.0);
6076 
6077  iRet = this->init_device();
6078  */
6079  if (shouldUseBinary == true)
6080  {
6081  this->setProtocolType(CoLa_B);
6082  }
6083  else
6084  {
6085  this->setProtocolType(CoLa_A);
6086  }
6087 
6088  useBinaryCmdNow = shouldUseBinary;
6089  retValue = false;
6090  }
6091  return (retValue);
6092  }
6093 
6094 
6095  bool SickScanCommon::setNewIpAddress(const std::string& ipNewIPAddr, bool useBinaryCmd)
6096  {
6097  int eepwritetTimeOut = 1;
6098  bool result = false;
6099 
6100 
6101  unsigned long adrBytesLong[4];
6102  std::string s = ipNewIPAddr; // convert to string, to_bytes not applicable for older linux version
6103  const char *ptr = s.c_str(); // char * to address
6104  // decompose pattern like aaa.bbb.ccc.ddd
6105  sscanf(ptr, "%lu.%lu.%lu.%lu", &(adrBytesLong[0]), &(adrBytesLong[1]), &(adrBytesLong[2]), &(adrBytesLong[3]));
6106 
6107  // convert into byte array
6108  unsigned char ipbytearray[4];
6109  for (int i = 0; i < 4; i++)
6110  {
6111  ipbytearray[i] = adrBytesLong[i] & 0xFF;
6112  }
6113 
6114 
6115  char ipcommand[255];
6116  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_IP_ADDR].c_str();
6117  sprintf(ipcommand, pcCmdMask, ipbytearray[0], ipbytearray[1], ipbytearray[2], ipbytearray[3]);
6118  if (useBinaryCmd)
6119  {
6120  std::vector<unsigned char> reqBinary;
6121  this->convertAscii2BinaryCmd(ipcommand, &reqBinary);
6122  result = (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_IP_ADDR]));
6123  reqBinary.clear();
6124  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_WRITE_EEPROM].c_str(), &reqBinary);
6125  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_WRITE_EEPROM]));
6126  reqBinary.clear();
6127  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_RUN].c_str(), &reqBinary);
6128  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_RUN]));
6129  reqBinary.clear();
6130  std::string UserLvlCmd = cmdSetAccessMode3();
6131  this->convertAscii2BinaryCmd(UserLvlCmd.c_str(), &reqBinary);
6132  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_ACCESS_MODE_3]));
6133  reqBinary.clear();
6134  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_REBOOT].c_str(), &reqBinary);
6135  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_REBOOT]));
6136  }
6137  else
6138  {
6139  std::vector<unsigned char> ipcomandReply;
6140  std::vector<unsigned char> resetReply;
6141  std::string runCmd = sopasCmdVec[CMD_RUN];
6142  std::string restartCmd = sopasCmdVec[CMD_REBOOT];
6143  std::string EEPCmd = sopasCmdVec[CMD_WRITE_EEPROM];
6144  std::string UserLvlCmd = cmdSetAccessMode3();
6145  result = (0 == sendSopasAndCheckAnswer(ipcommand, &ipcomandReply));
6146  result &= (0 == sendSopasAndCheckAnswer(EEPCmd, &resetReply));
6147  result &= (0 == sendSopasAndCheckAnswer(runCmd, &resetReply));
6148  result &= (0 == sendSopasAndCheckAnswer(UserLvlCmd, &resetReply));
6149  result &= (0 == sendSopasAndCheckAnswer(restartCmd, &resetReply));
6150  }
6151  return (result);
6152  }
6153 
6154  bool SickScanCommon::setNTPServerAndStart(const std::string& ipNewIPAddr, bool useBinaryCmd)
6155  {
6156  bool result = false;
6157 
6158 
6159  unsigned long adrBytesLong[4];
6160  std::string s = ipNewIPAddr; // convert to string, to_bytes not applicable for older linux version
6161  const char *ptr = s.c_str(); // char * to address
6162  // decompose pattern like aaa.bbb.ccc.ddd
6163  sscanf(ptr, "%lu.%lu.%lu.%lu", &(adrBytesLong[0]), &(adrBytesLong[1]), &(adrBytesLong[2]), &(adrBytesLong[3]));
6164 
6165  // convert into byte array
6166  unsigned char ipbytearray[4];
6167  for (int i = 0; i < 4; i++)
6168  {
6169  ipbytearray[i] = adrBytesLong[i] & 0xFF;
6170  }
6171 
6172 
6173  char ntpipcommand[255];
6174  char ntpupdatetimecommand[255];
6175  const char *pcCmdMask = sopasCmdMaskVec[CMD_SET_NTP_SERVER_IP_ADDR].c_str();
6176  sprintf(ntpipcommand, pcCmdMask, ipbytearray[0], ipbytearray[1], ipbytearray[2], ipbytearray[3]);
6177 
6178  const char *pcCmdMaskUpdatetime = sopasCmdMaskVec[CMD_SET_NTP_UPDATETIME].c_str();
6179  sprintf(ntpupdatetimecommand, pcCmdMaskUpdatetime, 5);
6180  std::vector<unsigned char> outputFilterntpupdatetimecommand;
6181  //
6182  if (useBinaryCmd)
6183  {
6184  std::vector<unsigned char> reqBinary;
6185  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_SET_NTP_INTERFACE_ETH].c_str(), &reqBinary);
6187  reqBinary.clear();
6188  this->convertAscii2BinaryCmd(ntpipcommand, &reqBinary);
6190  reqBinary.clear();
6191  this->convertAscii2BinaryCmd(ntpupdatetimecommand, &reqBinary);
6192  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_SET_NTP_UPDATETIME]));
6193  reqBinary.clear();
6194  this->convertAscii2BinaryCmd(sopasCmdVec[CMD_ACTIVATE_NTP_CLIENT].c_str(), &reqBinary);
6195  result &= (0 == sendSopasAndCheckAnswer(reqBinary, &sopasReplyBinVec[CMD_ACTIVATE_NTP_CLIENT]));
6196  reqBinary.clear();
6197 
6198  }
6199  else
6200  {
6201  std::vector<unsigned char> ipcomandReply;
6202  std::vector<unsigned char> resetReply;
6203  std::string ntpInterFaceETHCmd = sopasCmdVec[CMD_SET_NTP_INTERFACE_ETH];
6204  std::string activateNTPCmd = sopasCmdVec[CMD_ACTIVATE_NTP_CLIENT];
6205  result &= (0 == sendSopasAndCheckAnswer(ntpInterFaceETHCmd, &resetReply));
6206  result &= (0 == sendSopasAndCheckAnswer(ntpipcommand, &ipcomandReply));
6207  result &= (0 == sendSopasAndCheckAnswer(activateNTPCmd, &resetReply));
6208  result &= (0 == sendSopasAndCheckAnswer(ntpupdatetimecommand, &outputFilterntpupdatetimecommand));
6209  }
6210  return (result);
6211  }
6212 
6214  {
6215  sensorIsRadar = _isRadar;
6216  }
6217 
6219  {
6220  return (sensorIsRadar);
6221  }
6222 
6223  void SickScanCommon::ScanLayerFilterCfg::parse(const std::string& parameter)
6224  {
6225  // parse ascii integer args "<number of layers> <layer 1 on/off> ... <layer N on/off>", e.g. "4 1 1 1 1"
6226  scan_layer_filter = parameter;
6227  scan_layer_activated.clear();
6228  first_active_layer = INT_MAX;
6229  last_active_layer = -1;
6230  num_layers = 0;
6231  num_active_layers = 0;
6232  std::istringstream ascii_args(parameter);
6233  std::string ascii_arg;
6234  for (int arg_cnt = 0; getline(ascii_args, ascii_arg, ' '); arg_cnt++)
6235  {
6236  int arg_val = -1;
6237  if (sscanf(ascii_arg.c_str(), "%d", &arg_val) == 1 && arg_val >= 0)
6238  {
6239  if (num_layers == 0) // i.e. parameter <number of layers>
6240  {
6241  num_layers = arg_val;
6242  }
6243  else // i.e. parameter <layer n on/off>
6244  {
6245  int layer = scan_layer_activated.size();
6246  scan_layer_activated.push_back(arg_val);
6247  if (arg_val > 0)
6248  {
6249  num_active_layers += 1;
6250  first_active_layer = std::min<int>(layer, first_active_layer);
6251  last_active_layer = std::max<int>(layer, last_active_layer);
6252  }
6253  }
6254  }
6255  }
6256  print();
6257  }
6258 
6260  {
6261  std::stringstream s;
6262  s << "ScanLayerFilterCfg: filter_settings=\"" << scan_layer_filter << "\", " << scan_layer_activated.size() << " layers, layer_activation=[";
6263  for(int n = 0; n < scan_layer_activated.size(); n++)
6264  s << (n > 0 ? "," : "") << scan_layer_activated[n];
6265  s << "], ";
6266  s << "first_active_layer=" << first_active_layer << ", last_active_layer=" << last_active_layer;
6267  ROS_INFO_STREAM(s.str());
6268  }
6269 
6271  {
6272  //SAFTY FIELD PARSING
6273  int result = ExitSuccess;
6275  {
6277  int field_set_selection_method = -1; // set FieldSetSelectionMethod at startup: -1 = do not set (default), 0 = active field selection by digital inputs, 1 = active field selection by telegram (see operation manual for details about FieldSetSelectionMethod telegram)
6278  int active_field_set = -1; // set ActiveFieldSet at startup: -1 = do not set (default), index of active field otherwise (see operation manual for details about ActiveFieldSet telegram)
6279  rosDeclareParam(m_nh, "field_set_selection_method", field_set_selection_method);
6280  rosGetParam(m_nh, "field_set_selection_method", field_set_selection_method);
6281  rosDeclareParam(m_nh, "active_field_set", active_field_set);
6282  rosGetParam(m_nh, "active_field_set", active_field_set);
6283  std::vector<unsigned char> sopasReply;
6284  if (field_set_selection_method >= 0) // Write and re-read FieldSetSelectionMethod
6285  {
6286  result = writeFieldSetSelectionMethod(field_set_selection_method, sopasReply, useBinaryCmd);
6287  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6288  result = readFieldSetSelectionMethod(field_set_selection_method, sopasReply, useBinaryCmd);
6289  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6290  }
6291  if (active_field_set >= 0) // Write and re-read ActiveFieldSet
6292  {
6293  result = writeActiveFieldSet(active_field_set, sopasReply, useBinaryCmd);
6294  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6295  result = readActiveFieldSet(active_field_set, sopasReply, useBinaryCmd);
6296  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6297  }
6298  }
6299  if (this->parser_->getCurrentParamPtr()->getUseEvalFields() == USE_EVAL_FIELD_TIM7XX_LOGIC || this->parser_->getCurrentParamPtr()->getUseEvalFields() == USE_EVAL_FIELD_LMS5XX_LOGIC)
6300  {
6301  ROS_INFO("Reading safety fields");
6302  float rectFieldAngleRefPointOffsetRad = this->parser_->getCurrentParamPtr()->getRectEvalFieldAngleRefPointOffsetRad();
6304  int maxFieldnum = this->parser_->getCurrentParamPtr()->getMaxEvalFields();
6305  for(int fieldnum=0;fieldnum<maxFieldnum;fieldnum++)
6306  {
6307  char requestFieldcfg[MAX_STR_LEN];
6308  const char *pcCmdMask = sopasCmdMaskVec[CMD_GET_SAFTY_FIELD_CFG].c_str();
6309  sprintf(requestFieldcfg, pcCmdMask, fieldnum);
6310  if (useBinaryCmd) {
6311  std::vector<unsigned char> reqBinary;
6312  std::vector<unsigned char> fieldcfgReply;
6313  this->convertAscii2BinaryCmd(requestFieldcfg, &reqBinary);
6314  result = sendSopasAndCheckAnswer(reqBinary, &fieldcfgReply);
6315  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, fieldcfgReply); // No response, non-recoverable connection error (return error and do not try other commands)
6316  fieldMon->parseBinaryDatagram(fieldcfgReply, rectFieldAngleRefPointOffsetRad);
6317  } else {
6318  std::vector<unsigned char> fieldcfgReply;
6319  result = sendSopasAndCheckAnswer(requestFieldcfg, &fieldcfgReply);
6320  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, fieldcfgReply); // No response, non-recoverable connection error (return error and do not try other commands)
6321  fieldMon->parseAsciiDatagram(fieldcfgReply, rectFieldAngleRefPointOffsetRad);
6322  }
6323  }
6324  if(cloud_marker_)
6325  {
6326  if (readLIDinputstate(fieldMon, useBinaryCmd) != ExitSuccess)
6327  return ExitError;
6328  int fieldset = fieldMon->getActiveFieldset();
6329  std::string scanner_name = parser_->getCurrentParamPtr()->getScannerName();
6330  EVAL_FIELD_SUPPORT eval_field_logic = this->parser_->getCurrentParamPtr()->getUseEvalFields();
6331  cloud_marker_->updateMarker(fieldMon->getMonFields(), fieldset, eval_field_logic);
6332  std::stringstream field_info;
6333  field_info << "Safety fieldset " << fieldset << ", pointcounter = [ ";
6334  for(int n = 0; n < fieldMon->getMonFields().size(); n++)
6335  field_info << (n > 0 ? ", " : " ") << fieldMon->getMonFields()[n].getPointCount();
6336  field_info << " ]";
6337  ROS_INFO_STREAM(field_info.str());
6338  for(int n = 0; n < fieldMon->getMonFields().size(); n++)
6339  {
6340  if(fieldMon->getMonFields()[n].getPointCount() > 0)
6341  {
6342  std::stringstream field_info2;
6343  field_info2 << "Safety field " << n << ", type " << (int)(fieldMon->getMonFields()[n].fieldType()) << " : ";
6344  for(int m = 0; m < fieldMon->getMonFields()[n].getPointCount(); m++)
6345  {
6346  if(m > 0)
6347  field_info2 << ", ";
6348  field_info2 << "(" << fieldMon->getMonFields()[n].getFieldPointsX()[m] << "," << fieldMon->getMonFields()[n].getFieldPointsY()[m] << ")";
6349  }
6350  ROS_INFO_STREAM(field_info2.str());
6351  }
6352  }
6353  }
6354  }
6355  return result;
6356  } // SickScanCommon::readParseSafetyFields()
6357 
6359  {
6360  int result = ExitError;
6361  std::string LIDinputstateRequest = "\x02sRN LIDinputstate\x03";
6362  std::vector<unsigned char> LIDinputstateResponse;
6363  if (useBinaryCmd)
6364  {
6365  std::vector<unsigned char> reqBinary;
6366  this->convertAscii2BinaryCmd(LIDinputstateRequest.c_str(), &reqBinary);
6367  result = sendSopasAndCheckAnswer(reqBinary, &LIDinputstateResponse, -1);
6368  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, LIDinputstateResponse); // No response, non-recoverable connection error (return error and do not try other commands)
6369  if(result == 0)
6370  {
6371  sick_scan_msg::LIDinputstateMsg inputstate_msg;
6372  fieldMon->parseBinaryLIDinputstateMsg(LIDinputstateResponse.data(), LIDinputstateResponse.size(), inputstate_msg);
6373  ROS_INFO_STREAM("Safety fieldset response to \"sRN LIDinputstate\": " << DataDumper::binDataToAsciiString(LIDinputstateResponse.data(), LIDinputstateResponse.size())
6374  << ", active fieldset = " << fieldMon->getActiveFieldset() << ", " << fieldMon->LIDinputstateMsgToString(inputstate_msg));
6375  }
6376  }
6377  else
6378  {
6379  result = sendSopasAndCheckAnswer(LIDinputstateRequest.c_str(), &LIDinputstateResponse, -1);
6380  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, LIDinputstateResponse); // No response, non-recoverable connection error (return error and do not try other commands)
6381  }
6382  return 0;
6383  }
6384 
6385  // Write FieldSetSelectionMethod
6386  int SickScanCommon::writeFieldSetSelectionMethod(int field_set_selection_method, std::vector<unsigned char>& sopasReply, bool useBinaryCmd)
6387  {
6388  int result = ExitSuccess;
6389  if (field_set_selection_method >= 0 && this->parser_->getCurrentParamPtr()->getUseEvalFields() == USE_EVAL_FIELD_TIM7XX_LOGIC)
6390  {
6391  char reqAscii[MAX_STR_LEN];
6392  std::vector<unsigned char> reqBinary;
6393  sprintf(reqAscii, "\x02sWN FieldSetSelectionMethod %d\x03", field_set_selection_method);
6394  if (useBinaryCmd)
6395  {
6396  this->convertAscii2BinaryCmd(reqAscii, &reqBinary);
6397  result = sendSopasAndCheckAnswer(reqBinary, &sopasReply, -1);
6398  }
6399  else
6400  {
6401  result = sendSopasAndCheckAnswer(reqAscii, &sopasReply, -1);
6402  }
6403  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6404  }
6405  return result;
6406  }
6407 
6408  // Read FieldSetSelectionMethod
6409  int SickScanCommon::readFieldSetSelectionMethod(int& field_set_selection_method, std::vector<unsigned char>& sopasReply, bool useBinaryCmd)
6410  {
6411  int result = ExitSuccess;
6413  {
6414  char reqAscii[MAX_STR_LEN];
6415  std::vector<unsigned char> reqBinary;
6416  sprintf(reqAscii, "\x02sRN FieldSetSelectionMethod\x03");
6417  if (useBinaryCmd)
6418  {
6419  this->convertAscii2BinaryCmd(reqAscii, &reqBinary);
6420  result = sendSopasAndCheckAnswer(reqBinary, &sopasReply, -1);
6421  }
6422  else
6423  {
6424  result = sendSopasAndCheckAnswer(reqAscii, &sopasReply, -1);
6425  }
6426  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6428  uint8_t sopas_field_set_selection_method = (uint8_t)field_set_selection_method;
6429  fieldMon->parseFieldSetSelectionMethodResponse(sopasReply.data(), sopasReply.size(), &sopas_field_set_selection_method);
6430  field_set_selection_method = sopas_field_set_selection_method;
6431  fieldMon->setFieldSelectionMethod(field_set_selection_method);
6432  ROS_INFO_STREAM("Response to \"sRN FieldSetSelectionMethod\": \"" << DataDumper::binDataToAsciiString(sopasReply.data(), sopasReply.size()) << "\", FieldSetSelectionMethod = " << field_set_selection_method);
6433  }
6434  return result;
6435  }
6436 
6437  // Write ActiveFieldSet
6438  int SickScanCommon::writeActiveFieldSet(int active_field_set, std::vector<unsigned char>& sopasReply, bool useBinaryCmd)
6439  {
6440  int result = ExitSuccess;
6441  if (active_field_set >= 0 && this->parser_->getCurrentParamPtr()->getUseEvalFields() == USE_EVAL_FIELD_TIM7XX_LOGIC)
6442  {
6443  char reqAscii[MAX_STR_LEN];
6444  std::vector<unsigned char> reqBinary;
6445  sprintf(reqAscii, "\x02sWN ActiveFieldSet %02d\x03", active_field_set);
6446  if (useBinaryCmd)
6447  {
6448  this->convertAscii2BinaryCmd(reqAscii, &reqBinary);
6449  result = sendSopasAndCheckAnswer(reqBinary, &sopasReply, -1);
6450  }
6451  else
6452  {
6453  result = sendSopasAndCheckAnswer(reqAscii, &sopasReply, -1);
6454  }
6455  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6456  }
6457  return result;
6458  }
6459 
6460  // Read ActiveFieldSet
6461  int SickScanCommon::readActiveFieldSet(int& active_field_set, std::vector<unsigned char>& sopasReply, bool useBinaryCmd)
6462  {
6463  int result = ExitSuccess;
6464  int eval_field_logic = this->parser_->getCurrentParamPtr()->getUseEvalFields();
6465  if (eval_field_logic == USE_EVAL_FIELD_TIM7XX_LOGIC)
6466  {
6467  char reqAscii[MAX_STR_LEN];
6468  std::vector<unsigned char> reqBinary;
6469  sprintf(reqAscii, "\x02sRN ActiveFieldSet\x03");
6470  if (useBinaryCmd)
6471  {
6472  this->convertAscii2BinaryCmd(reqAscii, &reqBinary);
6473  result = sendSopasAndCheckAnswer(reqBinary, &sopasReply, -1);
6474  }
6475  else
6476  {
6477  result = sendSopasAndCheckAnswer(reqAscii, &sopasReply, -1);
6478  }
6479  RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, sopasReply);
6481  uint16_t sopas_active_field_set = (uint16_t)active_field_set;
6482  fieldMon->parseActiveFieldSetResponse(sopasReply.data(), sopasReply.size(), &sopas_active_field_set);
6483  active_field_set = sopas_active_field_set;
6484  fieldMon->setActiveFieldset(active_field_set);
6485  if(cloud_marker_)
6486  cloud_marker_->updateMarker(fieldMon->getMonFields(), active_field_set, eval_field_logic);
6487  ROS_INFO_STREAM("Response to \"sRN ActiveFieldSet\": " << DataDumper::binDataToAsciiString(sopasReply.data(), sopasReply.size()) << "\", ActiveFieldSet = " << active_field_set);
6488  }
6489  return result;
6490  }
6491 
6492 } /* namespace sick_scan_xd */
6493 
6494 
6495 
sick_scan_messages.h
sick_scan_xd::Encoder
::sick_scan_xd::Encoder_< std::allocator< void > > Encoder
Definition: Encoder.h:60
sick_scan_xd::SickGenericParser::parse_datagram
virtual int parse_datagram(char *datagram, size_t datagram_length, SickScanConfig &config, ros_sensor_msgs::LaserScan &msg, int &numEchos, int &echoMask)
Parsing Ascii datagram.
Definition: sick_generic_parser.cpp:1221
UINT16
uint16_t UINT16
Definition: BasicDatatypes.hpp:73
sick_scan_xd::SickScanRadarSingleton::setNameOfRadar
void setNameOfRadar(const std::string &_radarName, RADAR_TYPE_ENUM _radarType)
Definition: sick_generic_radar.h:211
sick_scan_xd::NAVLandmarkData
::sick_scan_xd::NAVLandmarkData_< std::allocator< void > > NAVLandmarkData
Definition: NAVLandmarkData.h:66
sick_scan_xd::SickScanCommon::SOPAS_CMD
SOPAS_CMD
Definition: sick_scan_common.h:105
sick_scan_xd::SickScanServices::convertFloatToHexString
static std::string convertFloatToHexString(float value, bool hexStrIsBigEndian)
Definition: sick_scan_services.cpp:668
sick_scan_xd::SickScanCommon::init_cmdTables
int init_cmdTables(rosNodePtr nh)
init command tables and define startup sequence
Definition: sick_scan_common.cpp:1541
sick_scan_xd::SickScanCommon::CMD_WRITE_EEPROM
@ CMD_WRITE_EEPROM
Definition: sick_scan_common.h:111
sick_scan_xd::convertNAVLandmarkDataToMarker
ros_visualization_msgs::MarkerArray convertNAVLandmarkDataToMarker(const std::vector< NAV350ReflectorData > &reflectors, ros_std_msgs::Header &header, SickGenericParser *parser_)
Definition: sick_nav_scandata_parser.cpp:839
binScanfGetStringFromVec
const std::string binScanfGetStringFromVec(std::vector< unsigned char > *replyDummy, int off, long len)
return diagnostic error code (small helper function) This small helper function was introduced to all...
Definition: sick_scan_common.cpp:176
sick_scan_xd::SickScanCommon::lferec_pub_
rosPublisher< sick_scan_msg::LFErecMsg > lferec_pub_
Definition: sick_scan_common.h:471
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_RES
@ CMD_SET_ENCODER_RES
Definition: sick_scan_common.h:153
sick_scan_xd::ScannerBasicParam::getUseBinaryProtocol
bool getUseBinaryProtocol(void)
flag to decide between usage of ASCII-sopas or BINARY-sopas
Definition: sick_generic_parser.cpp:341
sick_scan_xd::RADAR_3D
@ RADAR_3D
Definition: sick_generic_parser.h:106
sick_scan_xd::SickScanCommon::CMD_STOP_SCANDATA
@ CMD_STOP_SCANDATA
Definition: sick_scan_common.h:137
sick_scan_xd::SickScanConfig::auto_reboot
bool auto_reboot
Definition: sick_ros_wrapper.h:517
sick_scan_xd::ScannerBasicParam::getWaitForReady
bool getWaitForReady()
Definition: sick_generic_parser.cpp:439
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::num_active_layers
int num_active_layers
Definition: sick_scan_common.h:574
sick_scan_xd::SickScanConfig::max_ang
double max_ang
Definition: sick_ros_wrapper.h:519
sick_scan_xd::parseNAV350BinaryUnittest
bool parseNAV350BinaryUnittest()
Definition: sick_nav_scandata_parser.cpp:200
sick_scan_xd::NAV350mNPOSData::landmarkData
NAV350LandmarkData landmarkData
Definition: sick_nav_scandata.h:210
sick_scan_xd::SickScanCommon::CMD_SET_ACCESS_MODE_3
@ CMD_SET_ACCESS_MODE_3
Definition: sick_scan_common.h:127
sick_scan_xd::SickScanCommon::CMD_SET_NAV_POSE_DATA_FORMAT
@ CMD_SET_NAV_POSE_DATA_FORMAT
Definition: sick_scan_common.h:202
msg
msg
sick_scan_xd::SickScanCommon::publish_lidinputstate_
bool publish_lidinputstate_
Definition: sick_scan_common.h:475
sick_scan_xd::SickScanCommon::CMD_SET_LID_INPUTSTATE_ACTIVE
@ CMD_SET_LID_INPUTSTATE_ACTIVE
Definition: sick_scan_common.h:190
NULL
#define NULL
sick_scan_xd::ScannerBasicParam::getUseScancfgList
bool getUseScancfgList()
Definition: sick_generic_parser.cpp:420
sick_scan_xd::SickScanCommon::CMD_STOP_MEASUREMENT
@ CMD_STOP_MEASUREMENT
Definition: sick_scan_common.h:173
sick_scan_xd::SickScanCommon::CMD_SET_NAV_LANDMARK_DATA_FORMAT
@ CMD_SET_NAV_LANDMARK_DATA_FORMAT
Definition: sick_scan_common.h:200
sick_scan_xd::SickScanCommon::CMD_APPLICATION_MODE_FIELD_OFF
@ CMD_APPLICATION_MODE_FIELD_OFF
Definition: sick_scan_common.h:124
swap_endian
void swap_endian(unsigned char *ptr, int numBytes)
Universal swapping function.
Definition: sick_scan_common.cpp:125
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE_FI
@ CMD_SET_ENCODER_MODE_FI
Definition: sick_scan_common.h:149
rosSetParam
void rosSetParam(rosNodePtr nh, const std::string &param_name, const T &param_value)
Definition: sick_ros_wrapper.h:168
sick_scan_xd::SickScanCommon::CMD_SET_PARTIAL_SCAN_CFG
@ CMD_SET_PARTIAL_SCAN_CFG
Definition: sick_scan_common.h:133
sick_scan_xd::SickScanCommon::CMD_END
@ CMD_END
Definition: sick_scan_common.h:225
diagnostic_updater::TimeStampStatusParam
A structure that holds the constructor parameters for the TimeStampStatus class.
Definition: eloquent/include/diagnostic_updater/update_functions.hpp:218
angle
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
sick_scan_xd::SickScanCommon::CMD_SET_NTP_UPDATETIME
@ CMD_SET_NTP_UPDATETIME
Definition: sick_scan_common.h:175
sick_scan_xd::notifyLIDoutputstateListener
void notifyLIDoutputstateListener(rosNodePtr handle, const sick_scan_msg::LIDoutputstateMsg *msg)
Definition: sick_generic_callback.cpp:134
sick_scan_xd::SickScanCommon::CMD_SET_TRANSMIT_OBJECTS_ON
@ CMD_SET_TRANSMIT_OBJECTS_ON
Definition: sick_scan_common.h:162
pointcloud_utils.h
sick_scan_xd::SickScanCommon::isCompatibleDevice
bool isCompatibleDevice(const std::string identStr) const
check the identification string
Definition: sick_scan_common.cpp:4277
sick_scan_xd::SickScanCommon::cloud_marker_
SickScanMarker * cloud_marker_
Definition: sick_scan_common.h:477
sick_scan_xd::SickScanCommon::CMD_SET_NAV_ERASE_LAYOUT
@ CMD_SET_NAV_ERASE_LAYOUT
Definition: sick_scan_common.h:207
sick_scan_xd::SickScanCommon::CMD_SET_SCANDATACONFIGNAV
@ CMD_SET_SCANDATACONFIGNAV
Definition: sick_scan_common.h:180
sick_scan_xd::SickCloudTransform
Definition: sick_cloud_transform.h:85
sick_scan_xd::SickScanParseUtil::LMPscancfg::sector_cfg
std::vector< LMPscancfgSector > sector_cfg
Definition: sick_scan_parse_util.h:98
sick_scan_xd::ScannerBasicParam::getNumberOfLayers
int getNumberOfLayers(void)
Getting number of scanner layers.
Definition: sick_generic_parser.cpp:133
sick_scan_xd::SickScanCommon::CMD_SET_NAV_OPERATIONAL_MODE_3
@ CMD_SET_NAV_OPERATIONAL_MODE_3
Definition: sick_scan_common.h:197
sick_scan_xd::SickScanCommon::CMD_SET_NAV_OPERATIONAL_MODE_4
@ CMD_SET_NAV_OPERATIONAL_MODE_4
Definition: sick_scan_common.h:198
sick_scan_xd::SickScanCommon::sopasCmdVec
std::vector< std::string > sopasCmdVec
Definition: sick_scan_common.h:521
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE_SI
@ CMD_SET_ENCODER_MODE_SI
Definition: sick_scan_common.h:146
sick_scan_xd_api_test.sopas_response
sopas_response
Definition: sick_scan_xd_api_test.py:453
sick_scan_xd::SickScanCommon::CMD_SET_TRANSMIT_RAWTARGETS_ON
@ CMD_SET_TRANSMIT_RAWTARGETS_ON
Definition: sick_scan_common.h:159
SICK_SCANNER_NAV_350_NAME
#define SICK_SCANNER_NAV_350_NAME
Definition: sick_generic_parser.h:75
sick_scan_xd::SickScanCommon::writeFieldSetSelectionMethod
int writeFieldSetSelectionMethod(int field_set_selection_method, std::vector< unsigned char > &sopasReply, bool useBinaryCmd=true)
Definition: sick_scan_common.cpp:6386
sick_scan_xd::SickScanCommon::CMD_SET_LID_OUTPUTSTATE_ACTIVE
@ CMD_SET_LID_OUTPUTSTATE_ACTIVE
Definition: sick_scan_common.h:189
sick_scan_xd::ScannerBasicParam::setIntensityResolutionIs16Bit
void setIntensityResolutionIs16Bit(bool _IntensityResolutionIs16Bit)
Set the RSSI Value length.
Definition: sick_generic_parser.cpp:351
s
XmlRpcServer s
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::num_layers
int num_layers
Definition: sick_scan_common.h:573
rad2deg
#define rad2deg(x)
Definition: sick_scan_common.cpp:98
sick_scan_xd::SickScanCommon::CMD_SET_NAV_SCAN_DATA_FORMAT
@ CMD_SET_NAV_SCAN_DATA_FORMAT
Definition: sick_scan_common.h:201
binScanfVec
int binScanfVec(const std::vector< unsigned char > *vec, const char *fmt,...)
Definition: binScanf.cpp:417
DataDumper::dumpUcharBufferToConsole
int dumpUcharBufferToConsole(unsigned char *buffer, int bufLen)
Definition: dataDumper.cpp:67
sick_scan_xd::SickScanCommon::SickScanCommon
SickScanCommon(rosNodePtr nh, SickGenericParser *parser)
Construction of SickScanCommon.
Definition: sick_scan_common.cpp:462
sick_scan_xd::ScannerBasicParam::getExpectedFrequency
double getExpectedFrequency(void)
get expected scan frequency
Definition: sick_generic_parser.cpp:232
sick_generic_radar.h
sick_scan_xd::ScannerBasicParam::getScannerName
std::string getScannerName(void) const
Getting name (type) of scanner.
Definition: sick_generic_parser.cpp:97
sick_scan_xd::SickScanCommon::readWithTimeout
virtual int readWithTimeout(size_t timeout_ms, char *buffer, int buffer_size, int *bytes_read, const std::vector< std::string > &datagram_keywords)=0
sick_scan_xd::SickScanCommon::CMD_SET_LMDSCANDATASCALEFACTOR
@ CMD_SET_LMDSCANDATASCALEFACTOR
Definition: sick_scan_common.h:214
sick_scan_xd::SickScanCommon::checkColaTypeAndSwitchToConfigured
ExitCode checkColaTypeAndSwitchToConfigured(bool useBinaryCmd)
Definition: sick_scan_common.cpp:1289
deg2rad_const
#define deg2rad_const
Definition: sick_scan_common.cpp:101
sick_scan_xd::SickScanFieldMonSingleton::setActiveFieldset
void setActiveFieldset(int active_fieldset)
Definition: sick_generic_field_mon.h:171
sick_scan_xd::SickScanCommon::publish_datagram_
bool publish_datagram_
Definition: sick_scan_common.h:469
sick_scan_xd::SickScanCommon::CMD_SET_LFPMEDIANFILTER
@ CMD_SET_LFPMEDIANFILTER
Definition: sick_scan_common.h:213
sick_scan_xd::SickScanFieldMonSingleton::LIDinputstateMsgToString
std::string LIDinputstateMsgToString(const sick_scan_msg::LIDinputstateMsg &inputstate_msg)
Converts a LIDinputstateMsg to a readable string.
Definition: sick_generic_field_mon.cpp:285
sick_scan_xd::SickScanCommon::CMD_SET_IP_ADDR
@ CMD_SET_IP_ADDR
Definition: sick_scan_common.h:177
sick_scan_xd::SickScanCommon::sopasReplyVec
std::vector< std::string > sopasReplyVec
Definition: sick_scan_common.h:523
DataDumper::instance
static DataDumper & instance()
Definition: dataDumper.h:15
sick_scan_xd::SickScanCommon::outputChannelFlagId
int outputChannelFlagId
Definition: sick_scan_common.h:529
getDiagnosticErrorCode
static int getDiagnosticErrorCode()
Definition: sick_scan_common_tcp.cpp:87
sick_scan_xd::SickScanCommon::config_
SickScanConfig config_
Definition: sick_scan_common.h:395
sick_scan_xd::SickScanCommon::generateExpectedAnswerString
std::vector< std::string > generateExpectedAnswerString(const std::vector< unsigned char > &requestStr)
Generate expected answer strings from the command string.
Definition: sick_scan_common.cpp:932
sick_scan_xd::SickScanCommon::CMD_REBOOT
@ CMD_REBOOT
Definition: sick_scan_common.h:110
sick_scan_xd::SickScanCommon::CMD_DEVICE_TYPE
@ CMD_DEVICE_TYPE
Definition: sick_scan_common.h:169
sensor_msgs::PointCloud2
::sensor_msgs::PointCloud2_< std::allocator< void > > PointCloud2
Definition: PointCloud2.h:90
sick_scan_xd::SickScanConfig::sw_pll_only_publish
bool sw_pll_only_publish
Definition: sick_ros_wrapper.h:522
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::last_active_layer
int last_active_layer
Definition: sick_scan_common.h:572
sick_scan_xd::SickScanCommon::CMD_SET_ENOCDER_RES_1
@ CMD_SET_ENOCDER_RES_1
Definition: sick_scan_common.h:152
sick_scan_xd::NAV350LandmarkDataDoMappingResponse
Definition: sick_nav_scandata.h:157
SICK_SCANNER_LMS_1XX_NAME
#define SICK_SCANNER_LMS_1XX_NAME
Definition: sick_generic_parser.h:70
sick_scan_xd::SickScanCommon::CMD_SET_GATEWAY
@ CMD_SET_GATEWAY
Definition: sick_scan_common.h:178
sick_scan_xd::SickScanCommon::CMD_GET_PARTIAL_SCAN_CFG
@ CMD_GET_PARTIAL_SCAN_CFG
Definition: sick_scan_common.h:134
SoftwarePLL::instance
static SoftwarePLL & instance()
Definition: softwarePLL.h:24
sick_scan_xd::SickScanCommon::CMD_SET_INCREMENTSOURCE_ENC
@ CMD_SET_INCREMENTSOURCE_ENC
Definition: sick_scan_common.h:150
sick_scan_xd::ScannerBasicParam::setTrackingModeSupported
void setTrackingModeSupported(bool _trackingModeSupported)
Definition: sick_generic_parser.cpp:310
sick_scan_xd::SickScanCommon::update_config
void update_config(sick_scan_xd::SickScanConfig &new_config, uint32_t level=0)
updating configuration
Definition: sick_scan_common.cpp:5398
sick_scan_xd::SickScanCommon::loopOnce
int loopOnce(rosNodePtr nh)
parsing datagram and publishing ros messages
Definition: sick_scan_common.cpp:4360
sick_scan_xd::NAVPoseData
::sick_scan_xd::NAVPoseData_< std::allocator< void > > NAVPoseData
Definition: NAVPoseData.h:120
sick_scan_xd::SickScanCommon::CMD_DEVICE_IDENT_LEGACY
@ CMD_DEVICE_IDENT_LEGACY
Definition: sick_scan_common.h:107
sick_scan_xd::SickScanCommon::cloud_polar_
ros_sensor_msgs::PointCloud2 cloud_polar_
Definition: sick_scan_common.h:387
sick_scan_xd::SickScanCommon::CMD_SET_3_4_TO_ENCODER
@ CMD_SET_3_4_TO_ENCODER
Definition: sick_scan_common.h:151
sick_scan_xd::SickGenericParser::checkScanTiming
bool checkScanTiming(float time_increment, float scan_time, float angle_increment, float tol)
Definition: sick_generic_parser.cpp:1173
sick_scan_xd::NAV350LandmarkDataDoMappingResponse::print
void print()
Definition: sick_nav_scandata.h:163
sick_scan_xd::ScannerBasicParam::setDeviceIsRadar
void setDeviceIsRadar(RADAR_TYPE_ENUM _radar_type)
flag to mark the device as radar (instead of laser scanner)
Definition: sick_generic_parser.cpp:282
sick_nav_scandata_parser.h
colab::addFrameToBuffer
void addFrameToBuffer(UINT8 *sendBuffer, UINT8 *cmdBuffer, UINT16 *len)
Definition: colab.cpp:99
rosSleep
void rosSleep(double seconds)
Definition: sick_ros_wrapper.h:210
sick_scan_xd::SickScanCommon::CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER
@ CMD_SET_ACCESS_MODE_3_SAFETY_SCANNER
Definition: sick_scan_common.h:128
sick_scan_xd::SickGenericParser
Definition: sick_generic_parser.h:239
sick_scan_xd::SickScanCommon::convertSendSOPASCommand
virtual int convertSendSOPASCommand(const std::string &sopas_ascii_request, std::vector< unsigned char > *reply, bool wait_for_reply=true)
Converts a given SOPAS command from ascii to binary (in case of binary communication),...
Definition: sick_scan_common.cpp:830
sick_scan_xd::SickScanCommon::checkForBinaryAnswer
int checkForBinaryAnswer(const std::vector< unsigned char > *reply)
Check block for correct framing and starting sequence of a binary message.
Definition: sick_scan_common.cpp:793
sick_scan_xd::SickScanCommon::nav_landmark_data_pub_
rosPublisher< sick_scan_msg::NAVLandmarkData > nav_landmark_data_pub_
Definition: sick_scan_common.h:481
geometry_msgs::TransformStamped
::geometry_msgs::TransformStamped_< std::allocator< void > > TransformStamped
Definition: TransformStamped.h:60
sick_scan_xd::notifyPolarPointcloudListener
void notifyPolarPointcloudListener(rosNodePtr handle, const sick_scan_xd::PointCloud2withEcho *msg)
Definition: sick_generic_callback.cpp:94
sick_scan_xd::SickScanCommon::CMD_GET_SAFTY_FIELD_CFG
@ CMD_GET_SAFTY_FIELD_CFG
Definition: sick_scan_common.h:186
ROS_WARN
#define ROS_WARN(...)
Definition: sick_scan_logging.h:122
sick_scan_xd::ExitFatal
@ ExitFatal
Definition: abstract_parser.h:48
sick_scan_xd::SickScanCommon::writeActiveFieldSet
int writeActiveFieldSet(int active_field_set, std::vector< unsigned char > &sopasReply, bool useBinaryCmd=true)
Definition: sick_scan_common.cpp:6438
sick_scan_xd::SickScanCommon::angleCompensator
AngleCompensator * angleCompensator
Definition: sick_scan_common.h:550
sick_scan_xd::SickScanCommon::sopasReplyStrVec
std::vector< std::string > sopasReplyStrVec
Definition: sick_scan_common.h:525
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE
@ CMD_SET_ENCODER_MODE
Definition: sick_scan_common.h:144
SoftwarePLL::convSystemtimeToLidarTimestamp
bool convSystemtimeToLidarTimestamp(uint32_t systemtime_sec, uint32_t systemtime_nanosec, uint32_t &tick)
Definition: softwarePLL.cpp:264
sick_scan_xd::SickScanParseUtil::LMPscancfgSector::stop_angle
int32_t stop_angle
Definition: sick_scan_parse_util.h:90
sick_scan_xd::SickScanCommon::sopasCmdMaskVec
std::vector< std::string > sopasCmdMaskVec
Definition: sick_scan_common.h:522
sick_scan_xd::SickScanCommon::sendSopasAorBgetAnswer
int sendSopasAorBgetAnswer(const std::string &request, std::vector< unsigned char > *reply, bool useBinaryCmd)
Definition: sick_scan_common.cpp:1227
sick_scan_xd::SickScanConfig::min_ang
double min_ang
Definition: sick_ros_wrapper.h:518
SICK_SCANNER_OEM_15XX_NAME
#define SICK_SCANNER_OEM_15XX_NAME
Definition: sick_generic_parser.h:81
sick_scan_xd::PointCloud2withEcho
Definition: sick_generic_callback.h:76
sick_scan_xd::SickScanCommon::sopasSendMutex
std::mutex sopasSendMutex
Definition: sick_scan_common.h:545
sick_scan_xd::SickScanCommon::CMD_SET_TRACKING_MODE_0
@ CMD_SET_TRACKING_MODE_0
Definition: sick_scan_common.h:165
angle_compensator.h
sick_scan_xd::SickScanCommon::CMD_SET_NAV_STORE_LAYOUT
@ CMD_SET_NAV_STORE_LAYOUT
Definition: sick_scan_common.h:208
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::parse
void parse(const std::string &parameter)
Definition: sick_scan_common.cpp:6223
sick_scan_xd::NAV350mNPOSData
Definition: sick_nav_scandata.h:200
sick_scan_xd::SickScanCommon::CMD_SET_NTP_INTERFACE_ETH
@ CMD_SET_NTP_INTERFACE_ETH
Definition: sick_scan_common.h:141
sick_scan_xd::SickScanCommon::CMD_SET_NAV_POSE
@ CMD_SET_NAV_POSE
Definition: sick_scan_common.h:209
sick_scan_xd::SickScanCommon::sendSopasAndCheckAnswer
int sendSopasAndCheckAnswer(std::string request, std::vector< unsigned char > *reply, int cmdId)
send command and check answer
Definition: sick_scan_common.cpp:1068
sick_scan_xd::SickScanCommon::CMD_SET_TRACKING_MODE_1
@ CMD_SET_TRACKING_MODE_1
Definition: sick_scan_common.h:166
sick_scan_xd::SickScanCommon::cloud_pub_
rosPublisher< ros_sensor_msgs::PointCloud2 > cloud_pub_
Definition: sick_scan_common.h:383
toString
std::string toString(INT32 value)
Definition: toolbox.cpp:279
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg
Definition: sick_scan_common.h:562
sick_scan_xd
Definition: abstract_parser.cpp:65
sick_scan_common.h
sick_scan_xd::SickScanCommon::m_read_timeout_millisec_startup
int m_read_timeout_millisec_startup
Definition: sick_scan_common.h:558
sick_scan_xd::SickScanCommon::CMD_SET_NTP_TIMEZONE
@ CMD_SET_NTP_TIMEZONE
Definition: sick_scan_common.h:176
f
f
sick_scan_xd::SickScanFieldMonSingleton::parseBinaryLIDinputstateMsg
int parseBinaryLIDinputstateMsg(unsigned char *datagram, int datagram_length, sick_scan_msg::LIDinputstateMsg &inputstate_msg)
Parse binary LIDinputstate message and set active field set.
Definition: sick_generic_field_mon.cpp:187
sick_scan_xd::ScannerBasicParam::getElevationDegreeResolution
double getElevationDegreeResolution(void)
get angular resolution in VERTICAL direction for multilayer scanner
Definition: sick_generic_parser.cpp:254
DataDumper::binDataToAsciiString
static std::string binDataToAsciiString(const uint8_t *binary_data, int length)
Definition: dataDumper.cpp:108
nav_msgs::Odometry
::nav_msgs::Odometry_< std::allocator< void > > Odometry
Definition: Odometry.h:67
sick_scan_xd::SickScanCommon::CMD_SET_TO_COLA_A_PROTOCOL
@ CMD_SET_TO_COLA_A_PROTOCOL
Definition: sick_scan_common.h:184
sick_scan_xd::SickScanParseUtil::LMPscancfg::active_sector_cnt
int16_t active_sector_cnt
Definition: sick_scan_parse_util.h:97
sick_scan_xd::SickScanConfig::ang_res
double ang_res
Definition: sick_ros_wrapper.h:520
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE_NO
@ CMD_SET_ENCODER_MODE_NO
Definition: sick_scan_common.h:145
sick_scan_xd::SickScanCommon::CMD_SET_LFEREC_ACTIVE
@ CMD_SET_LFEREC_ACTIVE
Definition: sick_scan_common.h:188
sick_scan_xd::SickScanFieldMonSingleton::parseFieldSetSelectionMethodResponse
void parseFieldSetSelectionMethodResponse(unsigned char *datagram, int datagram_length, uint8_t *field_set_selection_method)
Parse binary FieldSetSelectionMethod response "sRA FieldSetSelectionMethod".
Definition: sick_generic_field_mon.cpp:274
sick_scan_xd::SickScanFieldMonSingleton::parseActiveFieldSetResponse
void parseActiveFieldSetResponse(unsigned char *datagram, int datagram_length, uint16_t *active_field_set)
Parse binary ActiveFieldSet response "sRA ActiveFieldSet".
Definition: sick_generic_field_mon.cpp:260
sick_scan_xd::SickScanImu::isImuDatagram
bool isImuDatagram(char *datagram, size_t datagram_length)
Definition: sick_generic_imu.cpp:91
sick_scan_xd::SickScanCommon::CMD_STOP_IMU_DATA
@ CMD_STOP_IMU_DATA
Definition: sick_scan_common.h:156
sick_scan_xd::SickScanParseUtil::LMPscancfgSector::angular_resolution
uint32_t angular_resolution
Definition: sick_scan_parse_util.h:88
binScanf.hpp
sick_scan_xd::LFErecMsg
::sick_scan_xd::LFErecMsg_< std::allocator< void > > LFErecMsg
Definition: LFErecMsg.h:61
sick_scan_xd::SickScanParseUtil::LMPscancfgSector::start_angle
int32_t start_angle
Definition: sick_scan_parse_util.h:89
sick_scan_xd::SickScanConfig::cloud_output_mode
int cloud_output_mode
Definition: sick_ros_wrapper.h:525
ROS_HEADER_SEQ
#define ROS_HEADER_SEQ(msgheader, value)
Definition: sick_ros_wrapper.h:162
sick_scan_xd::ScannerBasicParam::getEncoderMode
int8_t getEncoderMode()
Getter-Method for encoder mode.
Definition: sick_generic_parser.cpp:495
sick_scan_xd::SickScanConfig::frame_id
std::string frame_id
Definition: sick_ros_wrapper.h:514
CoLa_A
@ CoLa_A
Command Language ASCI.
Definition: sick_scan_common_nw.h:33
sick_scan_xd::parseCommonBinaryResultTelegram
bool parseCommonBinaryResultTelegram(const uint8_t *receiveBuffer, int receiveBufferLength, short &elevAngleX200, double elevAngleTelegramValToDeg, double &elevationAngleInRad, rosTime &recvTimeStamp, bool config_sw_pll_only_publish, bool use_generation_timestamp, SickGenericParser *parser_, bool &FireEncoder, sick_scan_msg::Encoder &EncoderMsg, int &numEchos, std::vector< float > &vang_vec, std::vector< float > &azimuth_vec, ros_sensor_msgs::LaserScan &msg)
Definition: sick_lmd_scandata_parser.cpp:135
sick_scan_xd::SickScanCommon::rebootScanner
virtual bool rebootScanner()
Send a SOPAS command to the scanner that should cause a soft reset.
Definition: sick_scan_common.cpp:853
sick_scan_xd::SickScanCommon::CMD_RUN
@ CMD_RUN
Definition: sick_scan_common.h:132
sick_scan_xd::SickScanParseUtil::LMPscancfg::scan_frequency
uint32_t scan_frequency
Definition: sick_scan_parse_util.h:96
AngleCompensator::parseReply
int parseReply(bool isBinary, std::vector< unsigned char > &replyVec)
Parse reply of angle compensation values given the command MCAngleCompSin (see testbed)
Definition: angle_compensator.cpp:268
sick_scan_xd::SickScanCommon::setSensorIsRadar
void setSensorIsRadar(bool _isRadar)
Definition: sick_scan_common.cpp:6213
sick_scan_xd::SickScanCommon::CMD_START_IMU_DATA
@ CMD_START_IMU_DATA
Definition: sick_scan_common.h:155
nsec
uint32_t nsec(const rosTime &time)
Definition: sick_ros_wrapper.h:175
sick_scan_xd::SickScanCommon::handleNAV350BinaryPositionData
bool handleNAV350BinaryPositionData(const uint8_t *receiveBuffer, int receiveBufferLength, short &elevAngleX200, double &elevationAngleInRad, rosTime &recvTimeStamp, bool config_sw_pll_only_publish, double config_time_offset, SickGenericParser *parser_, int &numEchos, ros_sensor_msgs::LaserScan &msg, NAV350mNPOSData &navdata)
Definition: sick_scan_common.cpp:1376
READ_TIMEOUT_MILLISEC_DEFAULT
#define READ_TIMEOUT_MILLISEC_DEFAULT
Definition: sick_scan_common.h:94
sensor_msgs::LaserScan
::sensor_msgs::LaserScan_< std::allocator< void > > LaserScan
Definition: LaserScan.h:94
colab.hpp
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
Definition: sick_scan_logging.h:123
sick_scan_xd::SickScanCommon::CMD_SET_TO_COLA_B_PROTOCOL
@ CMD_SET_TO_COLA_B_PROTOCOL
Definition: sick_scan_common.h:185
sick_scan_xd::ScannerBasicParam::getScanAngleShift
double getScanAngleShift()
Definition: sick_generic_parser.cpp:505
sick_scan_xd::SickScanCommon::CMD_SEN_SCANDATACONFIGNAV
@ CMD_SEN_SCANDATACONFIGNAV
Definition: sick_scan_common.h:182
ROS_INFO_STREAM
#define ROS_INFO_STREAM(...)
Definition: sick_scan_ros2_example.cpp:71
sick_scan_xd::SickScanCommon::CMD_SET_NAV_DO_MAPPING
@ CMD_SET_NAV_DO_MAPPING
Definition: sick_scan_common.h:205
sick_scan_xd::SickScanCommon::CMD_SERIAL_NUMBER
@ CMD_SERIAL_NUMBER
Definition: sick_scan_common.h:109
sick_scan_xd::SickScanCommon::CMD_LOCATION_NAME
@ CMD_LOCATION_NAME
Definition: sick_scan_common.h:116
sick_scan_xd::parseNAV350BinaryLandmarkDataDoMappingResponse
bool parseNAV350BinaryLandmarkDataDoMappingResponse(const uint8_t *receiveBuffer, int receiveBufferLength, NAV350LandmarkDataDoMappingResponse &landmarkData)
Definition: sick_nav_scandata_parser.cpp:466
rosTimeToSeconds
double rosTimeToSeconds(const rosTime &time)
Definition: sick_ros_wrapper.h:179
diagnostic_msgs_DiagnosticStatus_ERROR
#define diagnostic_msgs_DiagnosticStatus_ERROR
Definition: sick_ros_wrapper.h:126
sick_scan_xd::SickScanConfig
Definition: sick_ros_wrapper.h:512
sick_scan_xd::SickScanCommon::setNTPServerAndStart
bool setNTPServerAndStart(const std::string &ipNewIPAddr, bool useBinaryCmd)
Definition: sick_scan_common.cpp:6154
sick_scan_xd::SickScanCommon::CMD_SET_OUTPUT_RANGES
@ CMD_SET_OUTPUT_RANGES
Definition: sick_scan_common.h:129
sick_scan_xd::SickScanParseUtil::SopasToLMPscancfg
static bool SopasToLMPscancfg(const std::string &sopas_reply, LMPscancfg &scancfg)
Parse the sopas reply to "sRN LMPscancfg" and convert to LMPscancfg.
Definition: sick_scan_parse_util.cpp:174
sick_scan_xd::SickScanMessages::getSopasCmdKeyword
static std::string getSopasCmdKeyword(const uint8_t *sopasRequest, int requestLength)
Definition: sick_scan_messages.cpp:447
sick_scan_xd::SickScanCommon::init
virtual int init(rosNodePtr nh)
init routine of scanner
Definition: sick_scan_common.cpp:1513
sick_scan_xd::SickScanFieldMonSingleton::getInstance
static SickScanFieldMonSingleton * getInstance()
Definition: sick_generic_field_mon.cpp:155
sick_scan_xd::SickScanCommon::lidoutputstate_pub_
rosPublisher< sick_scan_msg::LIDoutputstateMsg > lidoutputstate_pub_
Definition: sick_scan_common.h:474
sick_scan_xd::SickScanCommon::diagnosticPub_
uint8_t * diagnosticPub_
Definition: sick_scan_common.h:511
sick_scan_xd::SickScanCommon::CMD_SET_NAV_OPERATIONAL_MODE_2
@ CMD_SET_NAV_OPERATIONAL_MODE_2
Definition: sick_scan_common.h:196
sick_generic_imu.h
sick_scan_xd::SickScanCommon::sopasCmdChain
std::vector< int > sopasCmdChain
Definition: sick_scan_common.h:527
sick_scan_xd::ScannerBasicParam::setScandatacfgAzimuthTableSupported
void setScandatacfgAzimuthTableSupported(bool _scandatacfgAzimuthTableSupported)
Definition: sick_generic_parser.cpp:454
sick_scan_xd::SickScanParseUtil::LMPscancfgToSopas
static bool LMPscancfgToSopas(const LMPscancfg &scancfg, std::string &sopas_cmd)
Convert LMPscancfg to sopas request "sMN mLMPsetscancfg ...".
Definition: sick_scan_parse_util.cpp:229
imu_delay_tester.parser
parser
Definition: imu_delay_tester.py:116
sick_scan_xd::ScannerBasicParam::getUseWriteOutputRanges
bool getUseWriteOutputRanges()
Definition: sick_generic_parser.cpp:430
ROS_FATAL
#define ROS_FATAL(...)
Definition: sick_scan_logging.h:132
sick_scan_xd::createNAV350BinarySetSpeedRequest
std::vector< uint8_t > createNAV350BinarySetSpeedRequest(const sick_scan_msg::NAVOdomVelocity &msg)
Definition: sick_nav_scandata_parser.cpp:552
diagnostic_updater::DiagnosedPublisher
A TopicDiagnostic combined with a ros::Publisher.
Definition: eloquent/include/diagnostic_updater/publisher.hpp:176
visualization_msgs::MarkerArray
::visualization_msgs::MarkerArray_< std::allocator< void > > MarkerArray
Definition: MarkerArray.h:50
sick_scan_xd::EVAL_FIELD_SUPPORT
EVAL_FIELD_SUPPORT
Definition: sick_generic_parser.h:93
sick_scan_xd::SickScanCommon::nav_tf_child_frame_id_
std::string nav_tf_child_frame_id_
Definition: sick_scan_common.h:486
sick_scan_xd::rotateXYbyAngleOffset
void rotateXYbyAngleOffset(float &x, float &y, double angle_offset)
Definition: sick_nav_scandata_parser.cpp:764
sick_scan_xd::ScannerBasicParam::getNumberOfMaximumEchos
int getNumberOfMaximumEchos(void)
Get number of maximum echoes for this laser scanner type.
Definition: sick_generic_parser.cpp:179
sick_scan_xd::SickScanCommon::CMD_SET_NAV_MAP_CFG
@ CMD_SET_NAV_MAP_CFG
Definition: sick_scan_common.h:203
sick_scan_xd::SickScanConfig::use_generation_timestamp
bool use_generation_timestamp
Definition: sick_ros_wrapper.h:523
sick_scan_xd::ScannerBasicParam::getAngularDegreeResolution
double getAngularDegreeResolution(void)
Get angular resolution in degress.
Definition: sick_generic_parser.cpp:211
sick_scan_xd::SickScanCommon::get_datagram
virtual int get_datagram(rosNodePtr nh, rosTime &recvTimeStamp, unsigned char *receiveBuffer, int bufferSize, int *actual_length, bool isBinaryProtocol, int *numberOfRemainingFifoEntries, const std::vector< std::string > &datagram_keywords)=0
Read a datagram from the device.
sick_scan_xd::SickScanRadarSingleton::parseDatagram
int parseDatagram(rosTime timeStamp, unsigned char *receiveBuffer, int actual_length, bool useBinaryProtocol)
Definition: sick_generic_radar.cpp:1472
sick_scan_xd::convertNAVPoseDataToTransform
ros_geometry_msgs::TransformStamped convertNAVPoseDataToTransform(NAV350PoseData &poseData, rosTime recvTimeStamp, double config_time_offset, const std::string &tf_parent_frame_id, const std::string &tf_child_frame_id, SickGenericParser *parser_)
Definition: sick_nav_scandata_parser.cpp:799
SICK_SCANNER_LRS_36x0_NAME
#define SICK_SCANNER_LRS_36x0_NAME
Definition: sick_generic_parser.h:79
sick_scan_xd::SickScanCommon::CMD_SET_NTP_SERVER_IP_ADDR
@ CMD_SET_NTP_SERVER_IP_ADDR
Definition: sick_scan_common.h:179
rosPublish
void rosPublish(rosPublisher< T > &publisher, const T &msg)
Definition: sick_ros_wrapper.h:203
SICK_SCANNER_LMS_1XXX_NAME
#define SICK_SCANNER_LMS_1XXX_NAME
Definition: sick_generic_parser.h:63
sick_scan_xd::SickScanCommon::cmdSetAccessMode3
std::string cmdSetAccessMode3(void)
Returns "sMN SetAccessMode 3 F4724744" resp. "\x02sMN SetAccessMode 3 6FD62C05\x03\0" for safety scan...
Definition: sick_scan_common.cpp:715
sick_scan_xd::SickScanCommon::setProtocolType
void setProtocolType(SopasProtocol cola_dialect_id)
set protocol type as enum
Definition: sick_scan_common.cpp:1492
SICK_SCANNER_NAV_31X_NAME
#define SICK_SCANNER_NAV_31X_NAME
Definition: sick_generic_parser.h:74
sick_scan_xd::SickScanCommon::init_scanner
virtual int init_scanner(rosNodePtr nh)
initialize scanner
Definition: sick_scan_common.cpp:2046
sick_scan_xd::SickScanCommon::nav_reflector_pub_
rosPublisher< ros_visualization_msgs::MarkerArray > nav_reflector_pub_
Definition: sick_scan_common.h:482
sick_scan_xd::RANGE_FILTER_DROP
@ RANGE_FILTER_DROP
Definition: sick_range_filter.h:75
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::print
void print()
Definition: sick_scan_common.cpp:6259
sick_scan_xd::SickScanCommon::CMD_SET_LFPMEANFILTER
@ CMD_SET_LFPMEANFILTER
Definition: sick_scan_common.h:212
sick_scan_xd::SickScanCommon::CMD_SET_NAV_REFL_SIZE
@ CMD_SET_NAV_REFL_SIZE
Definition: sick_scan_common.h:204
sick_scan_xd::SickScanCommon::lidinputstate_pub_
rosPublisher< sick_scan_msg::LIDinputstateMsg > lidinputstate_pub_
Definition: sick_scan_common.h:473
sick_scan_xd::SickScanCommon::dumpDatagramForDebugging
static bool dumpDatagramForDebugging(unsigned char *buffer, int bufLen, bool isBinary=true)
Definition: sick_scan_common.cpp:4233
sick_scan_xd::SickScanConfig::intensity
bool intensity
Definition: sick_ros_wrapper.h:516
sick_scan_xd::SickGenericParser::getCurrentParamPtr
ScannerBasicParam * getCurrentParamPtr()
Gets Pointer to parameter object.
Definition: sick_generic_parser.cpp:1040
sick_scan_xd::ExitSuccess
@ ExitSuccess
Definition: abstract_parser.h:46
sick_scan_xd::SickScanCommon::CMD_START_SCANDATA
@ CMD_START_SCANDATA
Definition: sick_scan_common.h:138
sick_scan_parse_util.h
sick_scan_xd::NAV350LandmarkData::reflectors
std::vector< NAV350ReflectorData > reflectors
Definition: sick_nav_scandata.h:153
sick_scan_xd::LIDoutputstateMsg
::sick_scan_xd::LIDoutputstateMsg_< std::allocator< void > > LIDoutputstateMsg
Definition: LIDoutputstateMsg.h:110
sick_scan_xd::SickScanCommon::readFieldSetSelectionMethod
int readFieldSetSelectionMethod(int &field_set_selection_method, std::vector< unsigned char > &sopasReply, bool useBinaryCmd=true)
Definition: sick_scan_common.cpp:6409
sick_scan_xd::SickScanCommon::check_angle_range
void check_angle_range(SickScanConfig &conf)
check angle setting in the config and adjust the min_ang to the max_ang if min_ang greater than max_a...
Definition: sick_scan_common.cpp:5384
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
Definition: sick_scan_logging.h:113
sick_scan_xd::SickScanCommon::CMD_SET_ECHO_FILTER
@ CMD_SET_ECHO_FILTER
Definition: sick_scan_common.h:174
sick_scan_xd::notifyLFErecListener
void notifyLFErecListener(rosNodePtr handle, const sick_scan_msg::LFErecMsg *msg)
Definition: sick_generic_callback.cpp:154
ros::NodeHandle::subscribe
Subscriber subscribe(const std::string &topic, uint32_t queue_size, const boost::function< void(C)> &callback, const VoidConstPtr &tracked_object=VoidConstPtr(), const TransportHints &transport_hints=TransportHints())
sick_scan_xd::SickScanCommon::CMD_READ_ACTIVE_APPLICATIONS
@ CMD_READ_ACTIVE_APPLICATIONS
Definition: sick_scan_common.h:126
ros::NodeHandle
sick_scan_xd::SickScanCommon::init_device
virtual int init_device()=0
sick_scan_xd::SickScanParseUtil::LMPscancfg
Definition: sick_scan_parse_util.h:93
sick_scan_xd::SickScanCommon::imuScan_pub_
rosPublisher< ros_sensor_msgs::Imu > imuScan_pub_
Definition: sick_scan_common.h:384
sick_scan_xd::SickScanCommon::~SickScanCommon
virtual ~SickScanCommon()
Destructor of SickScanCommon.
Definition: sick_scan_common.cpp:919
sec
uint32_t sec(const rosTime &time)
Definition: sick_ros_wrapper.h:174
sick_lmd_scandata_parser.h
sick_scan_xd::SickScanCommon::get2ndSopasResponse
int get2ndSopasResponse(std::vector< uint8_t > &sopas_response, const std::string &sopas_keyword)
Definition: sick_scan_common.cpp:1252
sick_scan_xd::SickScanCommon::getReadTimeOutInMs
int getReadTimeOutInMs()
get timeout in milliseconds
Definition: sick_scan_common.cpp:1473
sick_scan_xd::SickScanCommon::sopasCmdErrMsg
std::vector< std::string > sopasCmdErrMsg
Definition: sick_scan_common.h:526
sick_scan_xd::SickScanParseUtil::LMPscancfgSector
Definition: sick_scan_parse_util.h:85
dynamic_reconfigure::Server::CallbackType
std::function< void(ConfigType &, uint32_t level)> CallbackType
Definition: server.h:84
sick_scan_xd::SickScanCommon::CMD_SET_GLARE_DETECTION_SENS
@ CMD_SET_GLARE_DETECTION_SENS
Definition: sick_scan_common.h:217
rosGetParam
bool rosGetParam(rosNodePtr nh, const std::string &param_name, T &param_value)
Definition: sick_ros_wrapper.h:167
sick_scan_xd::SickScanRadarSingleton::getInstance
static SickScanRadarSingleton * getInstance(rosNodePtr nh)
Definition: sick_generic_radar.cpp:88
sick_scan_xd::ScannerBasicParam::isOneOfScannerNames
bool isOneOfScannerNames(const std::vector< std::string > &scanner_names) const
Returns true, if the scanner name (type) is found int a given list of scanner names.
Definition: sick_generic_parser.cpp:105
SICK_SCANNER_LMS_4XXX_NAME
#define SICK_SCANNER_LMS_4XXX_NAME
Definition: sick_generic_parser.h:72
ROS_INFO
#define ROS_INFO(...)
Definition: sick_scan_logging.h:117
sick_scan_xd::SickScanCommon::CMD_SET_TRANSMIT_OBJECTS_OFF
@ CMD_SET_TRANSMIT_OBJECTS_OFF
Definition: sick_scan_common.h:163
sick_scan_xd::SickScanCommon::CMD_DEVICE_STATE
@ CMD_DEVICE_STATE
Definition: sick_scan_common.h:113
sick_scan_xd::SickScanFieldMonSingleton::parseAsciiDatagram
int parseAsciiDatagram(std::vector< unsigned char > datagramm, float rectFieldAngleRefPointOffsetRad)
Parsing Ascii datagram.
Definition: sick_generic_field_mon.cpp:303
PARAM_MIN_ANG
#define PARAM_MIN_ANG
Definition: sick_scan_common.h:228
sick_scan_xd::ScannerBasicParam::getFREchoFilterAvailable
bool getFREchoFilterAvailable(void)
Definition: sick_generic_parser.cpp:449
sick_scan_xd::SickScanCommon::CMD_START_RADARDATA
@ CMD_START_RADARDATA
Definition: sick_scan_common.h:139
isFieldEvaluationActive
static bool & isFieldEvaluationActive()
Definition: sick_scan_common.cpp:206
sick_scan_xd::SickScanCommon::readParseSafetyFields
int readParseSafetyFields(bool useBinaryCmd)
Definition: sick_scan_common.cpp:6270
sick_scan_xd::USE_EVAL_FIELD_LMS5XX_LOGIC
@ USE_EVAL_FIELD_LMS5XX_LOGIC
Definition: sick_generic_parser.h:97
sick_scan_xd::ScannerBasicParam::getScanMirroredAndShifted
bool getScanMirroredAndShifted()
flag to mark mirroring of rotation direction
Definition: sick_generic_parser.cpp:331
sick_scan_xd::SickScanImu::isImuAckDatagram
bool isImuAckDatagram(char *datagram, size_t datagram_length)
Definition: sick_generic_imu.cpp:136
sick_scan_xd::SickScanCommon::getProtocolType
int getProtocolType(void)
get protocol type as enum
Definition: sick_scan_common.cpp:1483
AngleCompensator::getHumanReadableFormula
std::string getHumanReadableFormula(void)
Definition: angle_compensator.cpp:342
rosTimeNow
rosTime rosTimeNow(void)
Definition: sick_ros_wrapper.h:173
sick_scan_xd::SickScanCommon::CMD_SET_PARTIAL_SCANDATA_CFG
@ CMD_SET_PARTIAL_SCANDATA_CFG
Definition: sick_scan_common.h:136
sick_scan_xd::SickScanCommon::CMD_LOAD_APPLICATION_DEFAULT
@ CMD_LOAD_APPLICATION_DEFAULT
Definition: sick_scan_common.h:168
sick_generic_field_mon.h
SICK_SCANNER_MRS_1XXX_NAME
#define SICK_SCANNER_MRS_1XXX_NAME
Definition: sick_generic_parser.h:64
SICK_SCANNER_TIM_7XXS_NAME
#define SICK_SCANNER_TIM_7XXS_NAME
Definition: sick_generic_parser.h:68
sick_scan_xd::SickScanMessages::parseLFErecMsg
static bool parseLFErecMsg(const rosTime &timeStamp, uint8_t *receiveBuffer, int receiveLength, bool useBinaryProtocol, EVAL_FIELD_SUPPORT eval_field_logic, const std::string &frame_id, sick_scan_msg::LFErecMsg &output_msg)
Definition: sick_scan_messages.cpp:308
sick_scan_xd::SickScanImu
Definition: sick_generic_imu.h:189
sick_scan_xd::SickScanCommon::readActiveFieldSet
int readActiveFieldSet(int &active_field_set, std::vector< unsigned char > &sopasReply, bool useBinaryCmd=true)
Definition: sick_scan_common.cpp:6461
sick_scan_xd::SickScanCommon::CMD_SET_MEAN_FILTER
@ CMD_SET_MEAN_FILTER
Definition: sick_scan_common.h:119
AngleCompensator
Definition: angle_compensator.h:12
sick_scan_xd::SickScanCommon::sendNAV350mNPOSGetData
int sendNAV350mNPOSGetData(void)
Definition: sick_scan_common.cpp:1364
sick_scan_config_internal.h
SICK_SCANNER_LRS_36x1_NAME
#define SICK_SCANNER_LRS_36x1_NAME
Definition: sick_generic_parser.h:80
sick_scan_xd::SickScanCommon::messageCbNavOdomVelocity
void messageCbNavOdomVelocity(const sick_scan_msg::NAVOdomVelocity &msg)
Definition: sick_scan_common.cpp:1422
sick_scan_xd::SickScanCommon::setReadTimeOutInMs
void setReadTimeOutInMs(int timeOutInMs)
set timeout in milliseconds
Definition: sick_scan_common.cpp:1463
MAX_STR_LEN
static const int MAX_STR_LEN
Definition: sick_scan_common.cpp:118
sick_scan_xd::SickScanConfig::imu_frame_id
std::string imu_frame_id
Definition: sick_ros_wrapper.h:515
sick_scan_xd::LIDinputstateMsg
::sick_scan_xd::LIDinputstateMsg_< std::allocator< void > > LIDinputstateMsg
Definition: LIDinputstateMsg.h:110
sick_scan_xd::SickScanCommon::publish_lferec_
bool publish_lferec_
Definition: sick_scan_common.h:472
sick_scan_xd::SickScanCommon::generateUnexpectedAnswerString
std::vector< std::string > generateUnexpectedAnswerString(const std::string &requestStr)
Definition: sick_scan_common.cpp:1050
READ_TIMEOUT_MILLISEC_STARTUP
#define READ_TIMEOUT_MILLISEC_STARTUP
Definition: sick_scan_common.h:93
sick_scan_xd::SickScanCommon::cloud_
ros_sensor_msgs::PointCloud2 cloud_
Definition: sick_scan_common.h:386
sick_scan_xd::SickScanCommon::m_protocolId
SopasProtocol m_protocolId
Definition: sick_scan_common.h:464
ros::Time
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE_DL
@ CMD_SET_ENCODER_MODE_DL
Definition: sick_scan_common.h:148
sick_scan_xd::ScannerBasicParam::getIntensityResolutionIs16Bit
bool getIntensityResolutionIs16Bit(void)
Get the RSSI Value length.
Definition: sick_generic_parser.cpp:361
sick_scan_xd::SickScanCommon::sensorIsRadar
bool sensorIsRadar
Definition: sick_scan_common.h:548
sick_scan_xd::SickScanCommon::Encoder_pub
rosPublisher< sick_scan_msg::Encoder > Encoder_pub
Definition: sick_scan_common.h:385
sick_scan_xd::RADAR_1D
@ RADAR_1D
Definition: sick_generic_parser.h:105
sick_scan_services.h
ROS_ERROR
#define ROS_ERROR(...)
Definition: sick_scan_logging.h:127
sick_scan_xd_api_test.c
c
Definition: sick_scan_xd_api_test.py:445
sick_scan_xd::SickScanCommon::CMD_SET_NAV_OPERATIONAL_MODE_0
@ CMD_SET_NAV_OPERATIONAL_MODE_0
Definition: sick_scan_common.h:194
sick_scan_xd::NAV350LandmarkDataDoMappingResponse::landmarkData
NAV350LandmarkData landmarkData
Definition: sick_nav_scandata.h:162
sick_scan_xd::SickScanCommon::CMD_SET_NAV_OPERATIONAL_MODE_1
@ CMD_SET_NAV_OPERATIONAL_MODE_1
Definition: sick_scan_common.h:195
sick_scan_xd::SickScanCommon::CMD_SET_PARTICLE_FILTER
@ CMD_SET_PARTICLE_FILTER
Definition: sick_scan_common.h:118
sick_scan_xd::SickRangeFilter::apply
bool apply(float &range, bool &range_modified) const
Definition: sick_range_filter.h:107
sick_scan_xd::SickScanCommon::CMD_ORDER_NUMBER
@ CMD_ORDER_NUMBER
Definition: sick_scan_common.h:170
sick_scan_xd::SickScanCommon::CMD_APPLICATION_MODE_RANGING_ON
@ CMD_APPLICATION_MODE_RANGING_ON
Definition: sick_scan_common.h:125
sick_scan_xd::readNAVIMKfile
std::vector< sick_scan_xd::NAV350ImkLandmark > readNAVIMKfile(const std::string &nav_imk_file)
Definition: sick_nav_scandata_parser.cpp:908
sick_scan_xd::SickScanCommon::CMD_SET_NAV_CURR_LAYER
@ CMD_SET_NAV_CURR_LAYER
Definition: sick_scan_common.h:199
sick_scan_xd::SickRangeFilter::resizePointCloud
void resizePointCloud(size_t rangeNum, ros_sensor_msgs::PointCloud2 &cloud)
Definition: sick_range_filter.h:182
sick_scan_xd::SickScanCommon::replyToString
std::string replyToString(const std::vector< unsigned char > &reply)
Converts reply from sendSOPASCommand to string.
Definition: sick_scan_common.cpp:4190
SopasProtocol
SopasProtocol
Definition: sick_scan_common_nw.h:31
sick_scan_xd::SickScanFieldMonSingleton::parseBinaryDatagram
int parseBinaryDatagram(std::vector< unsigned char > datagramm, float rectFieldAngleRefPointOffsetRad)
Definition: sick_generic_field_mon.cpp:310
std_msgs::String
::std_msgs::String_< std::allocator< void > > String
Definition: String.h:48
sick_scan_xd::ScannerBasicParam::getUseEvalFields
EVAL_FIELD_SUPPORT getUseEvalFields()
Definition: sick_generic_parser.cpp:386
sick_scan_xd::SickScanFieldMonSingleton::getMonFields
const std::vector< SickScanMonField > & getMonFields(void) const
Definition: sick_generic_field_mon.h:169
dataDumper.h
sick_scan_xd::SickScanCommon::CMD_ACTIVATE_STANDBY
@ CMD_ACTIVATE_STANDBY
Definition: sick_scan_common.h:117
colaa.hpp
sick_scan_xd::SickScanCommon::setNewIpAddress
bool setNewIpAddress(const std::string &ipNewIPAddr, bool useBinaryCmd)
Definition: sick_scan_common.cpp:6095
SoftwarePLL::IsInitialized
bool IsInitialized() const
Definition: softwarePLL.h:47
sick_scan_xd::ExitCode
ExitCode
Definition: abstract_parser.h:44
tf2_ros::TransformBroadcaster
This class provides an easy way to publish coordinate frame transform information....
Definition: transform_broadcaster.h:49
PARAM_MAX_ANG
#define PARAM_MAX_ANG
Definition: sick_scan_common.h:229
SICK_SCANNER_LRS_4XXX_NAME
#define SICK_SCANNER_LRS_4XXX_NAME
Definition: sick_generic_parser.h:78
sick_scan_xd::SickScanCommon::expectedFrequency_
double expectedFrequency_
Definition: sick_scan_common.h:513
sick_scan_xd::SickScanCommon::pub_
rosPublisher< ros_sensor_msgs::LaserScan > pub_
Definition: sick_scan_common.h:467
sick_scan_xd::SickScanCommon::sendSOPASCommand
virtual int sendSOPASCommand(const char *request, std::vector< unsigned char > *reply, int cmdLen, bool wait_for_reply=true)=0
Send a SOPAS command to the device and print out the response to the console.
sick_scan_xd::SickScanCommon::m_add_transform_xyz_rpy
sick_scan_xd::SickCloudTransform m_add_transform_xyz_rpy
Definition: sick_scan_common.h:560
sick_scan_xd::SickScanFieldMonSingleton
Definition: sick_generic_field_mon.h:152
sick_scan_xd::SickScanCommon::CMD_SET_ENCODER_MODE_DP
@ CMD_SET_ENCODER_MODE_DP
Definition: sick_scan_common.h:147
RETURN_ERROR_ON_RESPONSE_TIMEOUT
#define RETURN_ERROR_ON_RESPONSE_TIMEOUT(result, reply)
Definition: sick_scan_common.cpp:116
ROS_DEBUG
#define ROS_DEBUG(...)
Definition: sick_scan_logging.h:112
sick_scan_xd::SickScanCommon::datagram_pub_
rosPublisher< ros_std_msgs::String > datagram_pub_
Definition: sick_scan_common.h:468
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::scan_layer_activated
std::vector< int > scan_layer_activated
Definition: sick_scan_common.h:570
sick_scan_xd::SickScanCommon::CMD_ACTIVATE_NTP_CLIENT
@ CMD_ACTIVATE_NTP_CLIENT
Definition: sick_scan_common.h:140
CoLa_B
@ CoLa_B
Command Language binary.
Definition: sick_scan_common_nw.h:34
sick_scan_xd::SickScanCommon::readTimeOutInMs
int readTimeOutInMs
Definition: sick_scan_common.h:543
sick_scan_xd::NAV350mNPOSData::landmarkDataValid
uint16_t landmarkDataValid
Definition: sick_nav_scandata.h:209
sick_scan_xd::SickScanCommon::stop_scanner
virtual int stop_scanner(bool force_immediate_shutdown=false)
Stops sending scan data.
Definition: sick_scan_common.cpp:727
sick_scan_common_nw.h
sick_scan_xd::SickScanCommon::CMD_APPLICATION_MODE_FIELD_ON
@ CMD_APPLICATION_MODE_FIELD_ON
Definition: sick_scan_common.h:123
rosNanosecTimestampNow
uint64_t rosNanosecTimestampNow(void)
Definition: sick_ros_wrapper.h:178
sick_scan_xd::ScannerBasicParam::setEncoderMode
void setEncoderMode(int8_t _EncoderMode)
Prama for encoder mode.
Definition: sick_generic_parser.cpp:484
sick_scan_xd::createNAV350BinaryAddLandmarkRequest
std::vector< uint8_t > createNAV350BinaryAddLandmarkRequest(const NAV350LandmarkData &landmarkData, int nav_curr_layer)
Definition: sick_nav_scandata_parser.cpp:509
sick_scan_xd::SickGenericParser::get_range_min
float get_range_min(void)
Getting minimum range.
Definition: sick_generic_parser.cpp:1519
sick_scan_xd::SickScanCommon::sopasReplyBinVec
std::vector< std::vector< unsigned char > > sopasReplyBinVec
Definition: sick_scan_common.h:524
sick_scan_xd::ScannerBasicParam::getScandatacfgAzimuthTableSupported
bool getScandatacfgAzimuthTableSupported(void) const
Definition: sick_generic_parser.cpp:459
sick_scan_xd::SickScanCommon::convertAscii2BinaryCmd
int convertAscii2BinaryCmd(const char *requestAscii, std::vector< unsigned char > *requestBinary)
Convert ASCII-message to Binary-message.
Definition: sick_scan_common.cpp:5455
rosDurationFromSec
rosDuration rosDurationFromSec(double seconds)
Definition: sick_ros_wrapper.h:211
sick_scan_xd::SickScanCommon::deviceIdentStr
std::string deviceIdentStr
Definition: sick_scan_common.h:514
sick_scan_xd::SickScanCommon::CMD_SET_NAV_ADD_LANDMARK
@ CMD_SET_NAV_ADD_LANDMARK
Definition: sick_scan_common.h:206
sick_scan_xd::SickScanCommon::CMD_GET_SCANDATACONFIGNAV
@ CMD_GET_SCANDATACONFIGNAV
Definition: sick_scan_common.h:181
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(...)
Definition: sick_scan_ros2_example.cpp:72
sick_scan_xd::SickCloudTransform::applyTransform
void applyTransform(float_type &x, float_type &y, float_type &z)
Definition: sick_cloud_transform.h:100
sick_scan_xd::SickScanCommon::setLengthAndCRCinBinarySopasRequest
void setLengthAndCRCinBinarySopasRequest(std::vector< uint8_t > *requestBinary)
Definition: sick_scan_common.cpp:6031
sick_scan_xd::SickScanMarker
Definition: sick_scan_marker.h:72
sick_scan_xd::SickScanCommon::m_read_timeout_millisec_default
int m_read_timeout_millisec_default
Definition: sick_scan_common.h:557
sick_scan_xd::NAV350mNPOSData::poseDataValid
uint16_t poseDataValid
Definition: sick_nav_scandata.h:207
sick_scan_xd::SickScanConfig::time_offset
double time_offset
Definition: sick_ros_wrapper.h:524
sick_scan_xd::SickScanCommon::publish_nav_pose_data_
bool publish_nav_pose_data_
Definition: sick_scan_common.h:480
SICK_SCANNER_NAV_2XX_NAME
#define SICK_SCANNER_NAV_2XX_NAME
Definition: sick_generic_parser.h:76
sick_scan_xd::SickScanCommon::CMD_GET_ANGLE_COMPENSATION_PARAM
@ CMD_GET_ANGLE_COMPENSATION_PARAM
Definition: sick_scan_common.h:183
args
sick_scan_xd::stripControl
std::string stripControl(std::vector< unsigned char > s, int max_strlen=-1)
Converts a SOPAS command to a human readable string.
Definition: sick_scan_common.cpp:239
SICK_SCANNER_LMS_5XX_NAME
#define SICK_SCANNER_LMS_5XX_NAME
Definition: sick_generic_parser.h:69
sick_scan_xd::SickScanCommon::m_nh
rosNodePtr m_nh
Definition: sick_scan_common.h:580
sick_scan_xd::notifyCartesianPointcloudListener
void notifyCartesianPointcloudListener(rosNodePtr handle, const sick_scan_xd::PointCloud2withEcho *msg)
Definition: sick_generic_callback.cpp:74
sick_scan_xd::SickScanParseUtil::LMPscancfg::print
std::string print() const
Definition: sick_scan_parse_util.cpp:156
sick_scan_xd::SickScanCommon::sendSopasRunSetAccessMode
bool sendSopasRunSetAccessMode(bool useBinaryCmd)
Definition: sick_scan_common.cpp:1337
sick_scan_xd::SickScanCommon::checkForProtocolChangeAndMaybeReconnect
bool checkForProtocolChangeAndMaybeReconnect(bool &useBinaryCmdNow)
checks the current protocol type and gives information about necessary change
Definition: sick_scan_common.cpp:6059
AngleCompensator::compensateAngleInRadFromRos
double compensateAngleInRadFromRos(double angleInRadFromRos)
Compensate raw angle given in [RAD] in the ROS axis orientation system.
Definition: angle_compensator.cpp:116
sick_scan_xd::ScannerBasicParam::getDeviceIsRadar
bool getDeviceIsRadar(void)
flag to mark the device as radar (instead of laser scanner)
Definition: sick_generic_parser.cpp:292
sick_scan_xd::SickScanCommon::CMD_SET_TRANSMIT_RAWTARGETS_OFF
@ CMD_SET_TRANSMIT_RAWTARGETS_OFF
Definition: sick_scan_common.h:160
sick_scan_xd::SickScanMessages::parseLIDoutputstateMsg
static bool parseLIDoutputstateMsg(const rosTime &timeStamp, uint8_t *receiveBuffer, int receiveLength, bool useBinaryProtocol, const std::string &frame_id, sick_scan_msg::LIDoutputstateMsg &output_msg)
Definition: sick_scan_messages.cpp:102
rosOk
bool rosOk(void)
Definition: sick_ros_wrapper.h:206
sick_scan_xd::SickScanCommon::readLIDinputstate
int readLIDinputstate(SickScanFieldMonSingleton *fieldMon, bool useBinaryCmd)
Definition: sick_scan_common.cpp:6358
sick_scan_xd::SickScanFieldMonSingleton::getActiveFieldset
int getActiveFieldset(void)
Definition: sick_generic_field_mon.h:172
sick_scan_xd::SickScanCommon::parser_
SickGenericParser * parser_
Definition: sick_scan_common.h:520
sick_scan_xd::SickScanCommon::CMD_SET_SCAN_CFG_LIST
@ CMD_SET_SCAN_CFG_LIST
Definition: sick_scan_common.h:191
sick_scan_xd::SickScanCommon::CMD_SCAN_LAYER_FILTER
@ CMD_SCAN_LAYER_FILTER
Definition: sick_scan_common.h:121
ROS_ASSERT
#define ROS_ASSERT(cond)
Asserts that the provided condition evaluates to true.
Definition: assert.h:123
sick_scan_xd::SickScanCommon::CMD_POWER_ON_COUNT
@ CMD_POWER_ON_COUNT
Definition: sick_scan_common.h:115
sick_scan_xd::SickScanCommon::nav_pose_data_pub_
rosPublisher< sick_scan_msg::NAVPoseData > nav_pose_data_pub_
Definition: sick_scan_common.h:479
sick_scan_xd::SickScanImu::parseDatagram
int parseDatagram(rosTime timeStamp, unsigned char *receiveBuffer, int actual_length, bool useBinaryProtocol)
Definition: sick_generic_imu.cpp:554
SICK_SCANNER_SCANSEGMENT_XD_NAME
#define SICK_SCANNER_SCANSEGMENT_XD_NAME
Definition: sick_generic_parser.h:82
sick_scan_xd::SickScanCommon::cloud_topic_val
std::string cloud_topic_val
Definition: sick_scan_common.h:465
PARAM_RES_ANG
#define PARAM_RES_ANG
Definition: sick_scan_common.h:230
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::first_active_layer
int first_active_layer
Definition: sick_scan_common.h:571
sick_scan_xd::SickScanCommon::publish_nav_landmark_data_
bool publish_nav_landmark_data_
Definition: sick_scan_common.h:483
sick_scan_xd::SickScanCommon::convertBigEndianCharArrayToUnsignedLong
unsigned long convertBigEndianCharArrayToUnsignedLong(const unsigned char *vecArr)
Convert little endian to big endian (should be replaced by swap-routine)
Definition: sick_scan_common.cpp:776
sick_scan_xd::SickScanCommon::CMD_SET_SCAN_LAYER_FILTER
@ CMD_SET_SCAN_LAYER_FILTER
Definition: sick_scan_common.h:220
sick_scan_xd::parseNAV350BinaryPositionData
bool parseNAV350BinaryPositionData(const uint8_t *receiveBuffer, int receiveBufferLength, NAV350mNPOSData &navdata)
Definition: sick_nav_scandata_parser.cpp:344
sick_scan_xd::ScannerBasicParam::getMaxEvalFields
int getMaxEvalFields(void)
Definition: sick_generic_parser.cpp:396
ros::Duration
sick_scan_xd::SickScanCommon::CMD_START_MEASUREMENT
@ CMD_START_MEASUREMENT
Definition: sick_scan_common.h:172
sick_scan_xd::SickScanFieldMonSingleton::setFieldSelectionMethod
void setFieldSelectionMethod(int field_selection_method)
Definition: sick_generic_field_mon.h:174
sick_scan_xd::ScannerBasicParam::getRectEvalFieldAngleRefPointOffsetRad
float getRectEvalFieldAngleRefPointOffsetRad(void)
Definition: sick_generic_parser.cpp:411
rosDeclareParam
void rosDeclareParam(rosNodePtr nh, const std::string &param_name, const T &param_value)
Definition: sick_ros_wrapper.h:166
sick_scan_xd::NAVOdomVelocity
::sick_scan_xd::NAVOdomVelocity_< std::allocator< void > > NAVOdomVelocity
Definition: NAVOdomVelocity.h:69
sick_scan_xd::SickScanCommon::CMD_FIRMWARE_VERSION
@ CMD_FIRMWARE_VERSION
Definition: sick_scan_common.h:112
SICK_SCANNER_RMS_XXXX_NAME
#define SICK_SCANNER_RMS_XXXX_NAME
Definition: sick_generic_parser.h:73
sick_scan_xd::SickCloudTransform::azimuthOffset
float azimuthOffset(void) const
Definition: sick_cloud_transform.h:133
sick_scan_xd::SickGenericParser::get_range_filter_config
RangeFilterResultHandling get_range_filter_config(void) const
Get range filter handling (range filter deactivated, drop, set to nan, etc.pp.)
Definition: sick_generic_parser.cpp:1535
sick_scan_xd::SickScanCommon::getSensorIsRadar
bool getSensorIsRadar(void)
Definition: sick_scan_common.cpp:6218
sick_scan_xd::ScannerBasicParam::getUseSafetyPasWD
bool getUseSafetyPasWD()
flag to mark the device uses the safety scanner password \reutrn Bool true for safety password false ...
Definition: sick_generic_parser.cpp:381
sick_scan_xd::USE_EVAL_FIELD_TIM7XX_LOGIC
@ USE_EVAL_FIELD_TIM7XX_LOGIC
Definition: sick_generic_parser.h:96
sick_scan_xd::SickScanCommon::CMD_SET_OUTPUT_RANGES_NAV3
@ CMD_SET_OUTPUT_RANGES_NAV3
Definition: sick_scan_common.h:130
sick_scan_xd::SickScanCommon::CMD_OPERATION_HOURS
@ CMD_OPERATION_HOURS
Definition: sick_scan_common.h:114
sick_scan_xd::SickScanMarker::updateMarker
void updateMarker(const std::vector< SickScanMonField > &fields, int fieldset, int eval_field_logic)
Definition: sick_scan_marker.cpp:119
parseFirmwareVersion
static std::vector< int > parseFirmwareVersion(const std::string &scannertype, const std::string &deviceIdentStr)
Definition: sick_scan_common.cpp:188
sick_scan_xd::SickScanCommon::switchColaProtocol
bool switchColaProtocol(bool useBinaryCmd)
Definition: sick_scan_common.cpp:1267
sick_scan_xd::SickScanCommon::ScanLayerFilterCfg::scan_layer_filter
std::string scan_layer_filter
Definition: sick_scan_common.h:569
sick_scan_xd::SickScanCommon::CMD_APPLICATION_MODE
@ CMD_APPLICATION_MODE
Definition: sick_scan_common.h:122
sick_scan_xd::SickScanCommon::publish_lidoutputstate_
bool publish_lidoutputstate_
Definition: sick_scan_common.h:476
sick_scan_xd::NAV350mNPOSData::poseData
NAV350PoseData poseData
Definition: sick_nav_scandata.h:208
sick_scan_xd::ExitError
@ ExitError
Definition: abstract_parser.h:47
sick_scan_xd::SickScanCommon::nav_tf_parent_frame_id_
std::string nav_tf_parent_frame_id_
Definition: sick_scan_common.h:485
sick_scan_xd::SickScanCommon::CMD_GET_PARTIAL_SCANDATA_CFG
@ CMD_GET_PARTIAL_SCANDATA_CFG
Definition: sick_scan_common.h:135
sick_scan_xd::SickScanCommon::m_scan_layer_filter_cfg
ScanLayerFilterCfg m_scan_layer_filter_cfg
Definition: sick_scan_common.h:578
stringToVector
std::vector< unsigned char > stringToVector(std::string s)
Universal swapping function.
Definition: sick_scan_common.cpp:145
sick_scan_xd::SickScanCommon::m_min_intensity
double m_min_intensity
Definition: sick_scan_common.h:552
sick_scan_xd::SickScanRadarSingleton
Definition: sick_generic_radar.h:170
binScanfGuessDataLenFromMask
int binScanfGuessDataLenFromMask(const char *scanfMask)
Definition: binScanf.cpp:438
SICK_SCANNER_PICOSCAN_NAME
#define SICK_SCANNER_PICOSCAN_NAME
Definition: sick_generic_parser.h:83
sick_scan_xd::notifyNavPoseLandmarkListener
void notifyNavPoseLandmarkListener(rosNodePtr handle, NAV350mNPOSData *msg)
Definition: sick_generic_callback.cpp:234
SICK_SCANNER_TIM_240_NAME
#define SICK_SCANNER_TIM_240_NAME
Definition: sick_generic_parser.h:65
sick_scan_xd::ScannerBasicParam::getDeviceRadarType
RADAR_TYPE_ENUM getDeviceRadarType(void)
Definition: sick_generic_parser.cpp:297
sick_scan_xd::SickScanCommon::CMD_DEVICE_IDENT
@ CMD_DEVICE_IDENT
Definition: sick_scan_common.h:108
sick_scan_xd::NAV350LandmarkDataDoMappingResponse::landmarkDataValid
uint16_t landmarkDataValid
Definition: sick_nav_scandata.h:161
sick_scan_xd::SickRangeFilter
Definition: sick_range_filter.h:85
diagnostic_updater::FrequencyStatusParam
A structure that holds the constructor parameters for the FrequencyStatus class.
Definition: eloquent/include/diagnostic_updater/update_functions.hpp:53
ROS_FATAL_STREAM
#define ROS_FATAL_STREAM(args)
Definition: sick_scan_logging.h:133
sick_scan_xd::SickScanConfig::skip
int skip
Definition: sick_ros_wrapper.h:521
sick_scan_xd::SickScanCommon::CMD_GET_OUTPUT_RANGES
@ CMD_GET_OUTPUT_RANGES
Definition: sick_scan_common.h:131
sick_scan_xd::sick_crc8
unsigned char sick_crc8(unsigned char *msgBlock, int len)
calculate crc-code for last byte of binary message XOR-calucation is done ONLY over message content (...
Definition: sick_scan_common.cpp:221


sick_scan_xd
Author(s): Michael Lehning , Jochen Sprickerhof , Martin Günther
autogenerated on Fri Oct 25 2024 02:47:10