- dyno view draft
This commit is contained in:
parent
d86ccf0b04
commit
cda5297c0a
|
@ -722,7 +722,8 @@ void updateTunerStudioState() {
|
||||||
tsOutputChannels->gyroYaw = engine->sensors.accelerometer.yawRate;
|
tsOutputChannels->gyroYaw = engine->sensors.accelerometer.yawRate;
|
||||||
|
|
||||||
#if EFI_DYNO_VIEW
|
#if EFI_DYNO_VIEW
|
||||||
tsOutputChannels->VssAcceleration = getDynoviewAcceleration();
|
tsOutputChannels->hp = getDynoviewHP();
|
||||||
|
tsOutputChannels->torque = getDynoviewTorque();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tsOutputChannels->turboSpeed = Sensor::getOrZero(SensorType::TurbochargerSpeed);
|
tsOutputChannels->turboSpeed = Sensor::getOrZero(SensorType::TurbochargerSpeed);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* @file dynoview.cpp
|
* @file dynoview.cpp
|
||||||
*
|
*
|
||||||
* @date Nov 29, 2020
|
* @date Jan 05, 2025
|
||||||
* @author Alexandru Miculescu, (c) 2012-2020
|
* @author Alexey Ershov, (c) 2012-2025
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
@ -12,155 +12,139 @@
|
||||||
|
|
||||||
static DynoView dynoInstance;
|
static DynoView dynoInstance;
|
||||||
|
|
||||||
void DynoView::update(vssSrc src) {
|
DynoView::DynoView()
|
||||||
|
{
|
||||||
|
wheelOverallDiameterMm = (uint16_t)(config->dynoCarWheelDiaInch * 25.4 + config->dynoCarWheelTireWidthMm * config->dynoCarWheelAspectRatio * 0.01 * 2);
|
||||||
|
|
||||||
efitimeus_t timeNow, deltaTime = 0.0;
|
saeVaporPressure = 6.1078 * pow(10.0, (7.5 * config->dynoSaeTemperatureC) / (237.3 + config->dynoSaeTemperatureC)) * .02953 * (config->dynoSaeRelativeHumidity / 100.0);
|
||||||
float speed,deltaSpeed = 0.0;
|
saeBaroMmhg = 29.23 * (config->dynoSaeBaro / 100.0);
|
||||||
timeNow = getTimeNowUs();
|
saeBaroCorrectionFactor = 29.23 / (saeBaroMmhg - saeVaporPressure);
|
||||||
speed = Sensor::getOrZero(SensorType::VehicleSpeed);
|
saeTempCorrectionFactor = pow(((config->dynoSaeTemperatureC + 273.0) / 298.0), 0.5);
|
||||||
if (src == ICU) {
|
saeCorrectionFactor = 1.176 * (saeBaroCorrectionFactor * saeTempCorrectionFactor) - .176;
|
||||||
speed = efiRound(speed,1.0);
|
|
||||||
} else {
|
reset();
|
||||||
//use speed with 0.001 precision from source CAN
|
}
|
||||||
speed = efiRound(speed,0.001);
|
|
||||||
|
void DynoView::update()
|
||||||
|
{
|
||||||
|
float rpm = Sensor::getOrZero(SensorType::Rpm);
|
||||||
|
rpm = efiRound(rpm, 1.0);
|
||||||
|
int intRpm = (int)rpm;
|
||||||
|
|
||||||
|
float tps = Sensor::getOrZero(SensorType::Tps1);
|
||||||
|
|
||||||
|
if(intRpm > 0) {
|
||||||
|
efitimeus_t timeStamp = getTimeNowUs();
|
||||||
|
float timeInSec = (float)(timeStamp) / US_PER_SECOND;
|
||||||
|
|
||||||
|
onRpm(intRpm, timeInSec, tps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynoView::reset()
|
||||||
|
{
|
||||||
|
dynoViewPointPrev.rpm = -1;
|
||||||
|
dynoViewPointPrev.time = -1;
|
||||||
|
dynoViewPointPrev.tps = -1;
|
||||||
|
count = 0;
|
||||||
|
currentTorque = 0;
|
||||||
|
currentHP = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DynoView::onRpm(int rpm, float time, float tps)
|
||||||
|
{
|
||||||
|
if(tps < dynoViewPointPrev.tps || tps < 80) {
|
||||||
|
reset();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(timeStamp != 0) {
|
if (dynoViewPointPrev.rpm > 0 && dynoViewPointPrev.time > 0)
|
||||||
|
{
|
||||||
|
if(time < dynoViewPointPrev.time || rpm < dynoViewPointPrev.rpm)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (vss != speed) {
|
int rpmDiff = rpm - dynoViewPointPrev.rpm;
|
||||||
deltaTime = timeNow - timeStamp;
|
|
||||||
if (vss > speed) {
|
if (rpmDiff < config->dynoRpmStep)
|
||||||
deltaSpeed = (vss - speed);
|
{
|
||||||
direction = 1; //decceleration
|
return false;
|
||||||
} else {
|
}
|
||||||
deltaSpeed = speed - vss;
|
}
|
||||||
direction = 0; //acceleration
|
|
||||||
|
dynoViewPoint.rpm = rpm;
|
||||||
|
dynoViewPoint.time = time;
|
||||||
|
dynoViewPoint.tps = tps;
|
||||||
|
|
||||||
|
dynoViewPoint.engineRps = dynoViewPoint.rpm / 60.0;
|
||||||
|
dynoViewPoint.axleRps = dynoViewPoint.engineRps / (config->dynoCarGearPrimaryEduction * config->dynoCarGearRatio * config->dynoCarGearFinalDrive);
|
||||||
|
dynoViewPoint.vMs = dynoViewPoint.axleRps * (wheelOverallDiameterMm / 1000.f) * 3.1416;
|
||||||
|
dynoViewPoint.mph = dynoViewPoint.vMs * 2.2369363;
|
||||||
|
|
||||||
|
if (dynoViewPointPrev.rpm > 0 && dynoViewPointPrev.time > 0)
|
||||||
|
{
|
||||||
|
dynoViewPoint.distanceM = ((dynoViewPoint.vMs + dynoViewPointPrev.vMs) / 2.0) * (dynoViewPoint.time - dynoViewPointPrev.time);
|
||||||
|
dynoViewPoint.aMs2 = (dynoViewPoint.vMs - dynoViewPointPrev.vMs) / (dynoViewPoint.time - dynoViewPointPrev.time);
|
||||||
|
dynoViewPoint.forceN = (config->dynoCarCargoMassKg + config->dynoCarCarMassKg) * dynoViewPoint.aMs2;
|
||||||
|
dynoViewPoint.forceDragN = 0.5 * airDensityKgM3 * (dynoViewPoint.vMs * dynoViewPoint.vMs) * config->dynoCarFrontalAreaM2 * config->dynoCarCoeffOfDrag;
|
||||||
|
|
||||||
|
dynoViewPoint.forceDragN = dynoViewPoint.forceDragN * saeCorrectionFactor;
|
||||||
|
|
||||||
|
dynoViewPoint.forceTotalN = dynoViewPoint.forceN + dynoViewPoint.forceDragN;
|
||||||
|
dynoViewPoint.torqueWheelNm = dynoViewPoint.forceTotalN * ((wheelOverallDiameterMm / 2.0) / 1000.0);
|
||||||
|
dynoViewPoint.torqueNm = dynoViewPoint.torqueWheelNm / (config->dynoCarGearPrimaryEduction * config->dynoCarGearRatio * config->dynoCarGearFinalDrive);
|
||||||
|
dynoViewPoint.torqueLbFt = dynoViewPoint.torqueNm * 0.737562;
|
||||||
|
dynoViewPoint.hp = dynoViewPoint.torqueLbFt * dynoViewPoint.rpm / 5252.0;
|
||||||
|
|
||||||
|
for(int i = 0; i < window_size-1; ++i)
|
||||||
|
{
|
||||||
|
memcpy(&tail_hp[i], &tail_hp[i + 1], sizeof(float));
|
||||||
|
memcpy(&tail_torque[i], &tail_torque[i + 1], sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
tail_torque[window_size-1] = dynoViewPoint.torqueNm;
|
||||||
|
tail_hp[window_size-1] = dynoViewPoint.hp;
|
||||||
|
|
||||||
|
if(count == 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < window_size-1; ++i) {
|
||||||
|
memcpy(&tail_hp[i], &tail_hp[window_size-1], sizeof(float));
|
||||||
|
memcpy(&tail_torque[i], &tail_torque[window_size-1], sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
//save data
|
|
||||||
timeStamp = timeNow;
|
|
||||||
vss = speed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//updating here would display acceleration = 0 at constant speed
|
int accumulate_window_size = std::min(count, window_size);
|
||||||
updateAcceleration(deltaTime, deltaSpeed);
|
|
||||||
#if EFI_TUNER_STUDIO
|
|
||||||
if (engineConfiguration->debugMode == DBG_LOGIC_ANALYZER) {
|
|
||||||
engine->outputChannels.debugIntField1 = deltaTime;
|
|
||||||
engine->outputChannels.debugFloatField1 = vss;
|
|
||||||
engine->outputChannels.debugFloatField2 = speed;
|
|
||||||
engine->outputChannels.debugFloatField3 = deltaSpeed;
|
|
||||||
engine->outputChannels.debugFloatField4 = acceleration;
|
|
||||||
}
|
|
||||||
#endif /* EFI_TUNER_STUDIO */
|
|
||||||
updateHP();
|
|
||||||
|
|
||||||
} else {
|
currentTorque = accumulate_window(accumulate_window_size, tail_torque);
|
||||||
//ensure we grab init values
|
currentHP = accumulate_window(accumulate_window_size, tail_hp);
|
||||||
timeStamp = timeNow;
|
|
||||||
vss = speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if(count < window_size) {
|
||||||
* input units: deltaSpeed in km/h
|
++count;
|
||||||
* deltaTime in uS
|
|
||||||
*/
|
|
||||||
void DynoView::updateAcceleration(efitimeus_t deltaTime, float deltaSpeed) {
|
|
||||||
// todo: explain why do we compare float with zero without threshold?
|
|
||||||
if (deltaSpeed != 0.0) {
|
|
||||||
acceleration = ((deltaSpeed / 3.6) / (deltaTime / US_PER_SECOND_F));
|
|
||||||
if (direction) {
|
|
||||||
//decceleration
|
|
||||||
acceleration *= -1;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
acceleration = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
dynoViewPointPrev = dynoViewPoint;
|
||||||
* E = m*a
|
return true;
|
||||||
* ex. 900 (kg) * 1.5 (m/s^2) = 1350N
|
|
||||||
* P = F*V
|
|
||||||
* 1350N * 35(m/s) = 47250Watt (35 m/s is the final velocity)
|
|
||||||
* 47250 * (1HP/746W) = 63HP
|
|
||||||
* https://www.youtube.com/watch?v=FnN2asvFmIs
|
|
||||||
* we do not take resistence into account right now.
|
|
||||||
*/
|
|
||||||
void DynoView::updateHP() {
|
|
||||||
|
|
||||||
//these are actually at the wheel
|
|
||||||
//we would need final drive to calcualte the correct torque at the wheel
|
|
||||||
if (acceleration != 0) {
|
|
||||||
engineForce = engineConfiguration->vehicleWeight * acceleration;
|
|
||||||
enginePower = engineForce * (vss / 3.6);
|
|
||||||
engineHP = enginePower / 746;
|
|
||||||
if (Sensor::getOrZero(SensorType::Rpm) > 0) {
|
|
||||||
engineTorque = ((engineHP * 5252) / Sensor::getOrZero(SensorType::Rpm));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//we should calculate static power
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dynoViewPointPrev = dynoViewPoint;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if EFI_UNIT_TEST
|
int getDynoviewHP() {
|
||||||
void DynoView::setAcceleration(float a) {
|
return dynoInstance.currentHP;
|
||||||
acceleration = a;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float DynoView::getAcceleration() {
|
|
||||||
return acceleration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int DynoView::getEngineForce() {
|
int getDynoviewTorque() {
|
||||||
return engineForce;
|
return dynoInstance.currentTorque;
|
||||||
}
|
|
||||||
|
|
||||||
int DynoView::getEnginePower() {
|
|
||||||
return (enginePower/1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DynoView::getEngineHP() {
|
|
||||||
return engineHP;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DynoView::getEngineTorque() {
|
|
||||||
return (engineTorque/0.73756);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float getDynoviewAcceleration() {
|
|
||||||
return dynoInstance.getAcceleration();
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDynoviewPower() {
|
|
||||||
return dynoInstance.getEnginePower();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodic update function called from SlowCallback.
|
* Periodic update function called from SlowCallback.
|
||||||
* Only updates if we have Vss from input pin.
|
|
||||||
*/
|
*/
|
||||||
void updateDynoView() {
|
void updateDynoView() {
|
||||||
if (isBrainPinValid(engineConfiguration->vehicleSpeedSensorInputPin) &&
|
dynoInstance.update();
|
||||||
(!engineConfiguration->enableCanVss)) {
|
|
||||||
dynoInstance.update(ICU);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called after every CAN msg received, we process it
|
|
||||||
* as soon as we can to be more acurate.
|
|
||||||
*/
|
|
||||||
void updateDynoViewCan() {
|
|
||||||
if (!engineConfiguration->enableCanVss) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dynoInstance.update(CAN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* EFI_DYNO_VIEW */
|
#endif /* EFI_DYNO_VIEW */
|
||||||
|
|
|
@ -1,54 +1,78 @@
|
||||||
/*
|
/*
|
||||||
* @file dynoview.h
|
* @file dynoview.h
|
||||||
*
|
*
|
||||||
* @date Nov 29, 2020
|
* @date Jan 05, 2025
|
||||||
* @author Alexandru Miculescu, (c) 2012-2020
|
* @author Alexey Ershov, (c) 2012-2025
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
void updateDynoView();
|
void updateDynoView();
|
||||||
void updateDynoViewCan();
|
int getDynoviewHP();
|
||||||
float getDynoviewAcceleration();
|
int getDynoviewTorque();
|
||||||
int getDynoviewPower();
|
|
||||||
|
|
||||||
typedef enum{
|
struct DynoPoint {
|
||||||
ICU = 0,
|
int rpm;
|
||||||
CAN,
|
float time;
|
||||||
}vssSrc;
|
float tps;
|
||||||
|
|
||||||
|
float engineRps;
|
||||||
|
float axleRps;
|
||||||
|
float vMs;
|
||||||
|
float mph;
|
||||||
|
float distanceM;
|
||||||
|
float aMs2;
|
||||||
|
float forceN;
|
||||||
|
float forceDragN;
|
||||||
|
float forceTotalN;
|
||||||
|
float torqueWheelNm;
|
||||||
|
float torqueNm;
|
||||||
|
float torqueLbFt;
|
||||||
|
float hp;
|
||||||
|
};
|
||||||
|
|
||||||
class DynoView {
|
class DynoView {
|
||||||
public:
|
public:
|
||||||
// Update the state of the launch control system
|
|
||||||
void update(vssSrc src);
|
|
||||||
void updateAcceleration(efitimeus_t deltaTime, float deltaSpeed);
|
|
||||||
void updateHP();
|
|
||||||
float getAcceleration();
|
|
||||||
int getEngineForce();
|
|
||||||
//in KW
|
|
||||||
int getEnginePower();
|
|
||||||
|
|
||||||
int getEngineHP();
|
DynoView();
|
||||||
//in NM
|
void update();
|
||||||
int getEngineTorque();
|
bool onRpm(int rpm, float time, float tps);
|
||||||
#if EFI_UNIT_TEST
|
|
||||||
void setAcceleration(float a);
|
float currentTorque;
|
||||||
#endif
|
float currentHP;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
efitimeus_t timeStamp = 0;
|
|
||||||
//km/h unit
|
void reset();
|
||||||
float vss = 0;
|
|
||||||
//m/s/s unit
|
static inline float accumulate_window(int size, const float* data)
|
||||||
float acceleration = 0;
|
{
|
||||||
//engine force in N
|
float sum = 0.0;
|
||||||
int engineForce;
|
|
||||||
//engine power in W
|
for(int i = 0; i < size; ++i) {
|
||||||
int enginePower;
|
sum += data[size - i - 1];
|
||||||
//engine powerin HP
|
}
|
||||||
int engineHP;
|
|
||||||
//Torque in lb-ft
|
return sum / window_size;
|
||||||
int engineTorque;
|
}
|
||||||
//sign
|
|
||||||
uint8_t direction;
|
float airDensityKgM3 = 1.225; // 15C
|
||||||
|
uint16_t wheelOverallDiameterMm = 0;
|
||||||
|
|
||||||
|
// SAE corrections
|
||||||
|
float saeBaroCorrectionFactor;
|
||||||
|
float saeBaroMmhg;
|
||||||
|
float saeTempCorrectionFactor;
|
||||||
|
float saeVaporPressure;
|
||||||
|
float saeCorrectionFactor;
|
||||||
|
|
||||||
|
DynoPoint dynoViewPoint;
|
||||||
|
DynoPoint dynoViewPointPrev;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
static constexpr int window_size = 7;
|
||||||
|
|
||||||
|
float tail_hp[window_size];
|
||||||
|
float tail_torque[window_size];
|
||||||
};
|
};
|
|
@ -183,10 +183,6 @@ void processCanRxVss(const CANRxFrame& frame, efitick_t nowNt) {
|
||||||
if (CAN_SID(frame) == filterVssCanID) {
|
if (CAN_SID(frame) == filterVssCanID) {
|
||||||
if (auto speed = processCanRxVssImpl(frame, nowNt)) {
|
if (auto speed = processCanRxVssImpl(frame, nowNt)) {
|
||||||
canSpeed.setValidValue(speed.Value * engineConfiguration->canVssScaling, nowNt);
|
canSpeed.setValidValue(speed.Value * engineConfiguration->canVssScaling, nowNt);
|
||||||
|
|
||||||
#if EFI_DYNO_VIEW
|
|
||||||
updateDynoViewCan();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
/*
|
||||||
#include "dynoview.h"
|
#include "dynoview.h"
|
||||||
|
|
||||||
|
|
||||||
void printResults(DynoView *dut) {
|
void printResults(DynoView *dut) {
|
||||||
#ifdef DBG_TESTS
|
#ifdef DBG_TESTS
|
||||||
std::cerr.precision(32);
|
std::cerr.precision(32);
|
||||||
|
@ -103,4 +105,5 @@ TEST(DynoView, VSS_Torque) {
|
||||||
ASSERT_EQ(242, dut.getEngineTorque());
|
ASSERT_EQ(242, dut.getEngineTorque());
|
||||||
printResults(&dut);
|
printResults(&dut);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue