Various fixes around Companion trainer mode (#7116)
[opentx.git] / radio / src / logs.cpp
blob2ada6d7fd8c0e419347feb88bebb3ba760863976
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 char * g_logError = nullptr;
26 uint8_t logDelay;
28 void writeHeader();
30 #if defined(PCBTARANIS) || defined(PCBHORUS)
31 int getSwitchState(uint8_t swtch) {
32 int value = getValue(MIXSRC_FIRST_SWITCH + swtch);
33 return (value == 0) ? 0 : (value < 0) ? -1 : +1;
35 #else
36 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw) ? -1 : 1)
37 #define GET_3POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : (switchState(SW_ ## sw ## 2) ? 1 : 0))
38 #endif
40 void logsInit()
42 memset(&g_oLogFile, 0, sizeof(g_oLogFile));
45 const char * logsOpen()
47 // Determine and set log file filename
48 FRESULT result;
49 char filename[34]; // /LOGS/modelnamexxx-2013-01-01.log
51 if (!sdMounted())
52 return STR_NO_SDCARD;
54 if (sdGetFreeSectors() == 0)
55 return STR_SDCARD_FULL;
57 // check and create folder here
58 strcpy(filename, STR_LOGS_PATH);
59 const char * error = sdCheckAndCreateDirectory(filename);
60 if (error) {
61 return error;
64 filename[sizeof(LOGS_PATH)-1] = '/';
65 memcpy(&filename[sizeof(LOGS_PATH)], g_model.header.name, sizeof(g_model.header.name));
66 filename[sizeof(LOGS_PATH)+sizeof(g_model.header.name)] = '\0';
68 uint8_t i = sizeof(LOGS_PATH)+sizeof(g_model.header.name)-1;
69 uint8_t len = 0;
70 while (i>sizeof(LOGS_PATH)-1) {
71 if (!len && filename[i])
72 len = i+1;
73 if (len) {
74 if (filename[i])
75 filename[i] = zchar2char(filename[i]);
76 else
77 filename[i] = '_';
79 i--;
82 if (len == 0) {
83 #if defined(EEPROM)
84 uint8_t num = g_eeGeneral.currModel + 1;
85 #else
86 // TODO
87 uint8_t num = 1;
88 #endif
89 strcpy(&filename[sizeof(LOGS_PATH)], STR_MODEL);
90 filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL)] = (char)((num / 10) + '0');
91 filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 1] = (char)((num % 10) + '0');
92 len = sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 2;
95 char * tmp = &filename[len];
97 #if defined(RTCLOCK)
98 tmp = strAppendDate(&filename[len]);
99 #endif
101 strcpy(tmp, STR_LOGS_EXT);
103 result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE | FA_OPEN_APPEND);
104 if (result != FR_OK) {
105 return SDCARD_ERROR(result);
108 if (f_size(&g_oLogFile) == 0) {
109 writeHeader();
112 return nullptr;
115 tmr10ms_t lastLogTime = 0;
117 void logsClose()
119 if (sdMounted()) {
120 if (f_close(&g_oLogFile) != FR_OK) {
121 // close failed, forget file
122 g_oLogFile.obj.fs = 0;
124 lastLogTime = 0;
129 void writeHeader()
131 #if defined(RTCLOCK)
132 f_puts("Date,Time,", &g_oLogFile);
133 #else
134 f_puts("Time,", &g_oLogFile);
135 #endif
138 char label[TELEM_LABEL_LEN+7];
139 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
140 if (isTelemetryFieldAvailable(i)) {
141 TelemetrySensor & sensor = g_model.telemetrySensors[i];
142 if (sensor.logs) {
143 memset(label, 0, sizeof(label));
144 zchar2str(label, sensor.label, TELEM_LABEL_LEN);
145 uint8_t unit = sensor.unit;
146 if (unit == UNIT_CELLS ) unit = UNIT_VOLTS;
147 if (UNIT_RAW < unit && unit < UNIT_FIRST_VIRTUAL) {
148 strcat(label, "(");
149 strncat(label, STR_VTELEMUNIT+1+3*unit, 3);
150 strcat(label, ")");
152 strcat(label, ",");
153 f_puts(label, &g_oLogFile);
158 #if defined(PCBTARANIS) || defined(PCBHORUS)
159 for (uint8_t i=1; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS+1; i++) {
160 const char * p = STR_VSRCRAW + i * STR_VSRCRAW[0] + 2;
161 for (uint8_t j=0; j<STR_VSRCRAW[0]-1; ++j) {
162 if (!*p) break;
163 f_putc(*p, &g_oLogFile);
164 ++p;
166 f_putc(',', &g_oLogFile);
169 for (uint8_t i=0; i<NUM_SWITCHES; i++) {
170 if (SWITCH_EXISTS(i)) {
171 char s[LEN_SWITCH_NAME + 2];
172 char * temp;
173 temp = getSwitchName(s, SWSRC_FIRST_SWITCH + i * 3);
174 *temp++ = ',';
175 *temp = '\0';
176 f_puts(s, &g_oLogFile);
179 f_puts("LSW,", &g_oLogFile);
180 #else
181 f_puts("Rud,Ele,Thr,Ail,P1,P2,P3,THR,RUD,ELE,3POS,AIL,GEA,TRN,", &g_oLogFile);
182 #endif
184 f_puts("TxBat(V)\n", &g_oLogFile);
187 uint32_t getLogicalSwitchesStates(uint8_t first)
189 uint32_t result = 0;
190 for (uint8_t i=0; i<32; i++) {
191 result |= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH+first+i) << i);
193 return result;
196 void logsWrite()
198 static const char * error_displayed = nullptr;
200 if (isFunctionActive(FUNCTION_LOGS) && logDelay > 0) {
201 tmr10ms_t tmr10ms = get_tmr10ms();
202 if (lastLogTime == 0 || (tmr10ms_t)(tmr10ms - lastLogTime) >= (tmr10ms_t)logDelay*10) {
203 lastLogTime = tmr10ms;
205 if (!g_oLogFile.obj.fs) {
206 const char * result = logsOpen();
207 if (result) {
208 if (result != error_displayed) {
209 error_displayed = result;
210 POPUP_WARNING(result);
212 return;
216 #if defined(RTCLOCK)
218 static struct gtm utm;
219 static gtime_t lastRtcTime = 0;
220 if (g_rtcTime != lastRtcTime) {
221 lastRtcTime = g_rtcTime;
222 gettime(&utm);
224 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);
226 #else
227 f_printf(&g_oLogFile, "%d,", tmr10ms);
228 #endif
230 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
231 if (isTelemetryFieldAvailable(i)) {
232 TelemetrySensor & sensor = g_model.telemetrySensors[i];
233 TelemetryItem & telemetryItem = telemetryItems[i];
234 if (sensor.logs) {
235 if (sensor.unit == UNIT_GPS) {
236 if (telemetryItem.gps.longitude && telemetryItem.gps.latitude) {
237 div_t qr = div((int)telemetryItem.gps.latitude, 1000000);
238 if (telemetryItem.gps.latitude < 0) f_printf(&g_oLogFile, "-");
239 f_printf(&g_oLogFile, "%d.%06d ", abs(qr.quot), abs(qr.rem));
240 qr = div((int)telemetryItem.gps.longitude, 1000000);
241 if (telemetryItem.gps.longitude < 0) f_printf(&g_oLogFile, "-");
242 f_printf(&g_oLogFile, "%d.%06d,", abs(qr.quot), abs(qr.rem));
244 else {
245 f_printf(&g_oLogFile, ",");
248 else if (sensor.unit == UNIT_DATETIME) {
249 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);
251 else if (sensor.prec == 2) {
252 div_t qr = div((int)telemetryItem.value, 100);
253 if (telemetryItem.value < 0) f_printf(&g_oLogFile, "-");
254 f_printf(&g_oLogFile, "%d.%02d,", abs(qr.quot), abs(qr.rem));
256 else if (sensor.prec == 1) {
257 div_t qr = div((int)telemetryItem.value, 10);
258 if (telemetryItem.value < 0) f_printf(&g_oLogFile, "-");
259 f_printf(&g_oLogFile, "%d.%d,", abs(qr.quot), abs(qr.rem));
261 else {
262 f_printf(&g_oLogFile, "%d,", telemetryItem.value);
268 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; i++) {
269 f_printf(&g_oLogFile, "%d,", calibratedAnalogs[i]);
272 #if defined(PCBTARANIS) || defined(PCBHORUS)
273 for (uint8_t i=0; i<NUM_SWITCHES; i++) {
274 if (SWITCH_EXISTS(i)) {
275 f_printf(&g_oLogFile, "%d,", getSwitchState(i));
278 f_printf(&g_oLogFile, "0x%08X%08X,", getLogicalSwitchesStates(32), getLogicalSwitchesStates(0));
279 #else
280 f_printf(&g_oLogFile, "%d,%d,%d,%d,%d,%d,%d,",
281 GET_2POS_STATE(THR),
282 GET_2POS_STATE(RUD),
283 GET_2POS_STATE(ELE),
284 GET_3POS_STATE(ID),
285 GET_2POS_STATE(AIL),
286 GET_2POS_STATE(GEA),
287 GET_2POS_STATE(TRN));
288 #endif
290 div_t qr = div(g_vbat100mV, 10);
291 int result = f_printf(&g_oLogFile, "%d.%d\n", abs(qr.quot), abs(qr.rem));
293 if (result<0 && !error_displayed) {
294 error_displayed = STR_SDCARD_ERROR;
295 POPUP_WARNING(STR_SDCARD_ERROR);
296 logsClose();
300 else {
301 error_displayed = nullptr;
302 if (g_oLogFile.obj.fs) {
303 logsClose();