- dyno view draft

This commit is contained in:
3er0.1ive 2025-02-06 23:08:53 +03:00 committed by rusefillc
parent d86ccf0b04
commit cda5297c0a
5 changed files with 180 additions and 172 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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];
}; };

View File

@ -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
} }
} }

View File

@ -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);
} }
*/