Exemple #1
46
void testJacobian(std::string group_name, std::string base_link, std::string tip_link)
{
 SCOPED_TRACE(group_name + ": " + base_link + " to " + tip_link);
 
 srand ( time(NULL) ); // initialize random seed:
 rdf_loader::RDFLoader model_loader("robot_description");
 robot_model::RobotModelPtr kinematic_model;
 kinematic_model.reset(new robot_model::RobotModel(model_loader.getURDF(),model_loader.getSRDF()));

 robot_state::RobotStatePtr kinematic_state;
 kinematic_state.reset(new robot_state::RobotState(kinematic_model));
 kinematic_state->setToDefaultValues();

 const moveit::core::JointModelGroup* joint_state_group = kinematic_state->getRobotModel()->getJointModelGroup(group_name);

 std::string link_name = tip_link;
 std::vector<double> joint_angles(7,0.0);
 geometry_msgs::Point ref_position;
 Eigen::MatrixXd jacobian;
 Eigen::Vector3d point(0.0,0.0,0.0);
 kinematic_state->setJointGroupPositions(joint_state_group, &joint_angles[0]);
 ASSERT_TRUE(kinematic_state->getJacobian(joint_state_group, kinematic_state->getRobotModel()->getLinkModel(link_name),point,jacobian));

 KDL::Tree tree;
 if (!kdl_parser::treeFromUrdfModel(*model_loader.getURDF(), tree))
 {
   ROS_ERROR("Could not initialize tree object");
 }
 KDL::Chain kdl_chain;
 std::string base_frame(base_link);
 std::string tip_frame(tip_link);
 if (!tree.getChain(base_frame, tip_frame, kdl_chain))
 {
   ROS_ERROR("Could not initialize chain object");
 }
 KDL::ChainJntToJacSolver kdl_solver(kdl_chain);
 KDL::Jacobian jacobian_kdl(7);
 KDL::JntArray q_in(7);
 EXPECT_TRUE(kdl_solver.JntToJac(q_in,jacobian_kdl) >= 0);

 unsigned int NUM_TESTS = 10000;
 for(unsigned int i=0; i < NUM_TESTS; i++)
 {
   for(int j=0; j < 7; j++)
   {
     q_in(j) = gen_rand(-M_PI,M_PI);
     joint_angles[j] = q_in(j);
   }
   EXPECT_TRUE(kdl_solver.JntToJac(q_in,jacobian_kdl) >= 0);
   kinematic_state->setJointGroupPositions(joint_state_group, &joint_angles[0]);
   EXPECT_TRUE(kinematic_state->getJacobian(joint_state_group, kinematic_state->getRobotModel()->getLinkModel(link_name), point, jacobian));
   for(unsigned int k=0; k < 6; k++)
   {
     for(unsigned int m=0; m < 7; m++)
     {
       EXPECT_FALSE(NOT_NEAR(jacobian_kdl(k,m),jacobian(k,m),1e-10));
     }
   }
 }
}
TEST(JacobianSolver, solver)
{
 srand ( time(NULL) ); // initialize random seed: 
 rdf_loader::RDFLoader model_loader("robot_description");
 robot_model::RobotModelPtr kinematic_model;
 kinematic_model.reset(new robot_model::RobotModel(model_loader.getURDF(),model_loader.getSRDF()));

 robot_state::RobotStatePtr kinematic_state;
 kinematic_state.reset(new robot_state::RobotState(kinematic_model));
 kinematic_state->setToDefaultValues();

 robot_state::JointStateGroup* joint_state_group = kinematic_state->getJointStateGroup("right_arm");
 
 std::string link_name = "r_wrist_roll_link";
 std::vector<double> joint_angles(7,0.0); 
 geometry_msgs::Point ref_position;
 Eigen::MatrixXd jacobian;
 Eigen::Vector3d point(0.0,0.0,0.0);
 joint_state_group->setVariableValues(joint_angles);
 ASSERT_TRUE(joint_state_group->getJacobian(link_name,point,jacobian));

 KDL::Tree tree;
 if (!kdl_parser::treeFromUrdfModel(*model_loader.getURDF(), tree)) 
 {
   ROS_ERROR("Could not initialize tree object");
 }
 KDL::Chain kdl_chain;
 std::string base_frame("torso_lift_link");
 std::string tip_frame("r_wrist_roll_link");
 if (!tree.getChain(base_frame, tip_frame, kdl_chain)) 
 {
   ROS_ERROR("Could not initialize chain object");
 }
 KDL::ChainJntToJacSolver kdl_solver(kdl_chain);
 KDL::Jacobian jacobian_kdl(7);
 KDL::JntArray q_in(7);
 EXPECT_TRUE(kdl_solver.JntToJac(q_in,jacobian_kdl) >= 0);

 unsigned int NUM_TESTS = 1000000;
 for(unsigned int i=0; i < NUM_TESTS; i++)
 {
   for(int j=0; j < 7; j++)
   {
     q_in(j) = gen_rand(-M_PI,M_PI);
     joint_angles[j] = q_in(j);
   }
   EXPECT_TRUE(kdl_solver.JntToJac(q_in,jacobian_kdl) >= 0);
   joint_state_group->setVariableValues(joint_angles);
   EXPECT_TRUE(joint_state_group->getJacobian(link_name,point,jacobian));
   for(unsigned int k=0; k < 6; k++)
   {
     for(unsigned int m=0; m < 7; m++)
     {
       EXPECT_FALSE(NOT_NEAR(jacobian_kdl(k,m),jacobian(k,m),1e-10));
     }
   }
 }
}
Exemple #3
0
void gtm_init_env(rhdtyp *base_addr, unsigned char *transfer_addr)
{
	assert(CURRENT_RHEAD_ADR(base_addr) == base_addr);
	base_frame(base_addr);

#ifdef HAS_LITERAL_SECT
	new_stack_frame(base_addr, (unsigned char *)LINKAGE_ADR(base_addr), transfer_addr);
#else
	/* Any platform that does not follow pv-based linkage model either
	 *	(1) uses the following calculation to determine the context pointer value, or
	 *	(2) doesn't need a context pointer
	 */
	new_stack_frame(base_addr, PTEXT_ADR(base_addr), transfer_addr);
#endif
}
Exemple #4
0
void loop(){
boost::shared_ptr<Pacer::Controller> ctrl(ctrl_weak_ptr);
  static double start_time = t;
  
  const  std::vector<std::string>
  eef_names_ = ctrl->get_data<std::vector<std::string> >("init.end-effector.id");
  
  int NUM_FEET = eef_names_.size();
  
  boost::shared_ptr<Ravelin::Pose3d> base_frame( new Ravelin::Pose3d(ctrl->get_data<Ravelin::Pose3d>("base_link_frame")));
  
  Ravelin::VectorNd qd  = ctrl->get_joint_generalized_value(Pacer::Controller::velocity);
  Ravelin::VectorNd qdd = ctrl->get_joint_generalized_value(Pacer::Controller::acceleration);
  
  int NUM_JOINT_DOFS = qd.rows();
  
  // Initialize end effectors
  Ravelin::VectorNd local_q = ctrl->get_generalized_value(Pacer::Controller::position);

#ifdef USE_OSG_DISPLAY
  // 1 w/ base position
  ctrl->set_model_state(local_q);
  for(unsigned i=0;i<NUM_FEET;i++){
    Ravelin::Pose3d foot_pose(Ravelin::Matrix3d(link->get_pose()->q)*Ravelin::Matrix3d(0,0,-1, -1,0,0, 0,1,0),link->get_pose()->x,link->get_pose()->rpose);
    foot_pose.update_relative_pose(Pacer::GLOBAL);
    Utility::visualize.push_back( Pacer::VisualizablePtr( new Pacer::Pose(foot_pose,0.8)));    
    OUT_LOG(logERROR) << eef_names_[i] << "-orientation: " << t << " " << foot_pose.q;
  }
#endif

  // q w/o base position
  local_q.set_sub_vec(NUM_JOINT_DOFS,Utility::pose_to_vec(Ravelin::Pose3d()));
  ctrl->set_model_state(local_q);
  for(unsigned i=0;i<NUM_FEET;i++){
    Ravelin::Origin3d xd,xdd;
    //angular
    Ravelin::Origin3d rpy,axd,axdd;
   

    // Calc jacobian for AB at this EEF
    Ravelin::MatrixNd J = ctrl->calc_link_jacobian(local_q,eef_names_[i]);
    
    // Now that model state is set ffrom jacobian calculation
    const boost::shared_ptr<Ravelin::RigidBodyd>  link = ctrl->get_link(eef_names_[i]);
   
    
    Ravelin::Pose3d foot_pose(Ravelin::Matrix3d(link->get_pose()->q)*Ravelin::Matrix3d(0,0,-1, -1,0,0, 0,1,0),link->get_pose()->x,link->get_pose()->rpose);
    foot_pose.update_relative_pose(Pacer::GLOBAL);
    

//    Ravelin::Origin3d x(Ravelin::Pose3d::transform_point(Pacer::GLOBAL,Ravelin::Vector3d(0,0,0,link->get_pose())).data());
    bool new_var = ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".state.x",foot_pose.x);
    ctrl->set_data<Ravelin::Quatd>(eef_names_[i]+".state.q",foot_pose.q);
    
    J.block(0,3,0,NUM_JOINT_DOFS).mult(qd,xd);
    J.block(0,3,0,NUM_JOINT_DOFS).mult(qdd,xdd);
    J.block(3,6,0,NUM_JOINT_DOFS).mult(qd,axd);
    J.block(3,6,0,NUM_JOINT_DOFS).mult(qdd,axdd);
    
    ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".state.xd",xd);
    ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".state.xdd",xdd);
    ctrl->set_foot_value(eef_names_[i],Pacer::Controller::position,foot_pose.x);
    ctrl->set_foot_value(eef_names_[i],Pacer::Controller::velocity,xd);
    ctrl->set_foot_value(eef_names_[i],Pacer::Controller::acceleration,xdd);
    ctrl->set_foot_value(eef_names_[i],Pacer::Controller::load,Ravelin::Origin3d(0,0,0));

    if(new_var){
      ctrl->set_data<Ravelin::Quatd>(eef_names_[i]+".init.q",foot_pose.q);
      ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".init.x",foot_pose.x);
      ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".init.xd",xd);
    }

    ctrl->set_data<bool>(eef_names_[i]+".stance",false);
  }
  
  if(start_time == t){
    local_q.segment(0,qd.rows()) = Ravelin::VectorNd::zero(qd.rows());
    ctrl->set_model_state(local_q);
    std::vector<Ravelin::Origin3d> x1(NUM_FEET) ,x2(NUM_FEET);

    for(unsigned i=0;i<NUM_FEET;i++){
      Ravelin::Origin3d x,base_x;
      ctrl->get_data<Ravelin::Origin3d>(eef_names_[i]+".init.x",x);
      base_x = x;
      base_x[2] = 0;
      ctrl->set_data<Ravelin::Origin3d>(eef_names_[i]+".base",base_x);
      x2[i] = base_x;
      const boost::shared_ptr<Ravelin::RigidBodyd>  link = ctrl->get_link(eef_names_[i]);
      x1[i] = Ravelin::Origin3d(Ravelin::Pose3d::transform_point(Pacer::GLOBAL,Ravelin::Vector3d(0,0,0,link->get_pose())).data());
    }
    
    std::fill(local_q.segment(0,qd.rows()).begin(),local_q.segment(0,qd.rows()).end(),M_PI);
    ctrl->set_model_state(local_q);

    for(unsigned i=0;i<NUM_FEET;i++){
      const boost::shared_ptr<Ravelin::RigidBodyd>  link = ctrl->get_link(eef_names_[i]);
      x2[i] = Ravelin::Origin3d(Ravelin::Pose3d::transform_point(Pacer::GLOBAL,Ravelin::Vector3d(0,0,0,link->get_pose())).data());

      // write in maximum reach from limb base
      double reach = (x1[i]-x2[i]).norm();
      ctrl->set_data<double>(eef_names_[i]+".reach",reach*0.95);
    }
  }
}
void callback(const sensor_msgs::PointCloud2::ConstPtr& msg)
{
    std::string base_frame("base_link");
    
    geometry_msgs::PointStamped pout;
    geometry_msgs::PointStamped pin;
    pin.header.frame_id = msg->header.frame_id;
    pin.point.x = 0; pin.point.y = 0; pin.point.z = 0;
    geometry_msgs::Vector3Stamped vout;
    geometry_msgs::Vector3Stamped vin;
    vin.header.frame_id = base_frame;
    vin.vector.x = 0; vin.vector.y = 0; vin.vector.z = 1;
    
    float height;
    Eigen::Vector3f normal;
    try {
        listener->transformPoint(base_frame, ros::Time(0), pin, msg->header.frame_id, pout);
        height = pout.point.z;
        listener->transformVector(msg->header.frame_id, ros::Time(0), vin, base_frame, vout);
        normal = Eigen::Vector3f(vout.vector.x, vout.vector.y, vout.vector.z);
    }
    catch (tf::TransformException ex) {
        ROS_INFO("%s",ex.what());
        return;
    }
    
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
    pcl::fromROSMsg(*msg, *cloud);
	
	Eigen::Vector3f p = -height*normal; // a point in the floor plane
	float d = -p.dot(normal); // = height, d in the plane equation
	
	obstacle_cloud->points.clear();
	obstacle_cloud->points.reserve(cloud->size());
	floor_cloud->points.clear();
	Eigen::Vector3f temp;
	float floor_dist;
	pcl::PointXYZ point;
	for (size_t i = 0; i < cloud->size(); ++i) {
	    
	    /*temp = cloud->points[i].getVector3fMap(); // DEBUG!
	    
	    if (i%640 < 300 && i%640 > 200 && i < 640*200 && i > 640*100) {
	        temp -= 0.06*normal;
	    }*/

        // check signed distance to floor
	    floor_dist = normal.dot(cloud->points[i].getVector3fMap()) + d;
	    //floor_dist = normal.dot(temp) + d; // DEBUG
	    
	    // if enough below, consider a stair point
	    if (floor_dist < below_threshold) {
	        temp = cloud->points[i].getVector3fMap(); // RELEASE
	        point.getVector3fMap() = -(d/normal.dot(temp))*temp + normal*0.11;
	        floor_cloud->points.push_back(point);
	    }
	    else { // add as a normal obstacle or clearing point
	        obstacle_cloud->points.push_back(cloud->points[i]);
	    }
	}

	sensor_msgs::PointCloud2 floor_msg;
    pcl::toROSMsg(*floor_cloud, floor_msg);
	floor_msg.header = msg->header;

	floor_pub.publish(floor_msg);
	
	sensor_msgs::PointCloud2 obstacle_msg;
    pcl::toROSMsg(*obstacle_cloud, obstacle_msg);
	obstacle_msg.header = msg->header;
	
	obstacle_pub.publish(obstacle_msg);
}
Exemple #6
0
int gtm_trigger(gv_trigger_t *trigdsc, gtm_trigger_parms *trigprm)
{
	mval		*lvvalue;
	lnr_tabent	*lbl_offset_p;
	uchar_ptr_t	transfer_addr;
	lv_val		*lvval;
	mname_entry	*mne_p;
	uint4		*indx_p;
	ht_ent_mname	*tabent;
	boolean_t	added;
	int		clrlen, rc, i, unwinds;
	mval		**lvvalarray;
	mv_stent	*mv_st_ent;
	symval		*new_symval;
	uint4		dollar_tlevel_start;
	stack_frame	*fp, *fpprev;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(!skip_dbtriggers);	/* should not come here if triggers are not supposed to be invoked */
	assert(trigdsc);
	assert(trigprm);
	assert((NULL != trigdsc->rtn_desc.rt_adr) || ((MV_STR & trigdsc->xecute_str.mvtype)
						      && (0 != trigdsc->xecute_str.str.len)
						      && (NULL != trigdsc->xecute_str.str.addr)));
	assert(dollar_tlevel);
	/* Determine if trigger needs to be compiled */
	if (NULL == trigdsc->rtn_desc.rt_adr)
	{	/* No routine hdr addr exists. Need to do compile */
		if (0 != gtm_trigger_complink(trigdsc, TRUE))
		{
			PRN_ERROR;	/* Leave record of what error caused the compilation failure if any */
			rts_error(VARLSTCNT(4) ERR_TRIGCOMPFAIL, 2, trigdsc->rtn_desc.rt_name.len, trigdsc->rtn_desc.rt_name.addr);
		}
	}
	assert(trigdsc->rtn_desc.rt_adr);
	assert(trigdsc->rtn_desc.rt_adr == CURRENT_RHEAD_ADR(trigdsc->rtn_desc.rt_adr));
	/* Setup trigger environment stack frame(s) for execution */
	if (!(frame_pointer->type & SFT_TRIGR))
	{	/* Create new trigger base frame first that back-stops stack unrolling and return to us */
		if (GTM_TRIGGER_DEPTH_MAX < (gtm_trigger_depth + 1))	/* Verify we won't nest too deep */
			rts_error(VARLSTCNT(3) ERR_MAXTRIGNEST, 1, GTM_TRIGGER_DEPTH_MAX);
		DBGTRIGR((stderr, "gtm_trigger: PUSH: frame_pointer 0x%016lx  ctxt value: 0x%016lx\n", frame_pointer, ctxt));
		/* Protect against interrupts while we have only a trigger base frame on the stack */
		DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND);
		/* The current frame invoked a trigger. We cannot return to it for a TP restart or other reason unless
		 * either the total operation (including trigger) succeeds and we unwind normally or unless the mpc is reset
		 * (like what happens in various error or restart conditions) because right now it returns to where a database
		 * command (KILL, SET or ZTRIGGER) was entered. Set flag in the frame to prevent MUM_TSTART unless the frame gets
		 * reset.
		 */
		frame_pointer->flags |= SFF_TRIGR_CALLD;	/* Do not return to this frame via MUM_TSTART */
		DBGTRIGR((stderr, "gtm_trigger: Setting SFF_TRIGR_CALLD in frame 0x"lvaddr"\n", frame_pointer));
		base_frame(trigdsc->rtn_desc.rt_adr);
		/* Finish base frame initialization - reset mpc/context to return to us without unwinding base frame */
		frame_pointer->type |= SFT_TRIGR;
#		if defined(__hpux) && defined(__hppa)
		/* For HPUX-HPPA (PA-RISC), we use longjmp() to return to gtm_trigger() to avoid some some space register
		 * corruption issues. Use call-ins already existing mechanism for doing this. Although we no longer support
		 * HPUX-HPPA for triggers due to some unlocated space register error, this code (effectively always ifdef'd
		 * out) left in in case it gets resurrected in the future (01/2010 SE).
		 */
		frame_pointer->mpc = CODE_ADDRESS(ci_ret_code);
		frame_pointer->ctxt = GTM_CONTEXT(ci_ret_code);
#		else
		frame_pointer->mpc = CODE_ADDRESS(gtm_levl_ret_code);
		frame_pointer->ctxt = GTM_CONTEXT(gtm_levl_ret_code);
#		endif
		/* This base stack frame is also where we save environmental info for all triggers invoked at this stack level.
		 * Subsequent triggers fired at this level in this trigger invocation need only reinitialize a few things but
		 * can avoid "the big save".
		 */
		if (NULL == trigr_symval_list)
		{	/* No available symvals for use with this trigger, create one */
			symbinit();	/* Initialize a symbol table the trigger will use */
			curr_symval->trigr_symval = TRUE;	/* Mark as trigger symval so will be saved not decommissioned */
		} else
		{	/* Trigger symval is available for reuse */
			new_symval = trigr_symval_list;
			assert(new_symval->trigr_symval);
			trigr_symval_list = new_symval->last_tab;		/* dequeue new curr_symval from list */
			REINIT_SYMVAL_BLK(new_symval, curr_symval);
			curr_symval = new_symval;
			PUSH_MV_STENT(MVST_STAB);
			mv_chain->mv_st_cont.mvs_stab = new_symval;		/* So unw_mv_ent() can requeue it for later use */
		}
		/* Push our trigger environment save mv_stent onto the chain */
		PUSH_MV_STENT(MVST_TRIGR);
		mv_st_ent = mv_chain;
		/* Initialize the mv_stent elements processed by stp_gcol which can be called by either op_gvsavtarg() or
		 * by the extnam saving code below. This initialization keeps stp_gcol - should it be called - from attempting
		 * to process unset fields filled with garbage in them as valid mstr address/length pairs.
		 */
		mv_st_ent->mv_st_cont.mvs_trigr.savtarg.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save.str.len = 0;
		mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth = dollar_truth;
		op_gvsavtarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
		if (extnam_str.len)
		{
			ENSURE_STP_FREE_SPACE(extnam_str.len);
			mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr = (char *)stringpool.free;
			memcpy(mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.addr, extnam_str.len);
			stringpool.free += extnam_str.len;
			assert(stringpool.free <= stringpool.top);
		}
		mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = extnam_str.len;
		mv_st_ent->mv_st_cont.mvs_trigr.ztname_save = dollar_ztname;
		mv_st_ent->mv_st_cont.mvs_trigr.ztdata_save = dollar_ztdata;
		mv_st_ent->mv_st_cont.mvs_trigr.ztoldval_save = dollar_ztoldval;
		mv_st_ent->mv_st_cont.mvs_trigr.ztriggerop_save = dollar_ztriggerop;
		mv_st_ent->mv_st_cont.mvs_trigr.ztupdate_save = dollar_ztupdate;
		mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_save = dollar_ztvalue;
		mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_changed_ptr = ztvalue_changed_ptr;
#		ifdef DEBUG
		/* In a debug process, these fields give clues of what trigger we are working on */
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigdsc_last_save = trigdsc;
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigprm_last_save = trigprm;
#		endif
		assert(((0 == gtm_trigger_depth) && (ch_at_trigger_init == ctxt->ch))
		       || ((0 < gtm_trigger_depth) && (&mdb_condition_handler == ctxt->ch)));
		mv_st_ent->mv_st_cont.mvs_trigr.ctxt_save = ctxt;
		mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigger_depth_save = gtm_trigger_depth;
		if (0 == gtm_trigger_depth)
		{	/* Only back up $*trap settings when initiating the first trigger level */
			mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save = dollar_etrap;
			mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save = dollar_ztrap;
			mv_st_ent->mv_st_cont.mvs_trigr.ztrap_explicit_null_save = ztrap_explicit_null;
			dollar_ztrap.str.len = 0;
			ztrap_explicit_null = FALSE;
			if (NULL != gtm_trigger_etrap.str.addr)
				/* An etrap was defined for the trigger environment - Else existing $etrap persists */
				dollar_etrap = gtm_trigger_etrap;
		}
		mv_st_ent->mv_st_cont.mvs_trigr.mumps_status_save = mumps_status;
		mv_st_ent->mv_st_cont.mvs_trigr.run_time_save = run_time;
		/* See if a MERGE launched the trigger. If yes, save some state so ZWRITE, ZSHOW and/or MERGE can be
		 * run in the trigger we dispatch. */
		if ((0 != merge_args) || TREF(in_zwrite))
			PUSH_MVST_MRGZWRSV;
		mumps_status = 0;
		run_time = TRUE;	/* Previous value saved just above restored when frame pops */
	} else
	{	/* Trigger base frame exists so reinitialize the symbol table for new trigger invocation */
		REINIT_SYMVAL_BLK(curr_symval, curr_symval->last_tab);
		/* Locate the MVST_TRIGR mv_stent containing the backed up values. Some of those values need
		 * to be restored so the 2nd trigger has the same environment as the previous trigger at this level
		 */
		for (mv_st_ent = mv_chain;
		     (NULL != mv_st_ent) && (MVST_TRIGR != mv_st_ent->mv_st_type);
		     mv_st_ent = (mv_stent *)(mv_st_ent->mv_st_next + (char *)mv_st_ent))
			;
		assert(NULL != mv_st_ent);
		assert((char *)mv_st_ent < (char *)frame_pointer); /* Ensure mv_stent associated this trigger frame */
		/* Reinit backed up values from the trigger environment backup */
		dollar_truth = mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth;
		op_gvrectarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
		extnam_str.len = mv_st_ent->mv_st_cont.mvs_trigr.savextref.len;
		if (extnam_str.len)
			memcpy(extnam_str.addr, mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.len);
		mumps_status = 0;
		assert(run_time);
		/* Note we do not reset the handlers for parallel triggers - set one time only when enter first level
		 * trigger. After that, whatever happens in trigger world, stays in trigger world.
		 */
	}
	assert(frame_pointer->type & SFT_TRIGR);
