diff --git a/ros_acomms/src/acomms_driver_node.py b/ros_acomms/src/acomms_driver_node.py index 4b1d86f64c2e54474f802926a53e85067f10925a..21b4204b33478320b306b7ab1088ef1236b4c684 100755 --- a/ros_acomms/src/acomms_driver_node.py +++ b/ros_acomms/src/acomms_driver_node.py @@ -522,8 +522,8 @@ class AcommsDriverNode(object): """ rospy.loginfo("Requesting modem send ping") self.um.send_fdp_ping(request.dest, request.rate, request.cdr, request.hexdata) - if request.reply_timeout <= 0: - request.reply_timeout = 5 + if request.reply_timeout < 1: + request.reply_timeout = 1 ping_reply, cst = self.um.wait_for_fdp_ping_reply( include_cst=True, timeout=request.reply_timeout ) diff --git a/ros_acomms/src/tdma_advanced_node.py b/ros_acomms/src/tdma_advanced_node.py index 0f82a1cddee6afc9b0d3528a5024906b67345159..74dfeba7857c76240dec83c8ccb2fb08e2934a78 100755 --- a/ros_acomms/src/tdma_advanced_node.py +++ b/ros_acomms/src/tdma_advanced_node.py @@ -48,12 +48,12 @@ class TdmaAdvancedMacNode(TdmaMacNode): """ def __init__(self, subclass=False): if not subclass: - rospy.init_node('tdma_advanced_mac') + rospy.init_node('tdma_advanced_mac') #, log_level=rospy.DEBUG) self.tdma_status_publisher = rospy.Publisher('~tdma_advanced_status' if rospy.get_param('~publish_private_status', False) else 'tdma_advanced_status', TdmaAdvancedStatus, queue_size=0) super().__init__(subclass=True) # take supers active_slots list of ints and generate comms_slot & nav_slots masks self.nav_slots = self.config_slot_mask(slots=rospy.get_param('~nav_slots', None), allow_empty=True) - self.comms_slots = self.config_slot_mask(slots=rospy.get_param('~comms_slots', None)) + self.comms_slots = self.config_slot_mask(slots=rospy.get_param('~comms_slots', None), allow_empty=True) self.cache_slot_ranges_for_status() # do advanced stuff @@ -82,6 +82,11 @@ class TdmaAdvancedMacNode(TdmaMacNode): # set up subscriber to toggle software_mute without overhead of dynamic_reconfigure rospy.Subscriber('~toggle_software_mute', Header, self.on_toggle_software_mute) + # this method is called everytime the slot changes. We reset modem ping flags here + self.sent_modem_pings = False + self.sent_transponder_ping = False + self.register_on_slot_change_callback(self.on_slot_changed) + if not subclass: # setup dynamic reconf for periodic ping params self.first_dynamic_reconf = True @@ -182,6 +187,7 @@ class TdmaAdvancedMacNode(TdmaMacNode): config['packet_length_seconds'] = self.packet_length_seconds config['miniframe_rate'] = self.miniframe_rate config['dataframe_rate'] = self.dataframe_rate + rospy.logdebug(f'DEBUG: First dynamic_reconfigure call, syncing config from init') return config @@ -307,12 +313,13 @@ class TdmaAdvancedMacNode(TdmaMacNode): return sent_modem_pings, sent_transponder_ping, update_last_tx + def on_slot_changed(self): + self.sent_modem_pings, self.sent_transponder_ping = False, False + def spin(self): - rate = rospy.Rate(5) + rate = rospy.Rate(15) last_tx_time = 0 msg = None - sent_modem_pings = False - sent_transponder_ping = False while not rospy.is_shutdown(): msg = self.get_current_slot_info(software_mute=self.software_mute) @@ -320,16 +327,17 @@ class TdmaAdvancedMacNode(TdmaMacNode): if self.can_transmit_now(msg=msg, last_tx_time=last_tx_time): # we are active and have atleast enough time to send another packet - sent_modem_pings, sent_transponder_ping, update_last_tx = self.handle_queuing_packet( + self.sent_modem_pings, self.sent_transponder_ping, update_last_tx = self.handle_queuing_packet( msg.current_slot, msg.remaining_active_seconds, - sent_modem_pings, - sent_transponder_ping + self.sent_modem_pings, + self.sent_transponder_ping ) if update_last_tx: last_tx_time = rospy.get_time() else: # nothing sent, sleep then check again + rospy.logdebug(f'DEBUG: Nothing sent during tdma... remaining_active_seconds: {msg.remaining_active_seconds}') rate.sleep() continue else: diff --git a/ros_acomms/src/tdma_node.py b/ros_acomms/src/tdma_node.py index 7ca3e8d938b6443951e278482ce64a2c8369a96a..b7c55818db88f37f170d1e5efc7b4d90ca8fb399 100755 --- a/ros_acomms/src/tdma_node.py +++ b/ros_acomms/src/tdma_node.py @@ -11,6 +11,7 @@ import datetime import dateutil.parser import json import logging +from threading import Thread from mac_utils import PSK_ROS_FDP_Rates, ManualTransmitRequestQueue # pure python class for non-ros functionality from mac_utils import validate_slot_range @@ -56,10 +57,18 @@ class TdmaMacNode(object): self.manual_transmit_queue.add_manual_transmit_topic(topic='nmea_to_modem', msg_type=String) self.allow_manual_tx_during_priority_slots = rospy.get_param('~allow_manual_tx_during_priority_slots', False) + # mechanism for registering callbacks in child classes on slot change + self.on_slot_change_callbacks = [] + self.on_slot_change_thread = Thread(target=self.on_slot_change, daemon=True) + self.on_slot_change_thread.start() + if not subclass: rospy.loginfo("~*+ tdma_mac node running.") self.spin() + def register_on_slot_change_callback(self, callback): + self.on_slot_change_callbacks.append(callback) + def wait_for_services(self, timeout=120): rospy.loginfo("INFO: tdma waiting for queue_tx_packet service") rospy.wait_for_service('queue_tx_packet', timeout=timeout) @@ -334,6 +343,27 @@ class TdmaMacNode(object): return True + def on_slot_change(self): + rate = rospy.Rate(1) + last_slot = None + + while not rospy.is_shutdown(): + rate.sleep() + + current_slot, remaining_slot_seconds = self.get_current_slot() + if last_slot is None: + last_slot = current_slot + + if last_slot != current_slot: + for callback in self.on_slot_change_callbacks: + try: + callback() + except: + rospy.logwarn(f'WARNING: Exception thrown during on_slot_change()') + + last_slot = current_slot + rospy.sleep(self.slot_duration_seconds / 2) + def spin(self): rate = rospy.Rate(5) last_tx_time = 0 diff --git a/ros_acomms_modeling/src/modem_sim_node.py b/ros_acomms_modeling/src/modem_sim_node.py index eefcbcaeceaee18abdbf41a9f1fce2ba29c2c1e7..8d24b04efef25911218cf504157bece32401b3b7 100755 --- a/ros_acomms_modeling/src/modem_sim_node.py +++ b/ros_acomms_modeling/src/modem_sim_node.py @@ -378,8 +378,8 @@ class ModemSimNode(object): # TODO should handle this error return PingModemResponse(timed_out=True) - if request.reply_timeout < 5: - request.reply_timeout = 5 + if request.reply_timeout < 1: + request.reply_timeout = 1 this_transaction, response, sim_packet_reply = None, None, None try: diff --git a/ros_acomms_tests/launch/tdma_fast_ping.launch b/ros_acomms_tests/launch/tdma_fast_ping.launch new file mode 100644 index 0000000000000000000000000000000000000000..3bd532789947fc6c523fa7288a51eefce9aad320 --- /dev/null +++ b/ros_acomms_tests/launch/tdma_fast_ping.launch @@ -0,0 +1,173 @@ +<launch> + <arg name="num_slots" default="2" doc="" /> + <arg name="slot_duration_seconds" default="6" doc="" /> + <arg name="tdma_type" default="tdma_advanced" doc="type of TDMA node to use: tdma_advanced, or tdma_scripted or tdma_slotted_aloha" /> + <arg name="test_cycling_rates" default="true" doc="" /> + <arg name="use_hw_modem" default="true" doc="" /> + + <env name="ROSCONSOLE_FORMAT" value="[${severity}] [${time:%Y-%m-%dT%H:%M:%S}] [${node}]: ${message}"/> + + <param name="use_sim_time" value="false"/> + + <group ns="modem0"> + <group if="$(arg use_hw_modem)"> + <node name="acomms_driver" pkg="ros_acomms" type="acomms_driver_node.py" respawn="true" respawn_delay="10" > + <param name="modem_serial_port" value="/dev/ttyUSB2" type="str" /> + <param name="modem_baud_rate" value="19200" /> + <param name="set_modem_time" value="true"/> + <param name="pwramp_txlevel" value="2" type="int" /> + <rosparam param="modem_config" subst_value="True"> + ALL: 0 + BND: 3 + FC0: 25000 + BW0: 5000 + SST: 1 + DTP: 50 + pwramp.txlevel: 2 + psk.packet.mod_hdr_version: 1 + SRC: 0 + </rosparam> + </node> + </group> + + <group unless="$(arg use_hw_modem)"> + <param name="random_seed" value="1" /> + + <node name="modem_sim_node" pkg="ros_acomms_modeling" type="modem_sim_node.py" output="screen" > + <param name="center_frequency_hz" value="10000" type="int" /> + <param name="bandwidth_hz" value="5000" type="int" /> + <param name="SRC" value="0" type="int" /> + </node> + + <node name="modem_location_sim" pkg="ros_acomms_modeling" type="modem_location_sim_node.py" > + <param name="simple_path" value="true" /> + </node> + + </group> + + <node name="acoustic_range_node" pkg="ros_acomms" type="acoustic_range_node.py" output="screen" > + <param name="sound_speed_max_age_sec" value="30" type="int" /> + </node> + + <node name="mac_switcher" pkg="ros_acomms" type="mac_switcher_node.py" output="screen" required="true" respawn="false" clear_params="true" > + <rosparam subst_value="True"> + default_mac_namespace: 'tdma_fast_ping' + managed_mac_namespaces: + - 'tdma' + + # TODO: require managed mac nodes to continually publish the select message at minimum every managed_mac_heartbeat_timeout_sec + # .. of the mac manager will revert to the default (or prior mac if configured that way) + # .. mac manager select topic name: mac_switcher/MANAGED_MAC_NAMESPACE/select + # .. select topic type: std_msgs/Bool (True for select, False for deselect) + managed_mac_heartbeat_timeout_sec: -1 # if set to -1, it will not timeout + </rosparam> + </node> + + <node name="tdma_fast_ping" pkg="ros_acomms" type="$(arg tdma_type)_node.py" output="screen" required="true" respawn="false"> + <param name="num_slots" value="$(arg num_slots)"/> + <param name="slot_duration_seconds" value="$(arg slot_duration_seconds)" /> + <param name="active_slots" value="0,1"/> + <param name="comms_slots" value=""/> + <param name="pings_per_slot" value="1"/> + <param name="ping_modem_src" value="1"/> + <param name="ping_modem_timeout_sec" value="4" /> + <param name="packet_length_seconds" value="0"/> + <param name="guard_time_seconds" value="0"/> + <param name="always_send_test_data" value="true"/> + <param name="publish_private_status" value="true"/> + <!-- <param name="software_mute" value="true"/> --> + </node> + + <node name="tdma" pkg="ros_acomms" type="$(arg tdma_type)_node.py" output="screen" required="true" respawn="false"> + <param name="num_slots" value="$(arg num_slots)"/> + <param name="slot_duration_seconds" value="$(arg slot_duration_seconds)" /> + <param name="active_slots" value="0,1"/> + <param name="packet_length_seconds" value="3"/> + <param name="guard_time_seconds" value="2.5"/> + <param name="publish_private_status" value="true"/> + <param name="software_mute" value="true"/> + </node> + + <node name="message_queue_node" pkg="ros_acomms" type="message_queue_node.py" output="screen" > + <param name="packet_codec_file" value="$(find ros_acomms_tests)/launch/message_codec_config.yaml"/> + </node> + + <node name="packet_dispatch" pkg="ros_acomms" type="packet_dispatch_node.py" output="screen" > + <param name="packet_codec_file" value="$(find ros_acomms_tests)/launch/message_codec_config.yaml"/> + </node> + </group> + + <group ns="modem1"> + <group if="$(arg use_hw_modem)"> + <node name="acomms_driver" pkg="ros_acomms" type="acomms_driver_node.py" respawn="true" respawn_delay="10" > + <param name="modem_serial_port" value="/dev/ttyUSB3" type="str" /> + <param name="modem_baud_rate" value="19200" /> + <param name="set_modem_time" value="true"/> + <param name="pwramp_txlevel" value="2" type="int" /> + <rosparam param="modem_config" subst_value="True"> + ALL: 0 + BND: 3 + FC0: 25000 + BW0: 5000 + SST: 1 + DTP: 50 + pwramp.txlevel: 2 + psk.packet.mod_hdr_version: 1 + SRC: 1 + </rosparam> + </node> + </group> + + <group unless="$(arg use_hw_modem)"> + <param name="random_seed" value="2" /> + + <node name="modem_sim_node" pkg="ros_acomms_modeling" type="modem_sim_node.py" output="screen" > + <param name="center_frequency_hz" value="10000" type="int" /> + <param name="bandwidth_hz" value="5000" type="int" /> + <param name="SRC" value="1" type="int" /> + </node> + + <node name="modem_location_sim" pkg="ros_acomms_modeling" type="modem_location_sim_node.py" > + <param name="simple_path" value="true" /> + </node> + + </group> + + <node name="acoustic_range_node" pkg="ros_acomms" type="acoustic_range_node.py" output="screen" > + <param name="sound_speed_max_age_sec" value="30" type="int" /> + </node> + + <node name="tdma" pkg="ros_acomms" type="$(arg tdma_type)_node.py" output="screen" required="true" respawn="false"> + <param name="software_mute" value="true"/> + <param name="num_slots" value="$(arg num_slots)"/> + <param name="slot_duration_seconds" value="$(arg slot_duration_seconds)" /> + <param name="active_slots" value="0,1"/> + <param name="comms_slots" value=""/> + <param name="pings_per_slot" value="1"/> + <param name="ping_modem_src" value="0"/> + <param name="always_send_test_data" value="True"/> + <param name="packet_length_seconds" value="2.5"/> + <param name="guard_time_seconds" value="2.5"/> + </node> + + <node name="message_queue_node" pkg="ros_acomms" type="message_queue_node.py" output="screen" > + <param name="packet_codec_file" value="$(find ros_acomms_tests)/launch/message_codec_config.yaml"/> + </node> + + <node name="packet_dispatch" pkg="ros_acomms" type="packet_dispatch_node.py" output="screen" > + <param name="packet_codec_file" value="$(find ros_acomms_tests)/launch/message_codec_config.yaml"/> + </node> + </group> + + <param name="random_seed" value="4" /> + <param name="multiplier" value="1" /> + <node name="sim_packet_performance_node" pkg="ros_acomms_modeling" type="sim_packet_performance_node.py" /> + + <node name="sim_transmission_loss_node" pkg="ros_acomms_modeling" type="sim_transmission_loss_node.py" > + <param name="water_depth" value="20" type="int" /> + <param name="bellhop_arrivals" value="false" type="bool" /> + </node> + + <node name="clock_generator" pkg="ros_acomms_modeling" type="clock_generator.py"/> + +</launch>