BOROSOFTWAREAll work →
scantool/scantool_dyno.c 763 lines
1 finding in this fileB24L141
1/*
2 * freediag - Vehicle Diagnostic Utility
3 *
4 *
5 * Copyright (C) 2001 Richard Almeida & Ibex Ltd ([email protected])
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 *************************************************************************
22 *
23 *
24 * Mostly ODBII Compliant Scan Tool (as defined in SAE J1978)
25 *
26 * CLI routines - dyno subcommand
27 */
28
29#include <stdint.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include "diag.h"
35#include "diag_err.h"
36#include "diag_os.h"
37#include "diag_l3.h"
38
39#include "libcli.h"
40
41#include "scantool.h"
42#include "scantool_cli.h"
43
44#include "dyno.h"
45
46/* uncomment this to make fake dyno (to test dyno with disconnected ECU) */
47/* #define DYNO_DEBUG 1 */
48
49
50#ifdef DYNO_DEBUG
51 #include <math.h> //for fake_loss_measure_data()
52#endif
53
54
55
56dyno_result *dyno_results;
57int dyno_nb_results;
58
59static enum cli_retval cmd_dyno_help(int argc, char **argv);
60static enum cli_retval cmd_dyno_mass(int argc, char **argv);
61static enum cli_retval cmd_dyno_loss(int argc, char **argv);
62static enum cli_retval cmd_dyno_setloss(int argc, char **argv);
63static enum cli_retval cmd_dyno_run(int argc, char **argv);
64static enum cli_retval cmd_dyno_measures(int argc, char **argv);
65static enum cli_retval cmd_dyno_result(int argc, char **argv);
66static enum cli_retval cmd_dyno_graph(int argc, char **argv);
67static enum cli_retval cmd_dyno_save(int argc, char **argv);
68
69void reset_results(void);
70
71const struct cmd_tbl_entry dyno_cmd_table[] = {
72 { "help", "help [command]", "Gives help for a command",
73 cmd_dyno_help, 0, NULL},
74 { "?", "? [command]", "Gives help for a command",
75 cmd_dyno_help, CLI_CMD_HIDDEN, NULL},
76
77 { "mass", "mass [mass]", "Step 1 : Shows/Sets the mass of the vehicle",
78 cmd_dyno_mass, 0, NULL},
79 { "loss", "loss", "Step 2 : Determines power lost by aerodynamic and friction forces",
80 cmd_dyno_loss, 0, NULL},
81 { "setloss", "setloss [d] [f]", "Manually enter aerodynamic and friction forces parameters",
82 cmd_dyno_setloss, 0, NULL},
83 { "run", "run", "Step 3 : Run dyno",
84 cmd_dyno_run, 0, NULL},
85 { "measures", "measures", "Display run measures",
86 cmd_dyno_measures, 0, NULL},
87 { "result", "result", "Display run results",
88 cmd_dyno_result, 0, NULL},
89 { "graph", "graph", "Display run graphs",
90 cmd_dyno_graph, 0, NULL},
91 { "save", "save [filename]", "Save measures and results in a file",
92 cmd_dyno_save, 0, NULL},
93
94 CLI_TBL_BUILTINS,
95 CLI_TBL_END
96};
97
98
99/*
100 * Show/Sets the mass of the vehicle
101 */
102static enum cli_retval cmd_dyno_mass(int argc, char **argv) {
103 int mass=0;
104
105 if (argc > 1) {
106 mass = htoi(argv[1]);
107 }
108
109 if (mass > 0) {
110 dyno_set_mass(mass);
111 } else {
112 printf("mass: %d kg\n", dyno_get_mass());
113 }
114
115 return 0;
116}
117
118/******************************************************************************
119* Functions to measure data *
120******************************************************************************/
121
122#define DYNDATA_1(p, n, d) (d[p].data[n])
123#define DYNDATA_2(p, n, d) (DYNDATA_1(p, n, d) * 256 + DYNDATA_1(p, n+1, d))
124#define RPM_PID (0x0c)
125#define RPM_DATA(d) (DYNDATA_2(RPM_PID, 2, d)*0.25)
126#define SPEED_PID (0x0d)
127#define SPEED_DATA(d) (DYNDATA_1(SPEED_PID, 2, d) * 10000./36.) /* m/s * 1000 */
128
129#define SPEED_ISO_TO_KMH(_speed_) ((_speed_)*36/10000)
130
131/* measure speed */
132// return <0 if error
133static int measure_data(uint8_t data_pid, ecu_data *ep) {
134 int rv;
135
136 if (global_l3_conn == NULL) {
137 fprintf(stderr, FLFMT "Error: there must be an active L3 connection!\n", FL);
138 return DIAG_ERR_GENERAL;
139 }
140 /* measure */
141 rv = l3_do_j1979_rqst(global_l3_conn, 0x1, data_pid, 0x00,
142 0x00, 0x00, 0x00, 0x00, (void *)&_RQST_HANDLE_NORMAL);
143 if (rv < 0) {
144 return rv;
145 }
146
147 /* data extraction */
148 if (data_pid == RPM_PID) {
149 return (int)(RPM_DATA(ep->mode1_data) + .50);
150 }
151 if (data_pid == SPEED_PID) {
152 return (int)(SPEED_DATA(ep->mode1_data) + .50);
153 }
154 return DYNDATA_1(data_pid, 2, ep->mode1_data);
155}
156
157
158#ifdef DYNO_DEBUG
159int counter;
160unsigned long tv0d; /* measuring time */
161
162/* fake loss measures */
163int fake_loss_measure_data() {
164 unsigned long tv;
165 unsigned long elapsed; /* elapsed time */
166 int speed;
167
168 if (counter == 0) {
169 tv0d=diag_os_getms();
170 }
171
172 diag_os_millisleep(250);
173
174 /* get elapsed time */
175 tv=diag_os_getms();
176 elapsed = tv - tv0d;
177
178
179 //I think we're supposed to simulate an exponential decreasing
180 //function ; speed = b * e^(elapsed / a).
181 float a= -62810;
182 float b= 25027;
183
184 counter++;
185
186 return (int) (b * exp(elapsed / a));
187}
188
189int counter2;
190/* fake run measures */
191int fake_run_measure_data(int data_pid) {
192 int rpm;
193
194 diag_os_millisleep(250);
195
196 rpm = 1000 + counter2 * 200;
197 if (counter2 < 5500/200) {
198 counter2++;
199 } else {
200 counter2--;
201 }
202
203 if (data_pid == RPM_PID) {
204 return rpm;
205 } else if (data_pid == SPEED_PID) {
206 return rpm * (9000*100/6000) / 36;
207 }
208
209 return 0;
210}
211
212#define LOSS_MEASURE_DATA(_pid_, _ep_) fake_loss_measure_data()
213#define RUN_MEASURE_DATA(_pid_, _ep_) fake_run_measure_data(_pid_)
214#else /* DYNO_DEBUG */
215#define LOSS_MEASURE_DATA(_pid_, _ep_) measure_data(_pid_, _ep_)
216#define RUN_MEASURE_DATA(_pid_, _ep_) measure_data(_pid_, _ep_)
217
218#endif
219
220
221/*****************************************************************************
222* Measuring loss function *
223*****************************************************************************/
224
225/* Indicate whether loss determination has been done */
226int dyno_loss_done;
227
228/*
229 * Determine power lost by aerodynamic and friction forces
230 */
231
232static enum cli_retval cmd_dyno_loss(UNUSED(int argc), UNUSED(char **argv)) {
233 ecu_data *ep;
234
235 int speed; /* measured speed */
236 int speed_previous = 0; /* previous speed */
237
238 unsigned long tv0, tv;
239 int elapsed; /* elapsed time */
240
241 int i, length; /* length of printed string */
242 int nb = 0; /* number of measures */
243
244 //make sure we have an L3 connection first !
245 if (global_l3_conn == NULL) {
246 fprintf(stderr, FLFMT "Error: there must be an active L3 connection!\n", FL);
247 return CMD_FAILED;
248 }
249
250 /* Check mass */
251 if (dyno_get_mass() <= 0) {
252 printf("The mass of the vehicle has not been set, please set the mass first\n");
253 return CMD_OK;
254 }
255
256 /* Show instructions */
257 printf("To proceed loss determination, reach the maximum speed you will reach during\n");
258 printf("dyno, then push in the clutch, leaving the car in gear. Allow the car to coast\n");
259 printf("down to the lowest possible speed. Press ENTER when finished.\n");
260 printf("\n");
261 wait_enter("Press ENTER when ready... ");
262 printf("\n");
263
264 /* Reset data */
265 dyno_loss_reset(); /* dyno data */
266 reset_results();
267 tv0=diag_os_getms(); /* initial time */
268 ep = ecu_info; /* ECU data */
269
270 /* exclude 1st measure */
271 speed_previous = LOSS_MEASURE_DATA(SPEED_PID, ep); /* m/s * 1000 */
272 if (speed_previous < 0) {
273 printf("invalid speed !\n");
274 return CMD_FAILED;
275 }
276
277 printf("Starting loss determination (max speed=%d km/h)\n", SPEED_ISO_TO_KMH(speed_previous));
278 printf("Number of measures : 0");
279 length = 1;
280
281 /* loss measures */
282 while (1) {
283 /* measure speed */
284 speed = LOSS_MEASURE_DATA(SPEED_PID, ep); /* m/s * 1000 */
285 if (speed < 0) {
286 printf("invalid speed !\n");
287 break;
288 }
289
290
291 /* get elapsed time */
292
293 tv=diag_os_getms();
294 elapsed = (int) (tv - tv0);
295
296 if (speed < speed_previous) {
297 /* Add measure */
298 dyno_loss_add_measure(elapsed, speed);
299 nb++;
300
301 speed_previous = speed;
302 }
303
304 if (pressed_enter() != 0) {
305 /* ENTER pressed : stops */
306 printf("Number of measures : %d (min speed=%d km/h)\n", nb, SPEED_ISO_TO_KMH(speed));
307 break;
308 }
309 if (speed_previous == speed) { /* measure added: update display */
310 /* erase previous measure */
311 for (i = 0; i < length; i++) {
312 printf("");
313 }
314
315 /* Display new measure */
316 length = printf("%d (speed=%d km/h, d=%5.5f, f=%4.2f)\t ",
317 nb, SPEED_ISO_TO_KMH(speed), dyno_loss_get_d(), dyno_loss_get_f());
318 fflush(stdout); /* force displaying now (may slow down dyno...) */
319 }
320 }
321
322 /* display dyno time */
323 //elapsed = MILLIS(tv) - MILLIS(tv0);
324 tv=diag_os_getms();
325 elapsed= (int) (tv - tv0);
326 printf("d=%5.5f, f=%4.2f\n", dyno_loss_get_d(), dyno_loss_get_f());
327 printf("Loss determination time : %ds.\n", (elapsed/1000));
328
329 printf("\n");
330
331 /* now dyno loss has been done */
332 dyno_loss_done = 1;
333
334 return CMD_OK;
335}
336
337/*
338 * Manually enter aerodynamic and friction forces d and f parameters
339 */
340static enum cli_retval cmd_dyno_setloss(int argc, char **argv) {
341 if (argc > 1) {
342 int assigned;
343 double d;
344 assigned = sscanf(argv[1], "%le", &d);
345 if (assigned > 0) {
346 dyno_loss_set_d(d);
347 }
348 }
349
350 if (argc > 2) {
351 int assigned;
352 double f;
353 assigned = sscanf(argv[2], "%le", &f);
354 if (assigned > 0) {
355 dyno_loss_set_f(f);
356 }
357 }
358
359 printf("d=%5.5f, f=%4.2f\n", dyno_loss_get_d(), dyno_loss_get_f());
360 printf("\n");
361
362 if (argc > 2) {
363 /* now consider that dyno loss has been done */
364 dyno_loss_done = 1;
365 }
366
367 return CMD_OK;
368}
369
370/*****************************************************************************
371* Dyno functions *
372*****************************************************************************/
373
374/*
375 * Run dyno
376 */
377
378static enum cli_retval cmd_dyno_run(UNUSED(int argc), UNUSED(char **argv)) {
379 ecu_data *ep;
380
381 int speed; /* measured speed */
382 int rpm; /* measured rpm */
383 int rpm_previous = 0; /* previous rpm */
384
385 unsigned long tv0, tv; /* measuring time */
386 int elapsed; /* elapsed time (ms) */
387
388 int i, length = 0; /* length of printed string */
389 int nb = 0; /* number of measures */
390
391 //make sure we're connected !
392 if (global_l3_conn == NULL) {
393 fprintf(stderr, FLFMT "No active L3 connection !\n", FL);
394 return CMD_FAILED;
395 }
396
397 /* Check mass */
398 if (dyno_get_mass() <= 0) {
399 printf("The mass of the vehicle has not been set, please set the mass first\n");
400 return CMD_OK;
401 }
402
403 /* Check mass */
404 if (dyno_loss_done <= 0) {
405 printf("The loss determination has not been done, please use command loss or setloss first\n");
406 return CMD_OK;
407 }
408
409 /* Show instructions */
410 printf("To proceed dyno, do a full-throttle acceleration run\n");
411 printf("in a single gear from a rolling start.\n");
412 printf("The run ends automatically when RPM begins to decrease.\n");
413 printf("\n");
414 wait_enter("Press ENTER when ready... ");
415 printf("\n");
416
417 /* Reset data */
418 dyno_reset(); /* dyno data */
419 reset_results();
420
421 tv0=diag_os_getms(); /* initial time */
422 ep = ecu_info; /* ECU data */
423
424 /* Measures */
425 while (1) {
426 /* measure RPM */
427 rpm = RUN_MEASURE_DATA(RPM_PID, ep);
428
429 if (rpm < 0) {
430 printf("invalid RPM !\n");
431 break;
432 }
433
434
435 if (rpm_previous == 0) {
436 /* this is the first measure */
437 printf("Starting dyno (min rpm=%d)\n", rpm);
438 printf("Number of measures : ");
439 }
440
441 /* if RPM starts decreasing, stop run */
442 if (rpm < rpm_previous) {
443 printf(" (max rpm=%d)\n", rpm_previous);
444 break;
445 }
446
447 /* get elapsed time */
448 tv=diag_os_getms();
449 elapsed = (int) (tv - tv0);
450
451 /* Add measure */
452 dyno_add_measure(elapsed, rpm);
453
454 /* Display number of measures */
455 nb++;
456 for (i = 0; i < length; i++) {
457 printf("");
458 }
459
460 length = printf("%d (%d RPM) ", nb, rpm);
461 fflush(stdout); /* force displaying now (may slow down dyno...) */
462
463 rpm_previous = rpm;
464 }
465
466 /* measure gear ratio */
467 rpm_previous = rpm;
468 speed = RUN_MEASURE_DATA(SPEED_PID, ep); /* m/s * 1000 */
469 rpm = RUN_MEASURE_DATA(RPM_PID, ep);
470
471 if ((speed < 0) || (rpm < 0)) {
472 printf("invalid RUN_MEASURE_DATA result !\n");
473 return CMD_FAILED;
474 }
475 dyno_set_gear(speed, (rpm_previous + rpm) / 2);
476
477 /* display dyno time */
478 tv=diag_os_getms();
479 elapsed = (int) (tv - tv0);
480 printf("Dyno time : %ds.\n", (elapsed/1000));
481
482 printf("\n");
483
484 return CMD_OK;
485}
486
487
488/*****************************************************************************
489* Displaying measures and results functions *
490*****************************************************************************/
491
492/* Get measures for specified type */
493static void get_measures(dyno_measure **measures, int *nb_measures) {
494 /* allocate memory */
495 (*nb_measures) = dyno_get_nb_measures();
496 if ((*nb_measures) == 0) {
497 return;
498 }
499 if (diag_malloc(measures, (*nb_measures))) {
500 return;
501 }
502
503 /* get measures */
504 dyno_get_measures((*measures), (*nb_measures));
505}
506
507/* Display given measures */
508static void display_measures(dyno_measure *measures, int nb_measures) {
509 int i;
510
511 for (i=0; i<nb_measures; i++) {
512 printf("measure %d:\t%3.3f s. \tRPM: %d\t%3.3f m/s\t%3.2f km/h\n", (i+1),
513 measures[i].millis/1000.0,
514 measures[i].rpm,
515 dyno_get_speed_from_rpm(measures[i].rpm)/1000.0,
516 dyno_get_speed_from_rpm(measures[i].rpm)*3.6/1000.0);
517
518 if (((i+1) % 22) == 0) {
519 wait_enter("Press ENTER to continue... ");
520 }
521 }
522}
523
524/* Display all measures */
525
526static enum cli_retval cmd_dyno_measures(UNUSED(int argc), UNUSED(char **argv)) {
527 dyno_measure *measures = NULL;
528 int nb_measures = 0;
529
530 printf("Dyno measures :\n");
531 get_measures(&measures, &nb_measures);
532 display_measures(measures, nb_measures);
533 free(measures);
534
535 printf("%d measures.\n", nb_measures);
536 printf("\n");
537
538 return CMD_OK;
539}
540
541
542/* Display results */
543static void display_results(dyno_result *results, int nb) {
544 int i;
545
546 int max_power_i = 0;
547 int max_power = 0;
548 int max_torque_i = 0;
549 int max_torque = 0;
550
551 for (i=0; i<nb; i++) {
552 printf("%d:\tRPM=%d\t\tpower=%d W (%d ch)\ttorque=%d Nm\n", i,
553 results[i].rpm, results[i].power, results[i].power_ch, results[i].torque);
554
555 if (results[i].power > max_power) {
556 max_power = results[i].power;
557 max_power_i = i;
558 }
559 if (results[i].torque > max_torque) {
560 max_torque = results[i].torque;
561 max_torque_i = i;
562 }
563
564 if (((i+1) % 22) == 0) {
565 wait_enter("Press ENTER to continue... ");
566 }
567 }
568 printf("\n");
569 printf("Max power : %d ch (at %d RPM)\n",
570 results[max_power_i].power_ch, results[max_power_i].rpm);
571 printf("Max torque : %d Nm (at %d RPM)\n",
572 results[max_torque_i].torque, results[max_torque_i].rpm);
573 printf("\n");
574}
575
576#define DYNO_GRAPH_HEIGHT 21
577
578/* Display graphs */
579static void display_graphs(dyno_result *results, int nb) {
580 int row, col, step;
581
582 int max_power_i = 0;
583 int max_power = 0;
584 int max_torque_i = 0;
585 int max_torque = 0;
586
587 /* Detect maximums */
588 for (col=0; col<nb; col++) {
589 if (results[col].power > max_power) {
590 max_power = results[col].power;
591 max_power_i = col;
592 }
593 if (results[col].torque > max_torque) {
594 max_torque = results[col].torque;
595 max_torque_i = col;
596 }
597 }
598
599 /* 80 columns max */
600 step = (nb / 80) + 1;
601
602 /* Displaying torque */
603 printf("Torque :\n");
604 for (row=DYNO_GRAPH_HEIGHT-1; row>=0; row--) {
605 for (col=0; col<nb; col+=step) {
606 if (results[col].torque * DYNO_GRAPH_HEIGHT >
607 results[max_torque_i].torque * row) {
608 printf("*");
609 } else {
610 printf(" ");
611 }
612 }
613 printf("\n");
614 }
615
616 printf("\n");
617
618 /* Pause */
619 wait_enter("Press ENTER to continue... ");
620
621 printf("\n");
622
623 /* Displaying power */
624 printf("Power :\n");
625 for (row=DYNO_GRAPH_HEIGHT-1; row>=0; row--) {
626 for (col=0; col<nb; col+=step) {
627 if (results[col].power * DYNO_GRAPH_HEIGHT >
628 results[max_power_i].power * row) {
629 printf("*");
630 } else {
631 printf(" ");
632 }
633 }
634 printf("\n");
635 }
636 printf("\n");
637}
638
639/* Get the results in global vars */
640static void get_results(void) {
641 /* allocating memory for the results table */
642 if (dyno_results == NULL) {
643 dyno_nb_results = dyno_get_nb_results();
644 if (dyno_nb_results == 0) {
645 return;
646 }
647 if (diag_malloc(&dyno_results, dyno_nb_results)) {
648 return;
649 }
650 }
651
652 /* raw results */
653 dyno_get_results(dyno_results, dyno_nb_results);
654
655 /* smooth results */
656 dyno_smooth_results(dyno_results, dyno_nb_results);
657}
658
659/* Reset results */
660void reset_results(void) {
661 if (dyno_results != NULL) {
662 free(dyno_results);
663 }
664 dyno_results = NULL;
665 dyno_nb_results = 0;
666}
667
668/*
669 * Display dyno results
670 */
671
672static enum cli_retval cmd_dyno_result(UNUSED(int argc), UNUSED(char **argv)) {
673 get_results();
674
675 /* Check data */
676 if (dyno_nb_results <= 0) {
677 printf("Dyno run has not been done, please do a run first\n");
678 return CMD_OK;
679 }
680
681 display_results(dyno_results, dyno_nb_results);
682 return CMD_OK;
683}
684
685/*
686 * Display dyno graphs
687 */
688
689static enum cli_retval cmd_dyno_graph(UNUSED(int argc), UNUSED(char **argv)) {
690 get_results();
691
692 /* Check data */
693 if (dyno_nb_results <= 0) {
694 printf("Dyno run has not been done, please do a run first\n");
695 return CMD_OK;
696 }
697
698 display_graphs(dyno_results, dyno_nb_results);
699 return CMD_OK;
700}
701
702
703/*****************************************************************************
704* Saving functions *
705*****************************************************************************/
706
707/*
708 * Save dyno measures and results to a file
709 */
710static enum cli_retval cmd_dyno_save(int argc, char **argv) {
711 char *filename;
712 int rv;
713
714 get_results();
715
716 /* Check data */
717 if (dyno_nb_results <= 0) {
718 printf("Dyno run has not been done, please do a run first\n");
719 return CMD_OK;
720 }
721
722
723 if (argc > 1) {
724 /* Get filename from command arguments */
725 size_t length = strlen(argv[1]);
726 rv = diag_malloc(&filename, length + 1);
727 if (rv != 0) {
728 return rv;
729 }
730 strcpy(filename, argv[1]);
731 } else {
732 /* Get filename from user input */
733 size_t nbytes = 256;
734 rv = diag_malloc(&filename, nbytes + 1);
735 if (rv != 0) {
736 return rv;
737 }
738
739 printf("Enter filename: ");
740 if (fgets(filename, (int)nbytes, stdin) == 0) {
741 return CMD_OK;
742 }
743
744 /* Remove pending "\n" and "\r", if any */
745 while ((filename[strlen(filename)-1] == '\n') ||
746 (filename[strlen(filename)-1] == '\r')) {
747 filename[strlen(filename)-1] = '\0';
748 }
749 }
750
751 dyno_save(filename, dyno_results, dyno_nb_results);
752 printf("\n");
753
754 free(filename);
755 return CMD_OK;
756}
757
758
759/* Display help */
760static enum cli_retval cmd_dyno_help(int argc, char **argv) {
761 return cli_help_basic(argc, argv, dyno_cmd_table);
762}
763