BOROSOFTWAREAll work →
scantool/dyno.c 589 lines
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 * Dyno functionnalities
24 *
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "diag.h"
32#include "dyno.h"
33
34
35#define SQR(_x_) ((_x_)*(_x_))
36#define CUB(_x_) ((_x_)*(_x_)*(_x_))
37#define PI 3.141592654
38#define MIN(_a_, _b_) (((_a_) < (_b_) ? (_a_) : (_b_)))
39
40/* generic measures table */
41struct dyno_measure_table {
42 int size; /* allocated size */
43 int nbr; /* number of elements in the table */
44 union { /* table of measures */
45 dyno_loss_measure * loss_measures;
46 dyno_measure * measures;
47 } meas;
48};
49
50/* measures and loss measures table */
51struct dyno_measure_table loss_measure_table;
52struct dyno_measure_table measure_table;
53
54/* default size of measure table */
55#define DYNO_DEFAULT_TABLE_SIZE 100
56
57
58/*****************************************************************************
59* Table allocation *
60*****************************************************************************/
61
62/* Check table allocated size before addind a new element */
63static int dyno_check_allocated_table(struct dyno_measure_table *table) {
64 int rv;
65 if (table->meas.measures == NULL) {
66 /* allocating the table */
67 table->size = DYNO_DEFAULT_TABLE_SIZE;
68 table->nbr = 0;
69 rv = diag_malloc(&table->meas.measures, table->size);
70 if (rv != 0) {
71 return rv;
72 }
73 } else if (table->nbr == table->size) {
74 /* reallocating the table if maximum capacity is reached */
75 dyno_measure *ptr;
76
77 /* new table size */
78 table->size *= 2;
79
80 /* allocating, transfering data */
81 rv = diag_malloc(&ptr, table->size);
82 if (rv != 0) {
83 return rv;
84 }
85
86 memcpy(ptr, table->meas.measures, table->nbr * sizeof(*(table->meas.measures)));
87
88 /* free old memory */
89 free(table->meas.measures);
90 table->meas.measures = ptr;
91 }
92 return 0;
93}
94
95/* reset table content */
96static int dyno_table_reset(struct dyno_measure_table *table) {
97 if (table->meas.measures != NULL) {
98 free(table->meas.measures);
99 table->meas.measures = NULL;
100 }
101
102 table->size = 0;
103 table->nbr = 0;
104 return 0;
105}
106
107
108/*****************************************************************************
109* Mass *
110*****************************************************************************/
111
112/* mass of vehicle */
113int dyno_mass;
114
115/*
116 * Sets the mass of the vehicle
117 */
118int dyno_set_mass(int mass) {
119 dyno_mass = mass;
120 return DYNO_OK;
121}
122
123/*
124 * Gets the mass of the vehicle
125 */
126int dyno_get_mass() {
127 return dyno_mass;
128}
129
130
131/*****************************************************************************
132* Gear *
133*****************************************************************************/
134
135/* Gear accuracy */
136#define DYNO_GEAR_ACCURACY 1000
137
138/* gear of vehicle */
139int dyno_gear;
140
141/*
142 * Set ratio from speed (m/s * 1000) and rpm
143 */
144int dyno_set_gear(int speed, int rpm) {
145 dyno_gear = speed * DYNO_GEAR_ACCURACY / rpm;
146 return DYNO_OK;
147}
148
149/*
150 * Get speed (in m/s * 1000) from rpm
151 */
152int dyno_get_speed_from_rpm(int rpm) {
153 int speed;
154 speed = rpm * dyno_gear / DYNO_GEAR_ACCURACY; /* m/s * 1000 */
155 return speed;
156}
157
158
159/*****************************************************************************
160* Loss measures *
161*****************************************************************************/
162
163/*
164 * How can "dyno_loss_needs_calculation" default to false?
165 * How come when we reset it it still doesn't need calculation?
166 */
167int dyno_loss_needs_calculation=0;
168
169/* Add loss measure */
170int dyno_loss_add_measure(int millis, int speed) {
171 /* check memory allocation */
172 dyno_check_allocated_table(&loss_measure_table);
173
174 /* storing measure */
175 loss_measure_table.meas.loss_measures[loss_measure_table.nbr].millis = millis;
176 loss_measure_table.meas.loss_measures[loss_measure_table.nbr].speed = speed;
177
178 /* one measure added */
179 loss_measure_table.nbr++;
180
181 /* d and f need to be re-calculated */
182 dyno_loss_needs_calculation = 1;
183
184 return DYNO_OK;
185}
186
187/*
188 * The loss power is : Pl = d * v^3 + f * v
189 * with :
190 * - d = aerodynamic loss factor
191 * - f = friction loss factor
192 */
193double dyno_loss_d;
194double dyno_loss_f;
195
196/*
197 * d and f are the solutions of the equation : 0 = Pl + M * v * a
198 * where a is the (negative) acceleration obtained when the output power
199 * of the engine is nul (i.e. clutch pushed in).
200 *
201 * If we call y(i) = - M * a(i), we have :
202 * d = average( (y(i+1) - y(i)) / (v(i+1)^2 - (vi)^2) )
203 * f = average( y(i) - d * v(i)^2 )
204 */
205
206/* get acceleration between 2 measures (m/s^2) */
207static double dyno_loss_a_inter(int i, int j) {
208 double a;
209 dyno_loss_measure *measure0;
210 dyno_loss_measure *measure1;
211
212 /* avoid bad use */
213 if ((i < 0) || (i >= j) || (j > loss_measure_table.nbr)) {
214 return 0;
215 }
216
217 /* select measures */
218 measure0 = &loss_measure_table.meas.loss_measures[i];
219 measure1 = &loss_measure_table.meas.loss_measures[j];
220
221 /* get acceleration */
222 a = 1.0
223 * (measure1->speed - measure0->speed)
224 / (measure1->millis - measure0->millis);
225
226 return a;
227}
228
229/* a(i) (m/s^2) */
230static double dyno_loss_a(int i) {
231 double a;
232
233 if (i <= 0) {
234 a = dyno_loss_a_inter(0, 1);
235 } else if (i >= loss_measure_table.nbr - 1) {
236 a = dyno_loss_a_inter(loss_measure_table.nbr - 2,
237 loss_measure_table.nbr - 1);
238 } else {
239 a = (dyno_loss_a_inter(i - 1, i) + dyno_loss_a_inter(i, i + 1)) / 2;
240 }
241
242 return a;
243}
244
245/* get y(i) */
246static double dyno_loss_y(int i) {
247 return (0 - dyno_mass * dyno_loss_a(i));
248}
249
250/* Calculate d and f factors */
251static int dyno_loss_calculate(void) {
252 int i, nb;
253 double sum;
254 dyno_loss_measure *measure0;
255 dyno_loss_measure *measure1;
256
257 /* calculate d */
258 nb = loss_measure_table.nbr - 1;
259 sum = 0;
260 for (i=0; i<nb; i++) {
261 double d;
262
263 /* select measures */
264 measure0 = &loss_measure_table.meas.loss_measures[i];
265 measure1 = &loss_measure_table.meas.loss_measures[i+1];
266
267 d = (dyno_loss_y(i+1) - dyno_loss_y(i))
268 / (SQR(measure1->speed/1000.0) - SQR(measure0->speed/1000.0));
269 sum += d;
270 }
271 dyno_loss_d = sum / nb;
272
273 /* calculate f */
274 nb = loss_measure_table.nbr;
275 sum = 0;
276 for (i=0; i<nb; i++) {
277 double f;
278
279 measure0 = &loss_measure_table.meas.loss_measures[i];
280
281 f = dyno_loss_y(i) - (dyno_loss_d * SQR(measure0->speed/1000.0));
282 sum += f;
283 }
284 dyno_loss_f = sum / nb;
285
286 dyno_loss_needs_calculation = 0;
287
288 return DYNO_OK;
289}
290
291/* Reset all dyno loss measures */
292int dyno_loss_reset() {
293 dyno_table_reset(&loss_measure_table);
294
295 dyno_loss_d = 0;
296 dyno_loss_f = 0;
297 dyno_loss_needs_calculation = 0;
298 return 0;
299}
300
301/* Get d value */
302double dyno_loss_get_d() {
303 if (dyno_loss_needs_calculation == 1) {
304 dyno_loss_calculate();
305 }
306
307 return dyno_loss_d;
308}
309
310/* Get f value */
311double dyno_loss_get_f() {
312 if (dyno_loss_needs_calculation == 1) {
313 dyno_loss_calculate();
314 }
315
316 return dyno_loss_f;
317}
318
319void dyno_loss_set_d(double d) {
320 dyno_loss_d = d;
321}
322
323void dyno_loss_set_f(double f) {
324 dyno_loss_f = f;
325}
326
327/* Calculate loss power from speed (m/s2 * 1000) */
328static long dyno_loss_power(long speed) {
329 double power;
330
331 if (dyno_loss_needs_calculation == 1) {
332 dyno_loss_calculate();
333 }
334
335 /* Pl = d * v^3 + f * v */
336 power = dyno_loss_d * CUB(speed/1000.0) + dyno_loss_f * (speed/1000.0);
337
338 return (long)(power + .50);
339}
340
341/*****************************************************************************
342* Measures *
343*****************************************************************************/
344
345/* Add a measure */
346int dyno_add_measure(int millis, int rpm) {
347 /* check memory allocation */
348 dyno_check_allocated_table(&measure_table);
349
350 /* storing measure */
351 measure_table.meas.measures[measure_table.nbr].millis = millis;
352 measure_table.meas.measures[measure_table.nbr].rpm = rpm;
353
354 /* one measure added */
355 measure_table.nbr++;
356
357 return DYNO_OK;
358}
359
360/* Reset all dyno data */
361int dyno_reset() {
362 return dyno_table_reset(&measure_table);
363}
364
365/* Get number of measures */
366int dyno_get_nb_measures() {
367 return measure_table.nbr;
368}
369
370/* Get all measures */
371int dyno_get_measures(dyno_measure *measures, int size) {
372 /* copy measures */
373 memcpy(measures, measure_table.meas.measures, MIN(size, measure_table.nbr) * sizeof(dyno_measure));
374
375 return DYNO_OK;
376}
377
378
379/*****************************************************************************
380* Results *
381*****************************************************************************/
382
383/* Calculate torque from power and RPM (power : W, RPM : rpm, torque : N.m) */
384#define TORQUE(_power_, _rpm_) (((_power_) * 60) / ((_rpm_) * 2 * PI))
385
386/* Convert power (W to ch DYN) */
387#define POWER_CH(_power_) ((_power_) * 10 / 7355)
388
389/*
390 * Calculate power between 2 acceleration measures
391 */
392static void dyno_calculate_result(dyno_measure *measure0, dyno_measure *measure1, dyno_result *result) {
393 /* effective power and lost power */
394 int p_dyno, p_loss;
395
396 /* calculate effective power from cinetic energy variation */
397 p_dyno = SQR(dyno_get_speed_from_rpm(measure1->rpm))
398 - SQR(dyno_get_speed_from_rpm(measure0->rpm)); /* m2/s2 * 1.000.000 */
399 p_dyno = (p_dyno / (measure1->millis - measure0->millis)); /* m2/s3 */
400 p_dyno = p_dyno * dyno_mass / 2000;
401
402 /* calculate loss power at given speed */
403 result->rpm = (measure0->rpm + measure1->rpm) / 2;
404 p_loss = dyno_loss_power(dyno_get_speed_from_rpm(result->rpm));
405
406 /* rpm, power and torque */
407 result->power = p_dyno + p_loss;
408 result->power_ch = POWER_CH(result->power);
409 result->torque = (int)(TORQUE(result->power, result->rpm) + .50);
410
411 return;
412}
413
414/*
415 * Calculate results
416 */
417static void dyno_calculate_results(dyno_result *results) {
418 int i;
419 int nb = dyno_get_nb_results();
420
421 /* for each measure, calculate result */
422 for (i=0; i<nb; i++) {
423 dyno_calculate_result(&measure_table.meas.measures[i], &measure_table.meas.measures[i+1], &results[i]);
424 }
425}
426
427/*
428 * Get number of results
429 */
430int dyno_get_nb_results() {
431 return (measure_table.nbr - 1);
432}
433
434/*
435 * Get dyno results
436 */
437
438int dyno_get_results(dyno_result *results, UNUSED(int size)) {
439 if ((dyno_mass == 0) || (dyno_gear == 0)) {
440 return DYNO_USAGE;
441 }
442
443 dyno_calculate_results(results);
444
445 return DYNO_OK;
446}
447
448/*
449 * Smooth results
450 */
451int dyno_smooth_results(dyno_result *results, int size) {
452 dyno_result *rawresults;
453 int i, rv;
454
455 /* smooth results */
456 rv = diag_malloc(&rawresults, size);
457 if (rv != 0) {
458 return rv;
459 }
460 memcpy(rawresults, results, size * sizeof(dyno_result));
461
462 for (i=1; i<size-1; i++) {
463 /* smooth using nearby values */
464 results[i].power = (rawresults[i-1].power + rawresults[i].power + rawresults[i+1].power) / 3;
465 results[i].power_ch = POWER_CH(results[i].power);
466 results[i].torque = (int)(TORQUE(results[i].power, results[i].rpm) + .50);
467 }
468
469 free(rawresults);
470
471 return DYNO_OK;
472}
473
474
475/*
476 * Save measures and results to a file
477 */
478void dyno_save(char *filename, dyno_result *results, int size) {
479 char buffer[MAXRBUF];
480 int i;
481
482 /* open file */
483 FILE *fd = fopen (filename, "w");
484 if (fd == NULL) {
485 printf("Failed opening %s for writing\n", filename);
486 return;
487 }
488
489 printf("Saving dyno to file %s\n", filename);
490
491
492 /* saving mass */
493 printf("-> saving mass...\n");
494
495 sprintf(buffer, "Mass (kg)\t%d\n", dyno_mass);
496 fwrite(buffer, strlen(buffer), sizeof(char), fd);
497
498 sprintf(buffer, "\n");
499 fwrite(buffer, strlen(buffer), sizeof(char), fd);
500
501
502 /* saving results */
503 printf("-> saving results...\n");
504
505 sprintf(buffer, "Run results\n");
506 fwrite(buffer, strlen(buffer), sizeof(char), fd);
507
508 sprintf(buffer, "RPM\tPower (W)\tPower (ch)\tTorque (N.m)\n");
509 fwrite(buffer, strlen(buffer), sizeof(char), fd);
510
511 for (i=0; i<size; i++) {
512 sprintf(buffer, "%d\t%d\t%d\t%d\n",
513 results[i].rpm, results[i].power, results[i].power_ch, results[i].torque);
514 fwrite(buffer, strlen(buffer), sizeof(char), fd);
515 }
516
517 sprintf(buffer, "\n");
518 fwrite(buffer, strlen(buffer), sizeof(char), fd);
519
520
521 /* saving run measures */
522 if (measure_table.nbr > 0) {
523 printf("-> saving run measures...\n");
524
525 sprintf(buffer, "Run measures\n");
526 fwrite(buffer, strlen(buffer), sizeof(char), fd);
527
528 sprintf(buffer, "Time (ms)\tRPM\tSpeed (m/s)\tSpeed (km/h)\n");
529 fwrite(buffer, strlen(buffer), sizeof(char), fd);
530
531 for (i=0; i<measure_table.nbr; i++) {
532 sprintf(buffer, "%d\t%d\t%7.3f\t%7.3f\n",
533 measure_table.meas.measures[i].millis,
534 measure_table.meas.measures[i].rpm,
535 dyno_get_speed_from_rpm(measure_table.meas.measures[i].rpm)/1000.0,
536 dyno_get_speed_from_rpm(measure_table.meas.measures[i].rpm)*3.6/1000.0);
537 fwrite(buffer, strlen(buffer), sizeof(char), fd);
538 }
539
540 sprintf(buffer, "\n");
541 fwrite(buffer, strlen(buffer), sizeof(char), fd);
542 }
543
544
545 /* saving d and f loss parameters */
546 printf("-> saving friction and aerodynamic parameters...\n");
547
548 sprintf(buffer, "d and f loss parameters\n");
549 fwrite(buffer, strlen(buffer), sizeof(char), fd);
550
551 sprintf(buffer, "d\t%8.5f\n", dyno_loss_d);
552 fwrite(buffer, strlen(buffer), sizeof(char), fd);
553
554 sprintf(buffer, "f\t%8.2f\n", dyno_loss_f);
555 fwrite(buffer, strlen(buffer), sizeof(char), fd);
556
557 sprintf(buffer, "\n");
558 fwrite(buffer, strlen(buffer), sizeof(char), fd);
559
560
561 /* saving loss measures, if any */
562 if (loss_measure_table.nbr > 0) {
563
564 printf("-> saving loss measures...\n");
565
566 sprintf(buffer, "Loss measures\n");
567 fwrite(buffer, strlen(buffer), sizeof(char), fd);
568
569 sprintf(buffer, "Time (ms)\tSpeed (m/s)\tSpeed (km/h)\n");
570 fwrite(buffer, strlen(buffer), sizeof(char), fd);
571
572 for (i=0; i<loss_measure_table.nbr; i++) {
573 sprintf(buffer, "%d\t%7.3f\t%7.3f\n",
574 loss_measure_table.meas.loss_measures[i].millis,
575 loss_measure_table.meas.loss_measures[i].speed/1000.0,
576 loss_measure_table.meas.loss_measures[i].speed*3.6/1000.0);
577 fwrite(buffer, strlen(buffer), sizeof(char), fd);
578 }
579 }
580
581 /* flush file to disk */
582 fflush(fd);
583
584 /* close file */
585 fclose(fd);
586
587 printf("Done.\n");
588}
589