natnet_messages.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018, Houston Mechatronics Inc., JD Yamokoski
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include "natnet_messages.h"
30 
31 #include <cstring>
32 #include <cinttypes>
33 
34 #include <ros/console.h>
35 
37 
38 namespace natnet
39 {
40 
41 namespace utilities
42 {
43  void seek(MessageBuffer::const_iterator& iter, size_t offset)
44  {
45  iter += offset;
46  }
47 
48  template <typename T>
49  void read_and_seek(MessageBuffer::const_iterator& iter, T& target)
50  {
51  // Breaking up the steps for clarity.
52  // *iter <- points to current value (const char) in message buffer
53  // &(*iter) <- return address of this const char (char const*)
54  // (T const*) pData <- casts char const* into T const*
55  // *((T const*) pData) <- dereference typed pointer and copy it into target
56  char const* pData = &(*iter);
57  target = *((T const*) pData);
58  // ROS_DEBUG("\t sizeof(%s) = %d", TypeParseTraits<T>::name, (int)sizeof(T));
59  seek(iter, sizeof(T));
60  }
61 
62  void decode_marker_id(int sourceID, int& pOutEntityID, int& pOutMemberID)
63  {
64  if (pOutEntityID)
65  pOutEntityID = sourceID >> 16;
66 
67  if (pOutMemberID)
68  pOutMemberID = sourceID & 0x0000ffff;
69  }
70 
71  // Funtion that assigns a time code values to 5 variables passed as arguments
72  // Requires an integer from the packet as the timecode and timecodeSubframe
73  void decode_timecode(unsigned int inTimecode, unsigned int inTimecodeSubframe, int& hour, int& minute, int& second, int& frame, int& subframe)
74  {
75  hour = (inTimecode>>24)&255;
76  minute = (inTimecode>>16)&255;
77  second = (inTimecode>>8)&255;
78  frame = inTimecode&255;
79  subframe = inTimecodeSubframe;
80  }
81 
82  // Takes timecode and assigns it to a string
83  void stringify_timecode(unsigned int inTimecode, unsigned int inTimecodeSubframe, char *Buffer, int BufferSize)
84  {
85  bool bValid;
86  int hour, minute, second, frame, subframe;
87  decode_timecode(inTimecode, inTimecodeSubframe, hour, minute, second, frame, subframe);
88 
89  snprintf(Buffer,BufferSize,"%2d:%2d:%2d:%2d.%d",hour, minute, second, frame, subframe);
90  for(unsigned int i=0; i < strlen(Buffer); i++)
91  {
92  if(Buffer[i]==' ')
93  {
94  Buffer[i]='0';
95  }
96  }
97  }
98 
99 } // namespace utilities
100 
101 
103  MessageBuffer& msgBuffer,
105 {
106  natnet::Packet pkt;
107  pkt.messageId = natnet::MessageType::Connect;
108  pkt.numDataBytes = 0;
109 
110  // Resize the message buffer and copy contents
111  msgBuffer.resize(4); // 2 bytes for messageId and 2 for numDataBtyes
112  char *pBuffer = &msgBuffer[0];
113  memcpy(pBuffer, &pkt, msgBuffer.size());
114 }
115 
116 
118  MessageBuffer const& msgBuffer,
119  mocap_optitrack::DataModel* dataModel)
120 {
121  char const* pBuffer = &msgBuffer[0];
122  natnet::Packet const* packet = (natnet::Packet const*)pBuffer;
123 
124  int nver[4] = {0, 0, 0, 0};
125  int sver[4] = {0, 0, 0, 0};
126  for(int i=0;i<4;++i) {
127  nver[i] = (int)(packet->data.sender.natNetVersion[i]);
128  sver[i] = (int)(packet->data.sender.version[i]);
129  }
130 
131  dataModel->setVersions(nver, sver);
132 }
133 
134 
136  MessageBuffer::const_iterator& msgBufferIter,
137  mocap_optitrack::RigidBody& rigidBody,
138  mocap_optitrack::Version const& natNetVersion)
139 {
140  // Read id, position and orientation of each rigid body
141  utilities::read_and_seek(msgBufferIter, rigidBody.bodyId);
142  utilities::read_and_seek(msgBufferIter, rigidBody.pose);
143 
144  ROS_DEBUG(" Rigid body ID: %d", rigidBody.bodyId);
145  ROS_DEBUG(" Pos: [%3.2f,%3.2f,%3.2f], Ori: [%3.2f,%3.2f,%3.2f,%3.2f]",
146  rigidBody.pose.position.x,
147  rigidBody.pose.position.y,
148  rigidBody.pose.position.z,
149  rigidBody.pose.orientation.x,
150  rigidBody.pose.orientation.y,
151  rigidBody.pose.orientation.z,
152  rigidBody.pose.orientation.w);
153 
154  // NatNet version 2.0 and later
155  if (natNetVersion >= mocap_optitrack::Version("2.0"))
156  {
157  // Mean marker error
158  utilities::read_and_seek(msgBufferIter, rigidBody.meanMarkerError);
159  ROS_DEBUG(" Mean marker error: %3.2f", rigidBody.meanMarkerError);
160  }
161 
162  // NatNet version 2.6 and later
163  if (natNetVersion >= mocap_optitrack::Version("2.6"))
164  {
165  // params
166  short params = 0;
167  utilities::read_and_seek(msgBufferIter, params);
168  rigidBody.isTrackingValid = params & 0x01; // 0x01 : rigid body was successfully tracked in this frame
169  ROS_DEBUG(" Successfully tracked in this frame: %s",
170  (rigidBody.isTrackingValid ? "YES" : "NO"));
171  }
172 }
173 
174 
176  MessageBuffer const& msgBuffer,
177  mocap_optitrack::DataModel* dataModel)
178 {
179  // Get iterator to beginning of buffer and skip the header
180  MessageBuffer::const_iterator msgBufferIter = msgBuffer.begin();
181  utilities::seek(msgBufferIter, 4); // Skip the header (4 bytes)
182 
183  // Next 4 bytes is the frame number
184  utilities::read_and_seek(msgBufferIter, dataModel->frameNumber);
185  ROS_DEBUG("=== BEGIN DATA FRAME ===");
186  ROS_DEBUG("Frame number: %d", dataModel->frameNumber);
187 
188  // Here on out its conveinent to get a pointer directly
189  // to the ModelFrame object as well as the NatNetVersion
190  mocap_optitrack::ModelFrame* dataFrame = &(dataModel->dataFrame);
191  mocap_optitrack::Version const& NatNetVersion = dataModel->getNatNetVersion();
192 
193  // Next 4 bytes is the number of data sets (markersets, rigidbodies, etc)
194  int numMarkerSets = 0;
195  utilities::read_and_seek(msgBufferIter, numMarkerSets);
196  ROS_DEBUG("*** MARKER SETS ***");
197  ROS_DEBUG("Marker set count: %d", numMarkerSets);
198  dataFrame->markerSets.resize(numMarkerSets);
199 
200  // Loop through number of marker sets and get name and data
201  // TODO: Whether this correctly parses marker sets has not been tested
202  int icnt = 0;
203  for (auto& markerSet : dataFrame->markerSets)
204  {
205  // Markerset name
206  strcpy(markerSet.name, &(*msgBufferIter));
207  utilities::seek(msgBufferIter, strlen(markerSet.name) + 1);
208  ROS_DEBUG(" Marker set %d: %s", icnt++, markerSet.name);
209 
210  // Read number of markers that belong to the model
211  int numMarkers = 0;
212  utilities::read_and_seek(msgBufferIter, numMarkers);
213  markerSet.markers.resize(numMarkers);
214  ROS_DEBUG(" Number of markers: %d", numMarkers);
215 
216  int jcnt = 0;
217  for (auto& marker : markerSet.markers)
218  {
219  // read marker positions
220  utilities::read_and_seek(msgBufferIter, marker);
221  ROS_DEBUG(" Marker %d: [x=%3.2f,y=%3.2f,z=%3.2f]",
222  jcnt++, marker.x, marker.y, marker.z);
223  }
224  }
225 
226  // Loop through unlabeled markers
227  ROS_DEBUG("*** UNLABELED MARKERS (Deprecated) ***");
228  int numUnlabeledMarkers = 0;
229  utilities::read_and_seek(msgBufferIter, numUnlabeledMarkers);
230  dataFrame->otherMarkers.resize(numUnlabeledMarkers);
231  ROS_DEBUG("Unlabled marker count: %d", numUnlabeledMarkers);
232 
233  // Loop over unlabled markers
234  icnt = 0;
235  for (auto& marker : dataFrame->otherMarkers)
236  {
237  // read positions of 'other' markers
238  utilities::read_and_seek(msgBufferIter, marker);
239  ROS_DEBUG(" Marker %d: [x=%3.2f,y=%3.2f,z=%3.2f]",
240  icnt++, marker.x, marker.y, marker.z);
241  // Deprecated
242  }
243 
244  // Loop through rigidbodies
245  ROS_DEBUG("*** RIGID BODIES ***");
246  int numRigidBodies = 0;
247  utilities::read_and_seek(msgBufferIter, numRigidBodies);
248  dataFrame->rigidBodies.resize(numRigidBodies);
249  ROS_DEBUG("Rigid count: %d", numRigidBodies);
250 
251  // Loop over rigid bodies
252  for (auto& rigidBody : dataFrame->rigidBodies)
253  {
254  DataFrameMessage::RigidBodyMessagePart rigidBodyMessagePart;
255  rigidBodyMessagePart.deserialize(msgBufferIter, rigidBody, dataModel->getNatNetVersion());
256  }
257 
258  // Skeletons (NatNet version 2.1 and later)
259  // TODO: skeletons not currently included in data model. Parsing
260  // is happening.. but need to copy into a model.
261  if (NatNetVersion >= mocap_optitrack::Version("2.1"))
262  {
263  ROS_DEBUG("*** SKELETONS ***");
264  int numSkeletons = 0;
265  utilities::read_and_seek(msgBufferIter, numSkeletons);
266  ROS_DEBUG("Skeleton count: %d", numSkeletons);
267 
268  // Loop through skeletons
269  for (int j=0; j < numSkeletons; j++)
270  {
271  // skeleton id
272  int skeletonId = 0;
273  utilities::read_and_seek(msgBufferIter, skeletonId);
274  ROS_DEBUG("Skeleton ID: %d", skeletonId);
275 
276  // Number of rigid bodies (bones) in skeleton
277  int numRigidBodies = 0;
278  utilities::read_and_seek(msgBufferIter, numRigidBodies);
279  ROS_DEBUG("Rigid body count: %d", numRigidBodies);
280 
281  // Loop through rigid bodies (bones) in skeleton
282  for (int j=0; j < numRigidBodies; j++)
283  {
284  mocap_optitrack::RigidBody rigidBody;
285  DataFrameMessage::RigidBodyMessagePart rigidBodyMessagePart;
286  rigidBodyMessagePart.deserialize(msgBufferIter, rigidBody, NatNetVersion);
287  } // next rigid body
288  } // next skeleton
289  }
290 
291  // Labeled markers (NatNet version 2.3 and later)
292  // TODO: like skeletons, labeled markers are not accounted for
293  // in the data model. They are being parsed but not recorded.
294  if (NatNetVersion >= mocap_optitrack::Version("2.3"))
295  {
296  ROS_DEBUG("*** LABELED MARKERS ***");
297  int numLabeledMarkers = 0;
298  utilities::read_and_seek(msgBufferIter, numLabeledMarkers);
299  ROS_DEBUG("Labeled marker count: %d", numLabeledMarkers);
300 
301  // Loop through labeled markers
302  for (int j=0; j < numLabeledMarkers; j++)
303  {
304  int id = 0;
305  utilities::read_and_seek(msgBufferIter, id);
306  int modelId, markerId;
307  utilities::decode_marker_id(id, modelId, markerId);
308 
310  utilities::read_and_seek(msgBufferIter, marker);
311 
312  float size;
313  utilities::read_and_seek(msgBufferIter, size);
314 
315  if (NatNetVersion >= mocap_optitrack::Version("2.6"))
316  {
317  // marker params
318  short params = 0;
319  utilities::read_and_seek(msgBufferIter, params);
320  // marker was not visible (occluded) in this frame
321  bool bOccluded = (params & 0x01) != 0;
322  // position provided by point cloud solve
323  bool bPCSolved = (params & 0x02) != 0;
324  // position provided by model solve
325  bool bModelSolved = (params & 0x04) != 0;
326  if (NatNetVersion >= mocap_optitrack::Version("3.0"))
327  {
328  // marker has an associated model
329  bool bHasModel = (params & 0x08) != 0;
330  // marker is an unlabeled marker
331  bool bUnlabeled = (params & 0x10) != 0;
332  // marker is an active marker
333  bool bActiveMarker = (params & 0x20) != 0;
334  }
335  }
336 
337  ROS_DEBUG(" MarkerID: %d, ModelID: %d", markerId, modelId);
338  ROS_DEBUG(" Pos: [%3.2f,%3.2f,%3.2f]",
339  marker.x, marker.y, marker.z);
340  ROS_DEBUG(" Size: %3.2f", size);
341 
342  // NatNet version 3.0 and later
343  if (NatNetVersion >= mocap_optitrack::Version("3.0"))
344  {
345  // Marker residual
346  float residual = 0.0f;
347  utilities::read_and_seek(msgBufferIter, residual);
348  ROS_DEBUG(" Residual: %3.2f", residual);
349  }
350  }
351  }
352 
353  // Force Plate data (NatNet version 2.9 and later)
354  // TODO: This is definitely not in the data model..
355  if (NatNetVersion >= mocap_optitrack::Version("2.9"))
356  {
357  ROS_DEBUG("*** FORCE PLATES ***");
358  int numForcePlates;
359  utilities::read_and_seek(msgBufferIter, numForcePlates);
360  ROS_DEBUG("Force plate count: %d", numForcePlates);
361  for (int iForcePlate = 0; iForcePlate < numForcePlates; iForcePlate++)
362  {
363  // ID
364  int forcePlateId = 0;
365  utilities::read_and_seek(msgBufferIter, forcePlateId);
366  ROS_DEBUG("Force plate ID: %d", forcePlateId);
367 
368  // Channel Count
369  int numChannels = 0;
370  utilities::read_and_seek(msgBufferIter, numChannels);
371  ROS_DEBUG(" Number of channels: %d", numChannels);
372 
373  // Channel Data
374  for (int i = 0; i < numChannels; i++)
375  {
376  ROS_DEBUG(" Channel %d: ", i);
377  int numFrames = 0;
378  utilities::read_and_seek(msgBufferIter, numFrames);
379  for (int j = 0; j < numFrames; j++)
380  {
381  float val = 0.0f;
382  utilities::read_and_seek(msgBufferIter, val);
383  ROS_DEBUG(" Frame %d: %3.2f", j, val);
384  }
385  }
386  }
387  }
388 
389  // Device data (NatNet version 3.0 and later)
390  // TODO: Also not in the data model..
391  if (NatNetVersion >= mocap_optitrack::Version("3.0"))
392  {
393  ROS_DEBUG("*** DEVICE DATA ***");
394  int numDevices;
395  utilities::read_and_seek(msgBufferIter, numDevices);
396  ROS_DEBUG("Device count: %d", numDevices);
397 
398  for (int iDevice = 0; iDevice < numDevices; iDevice++)
399  {
400  // ID
401  int deviceId = 0;
402  utilities::read_and_seek(msgBufferIter, deviceId);
403  ROS_DEBUG(" Device ID: %d", deviceId);
404 
405  // Channel Count
406  int numChannels = 0;
407  utilities::read_and_seek(msgBufferIter, numChannels);
408 
409  // Channel Data
410  for (int i = 0; i < numChannels; i++)
411  {
412  ROS_DEBUG(" Channel %d: ", i);
413  int nFrames = 0;
414  utilities::read_and_seek(msgBufferIter, nFrames);
415  for (int j = 0; j < nFrames; j++)
416  {
417  float val = 0.0f;
418  utilities::read_and_seek(msgBufferIter, val);
419  ROS_DEBUG(" Frame %d: %3.2f", j, val);
420  }
421  }
422  }
423  }
424 
425  // software latency (removed in version 3.0)
426  ROS_DEBUG("*** DIAGNOSTICS ***");
427  if (NatNetVersion < mocap_optitrack::Version("3.0"))
428  {
429  utilities::read_and_seek(msgBufferIter, dataFrame->latency);
430  ROS_DEBUG("Software latency : %3.3f", dataFrame->latency);
431  }
432 
433  // timecode
434  unsigned int timecode = 0;
435  utilities::read_and_seek(msgBufferIter, timecode);
436  unsigned int timecodeSub = 0;
437  utilities::read_and_seek(msgBufferIter, timecodeSub);
438  char szTimecode[128] = "";
439  utilities::stringify_timecode(timecode, timecodeSub, szTimecode, 128);
440 
441  // timestamp
442  double timestamp = 0.0f;
443 
444  // NatNet version 2.7 and later - increased from single to double precision
445  if (NatNetVersion >= mocap_optitrack::Version("2.7"))
446  {
447  utilities::read_and_seek(msgBufferIter, timestamp);
448  }
449  else
450  {
451  float fTimestamp = 0.0f;
452  utilities::read_and_seek(msgBufferIter, fTimestamp);
453  timestamp = (double)fTimestamp;
454  }
455  ROS_DEBUG("Timestamp: %3.3f", timestamp);
456 
457  // high res timestamps (version 3.0 and later)
458  if (NatNetVersion >= mocap_optitrack::Version("3.0"))
459  {
460  uint64_t cameraMidExposureTimestamp = 0;
461  utilities::read_and_seek(msgBufferIter, cameraMidExposureTimestamp);
462  ROS_DEBUG("Mid-exposure timestamp: %" PRIu64 "", cameraMidExposureTimestamp);
463 
464  uint64_t cameraDataReceivedTimestamp = 0;
465  utilities::read_and_seek(msgBufferIter, cameraDataReceivedTimestamp);
466  ROS_DEBUG("Camera data received timestamp: %" PRIu64 "", cameraDataReceivedTimestamp);
467 
468  uint64_t transmitTimestamp = 0;
469  utilities::read_and_seek(msgBufferIter, transmitTimestamp);
470  ROS_DEBUG("Transmit timestamp: %" PRIu64 "", transmitTimestamp);
471  }
472 
473  // frame params
474  short params = 0;
475  utilities::read_and_seek(msgBufferIter, params);
476  // 0x01 Motive is recording
477  bool bIsRecording = (params & 0x01) != 0;
478  // 0x02 Actively tracked model list has changed
479  bool bTrackedModelsChanged = (params & 0x02) != 0;
480 
481  // end of data tag
482  int eod = 0;
483  utilities::read_and_seek(msgBufferIter, eod);
484  ROS_DEBUG("=== END DATA FRAME ===");
485 }
486 
487 
489  MessageBuffer const& msgBuffer,
490  mocap_optitrack::DataModel* dataModel)
491 {
492  // Grab message ID by casting to a natnet packet type
493  char const* pMsgBuffer = &msgBuffer[0];
494  natnet::Packet const* packet = (natnet::Packet const*)(pMsgBuffer);
495  // ROS_DEBUG("Message ID: %d", packet->messageId);
496  // ROS_DEBUG("Byte count : %d", (int)msgBuffer.size());
497 
498  if (packet->messageId == natnet::MessageType::ModelDef ||
499  packet->messageId == natnet::MessageType::FrameOfData)
500  {
501  if (dataModel->hasServerInfo())
502  {
503  DataFrameMessage msg;
504  msg.deserialize(msgBuffer, dataModel);
505  }
506  else
507  {
508  ROS_WARN("Client has not received server info request. Parsing data message aborted.");
509  }
510  return;
511  }
512 
513  if (packet->messageId == natnet::MessageType::ServerInfo)
514  {
516  msg.deserialize(msgBuffer, dataModel);
517  ROS_INFO_ONCE("NATNet Version : %s",
518  dataModel->getNatNetVersion().getVersionString().c_str());
519  ROS_INFO_ONCE("Server Version : %s",
520  dataModel->getServerVersion().getVersionString().c_str());
521  return;
522  }
523 
524  if (packet->messageId == natnet::MessageType::UnrecognizedRequest)
525  {
526  ROS_WARN("Received unrecognized request");
527  }
528 }
529 
530 
531 } // namespace
static void dispatch(MessageBuffer const &, mocap_optitrack::DataModel *)
Version const & getNatNetVersion() const
Definition: data_model.cpp:94
void seek(MessageBuffer::const_iterator &iter, size_t offset)
void deserialize(MessageBuffer::const_iterator &, mocap_optitrack::RigidBody &, mocap_optitrack::Version const &)
#define ROS_INFO_ONCE(...)
std::string const & getVersionString() const
Definition: version.cpp:70
Data object holding information about a single rigid body within a mocap skeleton.
Definition: data_model.h:68
virtual void deserialize(MessageBuffer const &, mocap_optitrack::DataModel *)
Data object holding poses of a tracked model&#39;s components.
Definition: data_model.h:98
static const int UnrecognizedRequest
#define ROS_WARN(...)
std::vector< char > MessageBuffer
std::vector< RigidBody > rigidBodies
Definition: data_model.h:105
Version class containing the version information and helpers for comparison.
Definition: version.h:38
void read_and_seek(MessageBuffer::const_iterator &iter, T &target)
bool hasServerInfo() const
Definition: data_model.h:132
Version const & getServerVersion() const
Definition: data_model.cpp:99
void stringify_timecode(unsigned int inTimecode, unsigned int inTimecodeSubframe, char *Buffer, int BufferSize)
Data object holding the position of a single mocap marker in 3d space.
Definition: data_model.h:43
void decode_marker_id(int sourceID, int &pOutEntityID, int &pOutMemberID)
virtual void deserialize(MessageBuffer const &, mocap_optitrack::DataModel *)
The data model for this node.
Definition: data_model.h:119
void decode_timecode(unsigned int inTimecode, unsigned int inTimecodeSubframe, int &hour, int &minute, int &second, int &frame, int &subframe)
virtual void serialize(MessageBuffer &msgBuffer, mocap_optitrack::DataModel const *)
std::vector< MarkerSet > markerSets
Definition: data_model.h:103
void setVersions(int *nver, int *sver)
Definition: data_model.cpp:87
std::vector< Marker > otherMarkers
Definition: data_model.h:104
#define ROS_DEBUG(...)


mocap_optitrack
Author(s): Kathrin Gräve , Alex Bencz/ , Tony Baltovski , JD Yamokoski
autogenerated on Fri Mar 26 2021 02:05:51