novatel_gps_nodelet.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2017, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL SOUTHWEST RESEARCH INSTITUTE BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //
00028 // *****************************************************************************
00029 
00120 #include <exception>
00121 #include <string>
00122 
00123 #include <boost/accumulators/accumulators.hpp>
00124 #include <boost/accumulators/statistics/max.hpp>
00125 #include <boost/accumulators/statistics/mean.hpp>
00126 #include <boost/accumulators/statistics/min.hpp>
00127 #include <boost/accumulators/statistics/rolling_mean.hpp>
00128 #include <boost/accumulators/statistics/stats.hpp>
00129 #include <boost/accumulators/statistics/tail.hpp>
00130 #include <boost/accumulators/statistics/variance.hpp>
00131 #include <boost/circular_buffer.hpp>
00132 
00133 #include <diagnostic_msgs/DiagnosticStatus.h>
00134 #include <diagnostic_updater/diagnostic_updater.h>
00135 #include <diagnostic_updater/publisher.h>
00136 #include <gps_common/GPSFix.h>
00137 #include <nodelet/nodelet.h>
00138 #include <novatel_gps_msgs/NovatelCorrectedImuData.h>
00139 #include <novatel_gps_msgs/NovatelFRESET.h>
00140 #include <novatel_gps_msgs/NovatelMessageHeader.h>
00141 #include <novatel_gps_msgs/NovatelPosition.h>
00142 #include <novatel_gps_msgs/NovatelVelocity.h>
00143 #include <novatel_gps_msgs/Gpgga.h>
00144 #include <novatel_gps_msgs/Gprmc.h>
00145 #include <novatel_gps_msgs/Range.h>
00146 #include <novatel_gps_msgs/Time.h>
00147 #include <novatel_gps_driver/novatel_gps.h>
00148 #include <ros/ros.h>
00149 #include <sensor_msgs/Imu.h>
00150 #include <sensor_msgs/NavSatFix.h>
00151 #include <std_msgs/Time.h>
00152 #include <swri_math_util/math_util.h>
00153 #include <swri_roscpp/parameters.h>
00154 #include <swri_roscpp/publisher.h>
00155 #include <swri_roscpp/subscriber.h>
00156 
00157 namespace stats = boost::accumulators;
00158 
00159 namespace novatel_gps_driver
00160 {
00161   class NovatelGpsNodelet : public nodelet::Nodelet
00162   {
00163   public:
00164     NovatelGpsNodelet() :
00165       device_(""),
00166       connection_type_("serial"),
00167       polling_period_(0.05),
00168       publish_gpgsa_(false),
00169       publish_gpgsv_(false),
00170       imu_rate_(100.0),
00171       imu_sample_rate_(200.0),
00172       publish_imu_messages_(false),
00173       publish_novatel_positions_(false),
00174       publish_novatel_velocity_(false),
00175       publish_nmea_messages_(false),
00176       publish_range_messages_(false),
00177       publish_time_messages_(false),
00178       publish_trackstat_(false),
00179       publish_diagnostics_(true),
00180       publish_sync_diagnostic_(true),
00181       reconnect_delay_s_(0.5),
00182       use_binary_messages_(false),
00183       connection_(NovatelGps::SERIAL),
00184       last_sync_(ros::TIME_MIN),
00185       rolling_offset_(stats::tag::rolling_window::window_size = 10),
00186       expected_rate_(20),
00187       device_timeouts_(0),
00188       device_interrupts_(0),
00189       device_errors_(0),
00190       gps_parse_failures_(0),
00191       gps_insufficient_data_warnings_(0),
00192       publish_rate_warnings_(0),
00193       measurement_count_(0),
00194       last_published_(ros::TIME_MIN),
00195       imu_frame_id_(""),
00196       frame_id_("")
00197     {
00198     }
00199 
00200     ~NovatelGpsNodelet()
00201     {
00202       gps_.Disconnect();
00203     }
00204 
00209     void onInit()
00210     {
00211       ros::NodeHandle &node = getNodeHandle();
00212       ros::NodeHandle &priv = getPrivateNodeHandle();
00213 
00214       swri::param(priv, "device", device_, device_);
00215       swri::param(priv, "imu_rate", imu_rate_, imu_rate_);
00216       swri::param(priv, "imu_sample_rate", imu_sample_rate_, imu_sample_rate_);
00217       swri::param(priv, "publish_gpgsa", publish_gpgsa_, publish_gpgsa_);
00218       swri::param(priv, "publish_gpgsv", publish_gpgsv_, publish_gpgsv_);
00219       swri::param(priv, "publish_imu_messages", publish_imu_messages_, publish_imu_messages_);
00220       swri::param(priv, "publish_novatel_positions", publish_novatel_positions_, publish_novatel_positions_);
00221       swri::param(priv, "publish_novatel_velocity", publish_novatel_velocity_, publish_novatel_velocity_);
00222       swri::param(priv, "publish_nmea_messages", publish_nmea_messages_, publish_nmea_messages_);
00223       swri::param(priv, "publish_range_messages", publish_range_messages_, publish_range_messages_);
00224       swri::param(priv, "publish_time_messages", publish_time_messages_, publish_time_messages_);
00225       swri::param(priv, "publish_trackstat", publish_trackstat_, publish_trackstat_);
00226       swri::param(priv, "publish_diagnostics", publish_diagnostics_, publish_diagnostics_);
00227       swri::param(priv, "publish_sync_diagnostic", publish_sync_diagnostic_, publish_sync_diagnostic_);
00228       swri::param(priv, "polling_period", polling_period_, polling_period_);
00229       swri::param(priv, "reconnect_delay_s", reconnect_delay_s_, reconnect_delay_s_);
00230       swri::param(priv, "use_binary_messages", use_binary_messages_, use_binary_messages_);
00231 
00232       swri::param(priv, "connection_type", connection_type_, connection_type_);
00233       connection_ = NovatelGps::ParseConnection(connection_type_);
00234 
00235       swri::param(priv, "imu_frame_id", imu_frame_id_, std::string(""));
00236       swri::param(priv, "frame_id", frame_id_, std::string(""));
00237       
00238       //set NovatelGps parameters
00239       swri::param(priv, "gpgga_gprmc_sync_tol", gps_.gpgga_gprmc_sync_tol_, 0.01);
00240       swri::param(priv, "gpgga_position_sync_tol", gps_.gpgga_position_sync_tol_, 0.01);
00241       swri::param(priv, "wait_for_position", gps_.wait_for_position_, false);
00242 
00243       // Reset Service
00244       reset_service_ = priv.advertiseService("freset", &NovatelGpsNodelet::resetService, this);
00245 
00246       sync_sub_ = swri::Subscriber(node, "gps_sync", 100, &NovatelGpsNodelet::SyncCallback, this);
00247 
00248       std::string gps_topic = node.resolveName("gps");
00249       gps_pub_ = swri::advertise<gps_common::GPSFix>(node, gps_topic, 100);
00250       fix_pub_ = swri::advertise<sensor_msgs::NavSatFix>(node, "fix", 100);
00251 
00252       if (publish_nmea_messages_)
00253       {
00254         gpgga_pub_ = swri::advertise<novatel_gps_msgs::Gpgga>(node,"gpgga", 100);
00255         gprmc_pub_ = swri::advertise<novatel_gps_msgs::Gprmc>(node,"gprmc", 100);
00256       }
00257 
00258       if (publish_gpgsa_)
00259       {
00260         gpgsa_pub_ = swri::advertise<novatel_gps_msgs::Gpgsa>(node, "gpgsa", 100);
00261       }
00262 
00263       if (publish_imu_messages_)
00264       {
00265         imu_pub_ = swri::advertise<sensor_msgs::Imu>(node, "imu", 100);
00266         novatel_imu_pub_= swri::advertise<novatel_gps_msgs::NovatelCorrectedImuData>(node, "corrimudata", 100);
00267         insstdev_pub_ = swri::advertise<novatel_gps_msgs::Insstdev>(node, "insstdev", 100);
00268         inspva_pub_ = swri::advertise<novatel_gps_msgs::Inspva>(node, "inspva", 100);
00269         inscov_pub_ = swri::advertise<novatel_gps_msgs::Inscov>(node, "inscov", 100);
00270       }
00271 
00272       if (publish_gpgsv_)
00273       {
00274         gpgsv_pub_ = swri::advertise<novatel_gps_msgs::Gpgsv>(node, "gpgsv", 100);
00275       }
00276 
00277       if (publish_novatel_positions_)
00278       { 
00279         novatel_position_pub_ = swri::advertise<novatel_gps_msgs::NovatelPosition>(node, "bestpos", 100);
00280       }
00281 
00282       if (publish_novatel_velocity_)
00283       {
00284         novatel_velocity_pub_ = swri::advertise<novatel_gps_msgs::NovatelVelocity>(node, "bestvel", 100);
00285       }
00286 
00287       if (publish_range_messages_)
00288       {
00289         range_pub_ = swri::advertise<novatel_gps_msgs::Range>(node, "range", 100);
00290       }
00291 
00292       if (publish_time_messages_)
00293       {
00294         time_pub_ = swri::advertise<novatel_gps_msgs::Time>(node, "time", 100);
00295       }
00296 
00297       if (publish_trackstat_)
00298       {
00299         trackstat_pub_ = swri::advertise<novatel_gps_msgs::Trackstat>(node, "trackstat", 100);
00300       }
00301 
00302       hw_id_ = "Novatel GPS (" + device_ +")";
00303       if (publish_diagnostics_)
00304       {
00305         diagnostic_updater_.setHardwareID(hw_id_);
00306         diagnostic_updater_.add("Connection",
00307             this,
00308             &NovatelGpsNodelet::DeviceDiagnostic);
00309         diagnostic_updater_.add("Hardware",
00310             this,
00311             &NovatelGpsNodelet::GpsDiagnostic);
00312         diagnostic_updater_.add("Data",
00313             this,
00314             &NovatelGpsNodelet::DataDiagnostic);
00315         diagnostic_updater_.add("Rate",
00316             this,
00317             &NovatelGpsNodelet::RateDiagnostic);
00318         diagnostic_updater_.add("GPS Fix",
00319             this,
00320             &NovatelGpsNodelet::FixDiagnostic);
00321         if (publish_sync_diagnostic_)
00322         {
00323           diagnostic_updater_.add("Sync",
00324               this,
00325               &NovatelGpsNodelet::SyncDiagnostic);
00326         }
00327       }
00328       thread_ = boost::thread(&NovatelGpsNodelet::Spin, this);
00329       NODELET_INFO("%s initialized", hw_id_.c_str());
00330     }
00331 
00332     void SyncCallback(const std_msgs::TimeConstPtr& sync)
00333     {
00334       boost::unique_lock<boost::mutex> lock(mutex_);
00335       sync_times_.push_back(sync->data);
00336     }
00337 
00342     void Spin()
00343     {
00344       std::string format_suffix;
00345       if (use_binary_messages_)
00346       {
00347         // NovAtel logs in binary format always end with "b", while ones in
00348         // ASCII format always end in "a".
00349         format_suffix = "b";
00350       }
00351       else
00352       {
00353         format_suffix = "a";
00354       }
00355 
00356       NovatelMessageOpts opts;
00357       opts["gpgga"] = polling_period_;
00358       opts["gprmc"] = polling_period_;
00359       opts["bestpos" + format_suffix] = polling_period_;  // Best position
00360       opts["time" + format_suffix] = 1.0;  // Time
00361 
00362       if (publish_gpgsa_)
00363       {
00364         opts["gpgsa"] = polling_period_;
00365       }
00366       if (publish_gpgsv_)
00367       {
00368         opts["gpgsv"] = 1.0;
00369       }
00370       if (publish_imu_messages_)
00371       {
00372         double period = 1.0 / imu_rate_;
00373         opts["corrimudata" + format_suffix] = period;
00374         opts["inscov" + format_suffix] = 1.0;
00375         opts["inspva" + format_suffix] = period;
00376         opts["insstdev" + format_suffix] = 1.0;
00377         if (!use_binary_messages_)
00378         {
00379           NODELET_WARN("Using the ASCII message format with CORRIMUDATA logs is not recommended.  "
00380                        "A serial link will not be able to keep up with the data rate.");
00381         }
00382         gps_.SetImuRate(imu_sample_rate_);
00383       }
00384       if (publish_novatel_velocity_)
00385       {
00386         opts["bestvel" + format_suffix] = polling_period_;  // Best velocity
00387       }
00388       if (publish_range_messages_)
00389       {
00390         opts["range" + format_suffix] = 1.0;  // Range. 1 msg/sec is max rate
00391       }
00392       if (publish_trackstat_)
00393       {
00394         opts["trackstat" + format_suffix] = 1.0;  // Trackstat
00395       }
00396       while (ros::ok())
00397       {
00398         if (gps_.Connect(device_, connection_, opts))
00399         {
00400           // Connected to device. Begin reading/processing data
00401           NODELET_INFO("%s connected to device", hw_id_.c_str());
00402           while (gps_.IsConnected() && ros::ok())
00403           {
00404             // Read data from the device and publish any received messages
00405             CheckDeviceForData();
00406 
00407             // Poke the diagnostic updater. It will only fire diagnostics if
00408             // its internal timer (1 Hz) has elapsed. Otherwise, this is a
00409             // noop
00410             if (publish_diagnostics_)
00411             {
00412               diagnostic_updater_.update();
00413             }
00414 
00415             // Spin once to let the ROS callbacks fire
00416             ros::spinOnce();
00417             // Sleep for a microsecond to prevent CPU hogging
00418             boost::this_thread::sleep(boost::posix_time::microseconds(1));
00419           }  // While (gps_.IsConnected() && ros::ok()) (inner loop to process data from device)
00420         }
00421         else  // Could not connect to the device
00422         {
00423           NODELET_ERROR_THROTTLE(1, "Error connecting to device <%s:%s>: %s",
00424             connection_type_.c_str(),
00425             device_.c_str(),
00426             gps_.ErrorMsg().c_str());
00427           device_errors_++;
00428           error_msg_ = gps_.ErrorMsg();
00429         }
00430 
00431         if (ros::ok())
00432         {
00433           // If ROS is still OK but we got disconnected, we're going to try
00434           // to reconnect, but wait just a bit so we don't spam the device.
00435           ros::Duration(reconnect_delay_s_).sleep();
00436         }
00437 
00438         // Poke the diagnostic updater. It will only fire diagnostics if
00439         // its internal timer (1 Hz) has elapsed. Otherwise, this is a
00440         // noop
00441         if (publish_diagnostics_)
00442         {
00443           diagnostic_updater_.update();
00444         }
00445 
00446         // Spin once to let the ROS callbacks fire
00447         ros::spinOnce();
00448         // Sleep for a microsecond to prevent CPU hogging
00449         boost::this_thread::sleep(boost::posix_time::microseconds(1));
00450 
00451         if (connection_ == NovatelGps::PCAP)
00452         {
00453           // If we're playing a PCAP file, we only want to play it once.
00454           ros::shutdown();
00455         }
00456       }  // While (ros::ok) (outer loop to reconnect to device)
00457 
00458       gps_.Disconnect();
00459       NODELET_INFO("%s disconnected and shut down", hw_id_.c_str());
00460     }
00461 
00462   private:
00463     // Parameters
00465     std::string device_;
00467     std::string connection_type_;
00468     double polling_period_;
00469     bool publish_gpgsa_;
00470     bool publish_gpgsv_;
00472     double imu_rate_;
00474     double imu_sample_rate_;
00475     bool publish_imu_messages_;
00476     bool publish_novatel_positions_;
00477     bool publish_novatel_velocity_;
00478     bool publish_nmea_messages_;
00479     bool publish_range_messages_;
00480     bool publish_time_messages_;
00481     bool publish_trackstat_;
00482     bool publish_diagnostics_;
00483     bool publish_sync_diagnostic_;
00484     double reconnect_delay_s_;
00485     bool use_binary_messages_;
00486 
00487     ros::Publisher fix_pub_;
00488     ros::Publisher gps_pub_;
00489     ros::Publisher imu_pub_;
00490     ros::Publisher inscov_pub_;
00491     ros::Publisher inspva_pub_;
00492     ros::Publisher insstdev_pub_;
00493     ros::Publisher novatel_imu_pub_;
00494     ros::Publisher novatel_position_pub_;
00495     ros::Publisher novatel_velocity_pub_;
00496     ros::Publisher gpgga_pub_;
00497     ros::Publisher gpgsv_pub_;
00498     ros::Publisher gpgsa_pub_;
00499     ros::Publisher gprmc_pub_;
00500     ros::Publisher range_pub_;
00501     ros::Publisher time_pub_;
00502     ros::Publisher trackstat_pub_;
00503 
00504     ros::ServiceServer reset_service_;
00505 
00506     NovatelGps::ConnectionType connection_;
00507     NovatelGps gps_;
00508 
00509     boost::thread thread_;
00510     boost::mutex mutex_;
00511 
00513     swri::Subscriber sync_sub_;
00514     ros::Time last_sync_;
00516     boost::circular_buffer<ros::Time> sync_times_;
00518     boost::circular_buffer<ros::Time> msg_times_;
00520     stats::accumulator_set<float, stats::stats<
00521       stats::tag::max,
00522       stats::tag::min,
00523       stats::tag::mean,
00524       stats::tag::variance> > offset_stats_;
00526     stats::accumulator_set<float, stats::stats<stats::tag::rolling_mean> > rolling_offset_;
00527 
00528     // ROS diagnostics
00529     std::string error_msg_;
00530     diagnostic_updater::Updater diagnostic_updater_;
00531     std::string hw_id_;
00532     double expected_rate_;
00533     int32_t device_timeouts_;
00534     int32_t device_interrupts_;
00535     int32_t device_errors_;
00536     int32_t gps_parse_failures_;
00537     int32_t gps_insufficient_data_warnings_;
00538     int32_t publish_rate_warnings_;
00539     int32_t measurement_count_;
00540     ros::Time last_published_;
00541     novatel_gps_msgs::NovatelPositionPtr last_novatel_position_;
00542 
00543     std::string imu_frame_id_;
00544     std::string frame_id_;
00545 
00549     bool resetService(novatel_gps_msgs::NovatelFRESET::Request& req,
00550                       novatel_gps_msgs::NovatelFRESET::Response& res)
00551     {
00552       if (gps_.IsConnected() == false)
00553       {
00554         res.success = false;
00555       }
00556       
00557       // Formulate the reset command and send it to the device
00558       std::string command = "FRESET ";
00559       command += req.target.length() ? "STANDARD" : req.target;
00560       command += '\n';
00561       gps_.Write(command);
00562 
00563       if (req.target.length() == 0)
00564       {
00565         ROS_WARN("No FRESET target specified. Doing FRESET STANDARD. This may be undesired behavior.");
00566       }
00567 
00568       res.success = true;
00569       return true;
00570     }
00571 
00578     void CheckDeviceForData()
00579     {
00580       std::vector<novatel_gps_msgs::NovatelPositionPtr> position_msgs;
00581       std::vector<gps_common::GPSFixPtr> fix_msgs;
00582       std::vector<novatel_gps_msgs::GpggaPtr> gpgga_msgs;
00583       std::vector<novatel_gps_msgs::GprmcPtr> gprmc_msgs;
00584 
00585       // This call appears to block if the serial device is disconnected
00586       NovatelGps::ReadResult result = gps_.ProcessData();
00587       if (result == NovatelGps::READ_ERROR)
00588       {
00589         NODELET_ERROR_THROTTLE(1, "Error reading from device <%s:%s>: %s",
00590                                connection_type_.c_str(),
00591                                device_.c_str(),
00592                                gps_.ErrorMsg().c_str());
00593         device_errors_++;
00594       }
00595       else if (result == NovatelGps::READ_TIMEOUT)
00596       {
00597         device_timeouts_++;
00598       }
00599       else if (result == NovatelGps::READ_INTERRUPTED)
00600       {
00601         // If we are interrupted by a signal, ROS is probably
00602         // quitting, but we'll wait for ROS to tell us to quit.
00603         device_interrupts_++;
00604       }
00605       else if (result == NovatelGps::READ_PARSE_FAILED)
00606       {
00607         NODELET_ERROR("Error reading from device <%s:%s>: %s",
00608                                connection_type_.c_str(),
00609                                device_.c_str(),
00610                                gps_.ErrorMsg().c_str());
00611         gps_parse_failures_++;
00612       }
00613       else if (result == NovatelGps::READ_INSUFFICIENT_DATA)
00614       {
00615         gps_insufficient_data_warnings_++;
00616       }
00617 
00618       // Read messages from the driver into the local message lists
00619       gps_.GetGpggaMessages(gpgga_msgs);
00620       gps_.GetGprmcMessages(gprmc_msgs);
00621       gps_.GetNovatelPositions(position_msgs);
00622       gps_.GetFixMessages(fix_msgs);
00623 
00624       // Increment the measurement count by the number of messages we just
00625       // read
00626       measurement_count_ += gpgga_msgs.size();
00627 
00628       // If there are new position messages, store the most recent
00629       if (!position_msgs.empty())
00630       {
00631         last_novatel_position_ = position_msgs.back();
00632       }
00633 
00634       // Find all the gppga messages that are within 20 ms of whole
00635       // numbered UTC seconds and push their stamps onto the msg_times
00636       // buffer
00637       for (const auto& msg : gpgga_msgs)
00638       {
00639         if (msg->utc_seconds != 0)
00640         {
00641           int64_t second = static_cast<int64_t>(swri_math_util::Round(msg->utc_seconds));
00642           double difference = std::fabs(msg->utc_seconds - second);
00643 
00644           if (difference < 0.02)
00645           {
00646             msg_times_.push_back(msg->header.stamp);
00647           }
00648         }
00649       }
00650 
00651       // If timesync messages are available, CalculateTimeSync will
00652       // update a stat accumulator of the offset of the TimeSync message
00653       // stamp from the GPS message stamp
00654       CalculateTimeSync();
00655 
00656       // If TimeSync messages are available, CalculateTimeSync keeps
00657       // an acculumator of their offset, which is used to
00658       // calculate a rolling mean of the offset to apply to all messages
00659       ros::Duration sync_offset(0); // If no TimeSyncs, assume 0 offset
00660       if (last_sync_ != ros::TIME_MIN)
00661       {
00662         sync_offset = ros::Duration(stats::rolling_mean(rolling_offset_));
00663       }
00664       NODELET_DEBUG_STREAM("GPS TimeSync offset is " << sync_offset);
00665 
00666       if (publish_nmea_messages_)
00667       {
00668         for (const auto& msg : gpgga_msgs)
00669         {
00670           msg->header.stamp += sync_offset;
00671           msg->header.frame_id = frame_id_;
00672           gpgga_pub_.publish(msg);
00673         }
00674 
00675         for (const auto& msg : gprmc_msgs)
00676         {
00677           msg->header.stamp += sync_offset;
00678           msg->header.frame_id = frame_id_;
00679           gprmc_pub_.publish(msg);
00680         }
00681       }
00682 
00683       if (publish_gpgsa_)
00684       {
00685         std::vector<novatel_gps_msgs::GpgsaPtr> gpgsa_msgs;
00686         gps_.GetGpgsaMessages(gpgsa_msgs);
00687         for (const auto& msg : gpgsa_msgs)
00688         {
00689           msg->header.stamp = ros::Time::now();
00690           msg->header.frame_id = frame_id_;
00691           gpgsa_pub_.publish(msg);
00692         }
00693       }
00694 
00695       if (publish_gpgsv_)
00696       {
00697         std::vector<novatel_gps_msgs::GpgsvPtr> gpgsv_msgs;
00698         gps_.GetGpgsvMessages(gpgsv_msgs);
00699         for (const auto& msg : gpgsv_msgs)
00700         {
00701           msg->header.stamp = ros::Time::now();
00702           msg->header.frame_id = frame_id_;
00703           gpgsv_pub_.publish(msg);
00704         }
00705       }
00706 
00707       if (publish_novatel_positions_)
00708       {
00709         for (const auto& msg : position_msgs)
00710         {
00711           msg->header.stamp += sync_offset;
00712           msg->header.frame_id = frame_id_;
00713           novatel_position_pub_.publish(msg);
00714         }
00715       }
00716 
00717       if (publish_novatel_velocity_)
00718       {
00719         std::vector<novatel_gps_msgs::NovatelVelocityPtr> velocity_msgs;
00720         gps_.GetNovatelVelocities(velocity_msgs);
00721         for (const auto& msg : velocity_msgs)
00722         {
00723           msg->header.stamp += sync_offset;
00724           msg->header.frame_id = frame_id_;
00725           novatel_velocity_pub_.publish(msg);
00726         }
00727       }
00728       if (publish_time_messages_)
00729       {
00730         std::vector<novatel_gps_msgs::TimePtr> time_msgs;
00731         gps_.GetTimeMessages(time_msgs);
00732         for (const auto& msg : time_msgs)
00733         {
00734           msg->header.stamp += sync_offset;
00735           msg->header.frame_id = frame_id_;
00736           time_pub_.publish(msg);
00737         }
00738       }
00739       if (publish_range_messages_)
00740       {
00741         std::vector<novatel_gps_msgs::RangePtr> range_msgs;
00742         gps_.GetRangeMessages(range_msgs);
00743         for (const auto& msg : range_msgs)
00744         {
00745           msg->header.stamp += sync_offset;
00746           msg->header.frame_id = frame_id_;
00747           range_pub_.publish(msg);
00748         }
00749       }
00750       if (publish_trackstat_)
00751       {
00752         std::vector<novatel_gps_msgs::TrackstatPtr> trackstat_msgs;
00753         gps_.GetTrackstatMessages(trackstat_msgs);
00754         for (const auto& msg : trackstat_msgs)
00755         {
00756           msg->header.stamp += sync_offset;
00757           msg->header.frame_id = frame_id_;
00758           trackstat_pub_.publish(msg);
00759         }
00760       }
00761       if (publish_imu_messages_)
00762       {
00763         std::vector<novatel_gps_msgs::NovatelCorrectedImuDataPtr> novatel_imu_msgs;
00764         gps_.GetNovatelCorrectedImuData(novatel_imu_msgs);
00765         for (const auto& msg : novatel_imu_msgs)
00766         {
00767           msg->header.stamp += sync_offset;
00768           msg->header.frame_id = imu_frame_id_;
00769           novatel_imu_pub_.publish(msg);
00770         }
00771 
00772         std::vector<sensor_msgs::ImuPtr> imu_msgs;
00773         gps_.GetImuMessages(imu_msgs);
00774         for (const auto& msg : imu_msgs)
00775         {
00776           msg->header.stamp += sync_offset;
00777           msg->header.frame_id = imu_frame_id_;
00778           imu_pub_.publish(msg);
00779         }
00780 
00781         std::vector<novatel_gps_msgs::InscovPtr> inscov_msgs;
00782         gps_.GetInscovMessages(inscov_msgs);
00783         for (const auto& msg : inscov_msgs)
00784         {
00785           msg->header.stamp += sync_offset;
00786           msg->header.frame_id = imu_frame_id_;
00787           inscov_pub_.publish(msg);
00788         }
00789 
00790         std::vector<novatel_gps_msgs::InspvaPtr> inspva_msgs;
00791         gps_.GetInspvaMessages(inspva_msgs);
00792         for (const auto& msg : inspva_msgs)
00793         {
00794           msg->header.stamp += sync_offset;
00795           msg->header.frame_id = imu_frame_id_;
00796           inspva_pub_.publish(msg);
00797         }
00798 
00799         std::vector<novatel_gps_msgs::InsstdevPtr> insstdev_msgs;
00800         gps_.GetInsstdevMessages(insstdev_msgs);
00801         for (const auto& msg : insstdev_msgs)
00802         {
00803           msg->header.stamp += sync_offset;
00804           msg->header.frame_id = imu_frame_id_;
00805           insstdev_pub_.publish(msg);
00806         }
00807       }
00808 
00809       for (const auto& msg : fix_msgs)
00810       {
00811         msg->header.stamp += sync_offset;
00812         msg->header.frame_id = frame_id_;
00813         gps_pub_.publish(msg);
00814 
00815         if (fix_pub_.getNumSubscribers() > 0)
00816         {
00817           sensor_msgs::NavSatFixPtr fix_msg = ConvertGpsFixToNavSatFix(msg);
00818 
00819           fix_pub_.publish(fix_msg);
00820         }
00821 
00822         // If the time between GPS message stamps is greater than 1.5
00823         // times the expected publish rate, increment the
00824         // publish_rate_warnings_ counter, which is used in diagnostics
00825         if (last_published_ != ros::TIME_MIN &&
00826             (msg->header.stamp - last_published_).toSec() > 1.5 * (1.0 / expected_rate_))
00827         {
00828           publish_rate_warnings_++;
00829         }
00830 
00831         last_published_ = msg->header.stamp;
00832       }
00833     }
00834 
00835     sensor_msgs::NavSatFixPtr ConvertGpsFixToNavSatFix(const gps_common::GPSFixPtr& msg)
00836     {
00837       sensor_msgs::NavSatFixPtr fix_msg = boost::make_shared<sensor_msgs::NavSatFix>();
00838       fix_msg->header = msg->header;
00839       fix_msg->latitude = msg->latitude;
00840       fix_msg->longitude = msg->longitude;
00841       fix_msg->altitude = msg->altitude;
00842       fix_msg->position_covariance = msg->position_covariance;
00843       switch (msg->status.status)
00844       {
00845         case gps_common::GPSStatus::STATUS_NO_FIX:
00846           fix_msg->status.status = sensor_msgs::NavSatStatus::STATUS_NO_FIX;
00847           break;
00848         case gps_common::GPSStatus::STATUS_FIX:
00849           fix_msg->status.status = sensor_msgs::NavSatStatus::STATUS_FIX;
00850           break;
00851         case gps_common::GPSStatus::STATUS_SBAS_FIX:
00852           fix_msg->status.status = sensor_msgs::NavSatStatus::STATUS_SBAS_FIX;
00853           break;
00854         case gps_common::GPSStatus::STATUS_GBAS_FIX:
00855           fix_msg->status.status = sensor_msgs::NavSatStatus::STATUS_GBAS_FIX;
00856           break;
00857         case gps_common::GPSStatus::STATUS_DGPS_FIX:
00858         case gps_common::GPSStatus::STATUS_WAAS_FIX:
00859         default:
00860           ROS_WARN_ONCE("Unsupported fix status: %d", msg->status.status);
00861           fix_msg->status.status = sensor_msgs::NavSatStatus::STATUS_FIX;
00862           break;
00863       }
00864       switch (msg->position_covariance_type)
00865       {
00866         case gps_common::GPSFix::COVARIANCE_TYPE_KNOWN:
00867           fix_msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_KNOWN;
00868           break;
00869         case gps_common::GPSFix::COVARIANCE_TYPE_APPROXIMATED:
00870           fix_msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_APPROXIMATED;
00871           break;
00872         case gps_common::GPSFix::COVARIANCE_TYPE_DIAGONAL_KNOWN:
00873           fix_msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_DIAGONAL_KNOWN;
00874           break;
00875         case gps_common::GPSFix::COVARIANCE_TYPE_UNKNOWN:
00876           fix_msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_UNKNOWN;
00877           break;
00878         default:
00879           ROS_WARN_ONCE("Unsupported covariance type: %d", msg->position_covariance_type);
00880           fix_msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_UNKNOWN;
00881           break;
00882       }
00883       // TODO Figure out how to set this based on info in a GPSFix
00884       fix_msg->status.service = 0;
00885 
00886       return fix_msg;
00887     }
00888 
00893     void CalculateTimeSync()
00894     {
00895       boost::unique_lock<boost::mutex> lock(mutex_);
00896       int32_t synced_i = -1;  
00897       int32_t synced_j = -1;  
00898       // Loop over sync times buffer
00899       for (int32_t i = 0; i < sync_times_.size(); i++)
00900       {
00901         // Loop over message times buffer
00902         for (int32_t j = synced_j + 1; j < msg_times_.size(); j++)
00903         {
00904           // Offset is the difference between the sync time and message time
00905           double offset = (sync_times_[i] - msg_times_[j]).toSec();
00906           if (std::fabs(offset) < 0.49)
00907           {
00908             // If the offset is less than 0.49 sec, the messages match
00909             synced_i = static_cast<int32_t>(i);
00910             synced_j = static_cast<int32_t>(j);
00911             // Add the offset to the stats accumulators
00912             offset_stats_(offset);
00913             rolling_offset_(offset);
00914             // Update the last sync
00915             last_sync_ = sync_times_[i];
00916             // Break out of the inner loop and continue looping through sync times
00917             break;
00918           }
00919         }
00920       }
00921 
00922       // Remove all the timesync messages that have been matched from the queue
00923       for (int i = 0; i <= synced_i && !sync_times_.empty(); i++)
00924       {
00925         sync_times_.pop_front();
00926       }
00927 
00928       // Remove all the gps messages that have been matched from the queue
00929       for (int j = 0; j <= synced_j && !msg_times_.empty(); j++)
00930       {
00931         msg_times_.pop_front();
00932       }
00933     }
00934 
00935     void FixDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
00936     {
00937       status.clear();
00938       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal");
00939 
00940       if (!last_novatel_position_)
00941       {
00942         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "No Status");
00943         NODELET_WARN("No GPS status data.");
00944         return;
00945       }
00946 
00947       status.add("Solution Status", last_novatel_position_->solution_status);
00948       status.add("Position Type", last_novatel_position_->position_type);
00949       status.add("Solution Age", last_novatel_position_->solution_age);
00950       status.add("Satellites Tracked", static_cast<int>(last_novatel_position_->num_satellites_tracked));
00951       status.add("Satellites Used", static_cast<int>(last_novatel_position_->num_satellites_used_in_solution));
00952       status.add("Software Version", last_novatel_position_->novatel_msg_header.receiver_software_version);
00953 
00954       const novatel_gps_msgs::NovatelReceiverStatus& rcvr_status = last_novatel_position_->novatel_msg_header.receiver_status;
00955       status.add("Status Code", rcvr_status.original_status_code);
00956 
00957       if (last_novatel_position_->novatel_msg_header.receiver_status.original_status_code != 0)
00958       {
00959         uint8_t level = diagnostic_msgs::DiagnosticStatus::WARN;
00960         std::string msg = "Status Warning";
00961         // If the antenna is disconnected/broken, this is an error
00962         if (rcvr_status.antenna_is_open ||
00963             rcvr_status.antenna_is_shorted ||
00964             !rcvr_status.antenna_powered)
00965         {
00966           msg += " Antenna Problem";
00967           level = diagnostic_msgs::DiagnosticStatus::ERROR;
00968         }
00969         status.add("Error Flag", rcvr_status.error_flag?"true":"false");
00970         status.add("Temperature Flag", rcvr_status.temperature_flag?"true":"false");
00971         status.add("Voltage Flag", rcvr_status.voltage_supply_flag?"true":"false");
00972         status.add("Antenna Not Powered", rcvr_status.antenna_powered?"false":"true");
00973         status.add("Antenna Open", rcvr_status.antenna_is_open?"true":"false");
00974         status.add("Antenna Shorted", rcvr_status.antenna_is_shorted?"true":"false");
00975         status.add("CPU Overloaded", rcvr_status.cpu_overload_flag?"true":"false");
00976         status.add("COM1 Buffer Overrun", rcvr_status.com1_buffer_overrun?"true":"false");
00977         status.add("COM2 Buffer Overrun", rcvr_status.com2_buffer_overrun?"true":"false");
00978         status.add("COM3 Buffer Overrun", rcvr_status.com3_buffer_overrun?"true":"false");
00979         status.add("USB Buffer Overrun", rcvr_status.usb_buffer_overrun?"true":"false");
00980         status.add("RF1 AGC Flag", rcvr_status.rf1_agc_flag?"true":"false");
00981         status.add("RF2 AGC Flag", rcvr_status.rf2_agc_flag?"true":"false");
00982         status.add("Almanac Flag", rcvr_status.almanac_flag?"true":"false");
00983         status.add("Position Solution Flag", rcvr_status.position_solution_flag?"true":"false");
00984         status.add("Position Fixed Flag", rcvr_status.position_fixed_flag?"true":"false");
00985         status.add("Clock Steering Status", rcvr_status.clock_steering_status_enabled?"true":"false");
00986         status.add("Clock Model Flag", rcvr_status.clock_model_flag?"true":"false");
00987         status.add("OEMV External Oscillator Flag", rcvr_status.oemv_external_oscillator_flag?"true":"false");
00988         status.add("Software Resource Flag", rcvr_status.software_resource_flag?"true":"false");
00989         status.add("Auxiliary1 Flag", rcvr_status.aux1_status_event_flag?"true":"false");
00990         status.add("Auxiliary2 Flag", rcvr_status.aux2_status_event_flag?"true":"false");
00991         status.add("Auxiliary3 Flag", rcvr_status.aux3_status_event_flag?"true":"false");
00992         NODELET_WARN("Novatel status code: %d", rcvr_status.original_status_code);
00993         status.summary(level, msg);
00994       }
00995     }
00996 
00997     void SyncDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
00998     {
00999       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal");
01000 
01001       if (last_sync_ == ros::TIME_MIN)
01002       {
01003         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "No Sync");
01004         return;
01005       }
01006       else if (last_sync_ < ros::Time::now() - ros::Duration(10))
01007       {
01008         status.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Sync Stale");
01009         NODELET_ERROR("GPS time synchronization is stale.");
01010       }
01011 
01012       status.add("Last Sync", last_sync_);
01013       status.add("Mean Offset", stats::mean(offset_stats_));
01014       status.add("Mean Offset (rolling)", stats::rolling_mean(rolling_offset_));
01015       status.add("Offset Variance", stats::variance(offset_stats_));
01016       status.add("Min Offset", stats::min(offset_stats_));
01017       status.add("Max Offset", stats::max(offset_stats_));
01018     }
01019 
01020     void DeviceDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
01021     {
01022       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal");
01023 
01024       if (device_errors_ > 0)
01025       {
01026         status.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Device Errors");
01027       }
01028       else if (device_interrupts_ > 0)
01029       {
01030         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Device Interrupts");
01031         NODELET_WARN("device interrupts detected <%s:%s>: %d",
01032             connection_type_.c_str(), device_.c_str(), device_interrupts_);
01033       }
01034       else if (device_timeouts_)
01035       {
01036         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Device Timeouts");
01037         NODELET_WARN("device timeouts detected <%s:%s>: %d",
01038             connection_type_.c_str(), device_.c_str(), device_timeouts_);
01039       }
01040 
01041       status.add("Errors", device_errors_);
01042       status.add("Interrupts", device_interrupts_);
01043       status.add("Timeouts", device_timeouts_);
01044 
01045       device_timeouts_ = 0;
01046       device_interrupts_ = 0;
01047       device_errors_ = 0;
01048     }
01049 
01050     void GpsDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
01051     {
01052       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal");
01053 
01054       if (gps_parse_failures_ > 0)
01055       {
01056         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Parse Failures");
01057         NODELET_WARN("gps parse failures detected <%s>: %d",
01058             hw_id_.c_str(), gps_parse_failures_);
01059       }
01060 
01061       status.add("Parse Failures", gps_parse_failures_);
01062       status.add("Insufficient Data Warnings", gps_insufficient_data_warnings_);
01063 
01064       gps_parse_failures_ = 0;
01065       gps_insufficient_data_warnings_ = 0;
01066     }
01067 
01068     void DataDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
01069     {
01070       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal");
01071 
01072       double period = diagnostic_updater_.getPeriod();
01073       double measured_rate = measurement_count_ / period;
01074 
01075       if (measured_rate < 0.5 * expected_rate_)
01076       {
01077         status.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Insufficient Data Rate");
01078         NODELET_ERROR("insufficient data rate <%s>: %lf < %lf",
01079             hw_id_.c_str(), measured_rate, expected_rate_);
01080       }
01081       else if (measured_rate < 0.95 * expected_rate_)
01082       {
01083         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Insufficient Data Rate");
01084         NODELET_WARN("insufficient data rate <%s>: %lf < %lf",
01085             hw_id_.c_str(), measured_rate, expected_rate_);
01086       }
01087 
01088       status.add("Measurement Rate (Hz)", measured_rate);
01089 
01090       measurement_count_ = 0;
01091     }
01092 
01093     void RateDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &status)
01094     {
01095       status.summary(diagnostic_msgs::DiagnosticStatus::OK, "Nominal Publish Rate");
01096 
01097       double elapsed = (ros::Time::now() - last_published_).toSec();
01098       bool gap_detected = false;
01099       if (elapsed > 2.0 / expected_rate_)
01100       {
01101         publish_rate_warnings_++;
01102         gap_detected = true;
01103       }
01104 
01105       if (publish_rate_warnings_ > 1 || gap_detected)
01106       {
01107         status.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Insufficient Publish Rate");
01108         NODELET_WARN("publish rate failures detected <%s>: %d",
01109             hw_id_.c_str(), publish_rate_warnings_);
01110       }
01111 
01112       status.add("Warnings", publish_rate_warnings_);
01113 
01114       publish_rate_warnings_ = 0;
01115     }
01116   };
01117 }
01118 
01119 // Register nodelet plugin
01120 #include <swri_nodelet/class_list_macros.h>
01121 SWRI_NODELET_EXPORT_CLASS(novatel_gps_driver, NovatelGpsNodelet)


novatel_gps_driver
Author(s):
autogenerated on Sun Oct 8 2017 02:40:29