Paparazzi UAS  v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
nps_hitl_sensors.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009 Antoine Drouin <poinix@gmail.com>
3  * Copyright (C) 2012 The Paparazzi Team
4  * Copyright (C) 2016 Michal Podhradsky <http://github.com/podhrmic>
5  * Copyright (C) 2023 Gautier Hattenberger <gautier.hattenberger@enac.fr>
6  *
7  * This file is part of paparazzi.
8  *
9  * paparazzi is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  *
14  * paparazzi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with paparazzi; see the file COPYING. If not, write to
21  * the Free Software Foundation, 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24 
25 #include "nps_ins.h"
26 #include <sys/time.h>
27 #include "nps_fdm.h"
28 #include <time.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/poll.h>
36 #include "nps_sensors.h"
37 #include "nps_main.h"
38 #include "paparazzi.h"
39 #include "pprzlink/messages.h"
40 #include "pprzlink/dl_protocol.h"
41 #include "pprzlink/pprzlink_device.h"
42 #include "pprzlink/pprz_transport.h"
43 #include "arch/linux/serial_port.h"
44 
45 #ifndef AP_DEV
46 #warning "[hitl] Please define AP_DEV in your airframe file"
47 #define AP_DEV "/dev/ttyUSB0"
48 #endif
49 #ifndef AP_BAUD
50 #define AP_BAUD B921600
51 PRINT_CONFIG_MSG_VALUE("[hitl] Using default baudrate for AP_DEV (B921600)", AP_BAUD)
52 #endif
53 
54 #define NPS_HITL_DEBUG 0
55 #if NPS_HITL_DEBUG
56 #define DEBUG_PRINT printf
57 #else
58 #define DEBUG_PRINT(...) {}
59 #endif
60 
61 void *nps_sensors_loop(void *data __attribute__((unused)));
62 void *nps_ap_data_loop(void *data __attribute__((unused)));
63 pthread_t th_sensors; // send sensors to AP
64 pthread_t th_ap_data; // receive commands from AP
65 
66 /* implement pprzlink_device interface */
67 #define PPRZLINK_BUFFER_SIZE 256
68 struct linkdev {
75  //volatile uint8_t tx_running;
77  struct link_device device;
79  struct pprz_transport pprz_tp;
81  struct SerialPort *port;
82  struct pollfd fds[1];
83 };
84 
85 static struct linkdev dev;
86 
87 static int check_free_space(struct linkdev *d, long *fd __attribute__((unused)), uint16_t len)
88 {
89  int space = PPRZLINK_BUFFER_SIZE - d->tx_idx;
90  return space >= len ? space : 0;
91 }
92 
93 static void put_byte(struct linkdev *d, long fd __attribute__((unused)), uint8_t data)
94 {
95  d->tx_buf[d->tx_idx++] = data;
96 }
97 
98 static void put_buffer(struct linkdev *d, long fd __attribute__((unused)), const uint8_t *data, uint16_t len)
99 {
100  memcpy(&(d->tx_buf[d->tx_idx]), data, len);
101  d->tx_idx += len;
102 }
103 
104 static void send_message(struct linkdev *d, long fd __attribute__((unused)))
105 {
106  int ret = 0;
107  do {
108  ret = write((int)(d->port->fd), d->tx_buf, d->tx_idx);
109  } while (ret < 1 && errno == EAGAIN); //FIXME: max retry
110  if (ret < 1) {
111  DEBUG_PRINT("[hitl] put_byte: write failed [%d: %s]\n", ret, strerror(errno));
112  }
113  d->tx_idx = 0;
114 }
115 
116 static uint8_t getch(struct linkdev *d)
117 {
118  // this function should only be called when bytes are available
119  unsigned char c = 'B';
120  ssize_t ret = 0;
121  ret = read(d->port->fd, &c, 1);
122  if (ret > 0) {
123  d->rx_buf[d->rx_idx] = c;
124  if (d->rx_idx < PPRZLINK_BUFFER_SIZE) {
125  d->rx_idx++;
126  } else {
127  DEBUG_PRINT("[hitl] rx buffer overflow\n");
128  }
129  } else {
130  DEBUG_PRINT("[hitl] rx read error\n");
131  }
132  return c;
133 }
134 
135 static int char_available(struct linkdev *d)
136 {
137  int ret = poll(d->fds, 1, 1000);
138  if (ret > 0) {
139  if (d->fds[0].revents & POLLHUP) {
140  printf("[hitl] lost connection. Exiting\n");
141  exit(1);
142  }
143  if (d->fds[0].revents & POLLIN) {
144  return true;
145  }
146  } else if (ret == -1) {
147  DEBUG_PRINT("[hitl] poll failed\n");
148  }
149  return false;
150 }
151 
153 
155 {
156  pthread_create(&th_sensors, NULL, nps_sensors_loop, NULL);
157  pthread_create(&th_ap_data, NULL, nps_ap_data_loop, NULL);
158 
159  dev.device.periph = (void *) (&dev);
160  dev.device.check_free_space = (check_free_space_t) check_free_space;
161  dev.device.put_byte = (put_byte_t) put_byte;
162  dev.device.put_buffer = (put_buffer_t) put_buffer;
163  dev.device.send_message = (send_message_t) send_message;
164  dev.device.char_available = (char_available_t) char_available;
165  dev.device.get_byte = (get_byte_t) getch;
166  pprz_transport_init(&dev.pprz_tp);
167 
168  // open serial port
171  if (ret != 0) {
172  printf("[hitl] Error opening %s code %d\n", AP_DEV, ret);
174  }
175 
176  // poll
177  if (dev.port != NULL) {
178  dev.fds[0].fd = dev.port->fd;
179  dev.fds[0].events = POLLIN;
180  }
181 }
182 
183 void *nps_sensors_loop(void *data __attribute__((unused)))
184 {
185  struct timespec requestStart;
186  struct timespec requestEnd;
187  struct timespec waitFor;
188  long int period_ns = (1. / PERIODIC_FREQUENCY) * 1000000000L; // thread period in nanoseconds
189  long int task_ns = 0; // time it took to finish the task in nanoseconds
190 
191  while (TRUE) {
192  // lock mutex
193  pthread_mutex_lock(&fdm_mutex);
194 
195  // start timing
196  clock_get_current_time(&requestStart);
197 
198  uint8_t id = AC_ID;
199 
201  float gx = (float)sensors.gyro.value.x;
202  float gy = (float)sensors.gyro.value.y;
203  float gz = (float)sensors.gyro.value.z;
204  float ax = (float)sensors.accel.value.x;
205  float ay = (float)sensors.accel.value.y;
206  float az = (float)sensors.accel.value.z;
207  float mx = (float)sensors.mag.value.x;
208  float my = (float)sensors.mag.value.y;
209  float mz = (float)sensors.mag.value.z;
210  uint8_t id = AC_ID;
211  pprz_msg_send_HITL_IMU(&dev.pprz_tp.trans_tx, &dev.device, 0,
212  &gx, &gy, &gz,
213  &ax, &ay, &az,
214  &mx, &my, &mz,
215  &id);
216  }
217 
219  float gps_lat = (float)DegOfRad(sensors.gps.lla_pos.lat);
220  float gps_lon = (float)DegOfRad(sensors.gps.lla_pos.lon);
221  float gps_alt = (float)sensors.gps.lla_pos.alt;
222  float gps_hmsl = (float)sensors.gps.hmsl;
223  float gps_vx = (float)sensors.gps.ecef_vel.x;
224  float gps_vy = (float)sensors.gps.ecef_vel.y;
225  float gps_vz = (float)sensors.gps.ecef_vel.z;
226  float gps_time = (float)nps_main.sim_time;
227  uint8_t gps_fix = 3; // GPS fix
228  pprz_msg_send_HITL_GPS(&dev.pprz_tp.trans_tx, &dev.device, 0,
229  &gps_lat, &gps_lon, &gps_alt, &gps_hmsl,
230  &gps_vx, &gps_vy, &gps_vz,
231  &gps_time, &gps_fix, &id);
232  }
233 
234  uint8_t air_data_flag = 0;
235  float baro = -1.f;
236  float airspeed = -1.f;
237  float aoa = 0.f;
238  float sideslip = 0.f;
240  SetBit(air_data_flag, 0);
241  baro = (float) sensors.baro.value;
242  }
244  SetBit(air_data_flag, 1);
245  airspeed = (float) sensors.airspeed.value;
246  }
248  SetBit(air_data_flag, 2);
249  aoa = (float) sensors.aoa.value;
250  }
252  SetBit(air_data_flag, 3);
253  sideslip = (float) sensors.sideslip.value;
254  }
255  if (air_data_flag != 0) {
256  pprz_msg_send_HITL_AIR_DATA(&dev.pprz_tp.trans_tx, &dev.device, 0,
257  &baro, &airspeed, &aoa, &sideslip, &air_data_flag, &id);
258  }
259 
260  // unlock mutex
261  pthread_mutex_unlock(&fdm_mutex);
262 
263  clock_get_current_time(&requestEnd);
264 
265  // Calculate time it took
266  task_ns = (requestEnd.tv_sec - requestStart.tv_sec) * 1000000000L + (requestEnd.tv_nsec - requestStart.tv_nsec);
267 
268  // task took less than one period, sleep for the rest of time
269  if (task_ns < period_ns) {
270  waitFor.tv_sec = 0;
271  waitFor.tv_nsec = period_ns - task_ns;
272  nanosleep(&waitFor, NULL);
273  } else {
274  // task took longer than the period
275 #ifdef PRINT_TIME
276  printf("SENSORS: task took longer than one period, exactly %f [ms], but the period is %f [ms]\n",
277  (double)task_ns / 1E6, (double)period_ns / 1E6);
278 #endif
279  }
280  }
281  return(NULL);
282 }
283 
284 void *nps_ap_data_loop(void *data __attribute__((unused)))
285 {
286  struct timespec waitFor;
287 
288  uint8_t msg_buffer[PPRZLINK_BUFFER_SIZE];
289  bool msg_available = false;
290 
291  bool first_command = true;
292 
293  uint8_t cmd_len = 0;
295 
296  while (TRUE) {
297 
298  pprz_check_and_parse(&dev.device, &dev.pprz_tp, msg_buffer, &msg_available);
299  if (msg_available) {
300  // reset rx index to zero for next message
301  dev.rx_idx = 0;
302  //Parse message
303  uint8_t sender_id = SenderIdOfPprzMsg(msg_buffer);
304  uint8_t msg_id = IdOfPprzMsg(msg_buffer);
305 
306  if (sender_id != AC_ID) {
307  printf("[hitl] receiving message from wrong id (%d)\n", sender_id);
308  return(NULL); // wrong A/C ?
309  }
310  /* parse telemetry messages coming from the correct AC_ID */
311  switch (msg_id) {
312  case DL_COMMANDS:
313  // parse commands message
314  cmd_len = DL_COMMANDS_values_length(msg_buffer);
315  memcpy(&cmd_buf, DL_COMMANDS_values(msg_buffer), cmd_len * sizeof(int16_t));
316  pthread_mutex_lock(&fdm_mutex);
317  // update commands
318  for (uint8_t i = 0; i < NPS_COMMANDS_NB; i++) {
319  nps_autopilot.commands[i] = (double)cmd_buf[i] / MAX_PPRZ;
320  }
321  // hack: invert pitch to fit most JSBSim models
322  nps_autopilot.commands[COMMAND_PITCH] = -(double)cmd_buf[COMMAND_PITCH] / MAX_PPRZ;
323  if (first_command) {
324  printf("[hitl] receiving COMMANDS message\n");
325  first_command = false;
326  }
327  pthread_mutex_unlock(&fdm_mutex);
328  break;
329  case DL_MOTOR_MIXING:
330  // parse actuarors message
331  cmd_len = DL_MOTOR_MIXING_values_length(msg_buffer);
332  // check for out-of-bounds access
333  if (cmd_len > NPS_COMMANDS_NB) {
334  cmd_len = NPS_COMMANDS_NB;
335  }
336  memcpy(&cmd_buf, DL_MOTOR_MIXING_values(msg_buffer), cmd_len * sizeof(int16_t));
337  pthread_mutex_lock(&fdm_mutex);
338  // update commands
339  for (uint8_t i = 0; i < NPS_COMMANDS_NB; i++) {
340  nps_autopilot.commands[i] = (double)cmd_buf[i] / MAX_PPRZ;
341  }
342  if (first_command) {
343  printf("[hitl] receiving MOTOR_MIXING message\n");
344  first_command = false;
345  }
346  pthread_mutex_unlock(&fdm_mutex);
347  break;
348  default:
349  break;
350  }
351 
352  msg_available = false;
353  }
354 
355  // wait before next loop
356  waitFor.tv_sec = 0;
357  waitFor.tv_nsec = 1000;
358  nanosleep(&waitFor, NULL);
359 
360  }
361  return(NULL);
362 }
363 
364 
#define sensors(...)
Definition: cc2500_compat.h:68
struct NpsAutopilot nps_autopilot
#ifndef NPS_NO_MOTOR_MIXING #include "modules/actuators/motor_mixing.h"
double commands[NPS_COMMANDS_NB]
Definition: nps_autopilot.h:49
#define NPS_COMMANDS_NB
Number of commands sent to the FDM of NPS.
Definition: nps_autopilot.h:44
uint8_t rx_buf[PPRZLINK_BUFFER_SIZE]
Receive buffer.
static void put_buffer(struct linkdev *d, long fd, const uint8_t *data, uint16_t len)
void * nps_sensors_loop(void *data)
uint8_t tx_buf[PPRZLINK_BUFFER_SIZE]
Transmit buffer.
#define AP_DEV
pthread_t th_ap_data
static void send_message(struct linkdev *d, long fd)
#define PPRZLINK_BUFFER_SIZE
struct SerialPort * port
Serial port.
static struct linkdev dev
struct link_device device
Generic device interface.
void * nps_ap_data_loop(void *data)
static int char_available(struct linkdev *d)
#define DEBUG_PRINT(...)
uint16_t rx_idx
void nps_hitl_impl_init(void)
END pprzlink_dev.
pthread_t th_sensors
uint16_t tx_idx
#define AP_BAUD
static uint8_t getch(struct linkdev *d)
static int check_free_space(struct linkdev *d, long *fd, uint16_t len)
struct pprz_transport pprz_tp
transport
struct pollfd fds[1]
static void put_byte(struct linkdev *d, long fd, uint8_t data)
#define clock_get_current_time(_x)
Definition: nps_main.h:17
struct NpsMain nps_main
double sim_time
Definition: nps_main.h:57
pthread_mutex_t fdm_mutex
bool nps_sensors_airspeed_available(void)
Definition: nps_sensors.c:85
bool nps_sensors_gps_available(void)
Definition: nps_sensors.c:67
bool nps_sensors_sideslip_available(void)
Definition: nps_sensors.c:112
bool nps_sensors_aoa_available(void)
Definition: nps_sensors.c:103
bool nps_sensors_baro_available(void)
Definition: nps_sensors.c:58
bool nps_sensors_gyro_available(void)
Definition: nps_sensors.c:40
int16_t pprz_t
Definition: paparazzi.h:6
#define MAX_PPRZ
Definition: paparazzi.h:8
int fd
Definition: serial.c:26
void serial_port_free(struct SerialPort *me)
Definition: serial_port.c:80
int serial_port_open_raw(struct SerialPort *me, const char *device, speed_t speed)
Definition: serial_port.c:111
struct SerialPort * serial_port_new(void)
Definition: serial_port.c:74
#define TRUE
Definition: std.h:4
char cmd_buf[64]
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
Definition: vl53l1_types.h:88
short int16_t
Typedef defining 16 bit short type.
Definition: vl53l1_types.h:93
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98