[log] add logger_utils module

Definitions and tools to abstract ChibiOS/Linux logging.
Use with control effectiveness logger and traffic_info (adding logging
support).

fix alt scaling in ACINFO_LLA message
This commit is contained in:
Gautier Hattenberger
2026-04-02 23:24:30 +02:00
parent 345ae58312
commit 41b5c7ef82
9 changed files with 267 additions and 77 deletions
@@ -15,6 +15,9 @@
<define name="AIRSPEED" value="FALSE|TRUE" description="log airspeed (float)"/>
</section>
</doc>
<dep>
<depends>logger_utils</depends>
</dep>
<header>
<file name="logger_control_effectiveness.h" />
</header>
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="logger_utils" dir="loggers" task="core">
<doc>
<description>
Definitions and tools for logging with ChibiOS and Linux
</description>
</doc>
<header>
<file name="logger_utils.h" />
</header>
<makefile>
<file name="logger_utils.c"/>
<test/>
</makefile>
</module>
+8
View File
@@ -3,11 +3,16 @@
<module name="traffic_info" dir="multi" task="core">
<doc>
<description>Keeps track of other aircraft in airspace</description>
<define name="TRAFFIC_INFO_USE_LOG" value="FALSE|TRUE" description="Enable traffic info logging"/>
</doc>
<dep>
<depends>logger_utils</depends>
</dep>
<header>
<file name="traffic_info.h"/>
</header>
<init fun="traffic_info_init()"/>
<periodic fun="traffic_info_log()" start="traffic_info_log_start()" stop="traffic_info_log_stop()" autorun="FALSE"/>
<datalink message="ACINFO" fun="parse_acinfo_dl(buf)"/>
<datalink message="ACINFO_LLA" fun="parse_acinfo_dl(buf)"/>
@@ -19,5 +24,8 @@
<makefile>
<file name="traffic_info.c"/>
<define name="TRAFFIC_INFO"/>
<test firmware="rotorcraft">
<define name="TRAFFIC_INFO_USE_LOG"/>
</test>
</makefile>
</module>
@@ -24,37 +24,22 @@
*/
#include "modules/loggers/logger_control_effectiveness.h"
#include "modules/loggers/logger_utils"
#include "mcu_periph/sys_time.h"
#include "state.h"
#if USE_CHIBIOS_RTOS
#include "modules/loggers/sdlog_chibios.h"
#define LogWrite sdLogWriteLog
#define LogFileIsOpen() (pprzLogFile != -1)
#define LogOpen(_file) {}
#define LogClose(_file) {}
#define LogFormatHeader "%.5f,%ld,%ld,%ld,%ld,%ld,%ld"
#define LogFormatVect3 ",%ld,%ld,%ld"
#else // assume Linux based OS
#include <stdio.h>
#include <time.h>
#include <unistd.h>
static void open_log(FILE* file);
#define LogWrite fprintf
#define LogFileIsOpen() (pprzLogFile != NULL)
#define LogOpen(_file) open_log(_file)
#define LogClose(_file) { \
fclose(_file); \
_file = NULL; \
}
#define LogFormatHeader "%.5f,%d,%d,%d,%d,%d,%d"
#define LogFormatVect3 ",%d,%d,%d"
static FILE* pprzLogFile = NULL;
/* Set the default log path to bebop storage */
#ifndef LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH
#define LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH /data/ftp/internal_000
#define LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH /data/ftp/internal_000/control_eff
#endif
#endif
@@ -94,9 +79,9 @@ static FILE* pprzLogFile = NULL;
/** Write the log header line according to the enabled parts */
void logger_control_effectiveness_start(void)
{
LogOpen(pprzLogFile);
LogOpen(pprzLogFile, STRINGIFY(LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH), NULL);
if (LogFileIsOpen()) {
if (LogFileIsOpen(pprzLogFile)) {
LogWrite(pprzLogFile, "time,gyro_p,gyro_q,gyro_r,ax,ay,az");
#if LOGGER_CONTROL_EFFECTIVENESS_COMMANDS
for (unsigned int i = 0; i < COMMANDS_NB; i++) {
@@ -123,7 +108,7 @@ void logger_control_effectiveness_start(void)
void logger_control_effectiveness_stop(void)
{
if (LogFileIsOpen()) {
if (LogFileIsOpen(pprzLogFile)) {
LogClose(pprzLogFile);
}
}
@@ -131,7 +116,7 @@ void logger_control_effectiveness_stop(void)
/** Log the values to file */
void logger_control_effectiveness_periodic(void)
{
if (LogFileIsOpen()) {
if (!LogFileIsOpen(pprzLogFile)) {
return;
}
@@ -183,45 +168,3 @@ void logger_control_effectiveness_periodic(void)
LogWrite(pprzLogFile,"\n");
}
#if !USE_CHIBIOS_RTOS
static void open_log(FILE *file)
{
// Create output folder if necessary
if (access(STRINGIFY(LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH), F_OK)) {
char save_dir_cmd[256];
sprintf(save_dir_cmd, "mkdir -p %s", STRINGIFY(LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH));
if (system(save_dir_cmd) != 0) {
printf("[logger] Could not create log file directory %s.\n", STRINGIFY(LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH));
return;
}
}
// Get current date/time for filename
char date_time[80];
time_t now = time(0);
struct tm tstruct;
tstruct = *localtime(&now);
strftime(date_time, sizeof(date_time), "%Y%m%d-%H%M%S", &tstruct);
uint32_t counter = 0;
char filename[512];
// Check for available files
sprintf(filename, "%s/%s.csv", STRINGIFY(LOGGER_CONTROL_EFFECTIVENESS_FILE_PATH), date_time);
while ((file = fopen(filename, "r"))) {
fclose(file);
sprintf(filename, "%s/%s_%05d.csv", STRINGIFY(LOGGER_FILE_PATH), date_time, counter);
counter++;
}
file = fopen(filename, "w");
if(!file) {
printf("[logger] ERROR opening log file %s!\n", filename);
return;
}
printf("[logger] Start logging to %s...\n", filename);
}
#endif
+3 -3
View File
@@ -73,7 +73,7 @@ static void logger_file_write_header(FILE *file) {
fprintf(file, "rpm_ref_1,rpm_ref_2,rpm_ref_3,rpm_ref_4,");
#endif
#ifdef INS_EXT_POSE_H
ins_ext_pos_log_header(file);
ins_ext_pose_log_header(file);
#endif
#ifdef COMMAND_THRUST
fprintf(file, "cmd_thrust,cmd_roll,cmd_pitch,cmd_yaw\n");
@@ -104,7 +104,7 @@ static void logger_file_write_row(FILE *file) {
fprintf(file, "%d,%d,%d,%d,",actuators_bebop.rpm_ref[0],actuators_bebop.rpm_ref[1],actuators_bebop.rpm_ref[2],actuators_bebop.rpm_ref[3]);
#endif
#ifdef INS_EXT_POSE_H
ins_ext_pos_log_data(file);
ins_ext_pose_log_data(file);
#endif
#ifdef COMMAND_THRUST
fprintf(file, "%d,%d,%d,%d\n",
@@ -121,7 +121,7 @@ void logger_file_start(void)
{
// Ensure that the module is running when started with this function
logger_file_logger_file_periodic_status = MODULES_RUN;
// Create output folder if necessary
if (access(STRINGIFY(LOGGER_FILE_PATH), F_OK)) {
char save_dir_cmd[256];
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2026 Gautier Hattenberger <gautier.hattenberger@enac.fr>
*
* This file is part of paparazzi.
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
/** @file modules/loggers/logger_utils.c
* @brief Generic definitions and tools for logging on ChibiOS or Linux
*/
#include "modules/loggers/logger_utils.h"
#if !USE_CHIBIOS_RTOS
#include <time.h>
#include <stdlib.h>
#include <stdint.h>
FILE *open_log(char* path, char* name)
{
// Create output folder if necessary
if (access(path, F_OK)) {
char save_dir_cmd[256];
sprintf(save_dir_cmd, "mkdir -p %s", path);
if (system(save_dir_cmd) != 0) {
printf("[logger] Could not create log file directory %s.\n", path);
return NULL;
}
}
uint32_t counter = 0;
char filename[512];
char date_time[80];
if (name == NULL) {
// Get current date/time for filename
time_t now = time(0);
struct tm tstruct;
tstruct = *localtime(&now);
strftime(date_time, sizeof(date_time), "%Y%m%d-%H%M%S", &tstruct);
sprintf(filename, "%s/%s.csv", path, date_time);
} else {
// use specified name
sprintf(filename, "%s/%s.csv", path, name);
}
// Check for available files
FILE *file;
while ((file = fopen(filename, "r"))) {
fclose(file);
if (name == NULL) {
sprintf(filename, "%s/%s_%05d.csv", path, date_time, counter);
} else {
sprintf(filename, "%s/%s_%05d.csv", path, name, counter);
}
counter++;
}
file = fopen(filename, "w");
if(!file) {
printf("[logger] ERROR opening log file %s!\n", filename);
return NULL;
}
printf("[logger] Start logging to %s...\n", filename);
return file;
}
#endif
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2026 Gautier Hattenberger <gautier.hattenberger@enac.fr>
*
* This file is part of paparazzi.
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
/** @file modules/loggers/logger_utils.h
* @brief Generic definitions and tools for logging on ChibiOS or Linux
*/
#ifndef LOGGER_UTILS_H
#define LOGGER_UTILS_H
#if USE_CHIBIOS_RTOS
#include "modules/loggers/sdlog_chibios.h"
#define LogWrite sdLogWriteLog
#define LogFileIsOpen(_file) (_file != -1)
#define LogOpen(_file, _path, _name) {}
#define LogClose(_file) {}
typedef FileDes LogFile_t;
#else // assume Linux based OS
#include <stdio.h>
#include <unistd.h>
#define LogWrite fprintf
#define LogFileIsOpen(_file) (_file != NULL)
#define LogOpen(_file, _path, _name) { _file = open_log(_path, _name); }
#define LogClose(_file) { \
fclose(_file); \
_file = NULL; \
}
typedef FILE* LogFile_t;
/** Open a log file.
* If the log name already exists, a counter is the file name is automatically incremented.
* @param path log directory
* @param name use current time if NULL, specified name otherwise
* @return file pointer
*/
extern FILE *open_log(char* path, char* name);
#endif
#endif /* LOGGER_UTILS_H */
+78 -11
View File
@@ -30,6 +30,7 @@
#include "generated/flight_plan.h" // NAV_MSL0
#include "modules/datalink/datalink.h"
#include "modules/datalink/telemetry.h"
#include "pprzlink/dl_protocol.h" // datalink messages
#include "pprzlink/messages.h" // telemetry messages
@@ -37,6 +38,19 @@
#include "math/pprz_geodetic_utm.h"
#include "math/pprz_geodetic_wgs84.h"
#if TRAFFIC_INFO_USE_LOG
#include "modules/loggers/logger_utils.h"
#if !USE_CHIBIOS_RTOS
static FILE* pprzLogFile = NULL;
/* Set the default log path to bebop storage */
#ifndef TRAFFIC_INFO_FILE_PATH
#define TRAFFIC_INFO_FILE_PATH /data/ftp/internal_000/acinfo
#endif
#endif // !USE_CHIBIOS_RTOS
#endif // TRAFFIC_INFO_USE_LOG
/* number of ac being tracked */
uint8_t ti_acs_idx;
/* index of ac in the list of traffic info aircraft (ti_acs) */
@@ -55,6 +69,7 @@ static void send_acinfo_lla(struct transport_tx *trans, struct link_device *dev)
uint32_t itow = gps.tow;
uint16_t speed = (uint16_t)(stateGetHorizontalSpeedNorm_f()*10.f);
int16_t climb = (int16_t)(stateGetSpeedEnu_f()->z*10.f);
int32_t alt = (int32_t)(lla->alt/10);
uint8_t ac_id = AC_ID;
// broadcast GPS message
@@ -66,8 +81,20 @@ static void send_acinfo_lla(struct transport_tx *trans, struct link_device *dev)
msg.component_id = 0;
pprzlink_msg_send_ACINFO_LLA(&msg,
&course,
&lla->lat, &lla->lon, &lla->alt,
&lla->lat, &lla->lon, &alt,
&itow, &speed, &climb, &ac_id);
#if TRAFFIC_INFO_USE_LOG
struct LlaCoor_f* lla_f = stateGetPositionLla_f();
struct EnuCoor_f* enu_f = stateGetPositionEnu_f();
if (LogFileIsOpen(pprzLogFile)) {
LogWrite(pprzLogFile, "S,%d,%d,%.7f,%.7f,%.3f,%.3f,%.3f,%.3f,%d,0\n", // 0 at the end to have same length than receive log lines
msg.sender_id, msg.receiver_id,
DegOfRad(lla_f->lat), DegOfRad(lla_f->lon), lla_f->alt,
enu_f->x, enu_f->y, enu_f->z,
itow);
}
#endif
}
void traffic_info_init(void)
@@ -104,10 +131,12 @@ bool parse_acinfo_dl(uint8_t *buf)
{
uint8_t sender_id = SenderIdOfPprzMsg(buf);
uint8_t msg_id = IdOfPprzMsg(buf);
uint8_t class_id = pprzlink_get_msg_class_id(buf);
uint32_t itow = 0;
/* handle telemetry message */
#if PPRZLINK_DEFAULT_VER == 2
if (pprzlink_get_msg_class_id(buf) == DL_telemetry_CLASS_ID) {
if (class_id == DL_telemetry_CLASS_ID) {
#else
if (sender_id > 0) {
#endif
@@ -129,6 +158,7 @@ bool parse_acinfo_dl(uint8_t *buf)
if (climb & 0x200) {
climb |= 0xFC00; // fix for twos complements
}
itow = gps_tow_from_sys_ticks(sys_time.nb_tick);
set_ac_info_lla(sender_id,
DL_GPS_SMALL_lat(buf),
@@ -137,10 +167,11 @@ bool parse_acinfo_dl(uint8_t *buf)
course,
gspeed,
climb,
gps_tow_from_sys_ticks(sys_time.nb_tick));
itow);
}
break;
case DL_GPS: {
itow = DL_GPS_itow(buf);
set_ac_info_utm(sender_id,
DL_GPS_utm_east(buf),
DL_GPS_utm_north(buf),
@@ -149,10 +180,11 @@ bool parse_acinfo_dl(uint8_t *buf)
DL_GPS_course(buf),
DL_GPS_speed(buf),
DL_GPS_climb(buf),
DL_GPS_itow(buf));
itow);
}
break;
case DL_GPS_LLA: {
itow = DL_GPS_LLA_itow(buf);
set_ac_info_lla(sender_id,
DL_GPS_LLA_lat(buf),
DL_GPS_LLA_lon(buf),
@@ -160,7 +192,7 @@ bool parse_acinfo_dl(uint8_t *buf)
DL_GPS_LLA_course(buf),
DL_GPS_LLA_speed(buf),
DL_GPS_LLA_climb(buf),
DL_GPS_LLA_itow(buf));
itow);
}
break;
case DL_GPS_INT: {
@@ -183,6 +215,7 @@ bool parse_acinfo_dl(uint8_t *buf)
int16_t course = (int16_t)(10.f * DegOfRad(atan2f(ned_vel_f.y, ned_vel_f.x))); // decideg
uint16_t gspeed = (uint16_t)(CM_OF_M(FLOAT_VECT2_NORM(ned_vel_f)));
int16_t climb = (int16_t)(CM_OF_M(-ned_vel_f.z));
itow = DL_GPS_INT_tow(buf);
set_ac_info_lla(sender_id,
DL_GPS_INT_lat(buf),
DL_GPS_INT_lon(buf),
@@ -190,17 +223,19 @@ bool parse_acinfo_dl(uint8_t *buf)
course,
gspeed,
climb,
DL_GPS_INT_tow(buf));
itow);
}
break;
default:
return FALSE;
}
/* handle datalink message */
} else {
} else if (class_id == DL_datalink_CLASS_ID) {
switch (msg_id) {
case DL_ACINFO: {
set_ac_info_utm(DL_ACINFO_ac_id(buf),
sender_id = DL_ACINFO_ac_id(buf); // may overwrite GCS id
itow = DL_ACINFO_itow(buf);
set_ac_info_utm(sender_id,
DL_ACINFO_utm_east(buf),
DL_ACINFO_utm_north(buf),
DL_ACINFO_alt(buf) * 10,
@@ -208,24 +243,39 @@ bool parse_acinfo_dl(uint8_t *buf)
DL_ACINFO_course(buf),
DL_ACINFO_speed(buf),
DL_ACINFO_climb(buf),
DL_ACINFO_itow(buf));
itow);
}
break;
case DL_ACINFO_LLA: {
set_ac_info_lla(DL_ACINFO_LLA_ac_id(buf),
sender_id = DL_ACINFO_LLA_ac_id(buf); // may overwrite GCS id
itow = DL_ACINFO_LLA_itow(buf);
set_ac_info_lla(sender_id,
DL_ACINFO_LLA_lat(buf),
DL_ACINFO_LLA_lon(buf),
DL_ACINFO_LLA_alt(buf) * 10,
DL_ACINFO_LLA_course(buf),
DL_ACINFO_LLA_speed(buf),
DL_ACINFO_LLA_climb(buf),
DL_ACINFO_LLA_itow(buf));
itow);
}
break;
default:
return FALSE;
}
} else {
return FALSE; // unsupported class
}
#if TRAFFIC_INFO_USE_LOG
struct LlaCoor_f* lla_f = acInfoGetPositionLla_f(sender_id);
struct EnuCoor_f* enu_f = acInfoGetPositionEnu_f(sender_id);
if (LogFileIsOpen(pprzLogFile)) {
LogWrite(pprzLogFile, "R,%d,%d,%.7f,%.7f,%.3f,%.3f,%.3f,%.3f,%d,%d\n",
sender_id, AC_ID,
DegOfRad(lla_f->lat), DegOfRad(lla_f->lon), lla_f->alt,
enu_f->x, enu_f->y, enu_f->z,
itow, gps.tow);
}
#endif
return TRUE;
}
@@ -548,3 +598,20 @@ void acInfoCalcVelocityEnu_f(uint8_t ac_id)
}
SetBit(ti_acs[ac_nr].status, AC_INFO_VEL_ENU_F);
}
void traffic_info_log_start(void)
{
#if TRAFFIC_INFO_USE_LOG
LogOpen(pprzLogFile, STRINGIFY(TRAFFIC_INFO_FILE_PATH), NULL);
#endif
}
void traffic_info_log_stop(void)
{
#if TRAFFIC_INFO_USE_LOG
if (LogFileIsOpen(pprzLogFile)) {
LogClose(pprzLogFile);
}
#endif
}
+5
View File
@@ -442,6 +442,11 @@ static inline uint32_t acInfoGetItow(uint8_t ac_id)
return ti_acs[ti_acs_id[ac_id]].itow;
}
// Logging functions
#define traffic_info_log() {}
extern void traffic_info_log_start(void);
extern void traffic_info_log_stop(void);
/** @}*/
#endif