input.cc
Go to the documentation of this file.
1 // Copyright (C) 2007, 2009, 2010, 2015 Austin Robot Technology, Patrick Beeson, Jack O'Quin
2 // All rights reserved.
3 //
4 // Software License Agreement (BSD License 2.0)
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 // * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following
14 // disclaimer in the documentation and/or other materials provided
15 // with the distribution.
16 // * Neither the name of {copyright_holder} nor the names of its
17 // contributors may be used to endorse or promote products derived
18 // from this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 
47 #include <arpa/inet.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <pcap.h>
51 #include <poll.h>
52 #include <string>
53 #include <sstream>
54 #include <sys/file.h>
55 #include <sys/socket.h>
56 #include <unistd.h>
57 
58 #include <velodyne_driver/input.h>
60 
61 namespace velodyne_driver
62 {
63  static const size_t packet_size =
64  sizeof(velodyne_msgs::VelodynePacket().data);
65 
67  // Input base class implementation
69 
75  Input::Input(ros::NodeHandle private_nh, uint16_t port):
76  private_nh_(private_nh),
77  port_(port)
78  {
79  private_nh.param("device_ip", devip_str_, std::string(""));
80  private_nh.param("gps_time", gps_time_, false);
81  if (!devip_str_.empty())
82  ROS_INFO_STREAM("Only accepting packets from IP address: "
83  << devip_str_);
84  }
85 
87  // InputSocket class implementation
89 
95  InputSocket::InputSocket(ros::NodeHandle private_nh, uint16_t port):
96  Input(private_nh, port)
97  {
98  sockfd_ = -1;
99 
100  if (!devip_str_.empty()) {
101  inet_aton(devip_str_.c_str(),&devip_);
102  }
103 
104  // connect to Velodyne UDP port
105  ROS_INFO_STREAM("Opening UDP socket: port " << port);
106  sockfd_ = socket(PF_INET, SOCK_DGRAM, 0);
107  if (sockfd_ == -1)
108  {
109  perror("socket"); // TODO: ROS_ERROR errno
110  return;
111  }
112 
113  sockaddr_in my_addr; // my address information
114  memset(&my_addr, 0, sizeof(my_addr)); // initialize to zeros
115  my_addr.sin_family = AF_INET; // host byte order
116  my_addr.sin_port = htons(port); // port in network byte order
117  my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill in my IP
118 
119  // compatibility with Spot Core EAP, reuse port 2368
120  int val = 1;
121  if(setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == -1) {
122  perror("socketopt");
123  return;
124  }
125 
126  if (bind(sockfd_, (sockaddr *)&my_addr, sizeof(sockaddr)) == -1)
127  {
128  perror("bind"); // TODO: ROS_ERROR errno
129  return;
130  }
131 
132  if (fcntl(sockfd_,F_SETFL, O_NONBLOCK|FASYNC) < 0)
133  {
134  perror("non-block");
135  return;
136  }
137 
138  ROS_DEBUG("Velodyne socket fd is %d\n", sockfd_);
139  }
140 
143  {
144  (void) close(sockfd_);
145  }
146 
148  int InputSocket::getPacket(velodyne_msgs::VelodynePacket *pkt, const double time_offset)
149  {
150  double time1 = ros::Time::now().toSec();
151 
152  struct pollfd fds[1];
153  fds[0].fd = sockfd_;
154  fds[0].events = POLLIN;
155  static const int POLL_TIMEOUT = 1000; // one second (in msec)
156 
157  sockaddr_in sender_address;
158  socklen_t sender_address_len = sizeof(sender_address);
159 
160  while (true)
161  {
162  // Unfortunately, the Linux kernel recvfrom() implementation
163  // uses a non-interruptible sleep() when waiting for data,
164  // which would cause this method to hang if the device is not
165  // providing data. We poll() the device first to make sure
166  // the recvfrom() will not block.
167  //
168  // Note, however, that there is a known Linux kernel bug:
169  //
170  // Under Linux, select() may report a socket file descriptor
171  // as "ready for reading", while nevertheless a subsequent
172  // read blocks. This could for example happen when data has
173  // arrived but upon examination has wrong checksum and is
174  // discarded. There may be other circumstances in which a
175  // file descriptor is spuriously reported as ready. Thus it
176  // may be safer to use O_NONBLOCK on sockets that should not
177  // block.
178 
179  // poll() until input available
180  do
181  {
182  int retval = poll(fds, 1, POLL_TIMEOUT);
183  if (retval < 0) // poll() error?
184  {
185  if (errno != EINTR)
186  ROS_ERROR("poll() error: %s", strerror(errno));
187  return -1;
188  }
189  if (retval == 0) // poll() timeout?
190  {
191  ROS_WARN("Velodyne poll() timeout");
192  return 0;
193  }
194  if ((fds[0].revents & POLLERR)
195  || (fds[0].revents & POLLHUP)
196  || (fds[0].revents & POLLNVAL)) // device error?
197  {
198  ROS_ERROR("poll() reports Velodyne error");
199  return -1;
200  }
201  } while ((fds[0].revents & POLLIN) == 0);
202 
203  // Receive packets that should now be available from the
204  // socket using a blocking read.
205  ssize_t nbytes = recvfrom(sockfd_, &pkt->data[0],
206  packet_size, 0,
207  (sockaddr*) &sender_address,
208  &sender_address_len);
209 
210  if (nbytes < 0)
211  {
212  if (errno != EWOULDBLOCK)
213  {
214  perror("recvfail");
215  ROS_INFO("recvfail");
216  return -1;
217  }
218  }
219  else if ((size_t) nbytes == packet_size)
220  {
221  // read successful,
222  // if packet is not from the lidar scanner we selected by IP,
223  // continue otherwise we are done
224  if(devip_str_ != ""
225  && sender_address.sin_addr.s_addr != devip_.s_addr)
226  continue;
227  else
228  break; //done
229  }
230 
231  ROS_DEBUG_STREAM("incomplete Velodyne packet read: "
232  << nbytes << " bytes");
233  }
234 
235  if (!gps_time_) {
236  // Average the times at which we begin and end reading. Use that to
237  // estimate when the scan occurred. Add the time offset.
238  double time2 = ros::Time::now().toSec();
239  pkt->stamp = ros::Time((time2 + time1) / 2.0 + time_offset);
240  } else {
241  // time for each packet is a 4 byte uint located starting at offset 1200 in
242  // the data packet
243  pkt->stamp = rosTimeFromGpsTimestamp(&(pkt->data[1200]));
244  }
245 
246  return 1;
247  }
248 
250  // InputPCAP class implementation
252 
260  InputPCAP::InputPCAP(ros::NodeHandle private_nh, uint16_t port,
261  double packet_rate, std::string filename,
262  bool read_once, bool read_fast, double repeat_delay):
263  Input(private_nh, port),
264  packet_rate_(packet_rate),
265  filename_(filename)
266  {
267  pcap_ = NULL;
268  empty_ = true;
269 
270  // get parameters using private node handle
271  private_nh.param("read_once", read_once_, false);
272  private_nh.param("read_fast", read_fast_, false);
273  private_nh.param("repeat_delay", repeat_delay_, 0.0);
274  private_nh.param("pcap_time", pcap_time_, false);
275 
276  if (read_once_)
277  ROS_INFO("Read input file only once.");
278  if (read_fast_)
279  ROS_INFO("Read input file as quickly as possible.");
280  if (repeat_delay_ > 0.0)
281  ROS_INFO("Delay %.3f seconds before repeating input file.",
282  repeat_delay_);
283 
284  // Open the PCAP dump file
285  ROS_INFO("Opening PCAP file \"%s\"", filename_.c_str());
286  if ((pcap_ = pcap_open_offline(filename_.c_str(), errbuf_) ) == NULL)
287  {
288  ROS_FATAL("Error opening Velodyne socket dump file.");
289  return;
290  }
291 
292  std::stringstream filter;
293  if( devip_str_ != "" ) // using specific IP?
294  {
295  filter << "src host " << devip_str_ << " && ";
296  }
297  filter << "udp dst port " << port;
298  pcap_compile(pcap_, &pcap_packet_filter_,
299  filter.str().c_str(), 1, PCAP_NETMASK_UNKNOWN);
300  }
301 
304  {
305  pcap_close(pcap_);
306  }
307 
309  int InputPCAP::getPacket(velodyne_msgs::VelodynePacket *pkt, const double time_offset)
310  {
311  struct pcap_pkthdr *header;
312  const u_char *pkt_data;
313 
314  while (true)
315  {
316  int res;
317  if ((res = pcap_next_ex(pcap_, &header, &pkt_data)) >= 0)
318  {
319  // Skip packets not for the correct port and from the
320  // selected IP address.
321  if (0 == pcap_offline_filter(&pcap_packet_filter_,
322  header, pkt_data))
323  continue;
324 
325  // Keep the reader from blowing through the file.
326  if (read_fast_ == false)
328 
329  memcpy(&pkt->data[0], pkt_data+42, packet_size);
330  if (!gps_time_) {
331  if (!pcap_time_) {
332  pkt->stamp = ros::Time::now(); // time_offset not considered here, as no synchronization required
333  } else {
334  pkt->stamp = ros::Time(header->ts.tv_sec, header->ts.tv_usec * 1000); //
335  }
336  } else {
337  // time for each packet is a 4 byte uint located starting at offset 1200 in
338  // the data packet
339  pkt->stamp = rosTimeFromGpsTimestamp(&(pkt->data[1200]), header);
340  }
341  empty_ = false;
342  return 1; // success
343  }
344 
345  if (empty_) // no data in file?
346  {
347  ROS_WARN("Error %d reading Velodyne packet: %s",
348  res, pcap_geterr(pcap_));
349  return -1;
350  }
351 
352  if (read_once_)
353  {
354  ROS_INFO("end of file reached -- done reading.");
355  return -1;
356  }
357 
358  if (repeat_delay_ > 0.0)
359  {
360  ROS_INFO("end of file reached -- delaying %.3f seconds.",
361  repeat_delay_);
362  usleep(rint(repeat_delay_ * 1000000.0));
363  }
364 
365  ROS_DEBUG("replaying Velodyne dump file");
366 
367  // I can't figure out how to rewind the file, because it
368  // starts with some kind of header. So, close the file
369  // and reopen it with pcap.
370  pcap_close(pcap_);
371  pcap_ = pcap_open_offline(filename_.c_str(), errbuf_);
372  empty_ = true; // maybe the file disappeared?
373  } // loop back and try again
374  }
375 
376 } // velodyne namespace
velodyne_driver::InputPCAP::pcap_
pcap_t * pcap_
Definition: input.h:138
velodyne_driver::InputSocket::~InputSocket
virtual ~InputSocket()
destructor
Definition: input.cc:142
velodyne_driver::packet_size
static const size_t packet_size
Definition: input.cc:63
velodyne_driver::InputSocket::sockfd_
int sockfd_
Definition: input.h:109
velodyne_driver::InputPCAP::read_fast_
bool read_fast_
Definition: input.h:144
velodyne_driver::Input::gps_time_
bool gps_time_
Definition: input.h:93
velodyne_driver::Input
Velodyne input base class.
Definition: input.h:72
velodyne_driver::InputPCAP::errbuf_
char errbuf_[PCAP_ERRBUF_SIZE]
Definition: input.h:140
TimeBase< Time, Duration >::toSec
double toSec() const
velodyne_driver::InputPCAP::pcap_packet_filter_
bpf_program pcap_packet_filter_
Definition: input.h:139
velodyne_driver::InputPCAP::read_once_
bool read_once_
Definition: input.h:143
velodyne_driver::InputSocket::getPacket
virtual int getPacket(velodyne_msgs::VelodynePacket *pkt, const double time_offset)
Get one velodyne packet.
Definition: input.cc:148
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
velodyne_driver::InputPCAP::InputPCAP
InputPCAP(ros::NodeHandle private_nh, uint16_t port=DATA_PORT_NUMBER, double packet_rate=0.0, std::string filename="", bool read_once=false, bool read_fast=false, double repeat_delay=0.0)
constructor
Definition: input.cc:260
rosTimeFromGpsTimestamp
ros::Time rosTimeFromGpsTimestamp(const uint8_t *const data, const struct pcap_pkthdr *header=NULL)
Definition: time_conversion.hpp:64
ROS_DEBUG
#define ROS_DEBUG(...)
velodyne_driver::InputPCAP::empty_
bool empty_
Definition: input.h:141
velodyne_driver::InputSocket::InputSocket
InputSocket(ros::NodeHandle private_nh, uint16_t port=DATA_PORT_NUMBER)
constructor
Definition: input.cc:95
velodyne_driver::InputPCAP::pcap_time_
bool pcap_time_
Definition: input.h:142
time_conversion.hpp
ROS_WARN
#define ROS_WARN(...)
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
velodyne_driver::InputPCAP::getPacket
virtual int getPacket(velodyne_msgs::VelodynePacket *pkt, const double time_offset)
Get one velodyne packet.
Definition: input.cc:309
ros::Rate::sleep
bool sleep()
ROS_FATAL
#define ROS_FATAL(...)
velodyne_driver::Input::devip_str_
std::string devip_str_
Definition: input.h:92
velodyne_driver::InputSocket::devip_
in_addr devip_
Definition: input.h:110
ros::Time
velodyne_driver::InputPCAP::~InputPCAP
virtual ~InputPCAP()
Definition: input.cc:303
ROS_ERROR
#define ROS_ERROR(...)
velodyne_driver::InputPCAP::packet_rate_
ros::Rate packet_rate_
Definition: input.h:136
ros::NodeHandle::param
T param(const std::string &param_name, const T &default_val) const
velodyne_driver
Definition: driver.h:45
header
const std::string header
ROS_INFO
#define ROS_INFO(...)
velodyne_driver::InputPCAP::repeat_delay_
double repeat_delay_
Definition: input.h:145
input.h
velodyne_driver::Input::Input
Input(ros::NodeHandle private_nh, uint16_t port)
constructor
Definition: input.cc:75
velodyne_driver::InputPCAP::filename_
std::string filename_
Definition: input.h:137
ros::NodeHandle
ros::Time::now
static Time now()


velodyne_driver
Author(s): Jack O'Quin, Patrick Beeson, Michael Quinlan, Yaxin Liu
autogenerated on Tue May 2 2023 02:28:00