Skip to content

Commit ac79b9a

Browse files
committed
fix: speed, rpm, odometer and fuel calculations adjusted
1 parent caf46d6 commit ac79b9a

1 file changed

Lines changed: 126 additions & 66 deletions

File tree

src/server.h

Lines changed: 126 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void load_ecu_def(const ECUDef* ecu_progmem, ECUDef& ecu_ram)
114114
}
115115

116116
// Measurement value encoding formulas (VCDS/KWP1281 standard):
117-
// 0x01: rpm = A * B * 0.2 (A=40, B=rpm/8)
117+
// 0x01: rpm = A * B * 0.2 (A=160, B=rpm/32; max 8160 RPM)
118118
// 0x02: % = A * B * 0.002 (placeholder for fuel trim; negative not representable)
119119
// 0x04: - = A * |B-127| * 0.01 (K4; A=100 for fuel level: |B-127| = liters)
120120
// OR A * B * 0.001 (raw form used for some sensors, e.g. lateral accel)
@@ -484,14 +484,34 @@ static const ECUDef ECU_TABLE[] PROGMEM = {
484484
ECUDef current_ecu; // loaded from PROGMEM on connect
485485
uint8_t current_addr = 0x00; // decoded from 5-baud init
486486

487+
// VW 02J gearbox — Golf 4 1.6 16V, 195/65 R15 (circumference = 1.992 m)
488+
// km/h per RPM = (0.001992 * 60) / (gear_ratio * 4.238)
489+
static const float KMH_PER_RPM[6] = {
490+
0.0f, // index 0 unused
491+
0.007468f, // gear 1 (ratio 3.778)
492+
0.013314f, // gear 2 (ratio 2.118)
493+
0.020737f, // gear 3 (ratio 1.360)
494+
0.029045f, // gear 4 (ratio 0.971)
495+
0.037300f, // gear 5 (ratio 0.756)
496+
};
497+
// Upshift speed thresholds (km/h): index i = shift from gear i+1 → i+2
498+
static const float UPSHIFT_KMH[4] = {26.0f, 48.0f, 70.0f, 90.0f};
499+
500+
// Drive cycle: paired (time_s, speed_kmh) waypoints, linearly interpolated.
501+
// Narrative: idle → accel through gears → cruise at 120 → step-down decel → idle → repeat.
502+
static const uint8_t DRIVE_CYCLE_T[] = {0, 3, 8, 12, 18, 24, 28, 34, 40, 46, 52, 56, 62,
503+
68, 72, 78, 84, 90, 96, 100, 106, 112, 116, 120, 125, 130};
504+
static const uint8_t DRIVE_CYCLE_V[] = {0, 0, 30, 30, 50, 50, 35, 35, 60, 60, 90, 90, 70,
505+
70, 100, 100, 120, 120, 90, 90, 60, 60, 30, 30, 0, 0};
506+
#define DRIVE_CYCLE_POINTS 26
507+
static const uint32_t DRIVE_CYCLE_PERIOD_MS = 130000UL;
508+
487509
struct SimState
488510
{
489511
unsigned long start_ms;
490-
unsigned long last_dist_ms; // legacy, unused
491-
float distance_km; // accumulated travel distance since connect
492-
float fuel_L; // current fuel level, decreases with distance
493-
unsigned long last_odo_update_ms; // last odometer increment
494-
unsigned long last_fuel_update_ms; // last fuel decrement
512+
float distance_km; // accumulated travel distance since connect
513+
float fuel_L; // current fuel level, decreases with distance
514+
unsigned long last_update_ms; // timestamp for dt-based odometer/fuel integration
495515
} sim_state;
496516

497517
bool awake = false;
@@ -641,16 +661,40 @@ bool KWP_send_syncbytes()
641661
float get_simulated_speed_kmh()
642662
{
643663
unsigned long elapsed_ms = millis() - sim_state.start_ms;
664+
uint32_t t_ms = (uint32_t)(elapsed_ms % DRIVE_CYCLE_PERIOD_MS);
665+
uint16_t t_s_whole = (uint16_t)(t_ms / 1000UL);
666+
float frac = (float)(t_ms % 1000UL) * 0.001f;
644667

645-
// Short idle: first 2s at 0 km/h
646-
if (elapsed_ms < 2000UL)
647-
return 0.0f;
668+
uint8_t i = 0;
669+
for (uint8_t k = 0; k < DRIVE_CYCLE_POINTS - 1; k++)
670+
{
671+
if (DRIVE_CYCLE_T[k + 1] <= t_s_whole)
672+
i = k + 1;
673+
else
674+
break;
675+
}
676+
uint8_t next = (i + 1 < DRIVE_CYCLE_POINTS) ? i + 1 : i;
677+
if (next == i)
678+
return (float)DRIVE_CYCLE_V[i];
679+
680+
float dt_seg = (float)(DRIVE_CYCLE_T[next] - DRIVE_CYCLE_T[i]);
681+
float t_in = (float)(t_s_whole - DRIVE_CYCLE_T[i]) + frac;
682+
float alpha = t_in / dt_seg;
683+
return (float)DRIVE_CYCLE_V[i] +
684+
alpha * (float)((int)DRIVE_CYCLE_V[next] - (int)DRIVE_CYCLE_V[i]);
685+
}
648686

649-
// Sine-wave 0-120 km/h, 30s period (ms resolution so polls see changes)
650-
float phase = (float)(elapsed_ms % 30000UL) / 30000.0f; // 0..1
651-
float angle = phase * 6.283185f; // 2π
652-
float speed = (sinf(angle) + 1.0f) * 60.0f; // 0..120
653-
return speed;
687+
static uint8_t get_current_gear(float speed_kmh)
688+
{
689+
uint8_t gear = 1;
690+
for (uint8_t g = 0; g < 4; g++)
691+
{
692+
if (speed_kmh >= UPSHIFT_KMH[g])
693+
gear = g + 2;
694+
else
695+
break;
696+
}
697+
return gear;
654698
}
655699

656700
int8_t get_simulated_coolant_temp()
@@ -670,11 +714,15 @@ int8_t get_simulated_oil_temp()
670714
uint16_t get_simulated_rpm()
671715
{
672716
float speed = get_simulated_speed_kmh();
673-
if (speed < 5.0f)
674-
return 800; // idle
675-
// RPM ~= (speed/30 + 0.8) * 1000, peaks ~4800 at 120 km/h
676-
uint16_t rpm = (uint16_t)((speed / 30.0f + 0.8f) * 1000.0f);
677-
return rpm;
717+
if (speed < 2.0f)
718+
return 800;
719+
uint8_t gear = get_current_gear(speed);
720+
float rpm_f = speed / KMH_PER_RPM[gear];
721+
if (rpm_f < 800.0f)
722+
rpm_f = 800.0f;
723+
if (rpm_f > 6500.0f)
724+
rpm_f = 6500.0f;
725+
return (uint16_t)rpm_f;
678726
}
679727

680728
bool KWP_send_group_reading(uint8_t group)
@@ -709,8 +757,8 @@ bool KWP_send_group_reading(uint8_t group)
709757
buf[4] = 100;
710758
buf[5] = (uint8_t)speed; // km/h
711759
buf[6] = 0x01;
712-
buf[7] = 40;
713-
buf[8] = (uint8_t)(rpm / 8); // RPM
760+
buf[7] = 160;
761+
buf[8] = (uint8_t)(rpm / 32); // RPM
714762
// k=0x25 (37): F_B formula — stored value = b directly.
715763
// Normal: b=31. Fault sim: b=222 for 3 s every 15 s, starting after the first 15 s.
716764
buf[9] = 0x25;
@@ -726,28 +774,42 @@ bool KWP_send_group_reading(uint8_t group)
726774
}
727775
else if (current_addr == 0x17 && group == 2)
728776
{
729-
// Deterministic: +1 km every 3s (max 444444), -1L fuel every 10s (min 0)
730777
unsigned long now_ms = millis();
731-
// Odometer logic
732-
if ((uint32_t)sim_state.distance_km < 444444 &&
733-
(now_ms - sim_state.last_odo_update_ms) >= 3000)
778+
float dt_sec = (float)(now_ms - sim_state.last_update_ms) * 0.001f;
779+
if (dt_sec > 10.0f)
780+
dt_sec = 0.0f; // guard: first call or stale state after reset
781+
sim_state.last_update_ms = now_ms;
782+
783+
float speed = get_simulated_speed_kmh();
784+
785+
// Odometer: integrate speed over time
786+
if (sim_state.distance_km < 444444.0f)
734787
{
735-
sim_state.distance_km += 1.0f;
736-
sim_state.last_odo_update_ms +=
737-
3000 * ((now_ms - sim_state.last_odo_update_ms) / 3000); // catch up if polled late
788+
sim_state.distance_km += speed * dt_sec / 3600.0f;
789+
if (sim_state.distance_km > 444444.0f)
790+
sim_state.distance_km = 444444.0f;
738791
}
739-
if ((uint32_t)sim_state.distance_km > 444444)
740-
sim_state.distance_km = 444444;
741792

742-
// Fuel logic
743-
if ((int)sim_state.fuel_L > 0 && (now_ms - sim_state.last_fuel_update_ms) >= 10000)
793+
// Fuel: physics-based consumption
794+
if (sim_state.fuel_L > 0.0f)
744795
{
745-
sim_state.fuel_L -= 1.0f;
746-
sim_state.last_fuel_update_ms += 10000 * ((now_ms - sim_state.last_fuel_update_ms) /
747-
10000); // catch up if polled late
796+
float fuel_rate;
797+
if (speed < 2.0f)
798+
{
799+
fuel_rate = 0.6f / 3600.0f; // idle ~0.6 L/h
800+
}
801+
else
802+
{
803+
uint16_t rpm = get_simulated_rpm();
804+
// Base 7.0 L/100km + RPM load penalty + aerodynamic penalty above 100 km/h
805+
float l_per_100km = 7.0f + (float)(rpm - 800) * (1.5f / 5700.0f) +
806+
(speed > 100.0f ? (speed - 100.0f) * 0.015f : 0.0f);
807+
fuel_rate = (speed / 100.0f) * l_per_100km / 3600.0f;
808+
}
809+
sim_state.fuel_L -= fuel_rate * dt_sec;
810+
if (sim_state.fuel_L < 0.0f)
811+
sim_state.fuel_L = 0.0f;
748812
}
749-
if (sim_state.fuel_L < 0.0f)
750-
sim_state.fuel_L = 0.0f;
751813

752814
// Odometer: type 0x24, formula: km = A*2560 + B*10. Max ~653350 km.
753815
uint32_t raw_km = 50000UL + (uint32_t)sim_state.distance_km;
@@ -796,8 +858,8 @@ bool KWP_send_group_reading(uint8_t group)
796858
uint16_t rpm = get_simulated_rpm();
797859

798860
buf[3] = 0x01;
799-
buf[4] = 40;
800-
buf[5] = (uint8_t)(rpm / 8); // RPM
861+
buf[4] = 160;
862+
buf[5] = (uint8_t)(rpm / 32); // RPM
801863
buf[6] = 0x05;
802864
buf[7] = 10;
803865
buf[8] = 117; // 17.0°C air temp (K5: 10*(117-100)*0.1 = 17°C)
@@ -814,8 +876,8 @@ bool KWP_send_group_reading(uint8_t group)
814876
uint16_t rpm = get_simulated_rpm();
815877

816878
buf[3] = 0x01;
817-
buf[4] = 40;
818-
buf[5] = (uint8_t)(rpm / 8); // RPM
879+
buf[4] = 160;
880+
buf[5] = (uint8_t)(rpm / 32); // RPM
819881
buf[6] = 0x17;
820882
buf[7] = 100;
821883
buf[8] = 254; // 1016 mbar (100*254*0.04)
@@ -881,7 +943,7 @@ bool KWP_send_group_reading(uint8_t group)
881943
{
882944
// Engine ECU groups 30–125 (all [verify] from label file 036-906-034-APE)
883945
uint16_t rpm = get_simulated_rpm();
884-
uint8_t rpm_b = (uint8_t)(rpm / 8);
946+
uint8_t rpm_b = (uint8_t)(rpm / 32);
885947
int8_t coolant = get_simulated_coolant_temp();
886948

887949
switch (group)
@@ -912,7 +974,7 @@ bool KWP_send_group_reading(uint8_t group)
912974
break;
913975
case 34: // O2 sensor aging test (B1-S1)
914976
buf[3] = 0x01;
915-
buf[4] = 40;
977+
buf[4] = 160;
916978
buf[5] = rpm_b;
917979
buf[6] = 0x21;
918980
buf[7] = 10;
@@ -960,7 +1022,7 @@ bool KWP_send_group_reading(uint8_t group)
9601022
break;
9611023
case 46: // Catalytic converter efficiency test
9621024
buf[3] = 0x01;
963-
buf[4] = 40;
1025+
buf[4] = 160;
9641026
buf[5] = rpm_b;
9651027
buf[6] = 0x21;
9661028
buf[7] = 10;
@@ -974,11 +1036,11 @@ bool KWP_send_group_reading(uint8_t group)
9741036
break;
9751037
case 50: // Speed regulation
9761038
buf[3] = 0x01;
977-
buf[4] = 40;
1039+
buf[4] = 160;
9781040
buf[5] = rpm_b;
9791041
buf[6] = 0x01;
980-
buf[7] = 40;
981-
buf[8] = 100; // target 800 RPM (40*100*0.2)
1042+
buf[7] = 160;
1043+
buf[8] = 25; // target 800 RPM (160*25*0.2)
9821044
buf[9] = 0x0E;
9831045
buf[10] = 0;
9841046
buf[11] = 1; // A/C-Low (A/C-High/A/C-Low)
@@ -988,7 +1050,7 @@ bool KWP_send_group_reading(uint8_t group)
9881050
break;
9891051
case 54: // Throttle and pedal sensors
9901052
buf[3] = 0x01;
991-
buf[4] = 40;
1053+
buf[4] = 160;
9921054
buf[5] = rpm_b;
9931055
buf[6] = 0x0E;
9941056
buf[7] = 0;
@@ -1002,7 +1064,7 @@ bool KWP_send_group_reading(uint8_t group)
10021064
break;
10031065
case 55: // Idle regulator
10041066
buf[3] = 0x01;
1005-
buf[4] = 40;
1067+
buf[4] = 160;
10061068
buf[5] = rpm_b;
10071069
buf[6] = 0x02;
10081070
buf[7] = 10;
@@ -1016,11 +1078,11 @@ bool KWP_send_group_reading(uint8_t group)
10161078
break;
10171079
case 56: // Idle torque regulation
10181080
buf[3] = 0x01;
1019-
buf[4] = 40;
1081+
buf[4] = 160;
10201082
buf[5] = rpm_b;
10211083
buf[6] = 0x01;
1022-
buf[7] = 40;
1023-
buf[8] = 100; // target 800 RPM
1084+
buf[7] = 160;
1085+
buf[8] = 25; // target 800 RPM (160*25*0.2)
10241086
buf[9] = 0x21;
10251087
buf[10] = 10;
10261088
buf[11] = 0; // idle regulator 0.0 Nm
@@ -1044,7 +1106,7 @@ bool KWP_send_group_reading(uint8_t group)
10441106
break;
10451107
case 61: // EPC system status
10461108
buf[3] = 0x01;
1047-
buf[4] = 40;
1109+
buf[4] = 160;
10481110
buf[5] = rpm_b;
10491111
buf[6] = 0x06;
10501112
buf[7] = 100;
@@ -1100,7 +1162,7 @@ bool KWP_send_group_reading(uint8_t group)
11001162
break;
11011163
case 75: // EGR test
11021164
buf[3] = 0x01;
1103-
buf[4] = 40;
1165+
buf[4] = 160;
11041166
buf[5] = rpm_b;
11051167
buf[6] = 0x17;
11061168
buf[7] = 100;
@@ -1114,7 +1176,7 @@ bool KWP_send_group_reading(uint8_t group)
11141176
break;
11151177
case 99: // OBD compatibility
11161178
buf[3] = 0x01;
1117-
buf[4] = 40;
1179+
buf[4] = 160;
11181180
buf[5] = rpm_b;
11191181
buf[6] = 0x05;
11201182
buf[7] = 10;
@@ -1146,27 +1208,27 @@ bool KWP_send_group_reading(uint8_t group)
11461208
}
11471209
case 120: // Traction control (ASR/TCS)
11481210
buf[3] = 0x01;
1149-
buf[4] = 40;
1211+
buf[4] = 160;
11501212
buf[5] = rpm_b;
11511213
buf[6] = 0x01;
1152-
buf[7] = 40;
1153-
buf[8] = 100; // target 800 RPM
1214+
buf[7] = 160;
1215+
buf[8] = 25; // target 800 RPM (160*25*0.2)
11541216
buf[9] = 0x01;
1155-
buf[10] = 40;
1217+
buf[10] = 160;
11561218
buf[11] = rpm_b; // actual RPM
11571219
buf[12] = 0x0E;
11581220
buf[13] = 0;
11591221
buf[14] = 1; // ASR not active
11601222
break;
11611223
case 122: // Transmission torque reduction
11621224
buf[3] = 0x01;
1163-
buf[4] = 40;
1225+
buf[4] = 160;
11641226
buf[5] = rpm_b;
11651227
buf[6] = 0x01;
1166-
buf[7] = 40;
1167-
buf[8] = 100; // target 800 RPM
1228+
buf[7] = 160;
1229+
buf[8] = 25; // target 800 RPM (160*25*0.2)
11681230
buf[9] = 0x01;
1169-
buf[10] = 40;
1231+
buf[10] = 160;
11701232
buf[11] = rpm_b; // actual RPM
11711233
buf[12] = 0x0E;
11721234
buf[13] = 0;
@@ -1739,11 +1801,9 @@ bool connect()
17391801
// Initialize simulation state
17401802
unsigned long now = millis();
17411803
sim_state.start_ms = now;
1742-
sim_state.last_dist_ms = now; // legacy, unused
17431804
sim_state.distance_km = 0.0f;
17441805
sim_state.fuel_L = 55.0f; // full tank
1745-
sim_state.last_odo_update_ms = now;
1746-
sim_state.last_fuel_update_ms = now;
1806+
sim_state.last_update_ms = now;
17471807

17481808
g.setColor(TFT_YELLOW);
17491809
g.print("connected", RIGHT, rows[7]);

0 commit comments

Comments
 (0)