#	ifdef DEBUG
	gtm_trigdsc_last = trigdsc;
	gtm_trigprm_last = trigprm;
#	endif
	/* Set new value of trigger ISVs. Previous values already saved in trigger base frame */
	dollar_ztname = &trigdsc->rtn_desc.rt_name;
	dollar_ztdata = (mval *)trigprm->ztdata_new;
	dollar_ztoldval = trigprm->ztoldval_new;
	dollar_ztriggerop = (mval *)trigprm->ztriggerop_new;
	dollar_ztupdate = trigprm->ztupdate_new;
	dollar_ztvalue = trigprm->ztvalue_new;
	ztvalue_changed_ptr = &trigprm->ztvalue_changed;
	/* Set values associated with trigger into symbol table */
	lvvalarray = trigprm->lvvalarray;
	for (i = 0, mne_p = trigdsc->lvnamearray, indx_p = trigdsc->lvindexarray;
	     i < trigdsc->numlvsubs; ++i, ++mne_p, ++indx_p)
	{	/* Once thru for each subscript we are to set */
		lvvalue = lvvalarray[*indx_p];			/* Locate mval that contains value */
		assert(NULL != lvvalue);
		assert(MV_DEFINED(lvvalue));			/* No sense in defining the undefined */
		lvval = lv_getslot(curr_symval);		/* Allocate an lvval to put into symbol table */
		LVVAL_INIT(lvval, curr_symval);
		lvval->v = *lvvalue;				/* Copy mval into lvval */
		added = add_hashtab_mname_symval(&curr_symval->h_symtab, mne_p, lvval, &tabent);
		assert(added);
		assert(NULL != tabent);
	}
	/* While the routine header is available in trigdsc, we also need the <null> label address associated with
	 *  the first (and only) line of code.
	 */
	lbl_offset_p = LNRTAB_ADR(trigdsc->rtn_desc.rt_adr);
	transfer_addr = (uchar_ptr_t)LINE_NUMBER_ADDR(trigdsc->rtn_desc.rt_adr, lbl_offset_p);
	/* Create new stack frame for invoked trigger in same fashion as gtm_init_env() creates its 2ndary frame */
