Beispiel #1
0
void CAgent::print(std::vector<float> subspace)
{
	CLog log((char*)"q_map.txt", 3);

	u32 i;
	float y, x;
	std::vector<float> state;

	for (i = 0; i < agent.inputs_count; i++)
		state.push_back(0.0);

	for (i = 0; i < subspace.size(); i++)
		state[i] = subspace[i];

	printf("\n q max \n");
	for (y = -1.0; y < 1.0; y+= agent.state_density)
	{
		for (x = -1.0; x < 1.0; x+= agent.state_density)
		{
			state[subspace.size() + 1] = y;
			state[subspace.size() + 0] = x;

			float res = q_learning->get_max_q(state);

			printf("%2.3f ", res);

			//u32 state_idx = q_learning->get_state_idx();
			//printf("%3u ", state_idx);
			log.add(0, x);
			log.add(1, y);
			log.add(2, res);
		}

		printf("\n");
	}

	log.save();


	printf("\n\n");


	printf("\n q min \n");
	for (y = -1.0; y < 1.0; y+= agent.state_density)
	{
		for (x = -1.0; x < 1.0; x+= agent.state_density)
		{
			state[subspace.size() + 1] = y;
			state[subspace.size() + 0] = x;

			float res = q_learning->get_min_q(state);

			printf("%2.3f ", res);
		}

		printf("\n");
	}

	printf("\n\n");



	printf("\n q id \n");

	CLog log_action_id((char*)"q_action_id.txt", 3);

	for (y = -1.0; y < 1.0; y+= agent.state_density)
	{
		for (x = -1.0; x < 1.0; x+= agent.state_density)
		{
			state[subspace.size() + 1] = y;
			state[subspace.size() + 0] = x;

			u32 res = q_learning->get_max_q_id(state);

			printf("%3i ", res);

			log_action_id.add(0, x);
			log_action_id.add(1, y);
			log_action_id.add(2, res);
		}

		printf("\n");
	}

	printf("\n\n");

	log_action_id.save();


	printf("\nbest action matrix \n");

	CLog log_action((char*)"q_action.txt", 2 + actions->get_action_size());
	for (y = -1.0; y < 1.0; y+= agent.state_density)
	{
		for (x = -1.0; x < 1.0; x+= agent.state_density)
		{
			state[subspace.size() + 1] = y;
			state[subspace.size() + 0] = x;

			u32 action_id = q_learning->get_max_q_id(state);

			struct sAction action = actions->get(0, action_id);

			vect_print(action.action);

			log_action.add(0, x);
			log_action.add(1, y);
			for (i = 0; i < action.action.size(); i++)
				log_action.add(2 + i, action.action[i]);
		}

		printf("\n");
	}

	printf("\n\n");

	log_action.save();
}
Beispiel #2
0
/**
 * MEX function entry point.
 */
