sick_lmd_scandata_parser.cpp
Go to the documentation of this file.
1 /*
2  * sick_lmd_scandata_parser parses result telegrams of type LMDscandata.
3  *
4  * Copyright (C) 2022, Ing.-Buero Dr. Michael Lehning, Hildesheim
5  * Copyright (C) 2022, SICK AG, Waldkirch
6  * All rights reserved.
7  *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions are met:
25 *
26 * * Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * * Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * * Neither the name of Osnabrueck University nor the names of its
32 * contributors may be used to endorse or promote products derived from
33 * this software without specific prior written permission.
34 * * Neither the name of SICK AG nor the names of its
35 * contributors may be used to endorse or promote products derived from
36 * this software without specific prior written permission
37 * * Neither the name of Ing.-Buero Dr. Michael Lehning nor the names of its
38 * contributors may be used to endorse or promote products derived from
39 * this software without specific prior written permission
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51 * POSSIBILITY OF SUCH DAMAGE.
52  *
53  * Authors:
54  * Michael Lehning <michael.lehning@lehning.de>
55  *
56  */
57 
58 #include "softwarePLL.h"
61 
62 #define deg2rad_const (0.017453292519943295769236907684886f)
63 
64 namespace sick_scan_xd
65 {
66 
69  {
71  if (SoftwarePLL::instance().IsInitialized() == false)
72  {
73  if(SoftwarePLL::instance().packets_received <= 1)
74  {
75  ROS_INFO("Software PLL locking started, mapping ticks to system time.");
76  }
77  int packets_expected_to_drop = SoftwarePLL::instance().fifoSize - 1;
79  size_t packets_dropped = SoftwarePLL::instance().packets_dropped;
80  size_t packets_received = SoftwarePLL::instance().packets_received;
81  if (packets_dropped < packets_expected_to_drop)
82  {
83  ROS_INFO_STREAM("" << packets_dropped << " / " << packets_expected_to_drop << " packets dropped. Software PLL not yet locked.");
84  }
85  else if (packets_dropped == packets_expected_to_drop)
86  {
87  ROS_INFO("Software PLL is ready and locked now!");
88  }
89  else if (packets_dropped > packets_expected_to_drop && packets_received > 0)
90  {
91  double drop_rate = (double)packets_dropped / (double)packets_received;
92  ROS_WARN_STREAM("" << SoftwarePLL::instance().packets_dropped << " of " << SoftwarePLL::instance().packets_received << " packets dropped ("
93  << std::fixed << std::setprecision(1) << (100*drop_rate) << " perc.), maxAbsDeltaTime=" << std::fixed << std::setprecision(3) << SoftwarePLL::instance().max_abs_delta_time);
94  ROS_WARN_STREAM("More packages than expected were dropped!!\n"
95  "Check the network connection.\n"
96  "Check if the system time has been changed in a leap.\n"
97  "If the problems can persist, disable the software PLL with the option sw_pll_only_publish=False !");
98  }
99  }
100  }
101 
109  bool check_near_plus_minus_pi(float *angle_val)
110  {
111  bool angle_slightly_modified = false;
112  float pi_multiplier = *angle_val/M_PI;
113  // float check_deviation_to_abs_one = fabs(pi_multiplier) - 1.0;
114  // check for a small deviation
115  // if (check_deviation_to_abs_one < 10.0 * FLT_EPSILON )
116  if (pi_multiplier > 1.1 || pi_multiplier < -1.1)
117  {
118  ROS_WARN_STREAM("check_near_plus_minus_pi: min or max angle = " << *angle_val * 180 / M_PI << " degree, expected angle within -180 to +180 degree, check scan angle shift settings.");
119  }
120  else if (pi_multiplier > 1.0 - 10.0 * FLT_EPSILON || pi_multiplier < -1.0 + 10.0 * FLT_EPSILON)
121  {
122  float factor = (*angle_val < 0.0) ? (-1.0) : (1.0);
123  *angle_val = factor * (1.0 - FLT_EPSILON) * M_PI;
124  angle_slightly_modified = true;
125  }
126  else
127  {
128  angle_slightly_modified =false;
129  }
130  return(angle_slightly_modified);
131  }
132 
133 
135  bool parseCommonBinaryResultTelegram(const uint8_t* receiveBuffer, int receiveBufferLength, short& elevAngleX200, double elevAngleTelegramValToDeg, double& elevationAngleInRad, rosTime& recvTimeStamp,
136  bool config_sw_pll_only_publish, bool use_generation_timestamp, SickGenericParser* parser_, bool& FireEncoder, sick_scan_msg::Encoder& EncoderMsg, int& numEchos,
137  std::vector<float>& vang_vec, std::vector<float>& azimuth_vec, ros_sensor_msgs::LaserScan & msg)
138  {
139  // bool lms1000_debug = true; // LMS-1000 diagnosis
140  elevAngleX200 = 0; // signed short (F5 B2 -> Layer 24
141  // F5B2h -> -2638/200= -13.19°
142  int scanFrequencyX100 = 0;
143  double scanFrequency = 0.0;
144  long measurementFrequencyDiv100 = 0; // multiply with 100
145  int numOfEncoders = 0;
146  int numberOf16BitChannels = 0;
147  int numberOf8BitChannels = 0;
148  uint32_t SystemCountScan = 0;
149  static uint32_t lastSystemCountScan = 0;// this variable is used to ensure that only the first time stamp of an multi layer scann is used for PLL updating
150  uint32_t SystemCountTransmit = 0;
151 
152  memcpy(&elevAngleX200, receiveBuffer + 50, 2);
153  swap_endian((unsigned char *) &elevAngleX200, 2);
154 
155  // elevationAngleInRad = -elevAngleX200 / 200.0 * deg2rad_const;
156  elevationAngleInRad = -elevAngleX200 * elevAngleTelegramValToDeg * deg2rad_const; // MRS-6000: elevAngleTelegramValToDeg=1./200, MRS-1000: elevAngleTelegramValToDeg=1./100
157  // ROS_INFO_STREAM("LMDscandata: elevAngleX200=" << elevAngleX200 << " / " << (1/elevAngleTelegramValToDeg) << " = " << (elevationAngleInRad / deg2rad_const) << " [deg]");
158  ROS_HEADER_SEQ(msg.header, elevAngleX200); // should be multiple of 0.625° starting with -2638 (corresponding to 13.19°)
159 
160  // Time since start up in microseconds: Counting the time since power up the device; starting with 0. In the output telegram this is the time at the zero index before the measurement itself starts.
161  memcpy(&SystemCountScan, receiveBuffer + 0x26, 4); // 0x26 = 38 dec
162  swap_endian((unsigned char *) &SystemCountScan, 4);
163 
164  // Time of transmission in microseconds: Time in μs when the complete scan is transmitted to the buffer for data output; starting with 0 at scanner bootup.
165  memcpy(&SystemCountTransmit, receiveBuffer + 0x2A, 4);
166  swap_endian((unsigned char *) &SystemCountTransmit, 4);
167 
168  /*{
169  double system_count_scan_sec = SystemCountScan * 1e-6;
170  double system_count_transmit_sec = SystemCountTransmit * 1e-6;
171  ROS_INFO_STREAM("LMDscandata: SystemCountScan = " << system_count_scan_sec << " sec, SystemCountTransmit = " << system_count_transmit_sec << " sec, delta = " << 1.0e3*(system_count_transmit_sec - system_count_scan_sec) << " millisec.");
172  }*/
173 
174  double timestampfloat = sec(recvTimeStamp) + nsec(recvTimeStamp) * 1e-9;
175  bool bRet;
176  if (SystemCountScan !=
177  lastSystemCountScan)//MRS 6000 sends 6 packets with same SystemCountScan we should only update the pll once with this time stamp since the SystemCountTransmit are different and this will only increase jitter of the pll
178  {
179  bRet = SoftwarePLL::instance().updatePLL(sec(recvTimeStamp), nsec(recvTimeStamp),
180  SystemCountTransmit);
181  lastSystemCountScan = SystemCountScan;
182  }
183  // ROS_DEBUG_STREAM("recvTimeStamp before software-pll correction: " << recvTimeStamp);
184  rosTime tmp_time = recvTimeStamp;
185  uint32_t recvTimeStampSec = (uint32_t)sec(recvTimeStamp), recvTimeStampNsec = (uint32_t)nsec(recvTimeStamp);
186  uint32_t lidar_ticks = SystemCountScan;
187  if(use_generation_timestamp == 0)
188  lidar_ticks = SystemCountTransmit;
189  bRet = SoftwarePLL::instance().getCorrectedTimeStamp(recvTimeStampSec, recvTimeStampNsec, lidar_ticks);
190 
191  recvTimeStamp = rosTime(recvTimeStampSec, recvTimeStampNsec);
192  double timestampfloat_coor = sec(recvTimeStamp) + nsec(recvTimeStamp) * 1e-9;
193  double DeltaTime = timestampfloat - timestampfloat_coor;
194  // ROS_DEBUG_STREAM("recvTimeStamp after software-pll correction: " << recvTimeStamp);
195  //ROS_INFO("%F,%F,%u,%u,%F",timestampfloat,timestampfloat_coor,SystemCountTransmit,SystemCountScan,DeltaTime);
196  //TODO Handle return values
197  if (config_sw_pll_only_publish == true)
198  {
200  }
201 
202 #ifdef DEBUG_DUMP_ENABLED
203  double elevationAngleInDeg = elevationAngleInRad / deg2rad_const;
204  // DataDumper::instance().pushData((double)SystemCountScan, "LAYER", elevationAngleInDeg);
205  //DataDumper::instance().pushData((double)SystemCountScan, "LASESCANTIME", SystemCountScan);
206  //DataDumper::instance().pushData((double)SystemCountTransmit, "LASERTRANSMITTIME", SystemCountTransmit);
207  //DataDumper::instance().pushData((double)SystemCountScan, "LASERTRANSMITDELAY", debug_duration.toSec());
208 #endif
209 
210  // byte 48 + 49: output status (0 0)
211  // byte 50 + 51: reserved
212 
213  memcpy(&scanFrequencyX100, receiveBuffer + 52, 4);
214  swap_endian((unsigned char *) &scanFrequencyX100, 4);
215 
216  memcpy(&measurementFrequencyDiv100, receiveBuffer + 56, 4);
217  swap_endian((unsigned char *) &measurementFrequencyDiv100, 4);
218 
219 
220  msg.scan_time = 1.0 / (scanFrequencyX100 / 100.0);
221 
222  //due firmware inconsistency
223  if (measurementFrequencyDiv100 > 10000)
224  {
225  measurementFrequencyDiv100 /= 100;
226  }
227  if (measurementFrequencyDiv100 != 0)
228  {
229  msg.time_increment = 1.0 / (measurementFrequencyDiv100 * 100.0);
230  }
231  else
232  {
233  msg.time_increment = 0;
234  }
235  // timeIncrement = msg.time_increment;
236  msg.range_min = parser_->get_range_min();
237  msg.range_max = parser_->get_range_max();
238 
239  memcpy(&numOfEncoders, receiveBuffer + 60, 2);
240  swap_endian((unsigned char *) &numOfEncoders, 2);
241  // ROS_DEBUG_STREAM("numOfEncoders = " << numOfEncoders);
242  int encoderDataOffset = 6 * numOfEncoders;
243  int32_t EncoderPosTicks[4] = {0};
244  int16_t EncoderSpeed[4] = {0};
245 
246  if (numOfEncoders > 0 && numOfEncoders < 5)
247  {
248  FireEncoder = true;
249  for (int EncoderNum = 0; EncoderNum < numOfEncoders; EncoderNum++)
250  {
251  memcpy(&EncoderPosTicks[EncoderNum], receiveBuffer + 62 + EncoderNum * 6, 4);
252  swap_endian((unsigned char *) &EncoderPosTicks[EncoderNum], 4);
253  memcpy(&EncoderSpeed[EncoderNum], receiveBuffer + 66 + EncoderNum * 6, 2);
254  swap_endian((unsigned char *) &EncoderSpeed[EncoderNum], 2);
255  }
256  }
257  //TODO handle multi encoder with multiple encode msg or different encoder msg definition now using only first encoder
258  EncoderMsg.enc_position = EncoderPosTicks[0];
259  EncoderMsg.enc_speed = EncoderSpeed[0];
260  memcpy(&numberOf16BitChannels, receiveBuffer + 62 + encoderDataOffset, 2);
261  swap_endian((unsigned char *) &numberOf16BitChannels, 2);
262 
263  int parseOff = 64 + encoderDataOffset;
264 
265 
266  char szChannel[255] = {0};
267  float scaleFactor = 1.0;
268  float scaleFactorOffset = 0.0;
269  int32_t startAngleDiv10000 = 1;
270  int32_t sizeOfSingleAngularStepDiv10000 = 1;
271  double startAngle = 0.0;
272  double sizeOfSingleAngularStep = 0.0;
273  short numberOfItems = 0;
274 
275  //static int cnt = 0;
276  //cnt++;
277  // get number of 8 bit channels
278  // we must jump of the 16 bit data blocks including header ...
279  for (int i = 0; i < numberOf16BitChannels; i++)
280  {
281  int numberOfItems = 0x00;
282  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
283  swap_endian((unsigned char *) &numberOfItems, 2);
284  parseOff += 21; // 21 Byte header followed by data entries
285  parseOff += numberOfItems * 2;
286  }
287 
288  // now we can read the number of 8-Bit-Channels
289  memcpy(&numberOf8BitChannels, receiveBuffer + parseOff, 2);
290  swap_endian((unsigned char *) &numberOf8BitChannels, 2);
291 
292  parseOff = 64 + encoderDataOffset;
293  enum datagram_parse_task
294  {
295  process_dist,
296  process_vang,
297  process_azimuth,
298  process_rssi,
299  process_idle
300  };
301  int rssiCnt = 0;
302  int vangleCnt = 0;
303  int azimuthCnt = 0;
304  int distChannelCnt = 0;
305 
306  for (int processLoop = 0; processLoop < 2; processLoop++)
307  {
308  int totalChannelCnt = 0;
309 
310 
311  bool bCont = true;
312 
313  datagram_parse_task task = process_idle;
314  bool parsePacket = true;
315  parseOff = 64 + encoderDataOffset;
316  bool processData = false;
317 
318  if (processLoop == 0)
319  {
320  distChannelCnt = 0;
321  rssiCnt = 0;
322  vangleCnt = 0;
323  azimuthCnt = 0;
324  azimuth_vec.clear();
325  }
326 
327  if (processLoop == 1)
328  {
329  processData = true;
330  numEchos = distChannelCnt;
331  msg.ranges.resize(numberOfItems * numEchos);
332  if (rssiCnt > 0)
333  {
334  msg.intensities.resize(numberOfItems * rssiCnt);
335  }
336  else
337  {
338  }
339  if (vangleCnt > 0) // should be 0 or 1
340  {
341  vang_vec.resize(numberOfItems * vangleCnt);
342  }
343  else
344  {
345  vang_vec.clear();
346  }
347  if (azimuthCnt > 0)
348  {
349  azimuth_vec.resize(numberOfItems * azimuthCnt);
350  }
351  // echoMask = (1 << numEchos) - 1;
352 
353  // reset count. We will use the counter for index calculation now.
354  distChannelCnt = 0;
355  rssiCnt = 0;
356  vangleCnt = 0;
357  azimuthCnt = 0;
358 
359  }
360 
361  szChannel[6] = '\0';
362  scaleFactor = 1.0;
363  scaleFactorOffset = 0.0;
364  startAngleDiv10000 = 1;
365  sizeOfSingleAngularStepDiv10000 = 1;
366  startAngle = 0.0;
367  sizeOfSingleAngularStep = 0.0;
368  numberOfItems = 0;
369 
370 
371 #if 1 // prepared for multiecho parsing
372 
373  bCont = true;
374  // try to get number of DIST and RSSI from binary data
375  task = process_idle;
376  do
377  {
378  task = process_idle;
379  int processDataLenValuesInBytes = 2;
380 
381  if (totalChannelCnt == numberOf16BitChannels)
382  {
383  parseOff += 2; // jump of number of 8 bit channels- already parsed above
384  }
385 
386  if (totalChannelCnt >= numberOf16BitChannels)
387  {
388  processDataLenValuesInBytes = 1; // then process 8 bit values ...
389  }
390  bCont = false;
391  strcpy(szChannel, "");
392 
393  if (totalChannelCnt < (numberOf16BitChannels + numberOf8BitChannels))
394  {
395  szChannel[5] = '\0';
396  strncpy(szChannel, (const char *) receiveBuffer + parseOff, 5);
397  }
398  else
399  {
400  // all channels processed (16 bit and 8 bit channels)
401  }
402 
403  if (strstr(szChannel, "DIST") == szChannel)
404  {
405  task = process_dist;
406  distChannelCnt++;
407  bCont = true;
408  numberOfItems = 0;
409  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
410  swap_endian((unsigned char *) &numberOfItems, 2);
411  }
412  if (strstr(szChannel, "VANG") == szChannel) // MRS6000 transmits elevation angles on channel "VANG"
413  {
414  vangleCnt++;
415  task = process_vang;
416  bCont = true;
417  numberOfItems = 0;
418  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
419  swap_endian((unsigned char *) &numberOfItems, 2);
420  vang_vec.resize(numberOfItems);
421  }
422  if (strstr(szChannel, "ANGL") == szChannel) // MRS1xxx and LMS1xxx transmit azimuth angles on channel "ANGL"
423  {
424  azimuthCnt++;
425  task = process_azimuth;
426  bCont = true;
427  numberOfItems = 0;
428  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
429  swap_endian((unsigned char *) &numberOfItems, 2);
430  azimuth_vec.resize(numberOfItems);
431  }
432  if (strstr(szChannel, "RSSI") == szChannel)
433  {
434  task = process_rssi;
435  rssiCnt++;
436  bCont = true;
437  numberOfItems = 0;
438  // copy two byte value (unsigned short to numberOfItems
439  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
440  swap_endian((unsigned char *) &numberOfItems, 2); // swap
441  }
442  if (bCont)
443  {
444  scaleFactor = 0.0;
445  scaleFactorOffset = 0.0;
446  startAngleDiv10000 = 0;
447  sizeOfSingleAngularStepDiv10000 = 0;
448  numberOfItems = 0;
449 
450  memcpy(&scaleFactor, receiveBuffer + parseOff + 5, 4);
451  memcpy(&scaleFactorOffset, receiveBuffer + parseOff + 9, 4);
452  memcpy(&startAngleDiv10000, receiveBuffer + parseOff + 13, 4);
453  memcpy(&sizeOfSingleAngularStepDiv10000, receiveBuffer + parseOff + 17, 2);
454  memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
455 
456 
457  swap_endian((unsigned char *) &scaleFactor, 4);
458  swap_endian((unsigned char *) &scaleFactorOffset, 4);
459  swap_endian((unsigned char *) &startAngleDiv10000, 4);
460  swap_endian((unsigned char *) &sizeOfSingleAngularStepDiv10000, 2);
461  swap_endian((unsigned char *) &numberOfItems, 2);
462 
463  if(false) // if (lms1000_debug) // LMS-1000 diagnosis
464  {
465  ROS_DEBUG_STREAM("LMDscandata: lidar_scan_time=" << SystemCountScan << " microsec, lidar_transmit_time=" << SystemCountTransmit << " microsec, "
466  << "scan_frequency=" << (0.01 * scanFrequencyX100) << " Hz, measurement_frequency=" << measurementFrequencyDiv100 << " Hz,"
467  << "start_angle=" << (0.0001 * startAngleDiv10000) << " [deg], angular_step="<< (0.0001 * sizeOfSingleAngularStepDiv10000) << " [deg]");
468  // lms1000_debug = false;
469  } //
470 
471  if (processData)
472  {
473  unsigned short *data = (unsigned short *) (receiveBuffer + parseOff + 21);
474 
475  unsigned char *swapPtr = (unsigned char *) data;
476  // copy RSSI-Values +2 for 16-bit values +1 for 8-bit value
477  for (int i = 0;
478  i < numberOfItems * processDataLenValuesInBytes; i += processDataLenValuesInBytes)
479  {
480  if (processDataLenValuesInBytes == 1)
481  {
482  }
483  else
484  {
485  unsigned char tmp;
486  tmp = swapPtr[i + 1];
487  swapPtr[i + 1] = swapPtr[i];
488  swapPtr[i] = tmp;
489  }
490  }
491  int idx = 0;
492 
493  switch (task)
494  {
495 
496  case process_dist:
497  {
498  startAngle = startAngleDiv10000 / 10000.00;
499  sizeOfSingleAngularStep = sizeOfSingleAngularStepDiv10000 / 10000.0;
500  sizeOfSingleAngularStep *= (M_PI / 180.0);
501 
502  msg.angle_min = startAngle / 180.0 * M_PI + parser_->getCurrentParamPtr()->getScanAngleShift(); // msg.angle_min = startAngle / 180.0 * M_PI - M_PI / 2;
503  msg.angle_increment = sizeOfSingleAngularStep;
504  msg.angle_max = msg.angle_min + (numberOfItems - 1) * msg.angle_increment;
505 
506  if(msg.time_increment == 0) // NAV-350
507  {
508  msg.time_increment = fabs(parser_->getCurrentParamPtr()->getNumberOfLayers() * msg.scan_time * msg.angle_increment / (2.0 * M_PI));
509  }
510 
511  if (parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_NAV_31X_NAME) == 0
512  || parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_LRS_36x0_NAME) == 0
513  || parser_->getCurrentParamPtr()->getScannerName().compare(SICK_SCANNER_OEM_15XX_NAME) == 0)
514  {
515  msg.angle_min = (float)(-M_PI);
516  msg.angle_max = (float)(+M_PI);
517  msg.angle_increment *= -1.0;
518  if (msg.angle_increment < 0.0)
519  {
520  // angle_min corresponds to start angle
521  // i.e. if angle_increment is negative,
522  // the angle_min is greater than angle_max
523  msg.angle_min = (float)(+M_PI);
524  msg.angle_max = (float)(-M_PI);
525  }
526  }
527  else if (parser_->getCurrentParamPtr()->getScanMirroredAndShifted()) // i.e. for SICK_SCANNER_LRS_36x0_NAME and SICK_SCANNER_NAV_31X_NAME
528  {
529  msg.angle_min *= -1.0;
530  msg.angle_increment *= -1.0;
531  msg.angle_max *= -1.0;
532 
533  }
534  ROS_DEBUG_STREAM("process_dist: msg.angle_min=" << (msg.angle_min*180/M_PI) << ", msg.angle_max=" << (msg.angle_max*180/M_PI) << ", msg.angle_increment=" << (msg.angle_increment*180/M_PI) << ", scaleFactor=" << scaleFactor << ", scaleFactorOffset=" << scaleFactorOffset);
535 
536  // Avoid 2*PI wrap around, if (msg.angle_max - msg.angle_min - 2*PI) is slightly above 0.0 due to floating point arithmetics
537  bool wrap_avoid = false;
538  bool ret = check_near_plus_minus_pi(&(msg.angle_min));
539  if (ret)
540  {
541  wrap_avoid = true;
542  }
543  ret = check_near_plus_minus_pi(&(msg.angle_max));
544  if (ret)
545  {
546  wrap_avoid = true;
547  }
548 
549  // in the case of slighlty modified min/max angles,
550  // we recalculate the angle_increment.
551  if (wrap_avoid)
552  {
553  msg.angle_increment = (msg.angle_max - msg.angle_min) / (numberOfItems - 1);
554  }
555  ROS_DEBUG_STREAM("process_dist: msg.angle_min=" << (msg.angle_min*180/M_PI) << ", msg.angle_max=" << (msg.angle_max*180/M_PI) << ", msg.angle_increment=" << (msg.angle_increment*180/M_PI) << ", scaleFactor=" << scaleFactor << ", scaleFactorOffset=" << scaleFactorOffset << ", " << (8*processDataLenValuesInBytes) << "-bit channel");
556 
557  float *rangePtr = NULL;
558 
559  if (numberOfItems > 0)
560  {
561  rangePtr = &msg.ranges[0];
562  }
563  float scaleFactor_001 = 0.001F * scaleFactor;// to avoid repeated multiplication
564  for (int i = 0; i < numberOfItems; i++)
565  {
566  idx = i + numberOfItems * (distChannelCnt - 1);
567  rangePtr[idx] = (float) data[i] * scaleFactor_001 + scaleFactorOffset;
568 #ifdef DEBUG_DUMP_ENABLED
569  if (distChannelCnt == 1)
570  {
571  if (i == floor(numberOfItems / 2))
572  {
573  double curTimeStamp = SystemCountScan + i * msg.time_increment * 1E6;
574  //DataDumper::instance().pushData(curTimeStamp, "DIST", rangePtr[idx]);
575  }
576  }
577 #endif
578  //XXX
579  }
580 
581  }
582  break;
583  case process_rssi:
584  {
585  // Das muss vom Protokoll abgeleitet werden. !!!
586 
587  float *intensityPtr = NULL;
588 
589  if (numberOfItems > 0)
590  {
591  intensityPtr = &msg.intensities[0];
592 
593  }
594  for (int i = 0; i < numberOfItems; i++)
595  {
596  idx = i + numberOfItems * (rssiCnt - 1);
597  // we must select between 16 bit and 8 bit values
598  float rssiVal = 0.0;
599  if (processDataLenValuesInBytes == 2)
600  {
601  rssiVal = (float) data[i];
602  }
603  else
604  {
605  unsigned char *data8Ptr = (unsigned char *) data;
606  rssiVal = (float) data8Ptr[i];
607  }
608  intensityPtr[idx] = rssiVal * scaleFactor + scaleFactorOffset;
609  }
610  }
611  break;
612 
613  case process_vang: // elevation angles MRS6000
614  if (numberOfItems > 0)
615  {
616  float *vangPtr = &vang_vec[0]; // much faster, with vang_vec[i] each time the size will be checked
617  for (int i = 0; i < numberOfItems; i++)
618  {
619  vangPtr[i] = (float) data[i] * scaleFactor + scaleFactorOffset;
620  }
621  }
622  break;
623 
624  case process_azimuth: // azimuth angles MRS1xxx and LMS1xxx
625  if (numberOfItems > 0)
626  {
627  if (std::abs(scaleFactorOffset) <= FLT_EPSILON)
628  {
629  // Note: If the firmware of a previous uncalibrated MRS1xxx or LMS1xxx has been upgraded to firmware version 2.3.0 (or newer) without calibrating the azimuth table,
630  // the scaleFactorOffset is most likely 0.0, but the azimuth correction table is activated by default.
631  // In this case, the following warnings is displayed according to requirements. To avoid the warning, parameter scandatacfg_azimuth_table can be set to value 0
632  // (i.e. azimuth correction table deactivated) in the launchfile.
633  ROS_WARN_STREAM("## WARNING parseCommonBinaryResultTelegram(): process_azimuth with unexpected scaleFactorOffset=" << scaleFactorOffset << ", expected value is not 0.0");
634  ROS_WARN_STREAM("## WARNING parseCommonBinaryResultTelegram(): set parameter scandatacfg_azimuth_table=0 in the launchfile if the lidar has no calibrated azimuth table.");
635  }
636  float angle_min_rad = (1.0e-4f * startAngleDiv10000) * M_PI / 180.0 + parser_->getCurrentParamPtr()->getScanAngleShift();
637  float angle_inc_rad = (1.0e-4f * sizeOfSingleAngularStepDiv10000) * M_PI / 180.0;
638  float angle_max_rad = angle_min_rad + (numberOfItems - 1) * angle_inc_rad;
639  ROS_DEBUG_STREAM("process_dist: msg.angle_min=" << (msg.angle_min*180/M_PI) << ", msg.angle_max=" << (msg.angle_max*180/M_PI) << ", msg.angle_increment=" << (msg.angle_increment*180/M_PI));
640  ROS_DEBUG_STREAM("process_azimuth: angle_min=" << (angle_min_rad*180/M_PI) << ", angle_max=" << (angle_max_rad*180/M_PI) << ", angle_inc=" << (angle_inc_rad*180/M_PI)
641  << ", scaleFactor=" << scaleFactor << ", scaleFactorOffset=" << scaleFactorOffset << ", " << (8*processDataLenValuesInBytes) << "-bit channel");
642  // angular correction according to "LMS4000---Angular-correction.pptx" for scalefactor=1:
643  // azimuth = angle_min + (i * angle_inc) + (offset + data[i]) with 0 < i < numberOfItems
644  float *azimuthPtr = &azimuth_vec[0];
645  if (processDataLenValuesInBytes == 1)
646  {
647  uint8_t* data_ptr = (uint8_t*) data;
648  for (int i = 0; i < numberOfItems; i++)
649  azimuthPtr[i] = (angle_min_rad + i * angle_inc_rad + (float)(1.0e-4 * M_PI / 180.0) * ((float)data_ptr[i] * scaleFactor + scaleFactorOffset));
650  }
651  else if (processDataLenValuesInBytes == 2)
652  {
653  uint16_t* data_ptr = (uint16_t*) data;
654  for (int i = 0; i < numberOfItems; i++)
655  azimuthPtr[i] = (angle_min_rad + i * angle_inc_rad + (float)(1.0e-4 * M_PI / 180.0) * ((float)data_ptr[i] * scaleFactor + scaleFactorOffset));
656  }
657  // std::stringstream dbg_stream;
658  // for (int i = 0; i < numberOfItems; i++)
659  // dbg_stream << " " << std::fixed << std::setprecision(3) << (azimuth_vec[i]*180/M_PI);
660  // ROS_DEBUG_STREAM("azimuth table: " << dbg_stream.str());
661  }
662  break;
663  }
664  }
665  parseOff += 21 + processDataLenValuesInBytes * numberOfItems;
666 
667 
668  }
669  totalChannelCnt++;
670  } while (bCont);
671  }
672 #endif
673 
674  // double elevAngle = elevationAngleInRad; // elevAngleX200 / 200.0;
675  scanFrequency = scanFrequencyX100 / 100.0;
676 
677  return true;
678  }
679 
680 } /* namespace sick_scan_xd */
sick_scan_xd::Encoder
::sick_scan_xd::Encoder_< std::allocator< void > > Encoder
Definition: Encoder.h:60
msg
msg
NULL
#define NULL
SoftwarePLL::updatePLL
bool updatePLL(uint32_t sec, uint32_t nanoSec, uint32_t curtick)
Definition: softwarePLL.cpp:191
swap_endian
void swap_endian(unsigned char *ptr, int numBytes)
Universal swapping function.
Definition: sick_scan_common.cpp:125
sick_scan_xd::ScannerBasicParam::getNumberOfLayers
int getNumberOfLayers(void)
Getting number of scanner layers.
Definition: sick_generic_parser.cpp:133
sick_scan_xd::ScannerBasicParam::getScannerName
std::string getScannerName(void) const
Getting name (type) of scanner.
Definition: sick_generic_parser.cpp:97
sick_scan_xd::check_near_plus_minus_pi
bool check_near_plus_minus_pi(float *angle_val)
Definition: sick_lmd_scandata_parser.cpp:109
SoftwarePLL::packets_received
size_t packets_received
Definition: softwarePLL.h:103
SoftwarePLL::instance
static SoftwarePLL & instance()
Definition: softwarePLL.h:24
sick_scan_xd::SickGenericParser
Definition: sick_generic_parser.h:239
SICK_SCANNER_OEM_15XX_NAME
#define SICK_SCANNER_OEM_15XX_NAME
Definition: sick_generic_parser.h:81
SoftwarePLL::getCorrectedTimeStamp
bool getCorrectedTimeStamp(uint32_t &sec, uint32_t &nanoSec, uint32_t tick)
Definition: softwarePLL.cpp:226
sick_scan_xd
Definition: abstract_parser.cpp:65
data
data
sick_scan_common.h
f
f
ROS_HEADER_SEQ
#define ROS_HEADER_SEQ(msgheader, value)
Definition: sick_ros_wrapper.h:162
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
nsec
uint32_t nsec(const rosTime &time)
Definition: sick_ros_wrapper.h:175
sensor_msgs::LaserScan
::sensor_msgs::LaserScan_< std::allocator< void > > LaserScan
Definition: LaserScan.h:94
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
Definition: sick_scan_logging.h:123
sick_scan_xd::ScannerBasicParam::getScanAngleShift
double getScanAngleShift()
Definition: sick_generic_parser.cpp:505
ROS_INFO_STREAM
#define ROS_INFO_STREAM(...)
Definition: sick_scan_ros2_example.cpp:71
softwarePLL.h
SoftwarePLL::packets_dropped
size_t packets_dropped
Definition: softwarePLL.h:102
SICK_SCANNER_LRS_36x0_NAME
#define SICK_SCANNER_LRS_36x0_NAME
Definition: sick_generic_parser.h:79
SICK_SCANNER_NAV_31X_NAME
#define SICK_SCANNER_NAV_31X_NAME
Definition: sick_generic_parser.h:74
sick_scan_xd::SickGenericParser::getCurrentParamPtr
ScannerBasicParam * getCurrentParamPtr()
Gets Pointer to parameter object.
Definition: sick_generic_parser.cpp:1040
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
Definition: sick_scan_logging.h:113
sec
uint32_t sec(const rosTime &time)
Definition: sick_ros_wrapper.h:174
sick_lmd_scandata_parser.h
deg2rad_const
#define deg2rad_const
Definition: sick_lmd_scandata_parser.cpp:62
ROS_INFO
#define ROS_INFO(...)
Definition: sick_scan_logging.h:117
sick_scan_xd::ScannerBasicParam::getScanMirroredAndShifted
bool getScanMirroredAndShifted()
flag to mark mirroring of rotation direction
Definition: sick_generic_parser.cpp:331
ros::Time
sick_scan_xd::SickGenericParser::get_range_max
float get_range_max(void)
Getting maximum range.
Definition: sick_generic_parser.cpp:1509
sick_scan_xd::SickGenericParser::get_range_min
float get_range_min(void)
Getting minimum range.
Definition: sick_generic_parser.cpp:1519
sick_scan_xd::incSoftwarePLLPacketReceived
void incSoftwarePLLPacketReceived()
Definition: sick_lmd_scandata_parser.cpp:68
SoftwarePLL::fifoSize
static const int fifoSize
Definition: softwarePLL.h:101
rosTime
ros::Time rosTime
Definition: sick_ros_wrapper.h:172


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