sensor.cpp
Go to the documentation of this file.
1 
7 
8 namespace toposens_driver
9 {
14 {
15  std::string port;
16  private_nh.getParam("port", port);
17  private_nh.getParam("frame_id", _frame_id);
18 
19  // Set up serial connection to sensor
20  _serial = std::make_unique<Serial>(port);
21 
22  // Set up dynamic reconfigure to change sensor settings
23  _srv = std::make_unique<Cfg>(mutex, private_nh);
24  Cfg::CallbackType f = boost::bind(&Sensor::_reconfig, this, _1, _2);
25  _srv->setCallback(f);
26 
27  // Publishing topic for TsScans
28  _pub = nh.advertise<toposens_msgs::TsScan>(kScansTopic, kQueueSize);
29  ROS_INFO("Publishing toposens data to /%s", kScansTopic);
30 
33  //_synchronizeParameterValues();
34 }
35 
40 Sensor::Sensor(ros::NodeHandle nh, ros::NodeHandle private_nh, std::string port,
41  std::string frame_id)
42 {
43  _frame_id = frame_id;
44 
45  // Set up serial connection to sensor
46  _serial = std::make_unique<Serial>(port);
47 
48  // Set up dynamic reconfigure to change sensor settings
49  _srv = std::make_unique<Cfg>(mutex, private_nh);
50  Cfg::CallbackType f = boost::bind(&Sensor::_reconfig, this, _1, _2);
51  _srv->setCallback(f);
52 
53  // Publishing topic for TsScans
54  _pub = nh.advertise<toposens_msgs::TsScan>(kScansTopic, kQueueSize);
55  ROS_INFO("Publishing toposens data to /%s", kScansTopic);
56 
58  //_synchronizeParameterValues();
59 }
60 
62 void Sensor::setMode(ScanMode scan_mode)
63 {
64  Command cSMode(TsParam::ScanMode, scan_mode);
65  _serial->sendCmd(cSMode, _buffer);
66  if (!_evaluateAck(cSMode, _buffer.str())) ROS_WARN("Sending scan mode command failed");
67 }
68 
71 {
72  Command cFirmwareVers(TsService::FirmwareVersion);
73  _serial->sendCmd(cFirmwareVers, _buffer);
74 
75  std::string data = _buffer.str();
76  size_t frame_start = data.find('S');
77 
78  int ack = (data[frame_start + 6] - '0');
79  if (data[frame_start + 5] == '-') ack *= -1;
80 
81  // The retrieving firmware version number from acknowledgement message.
82  try
83  {
84  if (ack == 7)
85  {
86  auto i = data.begin() + 8;
87  ROS_INFO("Firmware version: %d", (int)_toNum(i));
88  }
89  else
90  throw "Invalid acknowledgement error";
91  }
92  catch (...)
93  {
94  ROS_INFO("Firmware version could not be retrieved");
95  }
96 }
97 
103 bool Sensor::poll(void)
104 {
105  _scan.header.stamp = ros::Time::now();
106  _scan.header.frame_id = _frame_id;
107  _scan.points.clear();
108 
109  _serial->getFrame(_buffer);
110  Sensor::_parse(_buffer.str());
111 
112  _pub.publish(_scan);
113 
114  _buffer.str(std::string());
115  _buffer.clear();
116 
117  if (_scan.points.empty())
118  return false;
119  else
120  return true;
121 }
122 
127 {
128  _serial.reset();
129  _srv.reset();
130 }
131 
141 void Sensor::_init(void)
142 {
143  bool success = true;
144 
145  Command cPulses(TsParam::NumberOfPulses, _cfg.num_pulses);
146  _serial->sendCmd(cPulses, _buffer);
147  if (!_evaluateAck(cPulses, _buffer.str())) success = false;
148 
149  Command cDetect(TsParam::PeakDetectionWindow, _cfg.peak_detection_window);
150  _serial->sendCmd(cDetect, _buffer);
151  if (!_evaluateAck(cDetect, _buffer.str())) success = false;
152 
153  Command cReject(TsParam::EchoRejectionThreshold, _cfg.echo_rejection_threshold);
154  _serial->sendCmd(cReject, _buffer);
155  if (!_evaluateAck(cReject, _buffer.str())) success = false;
156 
157  Command cNoise(TsParam::NoiseIndicatorThreshold, _cfg.noise_indicator_threshold);
158  _serial->sendCmd(cNoise, _buffer);
159  if (!_evaluateAck(cNoise, _buffer.str())) success = false;
160 
161  Command cTemp(TsParam::ExternalTemperature, _cfg.use_external_temperature
162  ? _cfg.external_temperature
164  _serial->sendCmd(cTemp, _buffer);
165  if (!_evaluateAck(cTemp, _buffer.str())) success = false;
166 
167  if (success)
168  ROS_INFO("Sensor settings initialized");
169  else
170  ROS_WARN("One or more settings failed to initialize");
171 }
172 
180 void Sensor::_reconfig(TsDriverConfig &cfg, uint32_t level)
181 {
182  if (level == 0b00000000) return;
183 
184  _cfg = cfg;
185  if (level == -1) return Sensor::_init();
186 
187  if ((int)level >= 0b10000000)
188  {
189  ROS_INFO("Update skipped: Parameter not recognized");
190  return;
191  }
192 
193  // Decode level Bit mask
194  if (level & 0b00000010)
195  {
196  Command cmd = Command(TsParam::EchoRejectionThreshold, _cfg.echo_rejection_threshold);
197  _serial->sendCmd(cmd, _buffer);
198 
199  if (_evaluateAck(cmd, _buffer.str()))
200  ROS_INFO("Sensor setting updated");
201  else
202  ROS_WARN("Settings update failed");
203  }
204 
205  if (level & 0b00000100)
206  {
207  Command cmd = Command(TsParam::NoiseIndicatorThreshold, _cfg.noise_indicator_threshold);
208  _serial->sendCmd(cmd, _buffer);
209 
210  if (_evaluateAck(cmd, _buffer.str()))
211  ROS_INFO("Sensor setting updated");
212  else
213  ROS_WARN("Settings update failed");
214  }
215 
216  if (level & 0b00001000)
217  {
219  _serial->sendCmd(cmd, _buffer);
220 
221  if (_evaluateAck(cmd, _buffer.str()))
222  ROS_INFO("Sensor setting updated");
223  else
224  ROS_WARN("Settings update failed");
225  }
226 
227  if (level & 0b00010000)
228  {
229  Command cmd = Command(TsParam::PeakDetectionWindow, _cfg.peak_detection_window);
230  _serial->sendCmd(cmd, _buffer);
231 
232  if (_evaluateAck(cmd, _buffer.str()))
233  ROS_INFO("Sensor setting updated");
234  else
235  ROS_WARN("Settings update failed");
236  }
237 
238  if (level & 0b00100001)
239  {
240  // Sends externally provided temperature to Ts sensor, or value indicating
241  // to read temperature from internal temperature sensor.
242  Command cmd = Command(TsParam::ExternalTemperature, _cfg.use_external_temperature
243  ? _cfg.external_temperature
245  _serial->sendCmd(cmd, _buffer);
246 
247  if (_evaluateAck(cmd, _buffer.str()))
248  ROS_INFO("Sensor setting updated");
249  else
250  ROS_WARN("Settings update failed");
251  }
252 }
253 
290 void Sensor::_parse(const std::string &frame)
291 {
292  auto i = frame.begin();
293 
294  while (*i != 'S')
295  if (++i == frame.end()) return;
296  _scan.noisy = (*(++i) == '1');
297 
298  for (i; i < frame.end(); ++i)
299  {
300  // Find next X-tag in the frame
301  while (*i != 'X')
302  if (++i == frame.end()) return;
303 
304  try
305  {
306  toposens_msgs::TsPoint pt;
307  pt.location.x = _toNum(++i) / 1000.0;
308 
309  if (*(++i) == 'Y')
310  pt.location.y = _toNum(++i) / 1000.0;
311  else
312  throw std::invalid_argument("Expected Y-tag not found");
313 
314  if (*(++i) == 'Z')
315  pt.location.z = _toNum(++i) / 1000.0;
316  else
317  throw std::invalid_argument("Expected Z-tag not found");
318 
319  if (*(++i) == 'V')
320  pt.intensity = _toNum(++i) / 100.0;
321  else
322  throw std::invalid_argument("Expected V-tag not found");
323 
324  if (pt.intensity > 0) _scan.points.push_back(pt);
325  }
326  catch (const std::exception &e)
327  {
328  ROS_INFO("Skipped invalid point in stream");
329  ROS_DEBUG("Error: %s in message %s", e.what(), frame.c_str());
330  }
331  }
332 }
333 
335 Command *Sensor::_parseAck(const std::string &data)
336 {
337  size_t frame_start = data.find('S');
338  int ack = (data[frame_start + 6] - '0');
339  if (data[frame_start + 5] == '-') ack *= -1;
340 
341  float val = std::atof(&data[frame_start + 8]);
342  if ((TsParam)(1 << ack) == TsParam::ExternalTemperature) val /= 10;
343 
344  return ack > 0 ? new Command((TsParam)(1 << ack), val) : nullptr;
345 }
346 
349 bool Sensor::_evaluateAck(Command &tx_cmd, const std::string &data)
350 {
351  Command *rx_cmd = _parseAck(data);
352  if (rx_cmd == nullptr) return false;
353 
354  // Compare parameter levels of command and acknowledgement frames.
355  if (tx_cmd.getParam() == rx_cmd->getParam())
356  {
357  // Compare 5 value bytes of command and acknowledgement frames.
358  if (strncmp(&rx_cmd->getBytes()[6], &tx_cmd.getBytes()[6], 5) == 0)
359  {
360  if (tx_cmd.getParam() != TsParam::ScanMode)
361  {
362  ROS_INFO_STREAM("TS parameter: " << tx_cmd.getParamName() << " updated to "
363  << (tx_cmd.getParam() == TsParam::ExternalTemperature
364  ? std::atof(&rx_cmd->getBytes()[6]) / 10
365  : std::atof(&rx_cmd->getBytes()[6])));
366  }
367  return true;
368  }
369  else if (strcmp(tx_cmd.getBytes(), "CsTemp-1000\r") == 0)
370  {
371  ROS_INFO_STREAM("TS parameter: " << tx_cmd.getParamName()
372  << " set to use internal temperature sensor.");
373  return true;
374  }
375  else
376  {
377  ROS_WARN_STREAM("TS parameter: " << tx_cmd.getParamName() << " clipped to "
378  << std::atof(&rx_cmd->getBytes()[6]));
379  }
380  }
381  else
382  {
383  ROS_WARN_STREAM("TS parameter: " << tx_cmd.getParamName() << " value update failed!");
384  }
385 
386  return false;
387 }
388 
390 void Sensor::_updateConfig(TsParam param, int value)
391 {
392  if (param == TsParam::NumberOfPulses)
393  _cfg.num_pulses = value;
394  else if (param == TsParam::PeakDetectionWindow)
395  _cfg.peak_detection_window = value;
396  else if (param == TsParam::EchoRejectionThreshold)
397  _cfg.echo_rejection_threshold = value;
398  else if (param == TsParam::ExternalTemperature)
399  _cfg.external_temperature = value;
400  else if (param == TsParam::NoiseIndicatorThreshold)
401  _cfg.noise_indicator_threshold = value;
402 
403  _srv->updateConfig(_cfg);
404  return;
405 }
406 
410 {
411  // Query current parameter configurations from TS sensor
413  _serial->sendCmd(cmd, _buffer);
414  std::string data;
415  std::size_t frame_start;
416  int ack = 0;
417 
418  while (true)
419  {
420  _buffer.str(std::string());
421  _buffer.clear();
422 
423  _serial->getFrame(_buffer);
424 
425  data = _buffer.str().c_str();
426 
427  if (_serial->isAcknowledgementFrame(_buffer.str()))
428  {
429  // Parse acknowledgement message
430  Command *rx_cmd = _parseAck(data);
431  _updateConfig(rx_cmd->getParam(), rx_cmd->getValue());
432  }
433  else
434  {
435  // All acknowledgement messages processed.
436  return;
437  }
438  }
439 }
440 
446 float Sensor::_toNum(auto &i)
447 {
448  // Size of X, Y, Z, V data is always 5 bytes
449  int abs = 0, factor = 1, length = 5;
450 
451  // Throw exception if first character is not a number or "-"
452  if (*i == '-')
453  factor = -1;
454  else if (*i != '0')
455  throw std::invalid_argument("Invalid value char");
456 
457  while (--length)
458  {
459  int d = *(++i) - '0';
460  if (d >= 0 && d <= 9)
461  abs = abs * 10 + d;
462  else
463  throw std::bad_cast();
464  }
465 
466  return (float)(factor * abs);
467 }
468 
469 } // namespace toposens_driver
d
toposens_msgs::TsScan _scan
Definition: sensor.h:168
void _reconfig(TsDriverConfig &cfg, uint32_t level)
Definition: sensor.cpp:180
std::string _frame_id
Definition: sensor.h:159
TsDriverConfig _cfg
Definition: sensor.h:160
f
std::string getParamName()
Definition: command.cpp:93
string cmd
void _synchronizeParameterValues()
Definition: sensor.cpp:409
std::stringstream _buffer
Definition: sensor.h:166
#define ROS_WARN(...)
Command * _parseAck(const std::string &data)
Definition: sensor.cpp:335
Sensor(ros::NodeHandle nh, ros::NodeHandle private_nh)
Definition: sensor.cpp:13
static const int kQueueSize
Definition: sensor.h:22
void _displayFirmwareVersion()
Definition: sensor.cpp:70
void publish(const boost::shared_ptr< M > &message) const
bool _evaluateAck(Command &cmd, const std::string &frame)
Definition: sensor.cpp:349
#define ROS_INFO(...)
bool getParam(const std::string &key, std::string &s) const
ros::Publisher _pub
Definition: sensor.h:164
float _toNum(auto &i)
Definition: sensor.cpp:446
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::unique_ptr< Cfg > _srv
Definition: sensor.h:161
boost::recursive_mutex mutex
Definition: sensor.h:162
#define ROS_WARN_STREAM(args)
static const char kScansTopic[]
Definition: sensor.h:20
void setMode(ScanMode scan_mode)
Definition: sensor.cpp:62
void _updateConfig(TsParam param, int value)
Definition: sensor.cpp:390
#define ROS_INFO_STREAM(args)
static Time now()
#define USE_INTERNAL_TEMPERATURE
Definition: command.h:10
Generates firmware-compatible commands for tuning performance parameters.
Definition: command.h:60
std::unique_ptr< Serial > _serial
Definition: sensor.h:165
void _parse(const std::string &frame)
Definition: sensor.cpp:290
#define ROS_DEBUG(...)


toposens_driver
Author(s): Adi Singh, Sebastian Dengler, Christopher Lang, Roua Mokchah, Nancy Seckel, Georgiana Barbut
autogenerated on Mon Feb 28 2022 23:57:40