diff --git a/.gitignore b/.gitignore
index 1d6e7da3c1..736e180b72 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,13 @@ paparazzi.sublime-workspace
# JetBrains (PyCharm, etc) IDE project files
.idea
+# QtCreator IDE
+*.creator
+*.creator.user
+*.config
+*.files
+*.includes
+
# Vagrant VM files
/.vagrant
@@ -126,6 +133,7 @@ paparazzi.sublime-workspace
/sw/logalizer/openlog2tlm
/sw/logalizer/ahrs2fg
/sw/logalizer/disp3d
+/sw/logalizer/sdlogger_download
# /sw/simulator/
/sw/simulator/gaia
diff --git a/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml b/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml
new file mode 100644
index 0000000000..0efc58da8c
--- /dev/null
+++ b/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/airframes/TUDelft/conf.xml b/conf/airframes/TUDelft/conf.xml
index 3f8491b59c..969e40342a 100644
--- a/conf/airframes/TUDelft/conf.xml
+++ b/conf/airframes/TUDelft/conf.xml
@@ -87,6 +87,17 @@
settings_modules="modules/geo_mag.xml modules/air_data.xml modules/video_thread.xml modules/video_rtp_stream.xml"
gui_color="red"
/>
+
+
+
+
+ Direct SPI SD Logger that saves pprzlog messages to SD Card.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SDLOGGER_DIRECT_SPI ?= spi2
+ SDLOGGER_DIRECT_SPI_LOWER=$(shell echo $(SDLOGGER_DIRECT_SPI) | tr A-Z a-z)
+ SDLOGGER_DIRECT_SPI_UPPER=$(shell echo $(SDLOGGER_DIRECT_SPI) | tr a-z A-Z)
+
+ SDLOGGER_DIRECT_SPI_SLAVE ?= spi_slave2
+ SDLOGGER_DIRECT_SPI_SLAVE_LOWER=$(shell echo $(SDLOGGER_DIRECT_SPI_SLAVE) | tr A-Z a-z)
+ SDLOGGER_DIRECT_SPI_SLAVE_UPPER=$(shell echo $(SDLOGGER_DIRECT_SPI_SLAVE) | tr a-z A-Z)
+
+ LOGGER_CONTROL_SWITCH ?= RADIO_AUX2
+
+ LOGGER_LED ?= none
+ ifneq ($(LOGGER_LED),none)
+ ap.CFLAGS += -DLOGGER_LED=$(LOGGER_LED)
+ endif
+
+ ap.srcs += subsystems/datalink/downlink.c subsystems/datalink/pprzlog_transport.c
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/telemetry/rotorcraft_with_logger.xml b/conf/telemetry/rotorcraft_with_logger.xml
new file mode 100644
index 0000000000..ae6ffd1307
--- /dev/null
+++ b/conf/telemetry/rotorcraft_with_logger.xml
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sw/airborne/modules/loggers/sdlogger_spi_direct.c b/sw/airborne/modules/loggers/sdlogger_spi_direct.c
new file mode 100644
index 0000000000..c47f0db9a8
--- /dev/null
+++ b/sw/airborne/modules/loggers/sdlogger_spi_direct.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) Bart Slinger
+ *
+ * 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
+ * .
+ */
+/**
+ * @file "modules/loggers/sdlogger_spi_direct.c"
+ * @author Bart Slinger
+ * SPI SD Logger that saves pprzlog messages to SD Card.
+ *
+ * Developed using Test Driven Development.
+ * Test code available at:
+ * https://github.com/bartslinger/paparazzi-unittest
+ */
+
+#define PERIODIC_C_LOGGER
+
+#include "modules/loggers/sdlogger_spi_direct.h"
+
+#ifdef LOGGER_LED
+#define LOGGER_LED_ON LED_ON(LOGGER_LED);
+#define LOGGER_LED_OFF LED_OFF(LOGGER_LED);
+#else
+#define LOGGER_LED_ON {}
+#define LOGGER_LED_OFF {}
+#endif
+
+#ifndef TELEMETRY_MODE_Main_empty
+#warning You need to define a main telemetry mode named "empty" without any \
+ messages in your config file in /conf/telemetry/. \
+ \
+ Add to your main telemetry process.
+#endif
+
+#ifndef TELEMETRY_PROCESS_Logger
+#error "You need to use a telemetry xml file with Logger process!"
+#endif
+
+#ifndef DOWNLINK_DEVICE
+#warning This module can only be used with uart downlink for now.
+#endif
+
+struct sdlogger_spi_periph sdlogger_spi;
+
+/* Private function declarations */
+void sdlogger_spi_direct_block_to_uart(void);
+
+/**
+ * @brief sdlogger_spi_direct_init
+ * Initialize the logger and SD Card.
+ */
+void sdlogger_spi_direct_init(void)
+{
+ /* Initialize the SD Card */
+ sdcard_spi_init(&sdcard1, &(SDLOGGER_SPI_LINK_DEVICE),
+ SDLOGGER_SPI_LINK_SLAVE_NUMBER);
+
+ /* Set values in the struct to their defaults */
+ sdlogger_spi.status = SDLogger_Initializing;
+ sdlogger_spi.next_available_address = 0;
+ sdlogger_spi.last_completed = 0;
+ sdlogger_spi.sdcard_buf_idx = 1;
+
+ /* Fill internal buffer with zeros */
+ for (uint8_t i = 0; i < sizeof(sdlogger_spi.buffer); i++) {
+ sdlogger_spi.buffer[i] = 0;
+ }
+ sdlogger_spi.idx = 0;
+ sdlogger_spi.log_len = 0;
+ sdlogger_spi.command = 0;
+ sdlogger_spi.download_id = 0;
+ sdlogger_spi.download_address = 0;
+ sdlogger_spi.download_length = 0;
+
+ /* Set function pointers in link_device to the logger functions */
+ sdlogger_spi.device.check_free_space = (check_free_space_t)sdlogger_spi_direct_check_free_space;
+ sdlogger_spi.device.put_byte = (put_byte_t)sdlogger_spi_direct_put_byte;
+ sdlogger_spi.device.send_message = (send_message_t)sdlogger_spi_direct_send_message;
+ sdlogger_spi.device.char_available = (char_available_t)sdlogger_spi_direct_char_available;
+ sdlogger_spi.device.get_byte = (get_byte_t)sdlogger_spi_direct_get_byte;
+ sdlogger_spi.device.periph = &sdlogger_spi;
+
+ /* Init pprzlog_tp */
+ pprzlog_transport_init();
+}
+
+/**
+ * @brief sdlogger_spi_direct_periodic
+ * Periodic function called at module frequency
+ */
+void sdlogger_spi_direct_periodic(void)
+{
+ sdcard_spi_periodic(&sdcard1);
+
+ switch (sdlogger_spi.status) {
+ case SDLogger_Initializing:
+ if (sdcard1.status == SDCard_Idle) {
+ sdcard_spi_read_block(&sdcard1, 0x00002000, &sdlogger_spi_direct_index_received);
+ sdlogger_spi.status = SDLogger_RetreivingIndex;
+ }
+ break;
+
+ case SDLogger_Ready:
+ if (radio_control.values[SDLOGGER_CONTROL_SWITCH] > 0 &&
+ sdcard1.status == SDCard_Idle) {
+ LOGGER_LED_ON;
+ sdcard_spi_multiwrite_start(&sdcard1, sdlogger_spi.next_available_address);
+ sdlogger_spi.status = SDLogger_Logging;
+ }
+ break;
+
+ case SDLogger_Logging:
+ /* This line is NOT unit-tested because it is an inline function */
+ #if PERIODIC_TELEMETRY
+ periodic_telemetry_send_Logger(DefaultPeriodic,
+ &pprzlog_tp.trans_tx,
+ &sdlogger_spi.device);
+ #endif
+ /* Check if SD Card buffer is full and SD Card is ready for new data */
+ if (sdlogger_spi.sdcard_buf_idx > 512 &&
+ sdcard1.status == SDCard_MultiWriteIdle) {
+ sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written);
+ }
+ /* Check if switch is flipped to stop logging */
+ if (radio_control.values[SDLOGGER_CONTROL_SWITCH] < 0) {
+ sdlogger_spi.status = SDLogger_LoggingFinalBlock;
+ }
+ break;
+
+ case SDLogger_LoggingFinalBlock:
+ if (sdcard1.status == SDCard_MultiWriteIdle) {
+ if (sdlogger_spi.sdcard_buf_idx > 512) {
+ sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written);
+ }
+ else if (sdlogger_spi.sdcard_buf_idx > 1) {
+ /* Fill with trailing zero's */
+ for (uint16_t i = sdlogger_spi.sdcard_buf_idx; i < (SD_BLOCK_SIZE+1); i++) {
+ sdcard1.output_buf[i] = 0x00;
+ }
+ sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written);
+ }
+ else if (sdlogger_spi.sdcard_buf_idx == 1) {
+ sdcard_spi_multiwrite_stop(&sdcard1);
+ sdlogger_spi.status = SDLogger_StoppedLogging;
+ }
+ }
+ break;
+
+ case SDLogger_StoppedLogging:
+ if (sdcard1.status == SDCard_Idle) {
+ sdcard_spi_read_block(&sdcard1, 0x00002000, &sdlogger_spi_direct_index_received);
+ sdlogger_spi.status = SDLogger_GettingIndexForUpdate;
+ }
+ break;
+
+ case SDLogger_UpdatingIndex:
+ if (sdcard1.status == SDCard_Idle) {
+ LOGGER_LED_OFF;
+ sdlogger_spi.status = SDLogger_Ready;
+ }
+ break;
+
+ case SDLogger_Downloading:
+ if (sdcard1.status == SDCard_Idle) {
+ /* Put bytes to the buffer until all is written or buffer is full */
+ for (uint16_t i = sdlogger_spi.sdcard_buf_idx; i < SD_BLOCK_SIZE; i++) {
+ if(uart_check_free_space(&(DOWNLINK_DEVICE), 1)) {
+ uart_put_byte(&(DOWNLINK_DEVICE), sdcard1.input_buf[i]);
+ }
+ else {
+ /* No free space left, abort for-loop */
+ break;
+ }
+ sdlogger_spi.sdcard_buf_idx++;
+ }
+ /* Request next block if entire buffer was written to uart */
+ if (sdlogger_spi.sdcard_buf_idx >= SD_BLOCK_SIZE) {
+ if (sdlogger_spi.download_length > 0) {
+ sdcard_spi_read_block(&sdcard1, sdlogger_spi.download_address, NULL);
+ sdlogger_spi.download_address++;
+ sdlogger_spi.download_length--;
+ }
+ else {
+ LOGGER_LED_OFF;
+ sdlogger_spi.status = SDLogger_Ready;
+ }
+ sdlogger_spi.sdcard_buf_idx = 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void sdlogger_spi_direct_start(void) {}
+void sdlogger_spi_direct_stop(void) {}
+
+/**
+ * @brief sdlogger_spi_direct_index_received
+ * Callback from SD Card when block at index location is received.
+ */
+void sdlogger_spi_direct_index_received(void)
+{
+
+ switch (sdlogger_spi.status) {
+ case SDLogger_RetreivingIndex:
+ sdlogger_spi.next_available_address = 0x00004000;
+ sdlogger_spi.last_completed = 0;
+ /* Save data for later use
+ sdlogger_spi.next_available_address = (sdcard1.input_buf[0] << 24) |
+ (sdcard1.input_buf[1] << 16) |
+ (sdcard1.input_buf[2] << 8) |
+ (sdcard1.input_buf[3]);
+ sdlogger_spi.last_completed = sdcard1.input_buf[4];
+ */
+
+ /* Ready to start logging */
+ sdlogger_spi.status = SDLogger_Ready;
+ break;
+
+ case SDLogger_GettingIndexForUpdate:
+ /* Copy input buffer to output buffer */
+ for (uint16_t i = 0; i < SD_BLOCK_SIZE; i++) {
+ sdcard1.output_buf[i+6] = sdcard1.input_buf[i];
+ }
+
+ /* Increment last completed log */
+ sdcard1.output_buf[4+6] = ++sdlogger_spi.last_completed;
+ /* Write log info at dedicated location */
+ {
+ uint16_t log_idx_start = 5 + 6 + (sdlogger_spi.last_completed - 1) * 12;
+
+ /* Set start address and length at location that belongs to the log nr */
+ sdcard1.output_buf[log_idx_start+0] = sdlogger_spi.next_available_address >> 24;
+ sdcard1.output_buf[log_idx_start+1] = sdlogger_spi.next_available_address >> 16;
+ sdcard1.output_buf[log_idx_start+2] = sdlogger_spi.next_available_address >> 8;
+ sdcard1.output_buf[log_idx_start+3] = sdlogger_spi.next_available_address >> 0;
+ sdcard1.output_buf[log_idx_start+4] = sdlogger_spi.log_len >> 24;
+ sdcard1.output_buf[log_idx_start+5] = sdlogger_spi.log_len >> 16;
+ sdcard1.output_buf[log_idx_start+6] = sdlogger_spi.log_len >> 8;
+ sdcard1.output_buf[log_idx_start+7] = sdlogger_spi.log_len >> 0;
+ }
+
+ /* Increment and update the next available address */
+ sdlogger_spi.next_available_address += sdlogger_spi.log_len;
+ sdcard1.output_buf[0+6] = sdlogger_spi.next_available_address >> 24;
+ sdcard1.output_buf[1+6] = sdlogger_spi.next_available_address >> 16;
+ sdcard1.output_buf[2+6] = sdlogger_spi.next_available_address >> 8;
+ sdcard1.output_buf[3+6] = sdlogger_spi.next_available_address >> 0;
+
+ sdcard_spi_write_block(&sdcard1, 0x00002000);
+ /* Reset log length */
+ sdlogger_spi.log_len = 0;
+ sdlogger_spi.status = SDLogger_UpdatingIndex;
+ break;
+
+ case SDLogger_GettingIndexForDownload:
+ {
+ uint16_t info_idx = 5 + (sdlogger_spi.download_id - 1) * 12;
+ sdlogger_spi.download_address = (sdcard1.input_buf[info_idx+0] << 24) |
+ (sdcard1.input_buf[info_idx+1] << 16) |
+ (sdcard1.input_buf[info_idx+2] << 8) |
+ (sdcard1.input_buf[info_idx+3] << 0);
+ sdlogger_spi.download_length = (sdcard1.input_buf[info_idx+4] << 24) |
+ (sdcard1.input_buf[info_idx+5] << 16) |
+ (sdcard1.input_buf[info_idx+6] << 8) |
+ (sdcard1.input_buf[info_idx+7] << 0);
+ if (sdlogger_spi.download_length > 0) {
+ /* Request the first block */
+ sdcard_spi_read_block(&sdcard1, sdlogger_spi.download_address, NULL);
+ /* After each read block, incr address, decr length */
+ sdlogger_spi.download_address++;
+ sdlogger_spi.download_length--;
+ sdlogger_spi.status = SDLogger_Downloading;
+ }
+ else {
+ LOGGER_LED_OFF;
+ sdlogger_spi.status = SDLogger_Ready;
+ }
+ sdlogger_spi.sdcard_buf_idx = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+/**
+ * @brief sdlogger_spi_direct_multiwrite_written
+ * Called when a multiwrite is complete. Data stored in the logger buffer is
+ * then moved to the SD Card buffer, which is now available again.
+ */
+void sdlogger_spi_direct_multiwrite_written(void)
+{
+ /* Increment log length */
+ sdlogger_spi.log_len++;
+
+ /* Copy data from logger buffer to SD Card buffer */
+ for (uint8_t i = 0; i < sdlogger_spi.idx; i++) {
+ sdcard1.output_buf[i+1] = sdlogger_spi.buffer[i];
+ }
+ /* Set sdcard buffer index to new value */
+ sdlogger_spi.sdcard_buf_idx = sdlogger_spi.idx + 1;
+ /* And reset the logger buffer index */
+ sdlogger_spi.idx = 0;
+}
+
+void sdlogger_spi_direct_command(void)
+{
+ if (sdcard1.status == SDCard_Idle && sdlogger_spi.command > 0 &&
+ sdlogger_spi.command < 43) {
+ LOGGER_LED_ON;
+ sdcard_spi_read_block(&sdcard1, 0x00002000,
+ &sdlogger_spi_direct_index_received);
+ sdlogger_spi.download_id = sdlogger_spi.command;
+ sdlogger_spi.status = SDLogger_GettingIndexForDownload;
+ }
+ else if (sdcard1.status == SDCard_Idle && sdlogger_spi.command == 255) {
+ telemetry_mode_Main = TELEMETRY_MODE_Main_empty;
+ LOGGER_LED_ON;
+ sdcard_spi_read_block(&sdcard1, 0x00002000, NULL);
+ sdlogger_spi.download_length = 0;
+ sdlogger_spi.sdcard_buf_idx = 0;
+ sdlogger_spi.status = SDLogger_Downloading;
+ }
+ /* Always reset command value back to zero */
+ sdlogger_spi.command = 0;
+}
+
+bool_t sdlogger_spi_direct_check_free_space(struct sdlogger_spi_periph *p, uint8_t len)
+{
+ if (p->status == SDLogger_Logging) {
+ /* Calculating free space in both buffers */
+ if ( (513 - p->sdcard_buf_idx) + (SDLOGGER_BUFFER_SIZE - p->idx) >= len) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void sdlogger_spi_direct_put_byte(struct sdlogger_spi_periph *p, uint8_t data)
+{
+ /* SD Buffer full, write in logger buffer */
+ if (p->sdcard_buf_idx > 512) {
+ if (p->idx < SDLOGGER_BUFFER_SIZE) {
+ p->buffer[p->idx++] = data;
+ }
+ /* else: data lost */
+ }
+ /* Writing directly to SD Card buffer */
+ else {
+ sdcard1.output_buf[p->sdcard_buf_idx++] = data;
+
+ /* Flush buffer */
+ if (p->sdcard_buf_idx > 512 && sdcard1.status == SDCard_MultiWriteIdle) {
+ sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written);
+ }
+ }
+}
+
+void sdlogger_spi_direct_send_message(void *p)
+{
+ (void) p;
+}
+
+int sdlogger_spi_direct_char_available(void *p){
+ (void) p;
+ return 0;
+}
+
+uint8_t sdlogger_spi_direct_get_byte(void *p)
+{
+ (void) p;
+ return 0;
+}
+
+
+
diff --git a/sw/airborne/modules/loggers/sdlogger_spi_direct.h b/sw/airborne/modules/loggers/sdlogger_spi_direct.h
new file mode 100644
index 0000000000..47bd7f62a9
--- /dev/null
+++ b/sw/airborne/modules/loggers/sdlogger_spi_direct.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) Bart Slinger
+ *
+ * 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
+ * .
+ */
+/**
+ * @file "modules/loggers/sdlogger_spi_direct.h"
+ * @author Bart Slinger
+ * SPI SD Logger that saves pprzlog messages to SD Card.
+ */
+
+#ifndef SDLOGGER_SPI_H
+#define SDLOGGER_SPI_H
+
+#define SDLOGGER_BUFFER_SIZE 128
+
+#include "std.h"
+#include "mcu_periph/link_device.h"
+#include "subsystems/radio_control.h"
+#include "subsystems/datalink/pprzlog_transport.h"
+#include "subsystems/datalink/telemetry.h"
+#include "peripherals/sdcard_spi.h"
+#include "mcu_periph/uart.h"
+#include "led.h"
+
+#include "generated/periodic_telemetry.h"
+
+enum SDLoggerStatus {
+ SDLogger_UnInit,
+ SDLogger_Error,
+ SDLogger_Initializing,
+ SDLogger_RetreivingIndex,
+ SDLogger_Ready,
+ SDLogger_Logging,
+ SDLogger_LoggingFinalBlock,
+ SDLogger_StoppedLogging,
+ SDLogger_GettingIndexForUpdate,
+ SDLogger_UpdatingIndex,
+ SDLogger_GettingIndexForDownload,
+ SDLogger_Downloading
+};
+
+struct sdlogger_spi_periph{
+ enum SDLoggerStatus status;
+ uint32_t next_available_address;
+ uint8_t last_completed;
+ uint16_t sdcard_buf_idx;
+ uint8_t buffer[SDLOGGER_BUFFER_SIZE];
+ uint8_t idx;
+ uint32_t log_len;
+ uint8_t command;
+ uint8_t download_id;
+ uint32_t download_address;
+ uint32_t download_length;
+ struct link_device device;
+};
+
+extern struct sdlogger_spi_periph sdlogger_spi;
+
+extern void sdlogger_spi_direct_init(void);
+extern void sdlogger_spi_direct_periodic(void);
+extern void sdlogger_spi_direct_start(void);
+extern void sdlogger_spi_direct_stop(void);
+
+extern void sdlogger_spi_direct_index_received(void);
+extern void sdlogger_spi_direct_multiwrite_written(void);
+extern void sdlogger_spi_direct_command(void);
+
+extern bool_t sdlogger_spi_direct_check_free_space(struct sdlogger_spi_periph *p, uint8_t len);
+extern void sdlogger_spi_direct_put_byte(struct sdlogger_spi_periph *p, uint8_t data);
+extern void sdlogger_spi_direct_send_message(void *p);
+extern int sdlogger_spi_direct_char_available(void *p);
+extern uint8_t sdlogger_spi_direct_get_byte(void *p);
+
+#endif
+
diff --git a/sw/airborne/peripherals/sdcard_spi.c b/sw/airborne/peripherals/sdcard_spi.c
index 90603b1993..88f7268d84 100644
--- a/sw/airborne/peripherals/sdcard_spi.c
+++ b/sw/airborne/peripherals/sdcard_spi.c
@@ -35,6 +35,11 @@
* The following resource was used as implementation reference: http://elm-chan.org/docs/mmc/mmc_e.html
* The initialization procedure is implemented according to the following diagram. Only the branches for SD ver.2 are currently included.
* \image html airborne/sdinit.png
+ *
+ * Developed using Test Driven Development.
+ * Test code available at:
+ * https://github.com/bartslinger/paparazzi-unittest
+ *
* @todo CRC checksums are not implemented. Fake values of 0xFF are used and they are ignored by the card.
*/
@@ -380,8 +385,8 @@ void sdcard_spi_spicallback(struct spi_transaction *t)
/* Data block received in buffer, process data */
case SDCard_ReadingDataBlock:
sdcard1.status = SDCard_Idle;
- if (sdcard1.read_callback != NULL) {
- sdcard1.read_callback();
+ if (sdcard1.external_callback != NULL) {
+ sdcard1.external_callback();
}
break;
@@ -408,6 +413,9 @@ void sdcard_spi_spicallback(struct spi_transaction *t)
case SDCard_MultiWriteWriting:
if ((sdcard1.input_buf[SD_BLOCK_SIZE + 3] & 0x0F) == 0x05 /* Data accepted */) {
sdcard1.status = SDCard_MultiWriteBusy;
+ if(sdcard1.external_callback != NULL) {
+ sdcard1.external_callback();
+ }
} else {
sdcard1.status = SDCard_Error;
}
@@ -576,7 +584,7 @@ void sdcard_spi_read_block(struct SDCard *sdcard, uint32_t addr, SDCardCallback
}
/* Set function to be called after the read action has finished. */
- sdcard->read_callback = callback;
+ sdcard->external_callback = callback;
/* Send command 17 (read block) to the SDCard */
sdcard_spi_send_cmd(sdcard, 17, addr);
@@ -611,7 +619,7 @@ void sdcard_spi_multiwrite_start(struct SDCard *sdcard, uint32_t addr)
* Use only after sdcard_spi_multiwrite_start().
* @param sdcard Pointer to the SDCard.
*/
-void sdcard_spi_multiwrite_next(struct SDCard *sdcard)
+void sdcard_spi_multiwrite_next(struct SDCard *sdcard, SDCardCallback callback)
{
/* Can only write next block if card is in multiwrite mode and not currently busy */
if (sdcard->status != SDCard_MultiWriteIdle) {
@@ -627,6 +635,7 @@ void sdcard_spi_multiwrite_next(struct SDCard *sdcard)
/* Set the callback */
sdcard->spi_t.after_cb = &sdcard_spi_spicallback;
+ sdcard->external_callback = callback;
/* Submit the spi transaction */
spi_submit(sdcard->spi_p, &sdcard->spi_t);
diff --git a/sw/airborne/peripherals/sdcard_spi.h b/sw/airborne/peripherals/sdcard_spi.h
index 8b9ae5ceb6..c8a976083e 100644
--- a/sw/airborne/peripherals/sdcard_spi.h
+++ b/sw/airborne/peripherals/sdcard_spi.h
@@ -96,7 +96,7 @@ struct SDCard {
uint8_t response_counter; /**< Response counter used at various locations */
uint32_t timeout_counter; /**< Timeout counter used for initialization checks with ACMD41 */
enum SDCardType card_type; /**< Type of SDCard */
- SDCardCallback read_callback; /**< Callback to call when read operation finishes */
+ SDCardCallback external_callback; /**< Callback to call when external operation finishes */
};
extern struct SDCard sdcard1;
@@ -107,7 +107,7 @@ extern void sdcard_spi_periodic(struct SDCard *sdcard);
extern void sdcard_spi_write_block(struct SDCard *sdcard, uint32_t addr);
extern void sdcard_spi_read_block(struct SDCard *sdcard, uint32_t addr, SDCardCallback callback);
extern void sdcard_spi_multiwrite_start(struct SDCard *sdcard, uint32_t addr);
-extern void sdcard_spi_multiwrite_next(struct SDCard *sdcard);
+extern void sdcard_spi_multiwrite_next(struct SDCard *sdcard, SDCardCallback callback);
extern void sdcard_spi_multiwrite_stop(struct SDCard *sdcard);
#endif // SDCARD_H_
diff --git a/sw/logalizer/Makefile b/sw/logalizer/Makefile
index 94dd243465..57cd3f890e 100644
--- a/sw/logalizer/Makefile
+++ b/sw/logalizer/Makefile
@@ -30,7 +30,7 @@ LINKPKG = $(PKG) -linkpkg -dllpath-pkg pprz
XPKG = -package pprz.xlib
XLINKPKG = $(XPKG) -linkpkg -dllpath-pkg pprz.xlib
-all: play plotter logplotter sd2log plotprofile openlog2tlm
+all: play plotter logplotter sd2log plotprofile openlog2tlm sdlogger_download
play : log_file.cmo play_core.cmo play.cmo $(LIBPPRZCMA)
@echo OL $@
@@ -52,6 +52,9 @@ sd2log : sd2log.cmo $(LIBPPRZCMA)
@echo OL $@
$(Q)$(OCAMLC) $(INCLUDES) -o $@ $(LINKPKG) $^
+sdlogger_download: sdlogger_download.c
+ @echo CC $@
+ $(Q)$(CC) $(CFLAGS) -std=gnu99 -Wno-unused-result -o $@ $^
# Target for bytecode executable (if ocamlopt is not available)
# plot : log_file.cmo gtk_export.cmo export.cmo plot.cmo
@@ -135,7 +138,7 @@ ctrlstick: ctrlstick.c
clean:
- $(Q)rm -f *.opt *.out *~ core *.o *.bak .depend *.cm* play ahrs2fg logplotter plotter gtk_export.ml openlog2tlm disp3d plotprofile tmclient ffjoystick ctrlstick sd2log
+ $(Q)rm -f *.opt *.out *~ core *.o *.bak .depend *.cm* play ahrs2fg logplotter plotter gtk_export.ml openlog2tlm disp3d plotprofile tmclient ffjoystick ctrlstick sd2log sdlogger_download
.PHONY: all clean
diff --git a/sw/logalizer/sdlogger_download.c b/sw/logalizer/sdlogger_download.c
new file mode 100644
index 0000000000..1442f2511b
--- /dev/null
+++ b/sw/logalizer/sdlogger_download.c
@@ -0,0 +1,655 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define PPRZ_STX 0x99
+
+/* File pointer for temp.tlm */
+FILE *fp;
+
+/* File pointer for serial link */
+int fd = 0;
+
+/* Paths */
+char sd2log[256];
+char pycommand[256];
+
+/* Setting associated with sdlogger_spi.command */
+unsigned char setting = 0;
+
+/* Global variable to check need for input */
+static volatile bool need_input = true;
+
+/* Global states enum and definition */
+enum global_states {
+ Idle,
+ WaitingForIndexRequestConfirmation,
+ ReadingIndexBlock,
+ GotIndex,
+ Downloading
+};
+enum global_states global_state = Idle;
+
+/* Counter for reading bytes in the index block */
+int index_cnt = 0;
+
+/* PPRZ message parser states */
+enum normal_parser_states {
+ SearchingPPRZ_STX,
+ ParsingLength,
+ ParsingSenderId,
+ ParsingMsgId,
+ ParsingMsgPayload,
+ CheckingCRCA,
+ CheckingCRCB
+};
+
+struct normal_parser_t {
+ enum normal_parser_states state;
+ unsigned char length;
+ int counter;
+ unsigned char sender_id;
+ unsigned char msg_id;
+ unsigned char payload[100];
+ unsigned char crc_a;
+ unsigned char crc_b;
+};
+
+struct normal_parser_t parser;
+
+/* Struct and definition of log info (read from index block) */
+struct log_info_t {
+ uint32_t address;
+ uint32_t length;
+ unsigned int reserved;
+};
+
+struct log_index_t {
+ uint32_t next_available_address;
+ uint8_t last_completed_log;
+ struct log_info_t logs[42];
+} __attribute__((packed));
+
+/* Log index initialization */
+bool index_available = false;
+struct log_index_t log_index;
+
+/* Identifier of the log that is currently being downloaded */
+uint8_t current_download = 0;
+
+
+/* Function definitions */
+void process_command(char *command);
+
+void new_logfile(void)
+{
+ fp = fopen("temp.tlm", "w+");
+}
+
+void close_logfile(void)
+{
+ fclose(fp);
+}
+
+
+/* For ctrl+c */
+static volatile int keep_running = 1;
+static volatile int searching = 1;
+void intHandler(int dummy) {
+ printf("\n");
+ keep_running = 0;
+}
+
+unsigned int get_baud(unsigned int baud_rate)
+{
+ unsigned int BAUD;
+ switch (baud_rate)
+ {
+ case 921600:
+ BAUD = B921600;
+ break;
+ case 460800:
+ BAUD = B460800;
+ break;
+ case 230400:
+ BAUD = B230400;
+ break;
+ case 115200:
+ BAUD = B115200;
+ break;
+ default:
+ printf("Baud rate not recognized, using default B57600\n");
+ case 57600:
+ BAUD = B57600;
+ break;
+ case 38400:
+ BAUD = B38400;
+ break;
+ case 19200:
+ BAUD = B19200;
+ break;
+ case 9600:
+ BAUD = B9600;
+ break;
+ case 4800:
+ BAUD = B4800;
+ break;
+ case 2400:
+ BAUD = B2400;
+ break;
+ case 1800:
+ BAUD = B1800;
+ break;
+ case 1200:
+ BAUD = B1200;
+ break;
+ case 600:
+ BAUD = B600;
+ break;
+ case 300:
+ BAUD = B300;
+ break;
+ case 200:
+ BAUD = B200;
+ break;
+ case 150:
+ BAUD = B150;
+ break;
+ case 134:
+ BAUD = B134;
+ break;
+ case 110:
+ BAUD = B110;
+ break;
+ case 75:
+ BAUD = B75;
+ break;
+ case 50:
+ BAUD = B50;
+ break;
+ } //end of switch baud_rate
+ return BAUD;
+}
+
+int serial_init(char *port_name, unsigned int baud)
+{
+ struct termios orig_termios, cur_termios;
+
+ int br = get_baud(baud);
+
+ fd = open(port_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+ if (fd == -1) {
+ printf("opening modem serial device : fd < 0\n");
+ return -1;
+ }
+
+ if (tcgetattr(fd, &orig_termios)) {
+ printf("getting modem serial device attr\n");
+ return -2;
+ }
+ cur_termios = orig_termios;
+
+ /* input modes - turn off input processing */
+ cur_termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
+ | ICRNL | IXON | IXANY | IXOFF | IMAXBEL);
+ /* pas IGNCR sinon il vire les 0x0D */
+ cur_termios.c_iflag |= BRKINT;
+
+ /* output_flags - turn off output processing */
+ cur_termios.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR | ONLRET);
+
+ /* control modes */
+ cur_termios.c_cflag &= ~(CSIZE | CSTOPB | CREAD | PARENB | PARODD | HUPCL | CLOCAL);
+ cur_termios.c_cflag |= CREAD | CS8 | CLOCAL;
+ cur_termios.c_cflag &= ~(CRTSCTS);
+
+ /* local modes */
+ cur_termios.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO | FLUSHO | PENDIN);
+ cur_termios.c_lflag |= NOFLSH;
+
+ if (cfsetspeed(&cur_termios, br)) {
+ printf("setting modem serial device speed\n");
+ return -3;
+ }
+
+ if (tcsetattr(fd, TCSADRAIN, &cur_termios)) {
+ printf("setting modem serial device attr\n");
+ return -4;
+ }
+
+ return 0;
+}
+
+void open_port(const char* device) {
+ fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd == -1) {
+ fprintf(stderr, "open_port: unable to open device %s - ", device);
+ perror(NULL);
+ exit(EXIT_FAILURE);
+ }
+ // setup connection options
+ struct termios options;
+
+ // get the current options
+ tcgetattr(fd, &options);
+
+ // set local mode, enable receiver, set comm. options:
+ // 8 data bits, no parity, 57600 Baud AND 2 STOP BITS @#!
+ options.c_cflag = CLOCAL | CREAD | CS8 | B57600;// | CSTOPB;
+
+ // write options back to port
+ tcsetattr(fd, TCSANOW, &options);
+}
+
+void close_port(void)
+{
+ close(fd);
+}
+
+void write_command(float value)
+{
+ unsigned char msg[12];
+ unsigned char crc_a = 0;
+ unsigned char crc_b = 0;
+ unsigned char length = 12;
+ unsigned char *pc;
+ pc = (unsigned char*)&value;
+
+ msg[0] = PPRZ_STX;
+ msg[1] = length;
+ crc_a += length; crc_b += crc_a;
+
+ msg[2] = 0; // Sender ID
+ crc_a += 0; crc_b += crc_a;
+
+ msg[3] = 4; // MSG_ID = SETTING
+ crc_a += 4; crc_b += crc_a;
+
+ msg[4] = setting; // setting index
+ crc_a += setting; crc_b += crc_a;
+
+ msg[5] = 114; // AC_ID
+ crc_a += 114; crc_b += crc_a;
+
+ msg[6] = pc[0]; // value
+ crc_a += msg[6]; crc_b += crc_a;
+ msg[7] = pc[1]; // value
+ crc_a += msg[7]; crc_b += crc_a;
+ msg[8] = pc[2]; // value
+ crc_a += msg[8]; crc_b += crc_a;
+ msg[9] = pc[3]; // value
+ crc_a += msg[9]; crc_b += crc_a;
+ msg[10] = crc_a;
+ msg[11] = crc_b;
+
+ write(fd, msg, length);
+}
+
+/*
+ * PPRZ-message: ABCxxxxxxxDE
+ A PPRZ_STX (0x99)
+ B LENGTH (A->E)
+ C PPRZ_DATA
+ 0 SENDER_ID
+ 1 MSG_ID
+ 2 MSG_PAYLOAD
+ . DATA (messages.xml)
+ D PPRZ_CHECKSUM_A (sum[B->C])
+ E PPRZ_CHECKSUM_B (sum[ck_a])
+*/
+void parse_single_byte(unsigned char byte)
+{
+ switch (parser.state) {
+ printf("GOT BYTE %u\n", byte);
+ case SearchingPPRZ_STX:
+ if (byte == PPRZ_STX) {
+ //printf("Got PPRZ_STX\n");
+ parser.crc_a = 0;
+ parser.crc_b = 0;
+ parser.counter = 1;
+ parser.state = ParsingLength;
+ }
+ break;
+
+ case ParsingLength:
+ parser.length = byte;
+ parser.crc_a += byte;
+ parser.crc_b += parser.crc_a;
+ parser.counter++;
+ parser.state = ParsingSenderId;
+ break;
+
+ case ParsingSenderId:
+ parser.sender_id = byte;
+ parser.crc_a += byte;
+ parser.crc_b += parser.crc_a;
+ parser.counter++;
+ parser.state = ParsingMsgId;
+ break;
+
+ case ParsingMsgId:
+ parser.msg_id = byte;
+ parser.crc_a += byte;
+ parser.crc_b += parser.crc_a;
+ parser.counter++;
+ parser.state = ParsingMsgPayload;
+ break;
+
+ case ParsingMsgPayload:
+ parser.payload[parser.counter-4] = byte;
+ parser.crc_a += byte;
+ parser.crc_b += parser.crc_a;
+ parser.counter++;
+ if (parser.counter == parser.length - 2) {
+ parser.state = CheckingCRCA;
+ }
+ break;
+
+ case CheckingCRCA:
+ //printf("CRCA: %d vs %d\n", byte, parser.crc_a);
+ if (byte == parser.crc_a) {
+ parser.state = CheckingCRCB;
+ }
+ else {
+ parser.state = SearchingPPRZ_STX;
+ }
+ break;
+
+ case CheckingCRCB:
+ //printf("CRCB: %d vs %d\n", byte, parser.crc_b);
+ if (byte == parser.crc_b && parser.msg_id == 31) {
+ /*printf("MSG ID: %d \t"
+ "SENDER_ID: %d\t"
+ "LEN: %d\t"
+ "SETTING: %d\n",
+ parser.msg_id,
+ parser.sender_id,
+ parser.length,
+ parser.payload[0]);*/
+ //printf("Request confirmed\n");
+
+ /* Check what to do next if the command was received */
+ if (global_state == WaitingForIndexRequestConfirmation
+ && parser.payload[0] == 60) {
+ global_state = ReadingIndexBlock;
+ index_cnt = 0;
+ }
+ if (global_state == GotIndex
+ && parser.payload[0] == 60) {
+ global_state = Downloading;
+ new_logfile();
+ index_cnt = 0;
+ }
+ }
+ parser.state = SearchingPPRZ_STX;
+ break;
+
+ default:
+ /* Should never get here */
+ break;
+ }
+
+}
+
+void parse_index_byte(unsigned char byte)
+{
+ unsigned char *pc;
+ pc = (unsigned char*)&log_index;
+ pc[index_cnt] = byte;
+ index_cnt++;
+ if (index_cnt >= 512) {
+ /* Plot results: */
+ printf("\n\nNumber of Logs: %u\n", log_index.last_completed_log);
+ printf("Log number \t Address \t Length [bytes]\n");
+ for (uint8_t i = 0; i < log_index.last_completed_log; i++) {
+ printf("%u \t\t 0x%08x \t %u \n",
+ i+1,
+ be32toh(log_index.logs[i].address),
+ be32toh(log_index.logs[i].length)*512);
+ }
+ searching = false;
+ global_state = GotIndex;
+ }
+}
+
+void parse_download_byte(unsigned char byte)
+{
+ static long long dcnt = 0;
+ dcnt++;
+ //printf("Got \t (%d) \t 0x%02x\n", dcnt, byte);
+ fputc(byte, fp);
+
+ /* Show progress */
+ if (dcnt % 512 == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ /* Show progress every 10% */
+ if ( dcnt % (be32toh(log_index.logs[current_download-1].length)*512/10) == 0 ){
+ int percent = (dcnt+1)*100/(be32toh(log_index.logs[current_download-1].length)*512);
+ printf("%i%%", percent);
+ fflush(stdout);
+ }
+
+ if (dcnt >= be32toh(log_index.logs[current_download-1].length)*512){
+ /* Download finished */
+ printf("\nDownloaded log %u\n", current_download);
+ /* Close temp file */
+ close_logfile();
+ /* Process data into log format */
+ system(sd2log);
+ /* Remove file */
+ remove("temp.tlm");
+ /* Reset and get ready for next command */
+ need_input = true;
+ dcnt = 0;
+ }
+}
+
+void parse_bytes(const unsigned char *buff, int len)
+{
+ /* For each received byte call the appropriate function */
+ for (int i = 0; i < len; i++) {
+ switch (global_state) {
+
+ /* Parse as pprz message */
+ case Idle:
+ case GotIndex:
+ case WaitingForIndexRequestConfirmation:
+ parse_single_byte(buff[i]);
+ break;
+
+ /* Read as index-block format */
+ case ReadingIndexBlock:
+ parse_index_byte(buff[i]);
+ break;
+
+ /* Download raw log data */
+ case Downloading:
+ parse_download_byte(buff[i]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return;
+}
+
+bool in_download_range(int download_id)
+{
+ if (download_id <= log_index.last_completed_log &&
+ download_id > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void process_command(char *command)
+{
+ char *token;
+ char *search = " ";
+ strtok(command, "\n");
+
+ token = strtok(command, search);
+
+ if (strcmp(token, "download") == 0) {
+ token = strtok(NULL, search);
+ int download_id = atoi(token);
+ if (in_download_range(download_id)) {
+ printf("Downloading %i", download_id);
+ write_command(download_id);
+ current_download = download_id;
+ global_state = GotIndex;
+ }
+ else {
+ printf("Log ID %i outside available range\n", download_id);
+ need_input = true;
+ }
+ }
+ else if (strcmp(token, "exit") == 0) {
+ keep_running = false;
+ }
+ else {
+ printf("Possible commands:\n"
+ " download \n"
+ " help\n"
+ " exit\n");
+ need_input = true;
+ }
+}
+
+/* Main function */
+int main ( int argc, char** argv)
+{
+
+ /* Default settings for serial connection */
+ char *port = "/dev/ttyUSB0";
+ int baud = 57600;
+
+ /* Serial read buffer */
+ int bytes;
+ unsigned char buff[32];
+
+ /* Aircraft ID */
+ int ac_id = 114;
+
+ /* Parse arguments */
+ int c;
+ while ((c = getopt (argc, argv, "a:p:b:h")) != -1) {
+ switch (c)
+ {
+ case 'a':
+ ac_id = atoi(optarg);
+ break;
+ case 'p':
+ port = optarg;
+ break;
+ case 'b':
+ baud = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ printf("usage: sdlogger_download [options]\n"
+ " options:\n"
+ " -a \tAircraft ID\n"
+ " -p \tPort (default: /dev/ttyUSB0).\n"
+ " -b \tBaudrate (default: 57600).\n"
+ " -h \tHelp, shows this message.\n");
+ exit(0);
+ }
+ }
+
+ /* Obtain sd2log directory */
+ char *pprz_home;
+ pprz_home = getenv( "PAPARAZZI_HOME" );
+ strcat(sd2log, pprz_home);
+ strcat(sd2log, "/sw/logalizer/sd2log temp.tlm");
+
+ /* Get the setting ID with a python script */
+ /* TODO: would be nicer to have a C xml parser */
+ FILE *in = NULL;
+ strcat(pycommand, pprz_home);
+ strcat(pycommand, "/sw/logalizer/sdlogger_get_setting_id.py %u sdlogger_spi.command");
+ char new_command[256];
+ sprintf(new_command, pycommand, ac_id, "ab");
+ strcpy(pycommand, new_command);
+
+ char returnvalue[128];
+ in = popen(pycommand, "r");
+ fgets(returnvalue, 128, in);
+ setting = atoi(returnvalue);
+ pclose(in);
+ if (setting == 0) {
+ printf("Aborting: %s\n", returnvalue);
+ exit(0);
+ }
+
+ /* Enable Ctrl+C */
+ signal(SIGINT, intHandler);
+
+ /* Open serial port */
+ printf("Opening port %s with baudrate B%i\n", port, baud);
+ if(serial_init(port, baud) < 0){
+ return -1;
+ }
+
+ /* Keep polling for logger */
+ printf("Searching for logger..");
+ time_t counter = time(0);
+ time_t timeout = 1; // every second
+ while (keep_running && searching) {
+ if (time(0) - counter >= timeout) {
+ printf(".");
+ fflush(stdout);
+ write_command(255);
+ global_state = WaitingForIndexRequestConfirmation;
+ counter = time(0);
+ }
+ else {
+ bytes = read(fd, (unsigned char*) buff, 32);
+ parse_bytes(buff, bytes);
+ usleep(5000);
+ }
+ }
+
+ char command[128];
+ strcpy(command, "help");
+ /* Show available commands */
+ printf("\n");
+ process_command(command);
+
+
+ while (keep_running) {
+ if (need_input) {
+ need_input = false;
+ /* Test user input */
+ printf("Command> ");
+ fgets (command, 128, stdin);
+ process_command(command);
+ }
+ else {
+ bytes = read(fd, (unsigned char*) buff, 32);
+ parse_bytes(buff, bytes);
+ }
+ }
+
+ printf("Closing app\n");
+
+ /* Close serial port */
+ close_port();
+
+ return 0;
+}
+
diff --git a/sw/logalizer/sdlogger_get_setting_id.py b/sw/logalizer/sdlogger_get_setting_id.py
new file mode 100755
index 0000000000..e85883726a
--- /dev/null
+++ b/sw/logalizer/sdlogger_get_setting_id.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import sys
+from os import path, getenv
+
+# if PAPARAZZI_SRC not set, then assume the tree containing this
+# file is a reasonable substitute
+PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../')))
+sys.path.append(PPRZ_SRC + "/sw/lib/python")
+from settings_xml_parse import PaparazziACSettings
+
+if __name__ == '__main__':
+ ac_id = int(sys.argv[1])
+ command_name = sys.argv[2]
+ settings = PaparazziACSettings(ac_id)
+ print settings.name_lookup[command_name].index