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.
25 const pm_char
* g_logError
= NULL
;
30 #if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS)
31 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : 1)
33 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw) ? -1 : 1)
36 #define GET_3POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : (switchState(SW_ ## sw ## 2) ? 1 : 0))
41 memset(&g_oLogFile
, 0, sizeof(g_oLogFile
));
44 const pm_char
* logsOpen()
46 // Determine and set log file filename
48 char filename
[34]; // /LOGS/modelnamexxx-2013-01-01.log
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
);
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;
69 while (i
>sizeof(LOGS_PATH
)-1) {
70 if (!len
&& filename
[i
])
74 filename
[i
] = idx2char(filename
[i
]);
83 uint8_t num
= g_eeGeneral
.currModel
+ 1;
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
];
97 tmp
= strAppendDate(&filename
[len
]);
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) {
114 tmr10ms_t lastLogTime
= 0;
119 if (f_close(&g_oLogFile
) != FR_OK
) {
120 // close failed, forget file
121 g_oLogFile
.obj
.fs
= 0;
128 getvalue_t
getConvertedTelemetryValue(getvalue_t val
, uint8_t unit
)
130 convertUnit(val
, unit
);
138 f_puts("Date,Time,", &g_oLogFile
);
140 f_puts("Time,", &g_oLogFile
);
143 #if defined(TELEMETRY_FRSKY)
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
);
153 #if defined(WS_HOW_HIGH)
154 if (IS_USR_PROTO_WS_HOW_HIGH()) {
155 f_puts("WSHH Alt,", &g_oLogFile
);
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
];
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
) {
172 strncat(label
, STR_VTELEMUNIT
+1+3*unit
, 3);
176 f_puts(label
, &g_oLogFile
);
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
) {
188 f_putc(*p
, &g_oLogFile
);
191 f_putc(',', &g_oLogFile
);
194 #define STR_SWITCHES_LOG_HEADER "SA,SB,SC,SD,SF,SH"
196 #define STR_SWITCHES_LOG_HEADER "SA,SB,SC,SD,SE,SF,SG,SH"
198 f_puts(STR_SWITCHES_LOG_HEADER
",LSW,", &g_oLogFile
);
200 f_puts("Rud,Ele,Thr,Ail,P1,P2,P3,THR,RUD,ELE,3POS,AIL,GEA,TRN,", &g_oLogFile
);
203 f_puts("TxBat(V)\n", &g_oLogFile
);
206 uint32_t getLogicalSwitchesStates(uint8_t first
)
209 for (uint8_t i
=0; i
<32; i
++) {
210 result
|= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH
+first
+i
) << i
);
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
);
237 static struct gtm utm
;
238 static gtime_t lastRtcTime
= 0;
239 if (g_rtcTime
!= lastRtcTime
) {
240 lastRtcTime
= g_rtcTime
;
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
);
246 f_printf(&g_oLogFile
, "%d,", tmr10ms
);
249 #if defined(TELEMETRY_FRSKY)
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
,
286 TELEMETRY_CURRENT_ARGS
287 telemetryData
.hub
.currentConsumption
,
289 telemetryData
.hub
.accelX
,
290 telemetryData
.hub
.accelY
,
291 telemetryData
.hub
.accelZ
);
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
);
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
];
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
));
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
));
333 f_printf(&g_oLogFile
, "%d,", telemetryItem
.value
);
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,",
349 // GET_3POS_STATE(SC),
353 f_printf(&g_oLogFile
, "%d,%d,%d,%d,%d,%d,0x%08X%08X,",
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,",
372 getLogicalSwitchesStates(32),
373 getLogicalSwitchesStates(0));
375 f_printf(&g_oLogFile
, "%d,%d,%d,%d,%d,%d,%d,",
382 GET_2POS_STATE(TRN
));
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
);
396 error_displayed
= NULL
;
397 if (g_oLogFile
.obj
.fs
) {