void 
mexFunction(
    int     nlhs,
    mxArray     *plhs[],
    int     nrhs,
    const mxArray   *prhs[]
) {
    double  *q, *qd, *qdd;
    double  *tau;
    unsigned int    m,n;
    int j, njoints, p, nq;
    double  *fext = NULL;
    double *grav = NULL;
    Robot       robot;
    mxArray     *link0;
    mxArray     *mx_robot;
    mxArray     *mx_links;
    static int  first_time = 0;

    /*
    fprintf(stderr, "Fast RNE: (c) Peter Corke 2002-2011\n");
    */

    if (  !mxIsClass(ROBOT_IN, "SerialLink") )
        mexErrMsgTxt("frne: first argument is not a robot structure\n");

    mx_robot = (mxArray *)ROBOT_IN;
    
    njoints = mstruct_getint(mx_robot, 0, "n");

/***********************************************************************
 * Handle the different calling formats.
 * Setup pointers to q, qd and qdd inputs 
 ***********************************************************************/
    switch (nrhs) {
    case 2:
    /*
     * TAU = RNE(ROBOT, [Q QD QDD])
     */ 
        if (NUMCOLS(A1_IN) != 3 * njoints)
            mexErrMsgTxt("frne: too few cols in [Q QD QDD]");
        q = POINTER(A1_IN);
        nq = NUMROWS(A1_IN);
        qd = &q[njoints*nq];
        qdd = &q[2*njoints*nq];
        break;
        
    case 3:
    /*
     * TAU = RNE(ROBOT, [Q QD QDD], GRAV)
     */ 
        if (NUMCOLS(A1_IN) != (3 * njoints))
            mexErrMsgTxt("frne: too few cols in [Q QD QDD]");
        q = POINTER(A1_IN);
        nq = NUMROWS(A1_IN);
        qd = &q[njoints*nq];
        qdd = &q[2*njoints*nq];

        if (NUMELS(A2_IN) != 3)
            mexErrMsgTxt("frne: gravity vector expected");
        grav = POINTER(A2_IN);
        break;

    case 4:
    /*
     * TAU = RNE(ROBOT, Q, QD, QDD)
     * TAU = RNE(ROBOT, [Q QD QDD], GRAV, FEXT)
     */ 
        if (NUMCOLS(A1_IN) == (3 * njoints)) {
            q = POINTER(A1_IN);
            nq = NUMROWS(A1_IN);
            qd = &q[njoints*nq];
            qdd = &q[2*njoints*nq];

            if (NUMELS(A2_IN) != 3)
                mexErrMsgTxt("frne: gravity vector expected");
            grav = POINTER(A2_IN);
            if (NUMELS(A3_IN) != 6)
                mexErrMsgTxt("frne: Fext vector expected");
            fext = POINTER(A3_IN);
        } else {
            int nqd = NUMROWS(A2_IN),
                nqdd = NUMROWS(A3_IN);

            nq = NUMROWS(A1_IN);
            if ((nq != nqd) || (nqd != nqdd))
                mexErrMsgTxt("frne: Q QD QDD must be same length");
            if ( (NUMCOLS(A1_IN) != njoints) ||
                 (NUMCOLS(A2_IN) != njoints) ||
                 (NUMCOLS(A3_IN) != njoints)
            ) 
                mexErrMsgTxt("frne: Q must have Naxis columns");
            q = POINTER(A1_IN);
            qd = POINTER(A2_IN);
            qdd = POINTER(A3_IN);
        }
        break;

    case 5: {
    /*
     * TAU = RNE(ROBOT, Q, QD, QDD, GRAV)
     */
        int nqd = NUMROWS(A2_IN),
            nqdd = NUMROWS(A3_IN);

        nq = NUMROWS(A1_IN);
        if ((nq != nqd) || (nqd != nqdd))
            mexErrMsgTxt("frne: Q QD QDD must be same length");
        if ( (NUMCOLS(A1_IN) != njoints) ||
             (NUMCOLS(A2_IN) != njoints) ||
             (NUMCOLS(A3_IN) != njoints)
        ) 
            mexErrMsgTxt("frne: Q must have Naxis columns");
        q = POINTER(A1_IN);
        qd = POINTER(A2_IN);
        qdd = POINTER(A3_IN);
        if (NUMELS(A4_IN) != 3)
            mexErrMsgTxt("frne: gravity vector expected");
        grav = POINTER(A4_IN);
        break;
    }

    case 6: {
    /*
     * TAU = RNE(ROBOT, Q, QD, QDD, GRAV, FEXT)
     */
        int nqd = NUMROWS(A2_IN),
            nqdd = NUMROWS(A3_IN);

        nq = NUMROWS(A1_IN);
        if ((nq != nqd) || (nqd != nqdd))
            mexErrMsgTxt("frne: Q QD QDD must be same length");
        if ( (NUMCOLS(A1_IN) != njoints) ||
             (NUMCOLS(A2_IN) != njoints) ||
             (NUMCOLS(A3_IN) != njoints)
        ) 
            mexErrMsgTxt("frne: Q must have Naxis columns");
        q = POINTER(A1_IN);
        qd = POINTER(A2_IN);
        qdd = POINTER(A3_IN);
        if (NUMELS(A4_IN) != 3)
            mexErrMsgTxt("frne: gravity vector expected");
        grav = POINTER(A4_IN);
        if (NUMELS(A5_IN) != 6)
            mexErrMsgTxt("frne: Fext vector expected");
        fext = POINTER(A5_IN);
        break;
    }
    default:
        mexErrMsgTxt("frne: wrong number of arguments.");
    }

    /*
     * fill out the robot structure
     */
    robot.njoints = njoints;

    if (grav)
        robot.gravity = (Vect *)grav;
    else
        robot.gravity = (Vect *)mxGetPr( mxGetProperty(mx_robot, (mwIndex)0, "gravity") );
    robot.dhtype = mstruct_getint(mx_robot, 0, "mdh");

    /* build link structure */
    robot.links = (Link *)mxCalloc((mwSize) njoints, (mwSize) sizeof(Link));


/***********************************************************************
 * Now we have to get pointers to data spread all across a cell-array
 * of Matlab structures.
 *
 * Matlab structure elements can be found by name (slow) or by number (fast).
 * We assume that since the link structures are all created by the same
 * constructor, the index number for each element will be the same for all
 * links.  However we make no assumption about the numbers themselves.
 ***********************************************************************/

    /* get pointer to the first link structure */
    link0 = mxGetProperty(mx_robot, (mwIndex) 0, "links");
    if (link0 == NULL)
        mexErrMsgTxt("couldnt find element link in robot object");

    /*
     * Elements of the link structure are:
     *
     *  alpha: 
     *  A:
     *  theta:
     *  D:
     *  offset:
     *  sigma:
     *  mdh:
     *  m:
     *  r:
     *  I:
     *  Jm:
     *  G:
     *  B:
     *  Tc:
     */

    if (first_time == 0) {
        mexPrintf("Fast RNE: (c) Peter Corke 2002-2012\n");
        first_time = 1;
    }

    /*
     * copy data from the Link objects into the local links structure
     * to save function calls later
     */
    for (j=0; j<njoints; j++) {
        Link    *l = &robot.links[j];
        mxArray *links = mxGetProperty(mx_robot, (mwIndex) 0, "links"); // links array

        l->alpha =  mxGetScalar( mxGetProperty(links, (mwIndex) j, "alpha") );
        l->A =      mxGetScalar( mxGetProperty(links, (mwIndex) j, "a") );
        l->theta =  mxGetScalar( mxGetProperty(links, (mwIndex) j, "theta") );
        l->D =      mxGetScalar( mxGetProperty(links, (mwIndex) j, "d") );
        l->sigma =  mxGetScalar( mxGetProperty(links, (mwIndex) j, "sigma") );
        l->offset = mxGetScalar( mxGetProperty(links, (mwIndex) j, "offset") );
        l->m =      mxGetScalar( mxGetProperty(links, (mwIndex) j, "m") );
        l->rbar =   (Vect *)mxGetPr( mxGetProperty(links, (mwIndex) j, "r") );
        l->I =      mxGetPr( mxGetProperty(links, (mwIndex) j, "I") );
        l->Jm =     mxGetScalar( mxGetProperty(links, (mwIndex) j, "Jm") );
        l->G =      mxGetScalar( mxGetProperty(links, (mwIndex) j, "G") );
        l->B =      mxGetScalar( mxGetProperty(links, (mwIndex) j, "B") );
        l->Tc =     mxGetPr( mxGetProperty(links, (mwIndex) j, "Tc") );
    }

    /* Create a matrix for the return argument */
    TAU_OUT = mxCreateDoubleMatrix((mwSize) nq, (mwSize) njoints, mxREAL);
    tau = mxGetPr(TAU_OUT);

#define MEL(x,R,C)  (x[(R)+(C)*nq])

    /* for each point in the input trajectory */
    for (p=0; p<nq; p++) {
        /*
         * update all position dependent variables
         */
        for (j = 0; j < njoints; j++) {
            Link    *l = &robot.links[j];

            switch (l->sigma) {
            case REVOLUTE:
                rot_mat(l, MEL(q,p,j)+l->offset, l->D, robot.dhtype);
                break;
            case PRISMATIC:
                rot_mat(l, l->theta, MEL(q,p,j)+l->offset, robot.dhtype);
                break;
            }
#ifdef  DEBUG
            rot_print("R", &l->R);
            vect_print("p*", &l->r);
#endif
        }

        newton_euler(&robot, &tau[p], &qd[p], &qdd[p], fext, nq);

    }

    mxFree(robot.links);
}
Beispiel #3
0
/**
 * Recursive Newton-Euler algorithm.
 *
 * @Note the parameter \p stride which is used to allow for input and output
 * arrays which are 2-dimensional but in column-major (Matlab) order.  We
 * need to access rows from the arrays.
 *
 */
