Digital cam pwm trigger (#3636)
Issues due date / Add labels to issues (push) Has been cancelled
Doxygen / build (push) Has been cancelled

* [gps] send NMEA frames to external sensors
* [digital_cam] add pwm trigger module
This commit is contained in:
Gautier Hattenberger
2026-04-13 10:02:14 +02:00
committed by GitHub
parent eaeee4879a
commit 879c971eb0
6 changed files with 641 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
<!DOCTYPE module SYSTEM "./module.dtd">
<module name="digital_cam_pwm" dir="digital_cam">
<doc>
<description>
Trigger using pwm cameras like MAPIR (https://www.mapir.camera/en-gb)
or ThermalCapture 2.0 from TeAx (https://thermalcapture.com/thermalcapture-2-0/)
**** MAPIR ****
Duty cycle == 2000us : TRIGGER_ON
Duty cycle == 1500us : SD Unmount
Duty cycle == 1000us : TRIGGER_OFF
Image capture frequency : 1.5s for JPG and 2.5-3.0s RAW+JPG
The quickest the 2000us command should be sent is about once every 1.5s as the camera cannot
capture JPG images more quickly than 1.5s. For RAW+JPG mode we recommend a 2.5-3.0s wait time.
RAW images are used for capturing data for reflectance measurements, otherwise the pixels in the JPG are not usable.
im_freq_timer should therefore be superior 1.5s (TRIGGER_CAMERA_CAPTURE_IMAGE_PERIOD = 1.5 seconds)
NMEA GPS data are stored in metadata
**** ThermalCapture ****
Duty cycle inferior 1500us : TRIGGER_OFF
Duty cycle superior 1500us : TRIGGER_ON
Image capture frequency up to 30 Hz
Possibility to capture one frame per trigger in configurator : Trigger frame mode
Convert 14-bit data into temperature values :
High gain mode: temp [°C] = raw * 0.04 - 273.15
Low gain mode : temp [°C] = raw * 0.4 - 273.15
NMEA GPS data are stored in metadata
</description>
<section name="DC_CAM_PWM">
<define name="DC_CAM_PWM_SHUTTER_DELAY" description="how long to push shutter in seconds"/>
<define name="DC_CAM_PWM_SERVO" value="DC_CAM_TRIGGER" description="name of the servo to trigger with PWM"/>
</section>
</doc>
<dep>
<depends>digital_cam_common</depends>
<conflicts>digital_cam_gpio,digital_cam_i2c,digital_cam_uart,digital_cam_video,digital_cam_pprzlink,digital_cam_servo</conflicts>
<recommends>gps_nmea_send</recommends>
</dep>
<header>
<file name="dc_shoot_pwm.h"/>
</header>
<init fun="dc_shoot_pwm_init()"/>
<periodic fun="dc_shoot_pwm_periodic()" freq="20" autorun="TRUE"/>
<makefile>
<file name="dc_shoot_pwm.c"/>
<test arch="chibios">
<define name="ACTUATORS_NB" value="1"/>
<define name="SERVO_DC_CAM_TRIGGER_IDX" value="0"/>
<define name="DC_SHOOT_PWM_PERIODIC_FREQ" value="20"/>
</test>
</makefile>
</module>
+36
View File
@@ -0,0 +1,36 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="gps_nmea_send" dir="gps">
<doc>
<description>
module used to send GPS data over for external instrument using NMEA input.
Example: MAPIR camera stores GPS data in metadata on each frame.
</description>
<configure name="NMEA_SEND_UART" value="UART3" description="UART on which NMEA frames are sent"/>
<configure name="NMEA_SEND_BAUD" value="B115200" description="UART Baudrate, default to 115200"/>
<define name="NMEA_SEND_USE_STATE_DATA" value="FALSE|TRUE" description="Use filtered state data rather than raw GPS data (default: FALSE)"/>
</doc>
<dep>
<depends>uart</depends>
</dep>
<header>
<file name="gps_nmea_send.h"/>
</header>
<init fun="gps_nmea_send_init()"/>
<periodic fun="gps_nmea_send_periodic()" freq="5" autorun="TRUE"/>
<makefile target="ap">
<configure name="NMEA_SEND_UART" default="uart3" case="upper|lower"/>
<configure name="NMEA_SEND_BAUD" default="B115200"/>
<file name="gps_nmea_send.c"/>
<define name="USE_$(NMEA_SEND_UART_UPPER)"/>
<define name="NMEA_SEND_UART" value="$(NMEA_SEND_UART_LOWER)"/>
<define name="$(NMEA_SEND_UART_UPPER)_BAUD" value="$(NMEA_SEND_BAUD)"/>
<test>
<define name="USE_UART1"/>
<define name="UART1_BAUD" value="B115200"/>
<define name="NMEA_SEND_UART" value="uart1"/>
</test>
</makefile>
</module>
@@ -0,0 +1,141 @@
/*
* Copyright (C) 2025 Jean-Baptiste Forestier <jean-baptiste.forestier@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/digital_cam/dc_shoot_pwm.c"
* @author Jean-Baptiste Forestier
* Trigger using pwm cameras like MAPIR (https://www.mapir.camera/en-gb)
* or ThermalCapture 2.0 from TeAx (https://thermalcapture.com/thermalcapture-2-0/)
*
* **** MAPIR ****
* Duty cycle == 2000us => TRIGGER_ON
* Duty cycle == 1500us => SD Unmount
* Duty cycle == 1000us => TRIGGER_OFF
* Image capture frequency = 1.5s for JPG and 2.5-3.0s RAW+JPG
*
* The quickest the 2000us command should be sent is about once every 1.5s as the camera cannot
* capture JPG images more quickly than 1.5s. For RAW+JPG mode we recommend a 2.5-3.0s wait time.
*
* RAW images are used for capturing data for reflectance measurements, otherwise the pixels in the JPG are not usable.
* im_freq_timer should therefore be > 1.5s (=>TRIGGER_CAMERA_CAPTURE_IMAGE_PERIOD = 1.5 seconds)
*
* NMEA GPS data are stored in metadata
*
* **** ThermalCapture ****
* Duty cycle < 1500us => TRIGGER_OFF
* Duty cycle > 1500us => TRIGGER_ON
* Image capture frequency up to 30 Hz
* Possibility to capture one frame per trigger in configurator : Trigger frame mode
* Convert 14-bit data into temperature values :
* High gain mode: temp [°C] = raw * 0.04 - 273.15
* Low gain mode : temp [°C] = raw * 0.4 - 273.15
*
* NMEA GPS data are stored in metadata
*
* **** Trigger system ****
*
* TIME ────────────────────────────────────────────────────────────>
*
* PWM 1000us 2000us 1000us 2000us
* Vcc ┌────┐ ┌───────┐ ┌────┐ ┌───────┐
* │ OFF│ │ ON │ │ OFF│ │ ON │
* 0V ────┘ └────────┘ └───────┘ └────────┘ └────
* im_freq_timer: ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 0 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 0 ↓↓↓↓↓↓↓↓↓↓
* shutter_timer: ↓↓↓↓ 0 ↓↓↓↓ 0
*
* ↑ ↑
* Picture 1 Picture 2
*
*/
#include "modules/digital_cam/dc_shoot_pwm.h"
// Include Standard Camera Control Interface
#include "dc.h"
#include "generated/airframe.h"
#include "generated/modules.h"
#include "modules/actuators/actuators.h"
/** how long to push shutter in seconds */
#ifndef DC_CAM_PWM_SHUTTER_DELAY
#define DC_CAM_PWM_SHUTTER_DELAY 0.1
#endif
/** Max PWM Value */
#ifndef DC_CAM_PWM_ON_VALUE
#define DC_CAM_PWM_ON_VALUE MAX_PPRZ
#endif
/** Min PWM Value */
#ifndef DC_CAM_PWM_OFF_VALUE
#define DC_CAM_PWM_OFF_VALUE MIN_PPRZ
#endif
/** Servo destination */
#ifndef DC_CAM_PWM_SERVO
#define DC_CAM_PWM_SERVO DC_CAM_TRIGGER
#endif
#define CamActuatorSet(_a, _v) ActuatorSet(_a, _v)
/**
* Timer used for Shutter delay control
*/
static uint8_t shutter_timer;
/**
* Initialization function
*/
void dc_shoot_pwm_init(void)
{
shutter_timer = 0;
CamActuatorSet(DC_CAM_PWM_SERVO, DC_CAM_PWM_OFF_VALUE);
}
/**
* Periodic function to send data
*/
void dc_shoot_pwm_periodic(void)
{
// Manage the shutter opening time each DC_CAM_PWM_SHUTTER_DELAY seconds
if (shutter_timer == 0) {
CamActuatorSet(DC_CAM_PWM_SERVO, DC_CAM_PWM_OFF_VALUE);
} else {
shutter_timer--;
}
// Common DC Periodic task
dc_periodic();
}
/* Command The Camera */
void dc_send_command(uint8_t cmd)
{
if (cmd == DC_SHOOT) {
CamActuatorSet(DC_CAM_PWM_SERVO, DC_CAM_PWM_ON_VALUE);
shutter_timer = DC_CAM_PWM_SHUTTER_DELAY * DC_SHOOT_PWM_PERIODIC_FREQ;
dc_send_shot_position();
}
// call command send_command function
dc_send_command_common(cmd);
}
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2025 Jean-Baptiste Forestier <jean-baptiste.forestier@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/digital_cam/dc_shoot_pwm.h
* @author Jean-Baptiste Forestier
* Trigger using pwm cameras like MAPIR (https://www.mapir.camera/en-gb)
* or ThermalCapture 2.0 from TeAx (https://thermalcapture.com/thermalcapture-2-0/)
*
* **** MAPIR ****
* Duty cycle == 2000us => TRIGGER_ON
* Duty cycle == 1500us => SD Unmount
* Duty cycle == 1000us => TRIGGER_OFF
* Image capture frequency = 1.5s for JPG and 2.5-3.0s RAW+JPG
*
* The quickest the 2000us command should be sent is about once every 1.5s as the camera cannot
* capture JPG images more quickly than 1.5s. For RAW+JPG mode we recommend a 2.5-3.0s wait time.
*
* RAW images are used for capturing data for reflectance measurements, otherwise the pixels in the JPG are not usable.
* im_freq_timer should therefore be > 1.5s (=>TRIGGER_CAMERA_CAPTURE_IMAGE_PERIOD = 1.5 seconds)
*
* NMEA GPS data are stored in metadata
*
* **** ThermalCapture ****
* Duty cycle < 1500us => TRIGGER_OFF
* Duty cycle > 1500us => TRIGGER_ON
* Image capture frequency up to 30 Hz
* Possibility to capture one frame per trigger in configurator : Trigger frame mode
* Convert 14-bit data into temperature values :
* High gain mode: temp [°C] = raw * 0.04 - 273.15
* Low gain mode : temp [°C] = raw * 0.4 - 273.15
*
* NMEA GPS data are stored in metadata
*
* **** Trigger system ****
*
* TIME ────────────────────────────────────────────────────────────>
*
* PWM 1000us 2000us 1000us 2000us
* Vcc ┌────┐ ┌───────┐ ┌────┐ ┌───────┐
* │ OFF│ │ ON │ │ OFF│ │ ON │
* 0V ────┘ └────────┘ └───────┘ └────────┘ └────
* im_freq_timer: ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 0 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 0 ↓↓↓↓↓↓↓↓↓↓
* shutter_timer: ↓↓↓↓ 0 ↓↓↓↓ 0
*
* ↑ ↑
* Picture 1 Picture 2
*
* im_freq_timer should therefore be > 1.5s (=>TRIGGER_CAMERA_CAPTURE_IMAGE_PERIOD = 1.5 seconds)
*
*/
#ifndef DC_SHOOT_PWM_H
#define DC_SHOOT_PWM_H
#include "std.h"
void dc_shoot_pwm_init(void);
void dc_shoot_pwm_periodic(void);
#endif
+277
View File
@@ -0,0 +1,277 @@
/*
* Copyright (C) 2025 Jean-Baptiste FORESTIER <jean-baptiste.forestier@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/gps/gps_nmea_send.c"
* @author Jean-Baptiste FORESTIER
* @brief module used to send GPS data over a Tawaki UART for extern instrument using NMEA protocol
* Exemple of use : MAPIR camera stores GPS data in metadata on each frame
*/
#include "modules/gps/gps_nmea_send.h"
#include "mcu_periph/uart.h"
#include "generated/airframe.h"
#include "state.h" // State interface for rotation compensation
#include "modules/gps/gps.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
// ** Declaration ** //
struct Gps_Nmea_Send gps_nmea_send;
static void recover_gps_data(void);
static void build_nmea_sentence(void);
static uint8_t nmea_checksum(const char *sentence, int length);
static void nmea_convert_deg_to_DDMM(double deg, char *buf, int is_lat);
static void gps_nmea_get_system_date_str(char *buf_date, size_t buf_date_size, char *buf_time, size_t buf_time_size);
static void nmea_send(const char *payload, int payload_length);
/**
* Initialization function
*/
void gps_nmea_send_init(void)
{
gps_nmea_send.error_init = false;
gps_nmea_send.msg.lat = 0;
gps_nmea_send.msg.lon = 0;
gps_nmea_send.msg.num_sv = 0;
gps_nmea_send.msg.pdop = 0;
gps_nmea_send.msg.hmsl = 0;
gps_nmea_send.msg.vground = 0;
gps_nmea_send.msg.course = 0;
}
/**
* Periodic function to send data
*/
void gps_nmea_send_periodic(void)
{
recover_gps_data();
build_nmea_sentence();
}
/**
* Function to recover data available onboard from GPS
*/
void recover_gps_data(void)
{
#if GPS_NMEA_SEND_USE_STATE_DATA
struct LlaCoor_f *lla = stateGetPositionLla_f();
gps_nmea_send.msg.lat = (double)gps->lat;
gps_nmea_send.msg.lon = (double)gps->lon;
gps_nmea_send.msg.hmsl = stateGetHmslOrigin_f() + stateGetPositionEnu_f()->z;
gps_nmea_send.msg.vground = stateGetHorizontalSpeedNorm_f();
gps_nmea_send.msg.course = NormCourseRad(stateGetHorizontalSpeedDir_f());
#else // Default, use unfiltered GPS data
gps_nmea_send.msg.lat = (double)gps.lla_pos.lat / 1e7;
gps_nmea_send.msg.lon = (double)gps.lla_pos.lon / 1e7;
gps_nmea_send.msg.hmsl = (float)gps.hmsl / 1000.f; //in meters
gps_nmea_send.msg.vground = (float)gps.gspeed / 100.f; // in meters
gps_nmea_send.msg.course = (float)DegOfRad(gps.course / 1e7);
#endif
gps_nmea_send.msg.num_sv = gps.num_sv;
gps_nmea_send.msg.pdop = gps.pdop;
}
/**
* Function to build sentence for NMEA Protocol
*/
void build_nmea_sentence(void)
{
// Latitude and Longitude
char lat_buf[16], lon_buf[16], gga[120], rmc[120], date_sys[6], time_sys[6];
int len_gga = 0;
int len_rmc = 0;
uint8_t cs;
nmea_convert_deg_to_DDMM(gps_nmea_send.msg.lat, lat_buf, 1);
nmea_convert_deg_to_DDMM(gps_nmea_send.msg.lon, lon_buf, 0);
char lat_hemi = (gps_nmea_send.msg.lat >= 0) ? 'N' : 'S';
char lon_hemi = (gps_nmea_send.msg.lon >= 0) ? 'E' : 'W';
for (int i = 0; i < 120; i++) { gga[i] = '\0'; rmc[i] = '\0'; }
if (gps.fix >= GPS_FIX_3D) {
gps_nmea_get_system_date_str(date_sys, sizeof(date_sys) + 1, time_sys, sizeof(time_sys) + 1);
// -------------------------------
// GPGGA Frame
// -------------------------------
sprintf(gga,
"$GPGGA,%s,%s,%c,%s,%c,1,%02u,%04u,%09.3f,M,0,M,,",
time_sys, lat_buf, lat_hemi, lon_buf, lon_hemi,
gps_nmea_send.msg.num_sv, gps_nmea_send.msg.pdop,
gps_nmea_send.msg.hmsl);
len_gga = 7; // $GPGGA, = 7
len_gga += 7; // time_nmea, = 7
len_gga += 10; // lat_buf = 9 + comma
len_gga += 2; // lat_hemi = N,
len_gga += 11; // lon_buf = 10 + comma
len_gga += 2; // lon_hemi = W,
len_gga += 2; // GPS Quality indicator GPS fix = 1,
len_gga += 3; // number of sats = 08,
len_gga += 5; // position dilution of precision scaled by 100 = 1020,
len_gga += 10; // height above mean sea level (MSL) in m = 02945.127,
len_gga += 2; // height above mean sea level unit = M,
len_gga += 2; // Ellipsoid to geoid distance set 0, don't where to find it = 0,
len_gga += 2; // Distance above unit = M,
len_gga += 1; // Empty = ,
cs = nmea_checksum(gga, len_gga);
sprintf(gga + len_gga, "*%02X\r\n", cs);
len_gga += 5; // * + Checksum + \r\n = *4F\r\n
nmea_send(gga, len_gga);
// -------------------------------
// GPRMC Frame
// -------------------------------
sprintf(rmc,
"$GPRMC,%s,A,%s,%c,%s,%c,%05.1f,%05.1f,%s,000.0,W",
time_sys, lat_buf, lat_hemi, lon_buf, lon_hemi,
gps_nmea_send.msg.vground, gps_nmea_send.msg.course, date_sys);
len_rmc = 7; // $GPRMC, = 7
len_rmc += 7; // time_nmea, = 7
len_rmc += 2; // Status A=active or V=void = A,
len_rmc += 10; // lat_buf = 9 + comma
len_rmc += 2; // lat_hemi = N,
len_rmc += 11; // lon_buf = 10 + comma
len_rmc += 2; // lon_hemi = W,
len_rmc += 6; // vground = 050.4,
len_rmc += 6; // track angle course = 050.4,
len_rmc += 7; // Date = 230394,
len_rmc += 7; // Magnetic variation, in degrees = 003.1,W,
cs = nmea_checksum(rmc, len_rmc);
sprintf(rmc + len_rmc, "*%02X\r\n", cs);
len_rmc += 5; // * + Checksum + \r\n = *4F\r\n
nmea_send(rmc, len_rmc);
} else {
//If no fix, empty GGA
sprintf(gga, "$GPGGA,,,,,,0,00,99.99,,,,,,*68\r\n");
nmea_send(gga, 33);
//If no fix, empty RMC
sprintf(rmc, "$GPRMC,,V,,,,,,,,,,*53\r\n");
nmea_send(rmc, 24);
}
}
/**
* Function to calculate checksum
*/
uint8_t nmea_checksum(const char *sentence, int length)
{
uint8_t cs = 0;
for (int i = 0; i < length; i++) {
cs ^= (uint8_t)sentence[i];
}
return cs;
}
/**
* Function to convert lat long in DDLL.MMMM Format
* Lat and long don't have the same length
*/
void nmea_convert_deg_to_DDMM(double deg, char *buf, int is_lat)
{
double abs_deg = fabs(deg);
int d = (int)abs_deg;
double minutes = (abs_deg - d) * 60.0;
if (is_lat) {
// 2 integers for degrees 0 to 90 without sign => %02d | 2 integers for minutes, 1 for point, 4 for minutes decimals
sprintf(buf, "%02d%07.4f", d, minutes);
} else {
// 3 integers for degrees 0 to 180 without sign => %03d | 2 integers for minutes, 1 for point, 4 for minutes decimals
sprintf(buf, "%03d%07.4f", d, minutes);
}
}
/**
* Get Date from system
*/
void gps_nmea_get_system_date_str(char *buf_date, size_t buf_date_size, char *buf_time, size_t buf_time_size)
{
// days from 1970-01-01
const int64_t gps_epoch_days = 3657;
// Total time gps
uint64_t gps_sec = (uint64_t)gps.week * 7 * 24 * 3600 + gps.tow / 1000;
// Day gps + gps epoch
int64_t days = gps_epoch_days + gps_sec / 86400;
// Algo to get year month day from Week and tow
// Shift day count so that day 0 corresponds to the Gregorian epoch (0000-03-01)
days += 719468;
// Determine the 400-year Gregorian cycle, One Gregorian era = 400 years = 146097 days.
int64_t era = (days >= 0 ? days : days - 146096) / 146097;
// Day Of Era: number of days elapsed within the current 400-year cycle.
uint64_t doe = (uint64_t)(days - era * 146097);
// Year Of Era: compute the year number inside the cycle, correcting for leap years (every 4 years, except centuries, except 400-year multiples).
uint64_t yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
// Convert "year in era" to an absolute Gregorian year.
int64_t y = (int64_t)yoe + era * 400;
// Day Of Year: subtract all full days from completed years to get day index within the year.
uint64_t doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
// Month part: day-of-year into a month index in a calendar where March = 0 and February = 11
uint64_t mp = (5 * doy + 2) / 153;
// Compute the day-of-month
uint64_t d = doy - (153 * mp + 2) / 5 + 1;
// Convert shifted month index back to standard month 0-12
uint64_t m = mp + (mp < 10 ? 3 : -9);
y += (m <= 2);
// Hours / minutes / secondes
uint32_t sec_of_day = gps_sec % 86400;
uint8_t hour = sec_of_day / 3600;
uint8_t min = (sec_of_day % 3600) / 60;
uint8_t sec = sec_of_day % 60;
// Format : DDMMYY
snprintf(buf_date, buf_date_size, "%02d%02d%02d",
(uint8_t)d,
(uint8_t)m,
(uint8_t)(y % 100));
// Format : HHMMSS
snprintf(buf_time, buf_time_size, "%02d%02d%02d",
hour,
min,
sec);
}
/**
* Function to send payload to UART
*/
void nmea_send(const char *payload, int payload_length)
{
uart_put_buffer(&NMEA_SEND_UART, 0, (const uint8_t *)payload, payload_length);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2025 Jean-Baptiste FORESTIER <jean-baptiste.forestier@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/gps/gps_nmea_send.h"
* @author Jean-Baptiste FORESTIER
* @brief module used to send GPS data over a Tawaki UART for extern instrument using NMEA protocol
* Exemple of use : MAPIR camera stores GPS data in metadata on each frame
*/
#ifndef GPS_NMEA_SEND_H
#define GPS_NMEA_SEND_H
#include "std.h"
struct gps_nmea_send_msg_t {
double lat; ///< Latitude
double lon; ///< Longiitude
uint8_t num_sv; ///< number of sat in fix
uint16_t pdop; ///< position dilution of precision scaled by 100
float hmsl; ///< Orthometric height (MSL reference)
float vground; ///< Speed over ground in m/s
float course; ///< GPS course over ground in rad*1e7, [0, 2*Pi]*1e7 (CW/north)
};
struct Gps_Nmea_Send {
bool error_init; // Flag to indicate if there was an error during initialization
struct gps_nmea_send_msg_t msg;
};
extern void gps_nmea_send_init(void);
extern void gps_nmea_send_periodic(void);
#endif