geodetic_survey.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #
4 # Title: geodetic_survey.py
5 # Description: Mean a fixed number of GPS points to survey base station position.
6 #
7 
8 import rospy
9 import roslib.packages
10 from piksi_rtk_msgs.srv import *
11 import std_srvs.srv
12 from sensor_msgs.msg import (NavSatFix, NavSatStatus)
13 import os
14 import time
15 
16 
18  kRosPackageName = "piksi_multi_rtk"
19  kServiceTimeOutSeconds = 10.0
20  kWaitBetweenReadReqAndResSeconds = 1.0
21  kRelativeTolleranceGeodeticComparison = 1e-10
22 
23  def __init__(self):
24  rospy.init_node('geodetic_survey')
25  rospy.loginfo(rospy.get_name() + " start")
26 
30  self.number_of_fixes = 0
31  self.surveyed_position_set = False
32 
33  # Settings
34  self.number_of_desired_fixes = rospy.get_param('~number_of_desired_fixes', 5000)
35  self.navsatfix_topics_name = rospy.get_param('~navsatfix_topics_name', 'piksi_multi_base_station/navsatfix_spp')
36  self.write_settings_service_name = rospy.get_param('~write_settings_service_name', 'piksi_multi_base_station/settings_write')
37  self.save_settings_service_name = rospy.get_param('~save_settings_service_name', 'piksi_multi_base_station/settings_save')
38  self.read_req_settings_service_name = rospy.get_param('~read_req_settings_service_name',
39  'piksi_multi_base_station/settings_read_req')
40  self.read_resp_settings_service_name = rospy.get_param('~read_resp_settings_service_name',
41  'piksi_multi_base_station/settings_read_resp')
42  self.height_base_station_from_ground = rospy.get_param('~height_base_station_from_ground', 0.0)
43 
44  # Subscribe.
45  rospy.Subscriber(self.navsatfix_topics_name, NavSatFix,
46  self.navsatfix_callback)
47 
48  rospy.spin()
49 
50  def navsatfix_callback(self, msg):
51  # Sanity check: we should have either SPP or SBAS fix.
52  if msg.status.status != NavSatStatus.STATUS_FIX and msg.status.status != NavSatStatus.STATUS_SBAS_FIX:
53  rospy.logerr(
54  "[navsatfix_callback] received a navsatfix message with status '%d'." % (msg.status.status) +
55  " Accepted status are 'STATUS_FIX' (%d) or 'STATUS_SBAS_FIX' (%d)" % (NavSatStatus.STATUS_FIX, NavSatStatus.STATUS_SBAS_FIX))
56  return
57 
58  self.latitude_accumulator += msg.latitude
59  self.longitude_accumulator += msg.longitude
60  self.altitude_accumulator += msg.altitude
61  self.number_of_fixes += 1
62 
63  rospy.loginfo(
64  "Received: [%.10f, %.10f, %.1f]; temporary average: [%.10f, %.10f, %.1f]; waiting for %d samples" % (
65  msg.latitude, msg.longitude, msg.altitude, self.latitude_accumulator / self.number_of_fixes,
68 
70  lat0 = self.latitude_accumulator / self.number_of_fixes
71  lon0 = self.longitude_accumulator / self.number_of_fixes
72  alt0 = self.altitude_accumulator / self.number_of_fixes
73 
74  if self.set_base_station_position(lat0, lon0, alt0):
75  self.surveyed_position_set = True
76  rospy.loginfo("Base station position set correctly.")
77  rospy.loginfo(
78  "Creating ENU frame on surveyed position and substructing specified height of base station.")
79  self.log_enu_origin_position(lat0, lon0, alt0)
80  rospy.signal_shutdown("Base station position set correctly. Stop this node and launch base station node.")
81  else:
82  rospy.logerr("Base station position not set correctly.")
83  rospy.signal_shutdown("Base station position not set correctly.")
84 
85  def set_base_station_position(self, lat0, lon0, alt0):
86  everything_ok = []
87  # Use only 2 digits for altitude as it is more than enough.
88  alt0 = float("%.2f" % alt0)
89  rospy.loginfo("Setting Piksi Multi surveyed position to: %.10f, %.10f, %.1f\n" % (lat0, lon0, alt0))
90 
91  # Write settings.
92  write_lat0_ok = self.write_settings_to_piksi("surveyed_position", "surveyed_lat", "%.10f" % lat0)
93  write_lon0_ok = self.write_settings_to_piksi("surveyed_position", "surveyed_lon", "%.10f" % lon0)
94  write_alt0_ok = self.write_settings_to_piksi("surveyed_position", "surveyed_alt", "%.2f" % alt0)
95 
96  if write_lat0_ok and write_lon0_ok and write_alt0_ok:
97  # Save and check what was actually written to flash
98  settings_saved = self.save_settings_to_piksi()
99  if settings_saved:
100  read_lat0, read_lat0_value = self.read_settings_from_piksi("surveyed_position", "surveyed_lat")
101  read_lon0, read_lon0_value = self.read_settings_from_piksi("surveyed_position", "surveyed_lon")
102  read_alt0, read_alt0_value = self.read_settings_from_piksi("surveyed_position", "surveyed_alt")
103 
104  if read_lat0 and read_lon0 and read_alt0:
105  # Check read values == computed values
106  if self.is_close(float(read_lat0_value), lat0,
107  rel_tol=self.kRelativeTolleranceGeodeticComparison) and self.is_close(
108  float(read_lon0_value),
109  lon0, rel_tol=self.kRelativeTolleranceGeodeticComparison) and self.is_close(
110  float(read_alt0_value), alt0, rel_tol=self.kRelativeTolleranceGeodeticComparison):
111  everything_ok = True
112  self.log_surveyed_position(lat0, lon0, alt0)
113 
114  # Make sure Piksi is properly configured to act as base station.
115  read_surveyed_broadcast, read_surveyed_broadcast_value = self.read_settings_from_piksi(
116  "surveyed_position", "broadcast")
117 
118  read_surveyed_broadcast_value = True if (read_surveyed_broadcast_value == "True") else False
119  if read_surveyed_broadcast_value:
120  rospy.loginfo(
121  "Your Piksi Multi is already configured to broadcast its surveyed position and act as base station.")
122  else:
123  rospy.logwarn(
124  "Your Piksi Multi is NOT configured to broadcast its surveyed position and act as base station. "
125  "Please use piksi console (by Siwftnav) to change settings. See Wiki page where you downloaded this ROS driver.")
126  else:
127  rospy.logwarn(
128  "Read values do NOT correspond to written ones. Please use piksi console (See swiftnav support).")
129  everything_ok = False
130  else:
131  rospy.logerr("Error while saving base station position to Piksi flash.")
132  everything_ok = False
133  else:
134  rospy.logerr("Error while saving base station position to Piksi flash.")
135  everything_ok = False
136  else:
137  rospy.logerr("Error while writing base station position to Piksi.")
138  everything_ok = False
139 
140  return everything_ok
141 
142  def write_settings_to_piksi(self, section_setting, setting, value):
143  rospy.wait_for_service(self.write_settings_service_name, timeout=GeodeticSurvey.kServiceTimeOutSeconds)
144  write_settings_service = rospy.ServiceProxy(self.write_settings_service_name, SettingsWrite)
145  try:
146  rospy.loginfo("Setting %s.%s to %s" % (section_setting, setting, value))
147  write_resp = write_settings_service(section_setting=section_setting, setting=setting, value=value)
148  return write_resp.success
149  except rospy.ServiceException as exc:
150  rospy.logerr("Service did not process request: " + str(exc))
151  return False
152 
154  rospy.wait_for_service(self.save_settings_service_name, timeout=GeodeticSurvey.kServiceTimeOutSeconds)
155  save_settings_service = rospy.ServiceProxy(self.save_settings_service_name, std_srvs.srv.SetBool)
156  try:
157  rospy.loginfo("Saving settings to Piksi Multi flash.")
158  save_resp = save_settings_service(True)
159  return save_resp.success
160  except rospy.ServiceException as exc:
161  rospy.logerr("Service did not process request: " + str(exc))
162  return False
163 
164  def read_settings_from_piksi(self, section_setting, setting):
165  # Read request.
166  rospy.wait_for_service(self.read_req_settings_service_name, timeout=GeodeticSurvey.kServiceTimeOutSeconds)
167  read_req_settings_service = rospy.ServiceProxy(self.read_req_settings_service_name, SettingsReadReq)
168  try:
169  read_req_resp = read_req_settings_service(section_setting=section_setting, setting=setting)
170  except rospy.ServiceException as exc:
171  rospy.logerr("Service did not process request: " + str(exc))
172  return False, -1
173 
174  if read_req_resp.success:
175  # Read req sent, wait before we read the response.
176  time.sleep(GeodeticSurvey.kWaitBetweenReadReqAndResSeconds)
177  # Read response.
178  rospy.wait_for_service(self.read_resp_settings_service_name, timeout=GeodeticSurvey.kServiceTimeOutSeconds)
179  read_resp_settings_service = rospy.ServiceProxy(self.read_resp_settings_service_name, SettingsReadResp)
180  try:
181  read_resp_resp = read_resp_settings_service()
182  except rospy.ServiceException as exc:
183  rospy.logerr("Service did not process request: " + str(exc))
184  return False, -1
185 
186  if read_resp_resp.success:
187  rospy.loginfo("Read [%s.%s: %s] from Piksi settings." % (
188  read_resp_resp.section_setting, read_resp_resp.setting, read_resp_resp.value))
189  return True, read_resp_resp.value
190 
191  return False, -1
192 
193  def log_surveyed_position(self, lat0, lon0, alt0):
194  now = time.strftime("%Y-%m-%d-%H-%M-%S")
195  package_path = roslib.packages.get_pkg_dir(self.kRosPackageName)
196  desired_path = "%s/log_surveys/base_station_survey_%s.txt" % (package_path, now)
197  file_obj = open(desired_path, 'w')
198  file_obj.write("# File automatically generated on %s\n\n" % now)
199  file_obj.write("latitude0_deg: %.10f\n" % lat0)
200  file_obj.write("longitude0_deg: %.10f\n" % lon0)
201  file_obj.write("altitude0: %.2f\n" % alt0)
202  file_obj.close()
203  rospy.loginfo("Surveyed position saved in \'" + desired_path + "\'")
204 
205  def log_enu_origin_position(self, lat0, lon0, alt0):
206  # current path of geodetic_survey.py file
207  now = time.strftime("%Y-%m-%d-%H-%M-%S")
208  package_path = roslib.packages.get_pkg_dir(self.kRosPackageName)
209  desired_path = "%s/log_surveys/enu_origin_%s.txt" % (package_path, now)
210  file_obj = open(desired_path, 'w')
211  file_obj.write("# File automatically generated on %s\n" % now)
212  file_obj.write(
213  "# ENU altitude0 = surveyed_altitude0 - %.3f (=height_base_station_from_ground)\n\n" % self.height_base_station_from_ground)
214  file_obj.write("latitude0_deg: %.10f\n" % lat0)
215  file_obj.write("longitude0_deg: %.10f\n" % lon0)
216  file_obj.write("altitude0: %.2f\n" % (alt0 - self.height_base_station_from_ground))
217  file_obj.close()
218  rospy.loginfo("ENU origin saved in \'" + desired_path + "\'")
219 
220  # https://www.python.org/dev/peps/pep-0485/#proposed-implementation
221  def is_close(self, a, b, rel_tol=1e-09, abs_tol=0.0):
222  return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def log_surveyed_position(self, lat0, lon0, alt0)
def read_settings_from_piksi(self, section_setting, setting)
def set_base_station_position(self, lat0, lon0, alt0)
def is_close(self, a, b, rel_tol=1e-09, abs_tol=0.0)
def log_enu_origin_position(self, lat0, lon0, alt0)
def write_settings_to_piksi(self, section_setting, setting, value)


piksi_multi_rtk
Author(s):
autogenerated on Mon Jun 10 2019 13:08:19