void
newton_euler (
	Robot	*robot,		/*!< robot object  */
	double	*tau,		/*!< returned joint torques */
	double	*qd,		/*!< joint velocities */
	double	*qdd,		/*!< joint accelerations */
	double	*fext,		/*!< external force on manipulator tip */
	int	stride		/*!< indexing stride for qd, qdd */
) {
	Vect			t1, t2, t3, t4;
	Vect			qdv, qddv;
	Vect			F, N;
	Vect		z0 = {0.0, 0.0, 1.0};
	Vect		zero = {0.0, 0.0, 0.0};
	Vect		f_tip = {0.0, 0.0, 0.0};
	Vect		n_tip = {0.0, 0.0, 0.0};
	register int		j;
	double			t;
	Link			*links = robot->links;

	/*
	 * angular rate and acceleration vectors only have finite
	 * z-axis component
	 */
	qdv = qddv = zero;

	/* setup external force/moment vectors */
	if (fext) {
		f_tip.x = fext[0];
		f_tip.y = fext[1];
		f_tip.z = fext[2];
		n_tip.x = fext[3];
		n_tip.y = fext[4];
		n_tip.z = fext[5];
	}


/******************************************************************************
 * forward recursion --the kinematics
 ******************************************************************************/

	if (robot->dhtype == MODIFIED) {
	    /*
	     * MODIFIED D&H CONVENTIONS
	     */
	    for (j = 0; j < robot->njoints; j++) {

		/* create angular vector from scalar input */
		qdv.z = qd[j*stride]; 
		qddv.z = qdd[j*stride];

		switch (links[j].sigma) {
		case REVOLUTE:
			/* 
			 * calculate angular velocity of link j
			 */
			if (j == 0)
				*OMEGA(j) = qdv;
			else {
				rot_trans_vect_mult (&t1, ROT(j), OMEGA(j-1));
				vect_add (OMEGA(j), &t1, &qdv);
			}

			/*
			 * calculate angular acceleration of link j 
			 */
			if (j == 0) 
				*OMEGADOT(j) = qddv;
			else {
				rot_trans_vect_mult (&t3, ROT(j), OMEGADOT(j-1));
				vect_cross (&t2, &t1, &qdv);
				vect_add (&t1, &t2, &t3);
				vect_add (OMEGADOT(j), &t1, &qddv);
			}

			/*
			 * compute acc[j]
			 */
			if (j == 0) {
				t1 = *robot->gravity;
			} else {
				vect_cross(&t1, OMEGA(j-1), PSTAR(j));
				vect_cross(&t2, OMEGA(j-1), &t1);
				vect_cross(&t1, OMEGADOT(j-1), PSTAR(j));
				vect_add(&t1, &t1, &t2);
				vect_add(&t1, &t1, ACC(j-1));
			}
			rot_trans_vect_mult(ACC(j), ROT(j), &t1);

			break;

		case PRISMATIC:
			/* 
			 * calculate omega[j]
			 */
			if (j == 0)
				*(OMEGA(j)) = qdv;
			else
				rot_trans_vect_mult (OMEGA(j), ROT(j), OMEGA(j-1));

			/*
			 * calculate alpha[j] 
			 */
			if (j == 0)
				*(OMEGADOT(j)) = qddv;
			else
				rot_trans_vect_mult (OMEGADOT(j), ROT(j), OMEGADOT(j-1));

			/*
			 * compute acc[j]
			 */
			if (j == 0) {
				*ACC(j) = *robot->gravity;
			} else {
				vect_cross(&t1, OMEGADOT(j-1), PSTAR(j));

				vect_cross(&t3, OMEGA(j-1), PSTAR(j));
				vect_cross(&t2, OMEGA(j-1), &t3);
				vect_add(&t1, &t1, &t2);
				vect_add(&t1, &t1, ACC(j-1));
				rot_trans_vect_mult(ACC(j), ROT(j), &t1);

				rot_trans_vect_mult(&t2, ROT(j), OMEGA(j-1));
				vect_cross(&t1, &t2, &qdv);
				scal_mult(&t1, &t1, 2.0);
				vect_add(ACC(j), ACC(j), &t1);

				vect_add(ACC(j), ACC(j), &qddv);
			}

			break;
		}

		/*
		 * compute abar[j]
		 */
		vect_cross(&t1, OMEGADOT(j), R_COG(j));
		vect_cross(&t2, OMEGA(j), R_COG(j));
		vect_cross(&t3, OMEGA(j), &t2);
		vect_add(ACC_COG(j), &t1, &t3);
		vect_add(ACC_COG(j), ACC_COG(j), ACC(j));

#ifdef	DEBUG
		vect_print("w", OMEGA(j));
		vect_print("wd", OMEGADOT(j));
		vect_print("acc", ACC(j));
		vect_print("abar", ACC_COG(j));
#endif
	    }
	} else {
	    /*
	     * STANDARD D&H CONVENTIONS
	     */
	    for (j = 0; j < robot->njoints; j++) {

		/* create angular vector from scalar input */
		qdv.z = qd[j*stride]; 
		qddv.z = qdd[j*stride];

		switch (links[j].sigma) {
		case REVOLUTE:
			/* 
			 * calculate omega[j]
			 */
			if (j == 0)
				t1 = qdv;
			else
				vect_add (&t1, OMEGA(j-1), &qdv);
			rot_trans_vect_mult (OMEGA(j), ROT(j), &t1);

			/*
			 * calculate alpha[j] 
			 */
			if (j == 0) 
				t3 = qddv;
			else {
				vect_add (&t1, OMEGADOT(j-1), &qddv);
				vect_cross (&t2, OMEGA(j-1), &qdv);
				vect_add (&t3, &t1, &t2);
			}
			rot_trans_vect_mult (OMEGADOT(j), ROT(j), &t3);

			/*
			 * compute acc[j]
			 */
			vect_cross(&t1, OMEGADOT(j), PSTAR(j));
			vect_cross(&t2, OMEGA(j), PSTAR(j));
			vect_cross(&t3, OMEGA(j), &t2);
			vect_add(ACC(j), &t1, &t3);
			if (j == 0) {
				rot_trans_vect_mult(&t1, ROT(j), robot->gravity);
			} else 
				rot_trans_vect_mult(&t1, ROT(j), ACC(j-1));
			vect_add(ACC(j), ACC(j), &t1);
			break;

		case PRISMATIC:
			/* 
			 * calculate omega[j]
			 */
			if (j == 0)
				*(OMEGA(j)) = zero;
			else
				rot_trans_vect_mult (OMEGA(j), ROT(j), OMEGA(j-1));

			/*
			 * calculate alpha[j] 
			 */
			if (j == 0)
				*(OMEGADOT(j)) = zero;
			else
				rot_trans_vect_mult (OMEGADOT(j), ROT(j), OMEGADOT(j-1));

			/*
			 * compute acc[j]
			 */
			if (j == 0) {
				vect_add(&qddv, &qddv, robot->gravity);
				rot_trans_vect_mult(ACC(j), ROT(j), &qddv);
			} else {
				vect_add(&t1, &qddv, ACC(j-1));
				rot_trans_vect_mult(ACC(j), ROT(j), &t1);

			}

			vect_cross(&t1, OMEGADOT(j), PSTAR(j));
			vect_add(ACC(j), ACC(j), &t1);

			rot_trans_vect_mult(&t1, ROT(j), &qdv);
			vect_cross(&t2, OMEGA(j), &t1);
			scal_mult(&t2, &t2, 2.0);
			vect_add(ACC(j), ACC(j), &t2);

			vect_cross(&t2, OMEGA(j), PSTAR(j));
			vect_cross(&t3, OMEGA(j), &t2);
			vect_add(ACC(j), ACC(j), &t3);
			break;
		}
		/*
		 * compute abar[j]
		 */
		vect_cross(&t1, OMEGADOT(j), R_COG(j));
		vect_cross(&t2, OMEGA(j), R_COG(j));
		vect_cross(&t3, OMEGA(j), &t2);
		vect_add(ACC_COG(j), &t1, &t3);
		vect_add(ACC_COG(j), ACC_COG(j), ACC(j));

#ifdef	DEBUG
		vect_print("w", OMEGA(j));
		vect_print("wd", OMEGADOT(j));
		vect_print("acc", ACC(j));
		vect_print("abar", ACC_COG(j));
#endif
	    }
	}

/******************************************************************************
 * backward recursion part --the kinetics
 ******************************************************************************/

	if (robot->dhtype == MODIFIED) {
	    /*
	     * MODIFIED D&H CONVENTIONS
	     */
	    for (j = robot->njoints - 1; j >= 0; j--) {

		/*
		 * compute F[j]
		 */
		scal_mult (&F, ACC_COG(j), M(j));

		/*
		 * compute f[j]
		 */
		if (j == (robot->njoints-1))
			t1 = f_tip;
		else
			rot_vect_mult (&t1, ROT(j+1), f(j+1));
		vect_add (f(j), &t1, &F);

		 /*
		  * compute N[j]
		  */
		mat_vect_mult(&t2, INERTIA(j), OMEGADOT(j));
		mat_vect_mult(&t3, INERTIA(j), OMEGA(j));
		vect_cross(&t4, OMEGA(j), &t3);
		vect_add(&N, &t2, &t4);

		 /*
		  * compute n[j]
		  */
		if (j == (robot->njoints-1))
			t1 = n_tip;
		else {
			rot_vect_mult(&t1, ROT(j+1), n(j+1));
			rot_vect_mult(&t4, ROT(j+1), f(j+1));
			vect_cross(&t3, PSTAR(j+1), &t4);

			vect_add(&t1, &t1, &t3);
		}

		vect_cross(&t2, R_COG(j), &F);
		vect_add(&t1, &t1, &t2);
		vect_add(n(j), &t1, &N);

#ifdef	DEBUG
		vect_print("f", f(j));
		vect_print("n", n(j));
#endif
	    }

	} else {
	    /*
	     * STANDARD D&H CONVENTIONS
	     */
	    for (j = robot->njoints - 1; j >= 0; j--) {

		/*
		 * compute f[j]
		 */
		scal_mult (&t4, ACC_COG(j), M(j));
		if (j != (robot->njoints-1)) {
			rot_vect_mult (&t1, ROT(j+1), f(j+1));
			vect_add (f(j), &t4, &t1);
		} else
			vect_add (f(j), &t4, &f_tip);

		 /*
		  * compute n[j]
		  */

			/* cross(pstar+r,Fm(:,j)) */
		vect_add(&t2, PSTAR(j), R_COG(j));
		vect_cross(&t1, &t2, &t4);

		if (j != (robot->njoints-1)) {
			/* cross(R'*pstar,f) */
			rot_trans_vect_mult(&t2, ROT(j+1), PSTAR(j));
			vect_cross(&t3, &t2, f(j+1));

			/* nn += R*(nn + cross(R'*pstar,f)) */
			vect_add(&t3, &t3, n(j+1));
			rot_vect_mult(&t2, ROT(j+1), &t3);
			vect_add(&t1, &t1, &t2);
		} else {
			/* cross(R'*pstar,f) */
			vect_cross(&t2, PSTAR(j), &f_tip);

			/* nn += R*(nn + cross(R'*pstar,f)) */
			vect_add(&t1, &t1, &t2);
			vect_add(&t1, &t1, &n_tip);
		}

		mat_vect_mult(&t2, INERTIA(j), OMEGADOT(j));
		mat_vect_mult(&t3, INERTIA(j), OMEGA(j));
		vect_cross(&t4, OMEGA(j), &t3);
		vect_add(&t2, &t2, &t4);

		vect_add(n(j), &t1, &t2);
#ifdef	DEBUG
		vect_print("f", f(j));
		vect_print("n", n(j));
#endif
	    }
	}

	/*
	 *  Compute the torque total for each axis
	 *
	 */
	for (j=0; j < robot->njoints; j++) {
		double	t;
		Link	*l = &links[j];

		if (robot->dhtype == MODIFIED)
			t1 = z0;
		else
			rot_trans_vect_mult(&t1, ROT(j), &z0);

		switch (l->sigma) {
		case REVOLUTE:
			t = vect_dot(n(j), &t1);
			break;
		case PRISMATIC:
			t = vect_dot(f(j), &t1);
			break;
		}

		/*
		 * add actuator dynamics and friction
		 */
		t +=   l->G * l->G * l->Jm * qdd[j*stride]; // inertia
        t += l->G * l->G * l->B * qd[j*stride];    // viscous friction
        t += fabs(l->G) * (
			(qd[j*stride] > 0 ? l->Tc[0] : 0.0) +    // Coulomb friction
			(qd[j*stride] < 0 ? l->Tc[1] : 0.0)
		);
		tau[j*stride] = t;
	}
}