multiscan_receiver.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3 Created on Wed Oct 21 11:27:24 2020
4 
5 @author: honalma
6 
7 Version 1.1.1R
8 """
9 
10 from common import connectionHandler
11 import zlib
12 import msgpack
13 import numpy as np
14 import struct
15 from matplotlib import cm
16 from matplotlib.colors import ListedColormap, LinearSegmentedColormap
17 import matplotlib.pyplot as plt
18 from mpl_toolkits.mplot3d import Axes3D
19 
20 # Map of tokenized msgpack keys:
21 msgpack_tokenized_keys_str2int = {
22  "class" : 0x10,
23  "data" : 0x11,
24  "numOfElems" : 0x12,
25  "elemSz" : 0x13,
26  "endian" : 0x14,
27  "elemTypes" : 0x15,
28  "little" : 0x30,
29  "float32" : 0x31,
30  "ChannelTheta" : 0x50,
31  "ChannelPhi" : 0x51,
32  "DistValues" : 0x52,
33  "RssiValues" : 0x53,
34  "PropertiesValues" : 0x54,
35  "Scan" : 0x70,
36  "TimestampStart" : 0x71,
37  "TimestampStop" : 0x72,
38  "ThetaStart" : 0x73,
39  "ThetaStop" : 0x74,
40  "ScanNumber" : 0x75,
41  "ModuleId" : 0x76,
42  "BeamCount" : 0x77,
43  "EchoCount" : 0x78,
44  "ScanSegment" : 0x90,
45  "SegmentCounter" : 0x91,
46  "FrameNumber" : 0x92,
47  "Availability" : 0x93,
48  "SenderId" : 0x94,
49  "SegmentSize" : 0x95,
50  "SegmentData" : 0x96,
51  "TelegramCounter" : 0xB0,
52  "TimestampTransmit" : 0xB1
53 }
54 msgpack_tokenized_keys_int2str = {
55  0x10 : "class",
56  0x11 : "data",
57  0x12 : "numOfElems",
58  0x13 : "elemSz",
59  0x14 : "endian",
60  0x15 : "elemTypes",
61  0x30 : "little",
62  0x31 : "float32",
63  0x50 : "ChannelTheta",
64  0x51 : "ChannelPhi",
65  0x52 : "DistValues",
66  0x53 : "RssiValues",
67  0x54 : "PropertiesValues",
68  0x70 : "Scan",
69  0x71 : "TimestampStart",
70  0x72 : "TimestampStop",
71  0x73 : "ThetaStart",
72  0x74 : "ThetaStop",
73  0x75 : "ScanNumber",
74  0x76 : "ModuleId",
75  0x77 : "BeamCount",
76  0x78 : "EchoCount",
77  0x90 : "ScanSegment",
78  0x91 : "SegmentCounter",
79  0x92 : "FrameNumber",
80  0x93 : "Availability",
81  0x94 : "SenderId",
82  0x95 : "SegmentSize",
83  0x96 : "SegmentData",
84  0xB0 : "TelegramCounter",
85  0xB1 : "TimestampTransmit"
86 }
87 
88 # Old version previous to integer keys uses string keys
89 # def msgpackKeyToken(s):
90 # return s
91 
92 # Return the integer keys of a tokenized msgpack key
94  return msgpack_tokenized_keys_str2int[s]
95 
97 
98  def __init__(self, scannerIP = "192.168.0.1", hostIP = "192.168.0.102", port=2115, nbScans = 100, writeMsgpackFile = ""):
99  self.scannerIP = scannerIP
100  self.hostIP = hostIP
101  self.port = port
102  self.crownName = "MSGPACKUDPOutput"
103  self.restAPI = connectionHandler.RestAPI_Handler(self.scannerIP, self.crownName)
104  self.nbScans = nbScans
105  self.writeMsgpackFile = writeMsgpackFile
106  self.bufferSize = 100000
107  self.showCount = 0
108  self.enableRestAPI = False
110  self.enableVisualization = False
111  self.debug = False
112 
113  def setEnableRestAPI(self, value):
114  self.enableRestAPI = value
115 
116  def setEnableMagicalActivate(self, value):
117  self.enableMagicalActivate = value
118 
119  def setEnableVisualization(self, value):
120  self.enableVisualization = value
121 
122 
123  def setupRecording(self):
124  if self.enableRestAPI:
125  self.restAPI.postRequest("setPort", {'args': {'port': self.port}})
126  self.restAPI.postRequest("setIpAddress", {'args': {'ipAddress': self.hostIP}})
127  else:
128  print("REST API not active, nothing to setup")
129 
130 
131  def doRecording(self):
132 
133  UDPsocket = connectionHandler.UPD_Handler(self.scannerIP, self.port, self.bufferSize)
134 
135  if self.enableMagicalActivate:
136  UDPsocket.sendData("magicalActivate")
137 
138  if self.enableRestAPI:
139  self.restAPI.postRequest("start")
140 
141  if(self.restAPI.hasNoError() == True) or (self.enableRestAPI == False):
142  for i in range(0, self.nbScans):
143  data = UDPsocket.waitOnNewScan()
144  if(UDPsocket.hasNoError() == True):
145  print("received scan", i)
146  payload = self.verifyData(data)
147  if payload != "":
148  scanVector = self.parseData(payload)
149  pointCloud = self.convertToPointCloud(scanVector)
150  if self.enableVisualization:
151  self.showScan(pointCloud)
152  if self.writeMsgpackFile != "":
153  msgpackfile = "{}_{}".format(self.writeMsgpackFile, len(payload))
154  with open(msgpackfile + ".msgpack", "wb") as f:
155  f.write(payload)
156  payload_hex_str = ' '.join(format(b, '02X') for b in payload)
157  with open(msgpackfile + ".msgpack.hex", "w") as f:
158  f.write(payload_hex_str)
159  self.writeMsgpackFile = ""
160  else:
161  print("Error extracting payload from data")
162  else:
163  print("error receiving scan ",i)
164 
165  if self.enableRestAPI:
166  self.restAPI.postRequest("stop")
167 
168  del UDPsocket
169 
170  def verifyData(self, data):
171  msgAsBytes = data[0]
172  stxString = b'\x02\x02\x02\x02'
173  status = True
174 
175  #check stxString
176  stxReceived = msgAsBytes[0:4]
177  if stxString != stxReceived:
178  status = False
179  print("Error receiving stx")
180  return ""
181  #else:
182  # print("STX")
183 
184  # check string lengths
185  stringLength = len(msgAsBytes) - 12 # msgAsBytes := 4 byte STX + 4 byte payloadLength + payload + 4 byte CRC32
186  payloadlength = int.from_bytes(msgAsBytes[4:8], 'little')
187  if stringLength < payloadlength:
188  status = False
189  print("Error payloadlengths do not match: received payloadlength =", stringLength, "decoded payloadlength =", payloadlength)
190  return ""
191 
192  # cutout stx, string length and checksum
193  payLoad = msgAsBytes[8:8+payloadlength]
194 
195  #check crc-Checksum
196  crcReceived = int.from_bytes(msgAsBytes[8+payloadlength:8+payloadlength+4], 'little')
197  crcComputed = zlib.crc32(payLoad)
198  if crcReceived != crcComputed:
199  status = False
200  print("Error Checksum: crcReceived =", crcReceived, "crcComputed =", crcComputed)
201  return ""
202 
203  if status == False:
204  payLoad = ""
205 
206  return payLoad
207 
208  def parseData(self, payload):
209  dataDict = msgpack.unpackb(payload, strict_map_key=False)
210  # extract meta data
211  segmentData = []
212  try:
213  availability = dataDict[msgpackKeyToken('data')][msgpackKeyToken('Availability')]
214  frameNumber = dataDict[msgpackKeyToken('data')][msgpackKeyToken('FrameNumber')]
215  segmentCounter = dataDict[msgpackKeyToken('data')][msgpackKeyToken('SegmentCounter')]
216  segmentSize = dataDict[msgpackKeyToken('data')][msgpackKeyToken('SegmentSize')]
217  senderId = dataDict[msgpackKeyToken('data')][msgpackKeyToken('SenderId')]
218  telegramCounter = dataDict[msgpackKeyToken('data')][msgpackKeyToken('TelegramCounter')]
219  timeStampTransmit = dataDict[msgpackKeyToken('data')][msgpackKeyToken('TimestampTransmit')]
220  segmentData = dataDict[msgpackKeyToken('data')][msgpackKeyToken('SegmentData')]
221 
222  # just for debugging
223  # if self.debug:
224  # phi = segmentData[0][msgpackKeyToken('data')][msgpackKeyToken('ChannelPhi')][msgpackKeyToken('data')]
225  # phi0 = struct.unpack('f', phi)
226  # theta = segmentData[0][msgpackKeyToken('data')][msgpackKeyToken('ChannelTheta')][msgpackKeyToken('data')]
227  # theta0 = struct.unpack('60f', theta)
228  # scanNumber = segmentData[0][msgpackKeyToken('data')][msgpackKeyToken('ScanNumber')]
229  # print("tgmCnt = {0}, theta={1:1.2f}, phi={2:1.2f}".format(telegramCounter, np.rad2deg(theta0[0]), np.rad2deg(phi0[0])) )
230  # print("theta =", theta0)
231  # print("phi =", phi0)
232  # end debugging
233  except Exception:
234  print("multiscan_receiver: parseData failed")
235  return segmentData
236 
237  def convertToPointCloud(self, scanVector):
238  segmentXYZI = [] # 2d list with dimensions nbLayers x nbEchos
239 
240  nbLayers = len(scanVector) # equals to segmentSize
241  segmentXYZI = [None for _ in range(nbLayers)] # nbLayers None objects
242  for layerIdx in range(nbLayers):
243  nbEchoes = scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('EchoCount')]
244  segmentXYZI[layerIdx] = [None for _ in range(nbEchoes)] # nbEchoes None objects for this layer
245 
246  # Elevation and azimuth are the same for each echo.
247  # Moreover these quantities are the same for each frame, i.e. they
248  # could be precomputed for speed. For the sake of simplicity this is not done here however.
249  nbBeams = scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('BeamCount')]
250  elevationPseudoArr = struct.unpack('f', scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('ChannelPhi')][msgpackKeyToken('data')])
251  # Elevation must be negated. A positive pitch-angle yields negative z-coordinates.
252  elevation = -1 * elevationPseudoArr[0]
253  formatFloatArray = str(nbBeams ) + 'f'
254  azimuthPseudoArr = struct.unpack(formatFloatArray, scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('ChannelTheta')][msgpackKeyToken('data')])
255  azimuth = np.asarray(azimuthPseudoArr)
256 
257  # just for debugging
258  if self.debug:
259  print("phi =", elevationPseudoArr[0])
260  print("theta =", azimuth)
261  # end debugging
262 
263  for echoIdx in range(nbEchoes):
264  segmentXYZI[layerIdx][echoIdx] = np.zeros([nbBeams , 4]) # 4 due to x,y,z,i
265  destArr = segmentXYZI[layerIdx][echoIdx]
266 
267  distPseudoArr = struct.unpack(formatFloatArray, scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('DistValues')][echoIdx][msgpackKeyToken('data')])
268  dist = np.asarray(distPseudoArr) * 0.001 # conversion to m
269  rssiPseudoArr = struct.unpack(formatFloatArray, scanVector[layerIdx][msgpackKeyToken('data')][msgpackKeyToken('RssiValues')][echoIdx][msgpackKeyToken('data')])
270  rssi = np.asarray(rssiPseudoArr)
271 
272  # Convert to cartesian coordinates
273  destArr[:,0] = dist * np.cos(azimuth) * np.cos(elevation) # x
274  destArr[:,1] = dist * np.sin(azimuth) * np.cos(elevation) # y
275  destArr[:,2] = dist * np.sin(elevation) # z
276  destArr[:,3] = rssi
277 
278  # just for debugging
279  if self.debug:
280  print("dist =", distPseudoArr)
281  print("rssi =", rssiPseudoArr)
282  # end debugging
283  # self.debug = False
284 
285  return segmentXYZI
286 
287 
288 
289  def showScan(self, pointCloud):
290  if (self.showCount == 0):
291  self.fig = plt.figure()
292  self.ax = self.fig.add_subplot(111, projection='3d')
293 
294 
295  else:
296  self.ax.clear()
297 
298  self.ax.set_title('multiScan Emulation Segment {}'.format(self.showCount))
299  self.ax.set_xlim(-1, 1)
300  self.ax.set_ylim(-1, 1)
301  self.ax.set_zlim(-1, 1)
302  self.ax.set_xlabel('X Label')
303  self.ax.set_ylabel('Y Label')
304  self.ax.set_zlabel('Z Label')
305 
306  for layerIdx in range(len(pointCloud)):
307  for echoIdx in range(len(pointCloud[layerIdx])):
308  curCloud = pointCloud[layerIdx][echoIdx]
309  self.ax.scatter(curCloud[:, 0], curCloud[:, 1], curCloud[:, 2], s=1)
310 
311 
312  plt.show(block=False)
313  plt.draw()
314 
315 
316  plt.pause(0.001)
317  self.showCount += 1
318 
319 
320 
321  def readFromMSGPACKFile(self, fileName):
322  with open(fileName, "rb") as f:
323  byte_data = f.read()
324  msgpack_dict = msgpack.unpackb(byte_data)
325 
326  for d in msgpack_dict:
327  if 'BeginMagic' in d:
328  dataDict = d['Tracks'][0]['Data'][0] # the is a ScanSegment now
329  # print some meta data
330  frameNumber = dataDict['data']['FrameNumber']
331  segmentCounter = dataDict['data']['SegmentCounter']
332  telegramCounter = dataDict['data']['TelegramCounter']
333  timeStampTransmit = dataDict['data']['TimestampTransmit']
334 
335  print("tgmCounter = {}, timeStampTransmit = {}, frameNumber = {}, segmentCounter = {}"
336  .format(telegramCounter, timeStampTransmit, frameNumber, segmentCounter))
337 
338  # convert to point cloud and plot
339  segmentData = dataDict['data']['SegmentData']
340  pointCloud = self.convertToPointCloud(segmentData)
341  if self.enableVisualization:
342  self.showScan(pointCloud)
343 
344 
345 if __name__ == "__main__":
346  receiver = MultiScanReceiver(scannerIP = "127.0.0.1", hostIP = "127.0.0.1", port = 2115, nbScans = 1000, writeMsgpackFile = "./multiscan_dump")
347 
348  mode = 'live'
349 
350  if mode == 'live':
351  receiver.setEnableVisualization(False)
352  receiver.setEnableMagicalActivate(False)
353  receiver.setEnableRestAPI(False)
354  receiver.setupRecording()
355  receiver.doRecording()
356  elif mode == 'file':
357  receiver.readFromMSGPACKFile("data\\recordFile_16.msgpack")
358  else:
359  print("Error: Unkown mode")
360 
361 
multiscan_receiver.MultiScanReceiver.setEnableMagicalActivate
def setEnableMagicalActivate(self, value)
Definition: multiscan_receiver.py:116
multiscan_receiver.MultiScanReceiver
Definition: multiscan_receiver.py:96
multiscan_receiver.MultiScanReceiver.restAPI
restAPI
Definition: multiscan_receiver.py:103
multiscan_receiver.MultiScanReceiver.scannerIP
scannerIP
Definition: multiscan_receiver.py:99
multiscan_receiver.MultiScanReceiver.crownName
crownName
Definition: multiscan_receiver.py:102
multiscan_receiver.MultiScanReceiver.enableVisualization
enableVisualization
Definition: multiscan_receiver.py:110
multiscan_receiver.MultiScanReceiver.port
port
Definition: multiscan_receiver.py:101
multiscan_receiver.msgpackKeyToken
def msgpackKeyToken(s)
Definition: multiscan_receiver.py:93
roswrap::console::print
ROSCONSOLE_DECL void print(FilterBase *filter, void *logger, Level level, const char *file, int line, const char *function, const char *fmt,...) ROSCONSOLE_PRINTF_ATTRIBUTE(7
Don't call this directly. Use the ROS_LOG() macro instead.
multiscan_receiver.MultiScanReceiver.ax
ax
Definition: multiscan_receiver.py:292
multiscan_receiver.MultiScanReceiver.nbScans
nbScans
Definition: multiscan_receiver.py:104
multiscan_receiver.MultiScanReceiver.hostIP
hostIP
Definition: multiscan_receiver.py:100
multiscan_receiver.MultiScanReceiver.showCount
showCount
Definition: multiscan_receiver.py:107
multiscan_receiver.MultiScanReceiver.fig
fig
Definition: multiscan_receiver.py:291
multiscan_receiver.MultiScanReceiver.verifyData
def verifyData(self, data)
Definition: multiscan_receiver.py:170
multiscan_receiver.MultiScanReceiver.bufferSize
bufferSize
Definition: multiscan_receiver.py:106
multiscan_receiver.MultiScanReceiver.readFromMSGPACKFile
def readFromMSGPACKFile(self, fileName)
Definition: multiscan_receiver.py:321
multiscan_receiver.MultiScanReceiver.enableMagicalActivate
enableMagicalActivate
Definition: multiscan_receiver.py:109
multiscan_receiver.MultiScanReceiver.debug
debug
Definition: multiscan_receiver.py:111
multiscan_receiver.MultiScanReceiver.showScan
def showScan(self, pointCloud)
Definition: multiscan_receiver.py:289
multiscan_receiver.MultiScanReceiver.setEnableVisualization
def setEnableVisualization(self, value)
Definition: multiscan_receiver.py:119
multiscan_receiver.MultiScanReceiver.__init__
def __init__(self, scannerIP="192.168.0.1", hostIP="192.168.0.102", port=2115, nbScans=100, writeMsgpackFile="")
Definition: multiscan_receiver.py:98
multiscan_receiver.MultiScanReceiver.parseData
def parseData(self, payload)
Definition: multiscan_receiver.py:208
multiscan_receiver.MultiScanReceiver.enableRestAPI
enableRestAPI
Definition: multiscan_receiver.py:108
multiscan_receiver.MultiScanReceiver.doRecording
def doRecording(self)
Definition: multiscan_receiver.py:131
multiscan_receiver.MultiScanReceiver.writeMsgpackFile
writeMsgpackFile
Definition: multiscan_receiver.py:105
multiscan_receiver.MultiScanReceiver.setEnableRestAPI
def setEnableRestAPI(self, value)
Definition: multiscan_receiver.py:113
multiscan_receiver.MultiScanReceiver.convertToPointCloud
def convertToPointCloud(self, scanVector)
Definition: multiscan_receiver.py:237
multiscan_receiver.MultiScanReceiver.setupRecording
def setupRecording(self)
Definition: multiscan_receiver.py:123


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