Lua memory (de)allocation tracer: (#5191)
[opentx.git] / radio / src / logs.cpp
blob85b76e866c0815cc4c4dc7d5ff178f90e965de95
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
22 #include "ff.h"
24 FIL g_oLogFile __DMA;
25 const pm_char * g_logError = NULL;
26 uint8_t logDelay;
28 void writeHeader();
30 #if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS)
31 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : 1)
32 #else
33 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw) ? -1 : 1)
34 #endif
36 #define GET_3POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : (switchState(SW_ ## sw ## 2) ? 1 : 0))
39 void logsInit()
41 memset(&g_oLogFile, 0, sizeof(g_oLogFile));
44 const pm_char * logsOpen()
46 // Determine and set log file filename
47 FRESULT result;
48 char filename[34]; // /LOGS/modelnamexxx-2013-01-01.log
50 if (!sdMounted())
51 return STR_NO_SDCARD;
53 if (sdGetFreeSectors() == 0)
54 return STR_SDCARD_FULL;
56 // check and create folder here
57 strcpy_P(filename, STR_LOGS_PATH);
58 const char * error = sdCheckAndCreateDirectory(filename);
59 if (error) {
60 return error;
63 filename[sizeof(LOGS_PATH)-1] = '/';
64 memcpy(&filename[sizeof(LOGS_PATH)], g_model.header.name, sizeof(g_model.header.name));
65 filename[sizeof(LOGS_PATH)+sizeof(g_model.header.name)] = '\0';
67 uint8_t i = sizeof(LOGS_PATH)+sizeof(g_model.header.name)-1;
68 uint8_t len = 0;
69 while (i>sizeof(LOGS_PATH)-1) {
70 if (!len && filename[i])
71 len = i+1;
72 if (len) {
73 if (filename[i])
74 filename[i] = idx2char(filename[i]);
75 else
76 filename[i] = '_';
78 i--;
81 if (len == 0) {
82 #if defined(EEPROM)
83 uint8_t num = g_eeGeneral.currModel + 1;
84 #else
85 // TODO
86 uint8_t num = 1;
87 #endif
88 strcpy_P(&filename[sizeof(LOGS_PATH)], STR_MODEL);
89 filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL)] = (char)((num / 10) + '0');
90 filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 1] = (char)((num % 10) + '0');
91 len = sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 2;
94 char * tmp = &filename[len];
96 #if defined(RTCLOCK)
97 tmp = strAppendDate(&filename[len]);
98 #endif
100 strcpy_P(tmp, STR_LOGS_EXT);
102 result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE | FA_OPEN_APPEND);
103 if (result != FR_OK) {
104 return SDCARD_ERROR(result);
107 if (f_size(&g_oLogFile) == 0) {
108 writeHeader();
111 return NULL;
114 tmr10ms_t lastLogTime = 0;
116 void logsClose()
118 if (sdMounted()) {
119 if (f_close(&g_oLogFile) != FR_OK) {
120 // close failed, forget file
121 g_oLogFile.obj.fs = 0;
123 lastLogTime = 0;
127 #if !defined(CPUARM)
128 getvalue_t getConvertedTelemetryValue(getvalue_t val, uint8_t unit)
130 convertUnit(val, unit);
131 return val;
133 #endif
135 void writeHeader()
137 #if defined(RTCLOCK)
138 f_puts("Date,Time,", &g_oLogFile);
139 #else
140 f_puts("Time,", &g_oLogFile);
141 #endif
143 #if defined(TELEMETRY_FRSKY)
144 #if !defined(CPUARM)
145 f_puts("Buffer,RX,TX,A1,A2,", &g_oLogFile);
146 #if defined(FRSKY_HUB)
147 if (IS_USR_PROTO_FRSKY_HUB()) {
148 f_puts("GPS Date,GPS Time,Long,Lat,Course,GPS Speed(kts),GPS Alt,Baro Alt(", &g_oLogFile);
149 f_puts(TELEMETRY_BARO_ALT_UNIT, &g_oLogFile);
150 f_puts("),Vertical Speed,Air Speed(kts),Temp1,Temp2,RPM,Fuel," TELEMETRY_CELLS_LABEL "Current,Consumption,Vfas,AccelX,AccelY,AccelZ,", &g_oLogFile);
152 #endif
153 #if defined(WS_HOW_HIGH)
154 if (IS_USR_PROTO_WS_HOW_HIGH()) {
155 f_puts("WSHH Alt,", &g_oLogFile);
157 #endif
158 #endif
160 #if defined(CPUARM)
161 char label[TELEM_LABEL_LEN+7];
162 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
163 if (isTelemetryFieldAvailable(i)) {
164 TelemetrySensor & sensor = g_model.telemetrySensors[i];
165 if (sensor.logs) {
166 memset(label, 0, sizeof(label));
167 zchar2str(label, sensor.label, TELEM_LABEL_LEN);
168 uint8_t unit = sensor.unit;
169 if (unit == UNIT_CELLS ) unit = UNIT_VOLTS;
170 if (UNIT_RAW < unit && unit < UNIT_FIRST_VIRTUAL) {
171 strcat(label, "(");
172 strncat(label, STR_VTELEMUNIT+1+3*unit, 3);
173 strcat(label, ")");
175 strcat(label, ",");
176 f_puts(label, &g_oLogFile);
180 #endif
181 #endif
183 #if defined(PCBTARANIS) || defined(PCBHORUS)
184 for (uint8_t i=1; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS+1; i++) {
185 const char * p = STR_VSRCRAW + i * STR_VSRCRAW[0] + 2;
186 for (uint8_t j=0; j<STR_VSRCRAW[0]-1; ++j) {
187 if (!*p) break;
188 f_putc(*p, &g_oLogFile);
189 ++p;
191 f_putc(',', &g_oLogFile);
193 #if defined(PCBX7)
194 #define STR_SWITCHES_LOG_HEADER "SA,SB,SC,SD,SF,SH"
195 #else
196 #define STR_SWITCHES_LOG_HEADER "SA,SB,SC,SD,SE,SF,SG,SH"
197 #endif
198 f_puts(STR_SWITCHES_LOG_HEADER ",LSW,", &g_oLogFile);
199 #else
200 f_puts("Rud,Ele,Thr,Ail,P1,P2,P3,THR,RUD,ELE,3POS,AIL,GEA,TRN,", &g_oLogFile);
201 #endif
203 f_puts("TxBat(V)\n", &g_oLogFile);
206 uint32_t getLogicalSwitchesStates(uint8_t first)
208 uint32_t result = 0;
209 for (uint8_t i=0; i<32; i++) {
210 result |= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH+first+i) << i);
212 return result;
215 void logsWrite()
217 static const pm_char * error_displayed = NULL;
219 if (isFunctionActive(FUNCTION_LOGS) && logDelay > 0) {
220 tmr10ms_t tmr10ms = get_tmr10ms();
221 if (lastLogTime == 0 || (tmr10ms_t)(tmr10ms - lastLogTime) >= (tmr10ms_t)logDelay*10) {
222 lastLogTime = tmr10ms;
224 if (!g_oLogFile.obj.fs) {
225 const pm_char * result = logsOpen();
226 if (result != NULL) {
227 if (result != error_displayed) {
228 error_displayed = result;
229 POPUP_WARNING(result);
231 return;
235 #if defined(RTCLOCK)
237 static struct gtm utm;
238 static gtime_t lastRtcTime = 0;
239 if (g_rtcTime != lastRtcTime) {
240 lastRtcTime = g_rtcTime;
241 gettime(&utm);
243 f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100);
245 #else
246 f_printf(&g_oLogFile, "%d,", tmr10ms);
247 #endif
249 #if defined(TELEMETRY_FRSKY)
250 #if !defined(CPUARM)
251 f_printf(&g_oLogFile, "%d,%d,%d,", telemetryStreaming, RAW_FRSKY_MINMAX(telemetryData.rssi[0]), RAW_FRSKY_MINMAX(telemetryData.rssi[1]));
252 for (uint8_t i=0; i<MAX_FRSKY_A_CHANNELS; i++) {
253 int16_t converted_value = applyChannelRatio(i, RAW_FRSKY_MINMAX(telemetryData.analog[i]));
254 f_printf(&g_oLogFile, "%d.%02d,", converted_value/100, converted_value%100);
257 #if defined(FRSKY_HUB)
258 TELEMETRY_BARO_ALT_PREPARE();
260 if (IS_USR_PROTO_FRSKY_HUB()) {
261 f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d,%03d.%04d%c,%03d.%04d%c,%03d.%02d," TELEMETRY_GPS_SPEED_FORMAT TELEMETRY_GPS_ALT_FORMAT TELEMETRY_BARO_ALT_FORMAT TELEMETRY_VSPEED_FORMAT TELEMETRY_ASPEED_FORMAT "%d,%d,%d,%d," TELEMETRY_CELLS_FORMAT TELEMETRY_CURRENT_FORMAT "%d," TELEMETRY_VFAS_FORMAT "%d,%d,%d,",
262 telemetryData.hub.year+2000,
263 telemetryData.hub.month,
264 telemetryData.hub.day,
265 telemetryData.hub.hour,
266 telemetryData.hub.min,
267 telemetryData.hub.sec,
268 telemetryData.hub.gpsLongitude_bp,
269 telemetryData.hub.gpsLongitude_ap,
270 telemetryData.hub.gpsLongitudeEW ? telemetryData.hub.gpsLongitudeEW : '-',
271 telemetryData.hub.gpsLatitude_bp,
272 telemetryData.hub.gpsLatitude_ap,
273 telemetryData.hub.gpsLatitudeNS ? telemetryData.hub.gpsLatitudeNS : '-',
274 telemetryData.hub.gpsCourse_bp,
275 telemetryData.hub.gpsCourse_ap,
276 TELEMETRY_GPS_SPEED_ARGS
277 TELEMETRY_GPS_ALT_ARGS
278 TELEMETRY_BARO_ALT_ARGS
279 TELEMETRY_VSPEED_ARGS
280 TELEMETRY_ASPEED_ARGS
281 telemetryData.hub.temperature1,
282 telemetryData.hub.temperature2,
283 telemetryData.hub.rpm,
284 telemetryData.hub.fuelLevel,
285 TELEMETRY_CELLS_ARGS
286 TELEMETRY_CURRENT_ARGS
287 telemetryData.hub.currentConsumption,
288 TELEMETRY_VFAS_ARGS
289 telemetryData.hub.accelX,
290 telemetryData.hub.accelY,
291 telemetryData.hub.accelZ);
293 #endif
295 #if defined(WS_HOW_HIGH)
296 if (IS_USR_PROTO_WS_HOW_HIGH()) {
297 f_printf(&g_oLogFile, "%d,", TELEMETRY_RELATIVE_BARO_ALT_BP);
299 #endif
300 #endif
302 #if defined(CPUARM)
303 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
304 if (isTelemetryFieldAvailable(i)) {
305 TelemetrySensor & sensor = g_model.telemetrySensors[i];
306 TelemetryItem & telemetryItem = telemetryItems[i];
307 if (sensor.logs) {
308 if (sensor.unit == UNIT_GPS) {
309 if (telemetryItem.gps.longitude && telemetryItem.gps.latitude) {
310 div_t qr = div((int)telemetryItem.gps.latitude, 1000000);
311 f_printf(&g_oLogFile, "%d.%06d ", qr.quot, abs(qr.rem));
312 qr = div((int)telemetryItem.gps.longitude, 1000000);
313 f_printf(&g_oLogFile, "%d.%06d,", qr.quot, abs(qr.rem));
315 else {
316 f_printf(&g_oLogFile, ",");
319 else if (sensor.unit == UNIT_DATETIME) {
320 f_printf(&g_oLogFile, "%4d-%02d-%02d %02d:%02d:%02d,", telemetryItem.datetime.year, telemetryItem.datetime.month, telemetryItem.datetime.day, telemetryItem.datetime.hour, telemetryItem.datetime.min, telemetryItem.datetime.sec);
322 else if (sensor.prec == 2) {
323 div_t qr = div((int)telemetryItem.value, 100);
324 if (telemetryItem.value < 0) f_printf(&g_oLogFile, "-");
325 f_printf(&g_oLogFile, "%d.%02d,", abs(qr.quot), abs(qr.rem));
327 else if (sensor.prec == 1) {
328 div_t qr = div((int)telemetryItem.value, 10);
329 if (telemetryItem.value < 0) f_printf(&g_oLogFile, "-");
330 f_printf(&g_oLogFile, "%d.%d,", abs(qr.quot), abs(qr.rem));
332 else {
333 f_printf(&g_oLogFile, "%d,", telemetryItem.value);
338 #endif
339 #endif
341 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; i++) {
342 f_printf(&g_oLogFile, "%d,", calibratedAnalogs[i]);
345 #if defined(PCBFLAMENCO)
346 f_printf(&g_oLogFile, "%d,%d,%d,%d,",
347 GET_3POS_STATE(SA),
348 GET_3POS_STATE(SB),
349 // GET_3POS_STATE(SC),
350 GET_2POS_STATE(SE),
351 GET_3POS_STATE(SF));
352 #elif defined(PCBX7)
353 f_printf(&g_oLogFile, "%d,%d,%d,%d,%d,%d,0x%08X%08X,",
354 GET_3POS_STATE(SA),
355 GET_3POS_STATE(SB),
356 GET_3POS_STATE(SC),
357 GET_3POS_STATE(SD),
358 GET_2POS_STATE(SF),
359 GET_2POS_STATE(SH),
360 getLogicalSwitchesStates(32),
361 getLogicalSwitchesStates(0));
362 #elif defined(PCBTARANIS) || defined(PCBHORUS)
363 f_printf(&g_oLogFile, "%d,%d,%d,%d,%d,%d,%d,%d,0x%08X%08X,",
364 GET_3POS_STATE(SA),
365 GET_3POS_STATE(SB),
366 GET_3POS_STATE(SC),
367 GET_3POS_STATE(SD),
368 GET_3POS_STATE(SE),
369 GET_2POS_STATE(SF),
370 GET_3POS_STATE(SG),
371 GET_2POS_STATE(SH),
372 getLogicalSwitchesStates(32),
373 getLogicalSwitchesStates(0));
374 #else
375 f_printf(&g_oLogFile, "%d,%d,%d,%d,%d,%d,%d,",
376 GET_2POS_STATE(THR),
377 GET_2POS_STATE(RUD),
378 GET_2POS_STATE(ELE),
379 GET_3POS_STATE(ID),
380 GET_2POS_STATE(AIL),
381 GET_2POS_STATE(GEA),
382 GET_2POS_STATE(TRN));
383 #endif
385 div_t qr = div(g_vbat100mV, 10);
386 int result = f_printf(&g_oLogFile, "%d.%d\n", abs(qr.quot), abs(qr.rem));
388 if (result<0 && !error_displayed) {
389 error_displayed = STR_SDCARD_ERROR;
390 POPUP_WARNING(STR_SDCARD_ERROR);
391 logsClose();
395 else {
396 error_displayed = NULL;
397 if (g_oLogFile.obj.fs) {
398 logsClose();