speed-dreams/src/modules/simu/simuv4/engine.cpp

387 lines
11 KiB
C++

/***************************************************************************
file : engine.cpp
created : Sun Mar 19 00:06:55 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id: engine.cpp 4786 2012-07-01 18:26:31Z kakukri $
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "sim.h"
void
SimEngineConfig(tCar *car)
{
void *hdle = car->params;
int i;
tdble maxTq;
tdble rpmMaxTq = 0;
char idx[64];
tEngineCurveElem *data;
tCarSetupItem *setupRevLimit = &(car->carElt->setup.revsLimiter);
struct tEdesc
{
tdble rpm;
tdble tq;
} *edesc;
setupRevLimit->desired_value = setupRevLimit->min = setupRevLimit->max = 800;
GfParmGetNumWithLimits(hdle, SECT_ENGINE, PRM_REVSLIM, (char*)NULL, &(setupRevLimit->desired_value), &(setupRevLimit->min), &(setupRevLimit->max));
setupRevLimit->changed = true;
setupRevLimit->stepsize = (tdble) RPM2RADS(100.0);
car->engine.revsLimiter = setupRevLimit->desired_value;
car->carElt->_enginerpmRedLine = car->engine.revsLimiter;
car->engine.revsMax = GfParmGetNum(hdle, SECT_ENGINE, PRM_REVSMAX, (char*)NULL, 1000);
car->carElt->_enginerpmMax = car->engine.revsMax;
car->engine.tickover = GfParmGetNum(hdle, SECT_ENGINE, PRM_TICKOVER, (char*)NULL, 150);
car->engine.I = GfParmGetNum(hdle, SECT_ENGINE, PRM_INERTIA, (char*)NULL, 0.2423f);
car->engine.fuelcons = GfParmGetNum(hdle, SECT_ENGINE, PRM_FUELCONS, (char*)NULL, 0.0622f);
car->engine.brakeCoeff = GfParmGetNum(hdle, SECT_ENGINE, PRM_ENGBRKCOEFF, (char*)NULL, 0.03f);
car->engine.brakeLinCoeff= GfParmGetNum(hdle, SECT_ENGINE, PRM_ENGBRKLINCOEFF, (char*)NULL, 0.03f);
car->engine.exhaust_pressure = 0.0f;
car->engine.exhaust_refract = 0.1f;
car->engine.Tq_response = 0.0f;
car->engine.I_joint = car->engine.I;
car->engine.timeInLimiter = 0.0f;
// Option TCL ...
if (car->features & FEAT_TCLINSIMU)
{
car->engine.TCL = 1.0f;
car->engine.EnableTCL = GfParmGetNum(hdle, SECT_ENGINE, PRM_TCLINSIMU, (char*)NULL, 0.0f) > 0;
/*
if (car->engine.EnableTCL)
fprintf(stderr,"TCL: Enabled\n");
else
fprintf(stderr,"TCL: Disabled\n");
*/
}
// ... Option TCL
sprintf(idx, "%s/%s", SECT_ENGINE, ARR_DATAPTS);
car->engine.curve.nbPts = GfParmGetEltNb(hdle, idx);
edesc = (struct tEdesc*)malloc((car->engine.curve.nbPts + 1) * sizeof(struct tEdesc));
for (i = 0; i < car->engine.curve.nbPts; i++)
{
sprintf(idx, "%s/%s/%d", SECT_ENGINE, ARR_DATAPTS, i+1);
edesc[i].rpm = GfParmGetNum(hdle, idx, PRM_RPM, (char*)NULL, car->engine.revsMax);
edesc[i].tq = GfParmGetNum(hdle, idx, PRM_TQ, (char*)NULL, 0);
}
if (i > 0)
{
edesc[i].rpm = edesc[i - 1].rpm;
edesc[i].tq = edesc[i - 1].tq;
}
maxTq = 0;
car->engine.curve.maxPw = 0;
car->engine.curve.data = (tEngineCurveElem *)malloc(car->engine.curve.nbPts * sizeof(tEngineCurveElem));
for(i = 0; i < car->engine.curve.nbPts; i++)
{
data = &(car->engine.curve.data[i]);
data->rads = edesc[i+1].rpm;
if ((data->rads>=car->engine.tickover)
&& (edesc[i+1].tq > maxTq)
&& (data->rads < car->engine.revsLimiter))
{
maxTq = edesc[i+1].tq;
rpmMaxTq = data->rads;
}
if ((data->rads>=car->engine.tickover)
&& (data->rads * edesc[i+1].tq > car->engine.curve.maxPw)
&& (data->rads < car->engine.revsLimiter))
{
car->engine.curve.TqAtMaxPw = edesc[i+1].tq;
car->engine.curve.maxPw = data->rads * edesc[i+1].tq;
car->engine.curve.rpmMaxPw = data->rads;
}
data->a = (edesc[i+1].tq - edesc[i].tq) / (edesc[i+1].rpm - edesc[i].rpm);
data->b = edesc[i].tq - data->a * edesc[i].rpm;
}
car->engine.curve.maxTq = maxTq;
car->carElt->_engineMaxTq = maxTq;
car->carElt->_enginerpmMaxTq = rpmMaxTq;
car->carElt->_engineMaxPw = car->engine.curve.maxPw;
car->carElt->_enginerpmMaxPw = car->engine.curve.rpmMaxPw;
car->engine.rads = car->engine.tickover;
free(edesc);
/* check engine brake */
if ( car->engine.brakeCoeff < 0.0 )
{
car->engine.brakeCoeff = 0.0;
}
car->engine.brakeCoeff *= maxTq;
/*sanity check of rev limits*/
if (car->engine.curve.nbPts > 0 && car->engine.revsMax > car->engine.curve.data[car->engine.curve.nbPts-1].rads)
{
car->engine.revsMax = car->engine.curve.data[car->engine.curve.nbPts-1].rads;
GfLogWarning("Revs maxi bigger than the maximum RPM in the curve data.\nIt is set to %g.\n",car->engine.revsMax);
}
if (car->engine.revsLimiter > car->engine.revsMax)
{
car->engine.revsLimiter = car->engine.revsMax;
GfLogWarning("Revs limiter is bigger than revs maxi.\nIt is set to %g.\n",car->engine.revsLimiter);
}
if (setupRevLimit->max > car->engine.revsMax)
{
setupRevLimit->max = car->engine.revsMax;
if (setupRevLimit->min > setupRevLimit->max)
{
setupRevLimit->min = setupRevLimit->max;
}
}
}
void
SimEngineReConfig(tCar *car)
{/* called by SimCarReConfig in car.cpp */
tCarSetupItem *setupRevLimit = &(car->carElt->setup.revsLimiter);
if (setupRevLimit->changed)
{
car->engine.revsLimiter = MIN(setupRevLimit->max, MAX(setupRevLimit->min, setupRevLimit->desired_value));
car->carElt->_enginerpmRedLine = car->engine.revsLimiter;
setupRevLimit->value = car->engine.revsLimiter;
setupRevLimit->changed = FALSE;
}
}
/* Update torque output with engine rpm and accelerator command */
void
SimEngineUpdateTq(tCar *car)
{
int i;
tEngine *engine = &(car->engine);
tEngineCurve *curve = &(engine->curve);
tTransmission *trans = &(car->transmission);
tClutch *clutch = &(trans->clutch);
if ((car->fuel <= 0.0f) || (car->carElt->_state & (RM_CAR_STATE_BROKEN | RM_CAR_STATE_ELIMINATED)))
{
engine->rads = 0;
engine->Tq = 0;
return;
}
// set clutch on when engine revs too low
if (engine->rads < engine->tickover)
{
clutch->state = CLUTCH_APPLIED;
clutch->transferValue = 0.0f;
// engine->rads = engine->tickover;
}
engine->rads = MIN(engine->rads, engine->revsMax);
tdble EngBrkK = engine->brakeLinCoeff * engine->rads;
if ( (engine->rads < engine->tickover) ||
( (engine->rads == engine->tickover) && (car->ctrl->accelCmd <= 1e-6) ) )
{
engine->Tq = 0.0f;
engine->rads = engine->tickover;
}
else
{
tdble Tq_max = 0.0;
for (i = 0; i < car->engine.curve.nbPts; i++)
{
if (engine->rads < curve->data[i].rads)
{
Tq_max = engine->rads * curve->data[i].a + curve->data[i].b;
break;
}
}
tdble alpha = car->ctrl->accelCmd;
if (engine->rads > engine->revsLimiter)
{
alpha = 0.0;
if (car->features & FEAT_REVLIMIT)
{
engine->timeInLimiter = 0.1f;
}
}
// Option TCL ...
if (car->features & FEAT_TCLINSIMU)
{
if (engine->EnableTCL)
Tq_max *= (tdble) MAX(0.0,MIN(1.0, engine->TCL));
/*
if (engine->EnableTCL)
fprintf(stderr,"TCL: %.1f %%\n", engine->TCL * 100);
*/
}
// ... Option TCL
if ( (car->features & FEAT_REVLIMIT) && (engine->timeInLimiter > 0.0f) )
{
alpha = 0.0;
engine->timeInLimiter -= SimDeltaTime;
}
tdble Tq_cur = (Tq_max + EngBrkK) * alpha;
engine->Tq = Tq_cur;
engine->Tq -= EngBrkK;
if (alpha <= 1e-6)
{
engine->Tq -= engine->brakeCoeff;
}
tdble cons = Tq_cur * 0.75f;
if (cons > 0)
{
car->fuel -= (tdble) (cons * engine->rads * engine->fuelcons * 0.0000001 * SimDeltaTime);
}
car->fuel = (tdble) MAX(car->fuel, 0.0);
}
}
/*
* Function
* SimEngineUpdateRpm
*
* Description
* update engine RPM with wheels RPM
*
* Parameters
* car and axle RPM
*
* Return
* axle rpm for wheel update
* 0 if no wheel update
*/
tdble
SimEngineUpdateRpm(tCar *car, tdble axleRpm)
{
tTransmission *trans = &(car->transmission);
tClutch *clutch = &(trans->clutch);
tEngine *engine = &(car->engine);
float freerads;
float transfer;
if (car->fuel <= 0.0)
{
engine->rads = 0;
clutch->state = CLUTCH_APPLIED;
clutch->transferValue = 0.0;
return 0.0;
}
freerads = engine->rads;
freerads += engine->Tq / engine->I * SimDeltaTime;
{
tdble dp = engine->pressure;
engine->pressure = engine->pressure*0.9f + 0.1f*engine->Tq;
dp = (0.001f*fabs(engine->pressure - dp));
dp = fabs(dp);
tdble rth = urandom();
if (dp > rth)
{
engine->exhaust_pressure += rth;
}
engine->exhaust_pressure *= 0.9f;
car->carElt->priv.smoke += 5.0f*engine->exhaust_pressure;
car->carElt->priv.smoke *= 0.99f;
}
// This is a method for the joint torque that the engine experiences
// to be changed smoothly and not instantaneously.
// The closest alpha is to 1, the faster the transition is.
transfer = 0.0;
float ttq = 0.0;
float I_response = trans->differential[0].feedBack.I + trans->differential[1].feedBack.I;
engine->Tq_response = 0.0;
tdble dI = fabs(trans->curI - engine->I_joint);
tdble sdI = dI;
// limit the difference to avoid model instability
if (sdI>1.0)
{
sdI = 1.0;
}
float alpha = 0.1f; // transition coefficient
engine->I_joint = (tdble) (engine->I_joint*(1.0-alpha) + alpha*trans->curI);
// only use these values when the clutch is engaged or the gear
// has changed.
if ((clutch->transferValue > 0.01) && (trans->gearbox.gear))
{
transfer = clutch->transferValue * clutch->transferValue * clutch->transferValue * clutch->transferValue;
ttq = (float) (dI* tanh(0.01*(axleRpm * trans->curOverallRatio * transfer + freerads * (1.0-transfer) -engine->rads))*100.0);
engine->rads = (tdble) ((1.0-sdI) * (axleRpm * trans->curOverallRatio * transfer + freerads * (1.0-transfer)) + sdI *(engine->rads + ((ttq)*SimDeltaTime)/(engine->I)));
if (engine->rads < 0.0)
{
engine->rads = 0;
engine->Tq = 0.0;
}
}
else
{
engine->rads = freerads;
}
if (engine->rads < engine->tickover)
{
engine->rads = engine->tickover;
engine->Tq = 0.0;
}
else if (engine->rads > engine->revsMax)
{
engine->rads = engine->revsMax;
if ( (clutch->transferValue > 0.01) &&
((trans->curOverallRatio > 0.01) || (trans->curOverallRatio < -0.01)) )
return engine->revsMax / trans->curOverallRatio;
else
{
return 0.0;
}
}
if ((trans->curOverallRatio!=0.0) && (I_response > 0))
{
return axleRpm - sdI * ttq * trans->curOverallRatio * SimDeltaTime / ( I_response);
}
else
{
return 0.0;
}
}
void
SimEngineShutdown(tCar *car)
{
free(car->engine.curve.data);
}