ntrip_ros_base.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import os
4 import json
5 import datetime
6 import importlib
7 
8 import rospy
9 from std_msgs.msg import Header
10 from nmea_msgs.msg import Sentence
11 from sensor_msgs.msg import NavSatFix
12 from sensor_msgs.msg import NavSatStatus
13 
14 from ntrip_client.ntrip_base import NTRIPBase
15 from ntrip_client.nmea_parser import NMEAParser, NMEA_DEFAULT_MAX_LENGTH, NMEA_DEFAULT_MIN_LENGTH
16 
17 # Try to import a couple different types of RTCM messages
18 _MAVROS_MSGS_NAME = "mavros_msgs"
19 _RTCM_MSGS_NAME = "rtcm_msgs"
20 have_mavros_msgs = False
21 have_rtcm_msgs = False
22 if importlib.util.find_spec(_MAVROS_MSGS_NAME) is not None:
23  have_mavros_msgs = True
24  from mavros_msgs.msg import RTCM as mavros_msgs_RTCM
25 if importlib.util.find_spec(_RTCM_MSGS_NAME) is not None:
26  have_rtcm_msgs = True
27  from rtcm_msgs.msg import Message as rtcm_msgs_RTCM
28 
30  def __init__(self, name):
31  # Read a debug flag from the environment that should have been set by the launch file
32  try:
33  self._debug = json.loads(os.environ["NTRIP_CLIENT_DEBUG"].lower())
34  except:
35  self._debug = False
36 
37  # Init the node and read some mandatory config
38  if self._debug:
39  rospy.init_node(name, anonymous=True, log_level=rospy.DEBUG)
40  else:
41  rospy.init_node(name, anonymous=True)
42 
43  # Read an optional Frame ID from the config
44  self._rtcm_frame_id = rospy.get_param('~rtcm_frame_id', 'odom')
45 
46  # Determine the type of RTCM message that will be published
47  rtcm_message_package = rospy.get_param('~rtcm_message_package', _MAVROS_MSGS_NAME)
48  if rtcm_message_package == _MAVROS_MSGS_NAME:
49  if have_mavros_msgs:
50  self._rtcm_message_type = mavros_msgs_RTCM
52  else:
53  rospy.logfatal('The requested RTCM package {} is a valid option, but we were unable to import it. Please make sure you have it installed'.format(rtcm_message_package))
54  elif rtcm_message_package == _RTCM_MSGS_NAME:
55  if have_rtcm_msgs:
56  self._rtcm_message_type = rtcm_msgs_RTCM
58  else:
59  rospy.logfatal('The requested RTCM package {} is a valid option, but we were unable to import it. Please make sure you have it installed'.format(rtcm_message_package))
60  else:
61  rospy.logfatal('The RTCM package {} is not a valid option. Please choose between the following packages {}'.format(rtcm_message_package, str.join([_MAVROS_MSGS_NAME, _RTCM_MSGS_NAME])))
62 
63  # Setup the RTCM publisher
64  self._rtcm_timer = None
65  self._rtcm_pub = rospy.Publisher('rtcm', self._rtcm_message_type, queue_size=10)
66 
67  # Initialize the client
69  logerr=rospy.logerr,
70  logwarn=rospy.logwarn,
71  loginfo=rospy.loginfo,
72  logdebug=rospy.logdebug
73  )
74 
75  # Set parameters on the client
76  self._nmea_max_length = rospy.get_param('~nmea_max_length', NMEA_DEFAULT_MAX_LENGTH)
77  self._nmea_min_length = rospy.get_param('~nmea_min_length', NMEA_DEFAULT_MIN_LENGTH)
78  self._reconnect_attempt_max = rospy.get_param('~reconnect_attempt_max', NTRIPBase.DEFAULT_RECONNECT_ATTEMPT_MAX)
79  self._reconnect_attempt_wait_seconds = rospy.get_param('~reconnect_attempt_wait_seconds', NTRIPBase.DEFAULT_RECONNECT_ATEMPT_WAIT_SECONDS)
80 
81  def run(self):
82  # Setup a shutdown hook
83  rospy.on_shutdown(self.stop)
84 
85  # Connect the client
86  if not self._client.connect():
87  rospy.logerr('Unable to connect to NTRIP server')
88  return 1
89 
90  # Setup our subscriber
91  self._nmea_sub = rospy.Subscriber('nmea', Sentence, self.subscribe_nmea, queue_size=10)
92  self._fix_sub = rospy.Subscriber('fix', NavSatFix, self.subscribe_fix, queue_size=10)
93 
94  # Start the timer that will check for RTCM data
95  self._rtcm_timer = rospy.Timer(rospy.Duration(0.1), self.publish_rtcm)
96 
97  # Spin until we are shutdown
98  rospy.spin()
99  return 0
100 
101  def stop(self):
102  rospy.loginfo('Stopping RTCM publisher')
103  if self._rtcm_timer:
104  self._rtcm_timer.shutdown()
105  self._rtcm_timer.join()
106  rospy.loginfo('Disconnecting NTRIP client')
107  self._client.shutdown()
108 
109  def subscribe_nmea(self, nmea):
110  # Just extract the NMEA from the message, and send it right to the server
111  self._client.send_nmea(nmea.sentence)
112 
113  def subscribe_fix(self, fix: NavSatFix):
114  # Calculate the timestamp of the message
115  timestamp_secs = fix.header.stamp.secs + fix.header.stamp.nsecs * 1e-9
116  timestamp = datetime.datetime.fromtimestamp(timestamp_secs)
117  time = timestamp.time()
118  hour = time.hour
119  minute = time.minute
120  second = time.second
121  millisecond = int(time.microsecond * 1e-4)
122  nmea_utc = f"{hour:02}{minute:02}{second:02}.{millisecond:02}"
123 
124  # Figure out the direction of the latitude and longitude
125  nmea_lat_direction = "N"
126  nmea_lon_direction = "E"
127  if fix.latitude < 0:
128  nmea_lat_direction = "S"
129  if fix.longitude < 0:
130  nmea_lon_direction = "W"
131 
132  # Convert the units of the latitude and longitude
133  nmea_lat = NMEAParser.lat_dd_to_dmm(fix.latitude)
134  nmea_lon = NMEAParser.lon_dd_to_dmm(fix.longitude)
135 
136  # Convert the GPS quality to the right format for the sentence
137  status = fix.status.status
138  if status == NavSatStatus.STATUS_FIX:
139  nmea_status = 1
140  elif status == NavSatStatus.STATUS_SBAS_FIX:
141  nmea_status = 2
142  elif status == NavSatStatus.STATUS_GBAS_FIX:
143  nmea_status = 5
144  else:
145  nmea_status = 0
146 
147  # Assemble the sentence
148  nmea_sentence_no_checksum = f"$GPGGA,{nmea_utc},{nmea_lat},{nmea_lat_direction},{nmea_lon},{nmea_lon_direction},{nmea_status},05,1.0,100.0,M,-32.0,M,,0000"
149  nmea_checksum = NMEAParser.checksum(nmea_sentence_no_checksum)
150  nmea_sentence = f"{nmea_sentence_no_checksum}*{nmea_checksum:x}\r\n"
151 
152  # Send the sentence to the client
153  self._client.send_nmea(nmea_sentence)
154 
155 
156  def publish_rtcm(self, event):
157  for raw_rtcm in self._client.recv_rtcm():
158  self._rtcm_pub.publish(self._create_rtcm_message(raw_rtcm))
159 
161  return mavros_msgs_RTCM(
162  header=Header(
163  stamp=rospy.Time.now(),
164  frame_id=self._rtcm_frame_id
165  ),
166  data=rtcm
167  )
168 
170  return rtcm_msgs_RTCM(
171  header=Header(
172  stamp=rospy.Time.now(),
173  frame_id=self._rtcm_frame_id
174  ),
175  message=rtcm
176  )
ntrip_client.ntrip_base.NTRIPBase
Definition: ntrip_base.py:7
ntrip_client.ntrip_ros_base.NTRIPRosBase.__init__
def __init__(self, name)
Definition: ntrip_ros_base.py:30
ntrip_client.ntrip_ros_base.NTRIPRosBase
Definition: ntrip_ros_base.py:29
ntrip_client.ntrip_ros_base.NTRIPRosBase._create_mavros_msgs_rtcm_message
def _create_mavros_msgs_rtcm_message(self, rtcm)
Definition: ntrip_ros_base.py:160
ntrip_client.ntrip_ros_base.NTRIPRosBase._nmea_min_length
_nmea_min_length
Definition: ntrip_ros_base.py:77
ntrip_client.ntrip_ros_base.NTRIPRosBase._create_rtcm_message
_create_rtcm_message
Definition: ntrip_ros_base.py:51
ntrip_client.ntrip_ros_base.NTRIPRosBase._nmea_sub
_nmea_sub
Definition: ntrip_ros_base.py:91
ntrip_client.ntrip_ros_base.NTRIPRosBase._rtcm_pub
_rtcm_pub
Definition: ntrip_ros_base.py:65
ntrip_client.ntrip_ros_base.NTRIPRosBase._debug
_debug
Definition: ntrip_ros_base.py:33
ntrip_client.ntrip_ros_base.NTRIPRosBase.subscribe_fix
def subscribe_fix(self, NavSatFix fix)
Definition: ntrip_ros_base.py:113
ntrip_client.ntrip_ros_base.NTRIPRosBase.publish_rtcm
def publish_rtcm(self, event)
Definition: ntrip_ros_base.py:156
ntrip_client.ntrip_ros_base.NTRIPRosBase._reconnect_attempt_wait_seconds
_reconnect_attempt_wait_seconds
Definition: ntrip_ros_base.py:79
ntrip_client.ntrip_ros_base.NTRIPRosBase.subscribe_nmea
def subscribe_nmea(self, nmea)
Definition: ntrip_ros_base.py:109
ntrip_client.ntrip_ros_base.NTRIPRosBase._client
_client
Definition: ntrip_ros_base.py:68
ntrip_client.ntrip_ros_base.NTRIPRosBase._rtcm_frame_id
_rtcm_frame_id
Definition: ntrip_ros_base.py:44
ntrip_client.ntrip_ros_base.NTRIPRosBase._create_rtcm_msgs_rtcm_message
def _create_rtcm_msgs_rtcm_message(self, rtcm)
Definition: ntrip_ros_base.py:169
ntrip_client.ntrip_ros_base.NTRIPRosBase._nmea_max_length
_nmea_max_length
Definition: ntrip_ros_base.py:76
ntrip_client.ntrip_ros_base.NTRIPRosBase._fix_sub
_fix_sub
Definition: ntrip_ros_base.py:92
ntrip_client.ntrip_base
Definition: ntrip_base.py:1
ntrip_client.ntrip_ros_base.NTRIPRosBase._reconnect_attempt_max
_reconnect_attempt_max
Definition: ntrip_ros_base.py:78
ntrip_client.nmea_parser
Definition: nmea_parser.py:1
ntrip_client.ntrip_ros_base.NTRIPRosBase._rtcm_message_type
_rtcm_message_type
Definition: ntrip_ros_base.py:50
ntrip_client.ntrip_ros_base.NTRIPRosBase._rtcm_timer
_rtcm_timer
Definition: ntrip_ros_base.py:64
ntrip_client.ntrip_ros_base.NTRIPRosBase.stop
def stop(self)
Definition: ntrip_ros_base.py:101
ntrip_client.ntrip_ros_base.NTRIPRosBase.run
def run(self)
Definition: ntrip_ros_base.py:81


ntrip_client
Author(s): Parker Hannifin Corp
autogenerated on Fri Apr 18 2025 02:32:24