sensor_msg_comparison.h
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Autoware Foundation. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <algorithm>
18 #include <iostream>
19 #include <sstream>
20 #include <stdlib.h>
21 #include <string>
22 #include <vector>
23 
24 #include <sensor_msgs/PointCloud2.h>
25 #include <sensor_msgs/PointField.h>
26 
27 bool equals(const sensor_msgs::PointCloud2& lhs, const sensor_msgs::PointCloud2& rhs)
28 {
29  //header
30  bool are_header_equals = (lhs.header.seq == rhs.header.seq) &&
31  (lhs.header.stamp.sec == rhs.header.stamp.sec) &&
32  (lhs.header.stamp.nsec == rhs.header.stamp.nsec) &&
33  (lhs.header.frame_id == rhs.header.frame_id);
34  if (!are_header_equals)
35  {
36  return false;
37  }
38 
39  //sensor_msg
40  bool are_parameters_equals = (lhs.height == rhs.height) &&
41  (lhs.width == rhs.width) &&
42  (lhs.is_bigendian == rhs.is_bigendian) &&
43  (lhs.point_step == rhs.point_step) &&
44  (lhs.row_step == rhs.row_step) &&
45  (lhs.is_dense == rhs.is_dense);
46  if (!are_parameters_equals)
47  {
48  return false;
49  }
50 
51  //fields
52  bool are_all_fields_equals = true;
53  if (lhs.fields.size() == rhs.fields.size()){
54  for (uint i=0; i < lhs.fields.size(); i++)
55  {
56  if (!((lhs.fields[i].name == rhs.fields[i].name) &&
57  (lhs.fields[i].offset == rhs.fields[i].offset) &&
58  (lhs.fields[i].datatype == rhs.fields[i].datatype) &&
59  (lhs.fields[i].count == rhs.fields[i].count)))
60  {
61  are_all_fields_equals = false;
62  break;
63  }
64  }
65  }
66  else
67  {
68  are_all_fields_equals = false;
69  }
70  if (!are_all_fields_equals)
71  {
72  return false;
73  }
74 
75  //binary blob
76  bool is_data_equal = (lhs.data.size() == rhs.data.size()) &&
77  (memcmp(lhs.data.data(),rhs.data.data(),lhs.data.size())) == 0;
78  if (!is_data_equal)
79  {
80  return false;
81  }
82 
83  return true;
84 }
85 
86 std::string decode_chunk(const sensor_msgs::PointCloud2& sensor_msg, uint index)
87 {
88  std::stringstream output_buffer;
89 
90  size_t point_size = sensor_msg.row_step/sensor_msg.width; // in Byte
91 
92  const uint8_t* chunk_start_ptr = reinterpret_cast<const uint8_t*>(sensor_msg.data.data()) + (index*point_size);
93 
94  output_buffer << "chunk#" << index << " : ";
95 
96  for (uint i = 0; i < sensor_msg.fields.size(); i++)
97  {
98  uint offset = sensor_msg.fields[i].offset; // In Byte from the chunk's start
99  for (uint current_count = 0; current_count < sensor_msg.fields[i].count ; current_count++)
100  {
101  uint read_size; // In byte
102  switch(sensor_msg.fields[i].datatype)
103  {
104  case sensor_msgs::PointField::INT8 :
105  read_size = 1;
106  int8_t val8;
107  memcpy(&val8, chunk_start_ptr+offset+(current_count*read_size), read_size);
108  output_buffer << sensor_msg.fields[i].name << " : " << val8 << "; ";
109  break;
110  case sensor_msgs::PointField::UINT8 :
111  read_size = 1;
112  uint8_t valu8;
113  memcpy(&valu8, chunk_start_ptr+offset+(current_count*read_size), read_size);
114  output_buffer << sensor_msg.fields[i].name << " : " << valu8 << "; ";
115  break;
116  case sensor_msgs::PointField::INT16 :
117  read_size = 2;
118  int16_t val16;
119  memcpy(&val16, chunk_start_ptr+offset+(current_count*read_size), read_size);
120  output_buffer << sensor_msg.fields[i].name << " : " << val16 << "; ";
121  break;
122  case sensor_msgs::PointField::UINT16 :
123  read_size = 2;
124  int16_t valu16;
125  memcpy(&valu16, chunk_start_ptr+offset+(current_count*read_size), read_size);
126  output_buffer << sensor_msg.fields[i].name << " : " << valu16 << "; ";
127  break;
128  case sensor_msgs::PointField::INT32 :
129  read_size = 4;
130  int32_t val32;
131  memcpy(&val32, chunk_start_ptr+offset+(current_count*read_size), read_size);
132  output_buffer << sensor_msg.fields[i].name << " : " << val32 << "; ";
133  break;
134  case sensor_msgs::PointField::UINT32 :
135  read_size = 4;
136  uint32_t valu32;
137  memcpy(&valu32, chunk_start_ptr+offset+(current_count*read_size), read_size);
138  output_buffer << sensor_msg.fields[i].name << " : " << valu32 << "; ";
139  break;
140  case sensor_msgs::PointField::FLOAT32 :
141  read_size = 4;
142  float valf32;
143  memcpy(&valf32, chunk_start_ptr+offset+(current_count*read_size), read_size);
144  output_buffer << sensor_msg.fields[i].name << " : " << valf32 << "; ";
145  break;
146  case sensor_msgs::PointField::FLOAT64 :
147  read_size = 8;
148  double valf64;
149  memcpy(&valf64, chunk_start_ptr+offset+(current_count*read_size), read_size);
150  output_buffer << sensor_msg.fields[i].name << " : " << valf64 << "; ";
151  break;
152  }
153  }
154  }
155  return output_buffer.str();
156 }
157 
158 // Get a hex string of the first mismatch between two uint8_t vectors. They are considered containing data aligned on chunk_size
159 std::string get_first_mismatch(const std::vector<uint8_t>& expected,
160  const std::vector<uint8_t>& tested,
161  uint chunk_size, uint* out_index = nullptr)
162 {
163  uint min_size = std::min(expected.size(),tested.size());
164  for (uint i = 0; i < min_size; i++)
165  {
166  if (expected[i]!=tested[i])
167  {
168  // We have a mismatch. We make the string for up to chunk_size bytes
169  uint chunk_id = i / chunk_size; // Use the rounding down
170  uint chunk_start_index = chunk_id * chunk_size;
171  std::stringstream output_buffer;
172  output_buffer << "first mismatch at index " << i << " (chunk #" << chunk_id << ") : ";
173  for (uint j = chunk_start_index; j < std::min(chunk_start_index+chunk_size, min_size); j++)
174  {
175  output_buffer << std::hex << static_cast<int>(expected[j]);
176  }
177  output_buffer << " | ";
178  for (uint j = chunk_start_index; j < std::min(chunk_start_index+chunk_size, min_size); j++)
179  {
180  output_buffer << std::hex << static_cast<int>(tested[j]);
181  }
182  if (out_index != nullptr)
183  {
184  *out_index = chunk_id;
185  }
186  return output_buffer.str();
187  }
188  }
189  if (out_index != nullptr)
190  {
191  *out_index = ~0;
192  }
193  return "";
194 }
195 
196 std::string find_mismatches(const sensor_msgs::PointCloud2& expected,
197  const sensor_msgs::PointCloud2& tested,
198  bool locate_first_data_disparity = false)
199 {
200  std::stringstream output_buffer;
201 
202  //header
203  if (expected.header.seq != tested.header.seq)
204  {
205  output_buffer << "header.seq are differents : expected : " << expected.header.seq
206  << "; got : " << tested.header.seq << std::endl;
207  }
208  if (expected.header.stamp.sec != tested.header.stamp.sec)
209  {
210  output_buffer << "header.stamp.sec are differents : expected : " << expected.header.stamp.sec
211  << "; got : " << tested.header.stamp.sec << std::endl;
212  }
213  if (expected.header.stamp.nsec != tested.header.stamp.nsec)
214  {
215  output_buffer << "header.stamp.nsec are differents : expected : " << expected.header.stamp.nsec
216  << "; got : " << tested.header.stamp.nsec << std::endl;
217  }
218  if (expected.header.frame_id != tested.header.frame_id)
219  {
220  output_buffer << "header.frame_id are differents : expected : " << expected.header.frame_id
221  << "; got : " << tested.header.frame_id << std::endl;
222  }
223 
224  //sensor_msg
225  if (expected.height != tested.height)
226  {
227  output_buffer << "height are differents : expected : " << expected.height
228  << "; got : " << tested.height << std::endl;
229  }
230  if (expected.width != tested.width)
231  {
232  output_buffer << "width are differents : expected : " << expected.width <<
233  "; got : " << tested.width << std::endl;
234  }
235  if (expected.is_bigendian != tested.is_bigendian)
236  {
237  output_buffer << "is_bigendian are differents : expected : " << expected.is_bigendian
238  << "; got : " << tested.is_bigendian << std::endl;
239  }
240  if (expected.point_step != tested.point_step)
241  {
242  output_buffer << "point_step are differents : expected : " << expected.point_step
243  << "; got : " << tested.point_step << std::endl;
244  }
245  if (expected.row_step != tested.row_step)
246  {
247  output_buffer << "row_step are differents : expected : " << expected.row_step
248  << "; got : " << tested.row_step << std::endl;
249  }
250  if (expected.is_dense != tested.is_dense)
251  {
252  output_buffer << "is_dense are differents : expected : " << expected.is_dense
253  << "; got : " << tested.is_dense << std::endl;
254  }
255 
256  //fields
257  if (expected.fields.size() != tested.fields.size())
258  {
259  output_buffer << "field sizes are differents : expected : " << expected.fields.size()
260  << "; got : " << tested.fields.size() << std::endl;
261  }
262 
263  uint min_field_size = std::min(expected.fields.size(),tested.fields.size());
264  for (uint i=0; i < min_field_size; i++)
265  {
266  if (expected.fields[i].name != tested.fields[i].name)
267  {
268  output_buffer << "fields[" << i << "].name are differents : expected : "
269  << expected.fields[i].name << "; got : " << tested.fields[i].name << std::endl;
270  }
271  if (expected.fields[i].offset != tested.fields[i].offset)
272  {
273  output_buffer << "fields[" << i << "].offset are differents : expected : "
274  << expected.fields[i].offset << "; got : " << tested.fields[i].offset << std::endl;
275  }
276  if (expected.fields[i].datatype != tested.fields[i].datatype)
277  {
278  output_buffer << "fields[" << i << "].datatype are differents : expected : "
279  << expected.fields[i].datatype << "; got : " << tested.fields[i].datatype << std::endl;
280  }
281  if (expected.fields[i].count != tested.fields[i].count)
282  {
283  output_buffer << "fields[" << i << "].count are differents : expected : "
284  << expected.fields[i].count << "; got : " << tested.fields[i].count << std::endl;
285  }
286  }
287 
288  //binary blob
289  if(expected.data.size() != tested.data.size())
290  {
291  output_buffer << "data sizes are differents : expected : " << expected.data.size()
292  << "; got : " << tested.data.size() << std::endl;
293  }
294  if (memcmp(expected.data.data(),tested.data.data(),expected.data.size()) != 0)
295  {
296  output_buffer << "data are differents";
297  if (locate_first_data_disparity)
298  {
299  uint point_size = expected.row_step/expected.width; // In Byte
300  uint mismatch_index = 0;
301  output_buffer << " : " << get_first_mismatch(expected.data, tested.data, point_size, &mismatch_index);
302  output_buffer << std::endl << "expected : " << decode_chunk(expected, mismatch_index);
303  output_buffer << std::endl << " got : " << decode_chunk(tested, mismatch_index);
304  }
305  output_buffer << std::endl;
306  }
307 
308  return output_buffer.str();
309 }
310 
311 
312 
313 bool count_data_mismatch(const sensor_msgs::PointCloud2& expected_ground,
314  const sensor_msgs::PointCloud2& expected_no_ground,
315  const sensor_msgs::PointCloud2& tested_ground,
316  const sensor_msgs::PointCloud2& tested_no_ground,
317  uint* out_false_ground_count,
318  uint* out_false_no_ground_count)
319 {
320  uint point_size = expected_no_ground.row_step/expected_no_ground.width; // In Byte
321  if (((expected_ground.data.size()%point_size) != 0) ||
322  ((expected_no_ground.data.size()%point_size) != 0) ||
323  ((tested_ground.data.size()%point_size) != 0) ||
324  ((tested_no_ground.data.size()%point_size) != 0))
325  {
326  return 0;
327  }
328 
329  uint expected_ground_point_count = expected_ground.data.size()/point_size;
330  uint expected_no_ground_point_count = expected_no_ground.data.size()/point_size;
331  uint tested_ground_point_count = tested_ground.data.size()/point_size;
332  uint tested_no_ground_point_count = tested_no_ground.data.size()/point_size;
333 
334  uint tested_no_ground_index = 0;
335  uint expected_no_ground_index = 0;
336 
337  *out_false_ground_count = 0;
338  *out_false_no_ground_count = 0;
339 
340  bool all_mismatches_are_legit = true;
341 
342  while ((tested_no_ground_index < tested_no_ground_point_count) &&
343  (expected_no_ground_index < expected_no_ground_point_count))
344  {
345  // Progress on the no_ground point until we hit a mismatch
346  bool is_equal;
347  do{
348  is_equal = memcmp(tested_no_ground.data.data() + tested_no_ground_index*point_size,
349  expected_no_ground.data.data() + (expected_no_ground_index)*point_size,
350  point_size) == 0;
351  tested_no_ground_index++;
352  expected_no_ground_index++;
353  }while (is_equal && (tested_no_ground_index < tested_no_ground_point_count) &&
354  (expected_no_ground_index < expected_no_ground_point_count));
355 
356  if (!is_equal)
357  {
358  // ---- Test for a false no_ground (a true ground point in the no_ground vector) ----
359  bool is_false_no_ground = false;
360  for (uint i=0; i<expected_ground_point_count; i++)
361  {
362  if (0 == memcmp(tested_no_ground.data.data() + (tested_no_ground_index-1)*point_size,
363  expected_ground.data.data() + i*point_size,point_size))
364  {
365  is_false_no_ground = true;
366  break;
367  }
368  }
369  // ---- Test for a false ground (a true no_ground point in the ground vector) ----
370  bool is_false_ground = false;
371  for (uint i=0; i<tested_ground_point_count; i++)
372  {
373  if (0 == memcmp(expected_no_ground.data.data() + (expected_no_ground_index-1)*point_size,
374  tested_ground.data.data() + i*point_size,point_size))
375  {
376  is_false_ground = true;
377  break;
378  }
379  }
380 
381  // ---- Now we update the two indexes ----
382  if (is_false_no_ground)
383  {
384  if (is_false_ground)
385  {
386  //Two values are swapped. We don't change any offset.
387  *out_false_ground_count += 1;
388  *out_false_no_ground_count += 1;
389  }
390  else
391  {
392  //revert the expected index but keep the tested index the same
393  expected_no_ground_index--;
394  *out_false_no_ground_count += 1;
395  }
396  }
397  else
398  {
399  if (is_false_ground)
400  {
401  //revert the tested index but keep the expected index the same
402  tested_no_ground_index--;
403  *out_false_ground_count += 1;
404  }
405  else
406  {
407  //Nothing ? We have a problem... It means some point is either lost or a new one created
408  all_mismatches_are_legit = false;
409  }
410  }
411  }
412  }
413  return all_mismatches_are_legit;
414 }
count_data_mismatch
bool count_data_mismatch(const sensor_msgs::PointCloud2 &expected_ground, const sensor_msgs::PointCloud2 &expected_no_ground, const sensor_msgs::PointCloud2 &tested_ground, const sensor_msgs::PointCloud2 &tested_no_ground, uint *out_false_ground_count, uint *out_false_no_ground_count)
Definition: sensor_msg_comparison.h:313
equals
bool equals(const sensor_msgs::PointCloud2 &lhs, const sensor_msgs::PointCloud2 &rhs)
Definition: sensor_msg_comparison.h:27
find_mismatches
std::string find_mismatches(const sensor_msgs::PointCloud2 &expected, const sensor_msgs::PointCloud2 &tested, bool locate_first_data_disparity=false)
Definition: sensor_msg_comparison.h:196
decode_chunk
std::string decode_chunk(const sensor_msgs::PointCloud2 &sensor_msg, uint index)
Definition: sensor_msg_comparison.h:86
get_first_mismatch
std::string get_first_mismatch(const std::vector< uint8_t > &expected, const std::vector< uint8_t > &tested, uint chunk_size, uint *out_index=nullptr)
Definition: sensor_msg_comparison.h:159


points_preprocessor
Author(s): n-patiphon , aohsato
autogenerated on Wed Mar 2 2022 00:12:07