#	ifdef HAS_LITERAL_SECT
	new_stack_frame(trigdsc->rtn_desc.rt_adr, (unsigned char *)LINKAGE_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
#	else
	/* Any platform that does not follow pv-based linkage model either
	 *	(1) uses the following calculation to determine the context pointer value, or
	 *	(2) doesn't need a context pointer
	 */
	new_stack_frame(trigdsc->rtn_desc.rt_adr, PTEXT_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
#	endif
	dollar_tlevel_start = dollar_tlevel;
	assert(gv_target->gd_csa == cs_addrs);
	gv_target->trig_local_tn = local_tn;			/* Record trigger being driven for this global */
	/* Invoke trigger generated code */
	rc = gtm_trigger_invoke();
	if (1 == rc)
	{	/* Normal return code (from dm_start). Check if TP has been unwound or not */
		assert(dollar_tlevel <= dollar_tlevel_start);	/* Bigger would be quite the surprise */
		if (dollar_tlevel < dollar_tlevel_start)
		{	/* Our TP level was unwound during the trigger so throw an error */
			DBGTRIGR((stderr, "gtm_trigger: $TLEVEL less than at start - throwing TRIGTLVLCHNG\n"));
			gtm_trigger_fini(TRUE, FALSE);	/* dump this trigger level */
			rts_error(VARLSTCNT(4) ERR_TRIGTLVLCHNG, 2, trigdsc->rtn_desc.rt_name.len,
				  trigdsc->rtn_desc.rt_name.addr);
		}
		rc = 0;			/* Be polite and return 0 for the (hopefully common) success case */
	} else if (ERR_TPRETRY == rc)
	{	/* We are restarting the entire transaction. There are two possibilities here:
		 * 1) This is a nested trigger level in which case we need to unwind further or
		 *    the outer trigger level was created by M code. If either is true, just
		 *    rethrow the TPRETRY error.
		 * 2) This is the outer trigger and the call to op_tstart() was done by our caller.
		 *    In this case, we just return to our caller with a code signifying they need
		 *    to restart the implied transaction.
		 */
		assert(dollar_tlevel && (tstart_trigger_depth <= gtm_trigger_depth));
		if ((tstart_trigger_depth < gtm_trigger_depth) || !tp_pointer->implicit_tstart || !tp_pointer->implicit_trigger)
		{	/* Unwind a trigger level to restart level or to next trigger boundary */
			gtm_trigger_fini(FALSE, FALSE);	/* Get rid of this trigger level - we won't be returning */
			DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - rethrowing ERR_TPRETRY\n"));
			INVOKE_RESTART;
		} else
		{	/* It is possible we are restarting a transaction that never got around to creating a base
			 * frame yet the implicit TStart was done. So if there is no trigger base frame, do not
			 * run gtm_trigger_fini() but instead do the one piece of cleanup it does that we still need.
			 */
			assert(donot_INVOKE_MUMTSTART);
			if (SFT_TRIGR & frame_pointer->type)
			{	/* Normal case when TP restart unwinding back to implicit beginning */
				gtm_trigger_fini(FALSE, FALSE);
				DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - returning to gvcst_<caller>\n"));
			} else
			{       /* Unusual case of trigger that died in no-mans-land before trigger base frame established.
				 * Remove the "do not return to me" flag only on non-error unwinds */
				assert(tp_pointer->implicit_tstart);
				assert(SFF_TRIGR_CALLD & frame_pointer->flags);
				frame_pointer->flags &= SFF_TRIGR_CALLD_OFF;
				DBGTRIGR((stderr, "gtm_trigger: turning off SFF_TRIGR_CALLD (1) in frame 0x"lvaddr"\n",
					  frame_pointer));
				DBGTRIGR((stderr, "gtm_trigger: unwinding no-base-frame trigger for TP restart\n"));
			}
		}
		/* Fall out and return ERR_TPRETRY to caller */
	} else if (0 == rc)
		/* We should never get a return code of 0. This would be out-of-design and a signal that something
		 * is quite broken. We cannot "rethrow" outside the trigger because it was not initially an error so
		 * mdb_condition_handler would have no record of it (rethrown errors must have originally occurred in
		 * or to be RE-thrown) and assert fail at best.
		 */
		GTMASSERT;
	else
	{	/* We have an unexpected return code due to some error during execution of the trigger that tripped
		 * gtm_trigger's safety handler (i.e. an error occurred in mdb_condition_handler() established by
		 * dm_start(). Since we are going to unwind the trigger frame and rethrow the error, we also have
		 * to unwind all the stack frames on top of the trigger frame. Figure out how many frames that is,
		 * unwind them all plus the trigger base frame before rethrowing the error.
		 */
		for (unwinds = 0, fp = frame_pointer; (NULL != fp) && !(SFT_TRIGR & fp->type); fp = fp->old_frame_pointer)
			unwinds++;
		assert((NULL != fp) && (SFT_TRIGR & fp->type));
		GOFRAMES(unwinds, TRUE, FALSE);
		assert((NULL != frame_pointer) && !(SFT_TRIGR & frame_pointer->type));
		DBGTRIGR((stderr, "gtm_trigger: Unsupported return code (%d) - unwound %d frames and now rethrowing error\n",
			  rc, unwinds));
		rts_error(VARLSTCNT(1) ERR_REPEATERROR);
	}
	return rc;
}