SELKIELogger  1.0.0
LoggerLPMS.c
1 /*
2  * Copyright (C) 2023 Swansea University
3  *
4  * This file is part of the SELKIELogger suite of tools.
5  *
6  * SELKIELogger is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * SELKIELogger is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this SELKIELogger product.
18  * If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "Logger.h"
22 
23 #include "LoggerLPMS.h"
24 #include "LoggerSignals.h"
25 
26 #define CHAN_ACC_RAW_X 4
27 #define CHAN_ACC_RAW_Y 5
28 #define CHAN_ACC_RAW_Z 6
29 #define CHAN_ACC_CAL_X 7
30 #define CHAN_ACC_CAL_Y 8
31 #define CHAN_ACC_CAL_Z 9
32 #define CHAN_G_RAW_X 10
33 #define CHAN_G_RAW_Y 11
34 #define CHAN_G_RAW_Z 12
35 #define CHAN_G_CAL_X 13
36 #define CHAN_G_CAL_Y 14
37 #define CHAN_G_CAL_Z 15
38 #define CHAN_G_ALIGN_X 16
39 #define CHAN_G_ALIGN_Y 17
40 #define CHAN_G_ALIGN_Z 18
41 #define CHAN_OMEGA_X 19
42 #define CHAN_OMEGA_Y 20
43 #define CHAN_OMEGA_Z 21
44 #define CHAN_ROLL 22
45 #define CHAN_PITCH 23
46 #define CHAN_YAW 24
47 #define CHAN_ACC_LIN_X 25
48 #define CHAN_ACC_LIN_Y 26
49 #define CHAN_ACC_LIN_Z 27
50 #define CHAN_ALTITUDE 28
51 
58 void *lpms_setup(void *ptargs) {
59  log_thread_args_t *args = (log_thread_args_t *)ptargs;
60  lpms_params *lpmsInfo = (lpms_params *)args->dParams;
61 
62  lpmsInfo->handle = lpms_openConnection(lpmsInfo->portName, lpmsInfo->baudRate);
63  if (lpmsInfo->handle < 0) {
64  log_error(args->pstate, "[LPMS:%s] Unable to open a connection", args->tag);
65  args->returnCode = -1;
66  return NULL;
67  }
68 
69  args->returnCode = 0;
70 
71  lpms_send_command_mode(lpmsInfo->handle);
72  usleep(250000);
73  lpms_message getModel = {.id = lpmsInfo->unitID, .command = LPMS_MSG_GET_SENSORMODEL};
74  lpms_send_command(lpmsInfo->handle, &getModel);
75  usleep(10000);
76 
77  lpms_message getFW = {.id = lpmsInfo->unitID, .command = LPMS_MSG_GET_FIRMWAREVER};
78  lpms_send_command(lpmsInfo->handle, &getFW);
79  usleep(10000);
80 
81  lpms_message getSerial = {.id = lpmsInfo->unitID, .command = LPMS_MSG_GET_SERIALNUM};
82  lpms_send_command(lpmsInfo->handle, &getSerial);
83  usleep(10000);
84 
85  uint8_t rateData[4] = {
86  (lpmsInfo->pollFreq & 0xFF),
87  (lpmsInfo->pollFreq & 0xFF00) >> 8,
88  (lpmsInfo->pollFreq & 0xFF0000) >> 16,
89  (lpmsInfo->pollFreq & 0xFF000000) >> 24,
90  };
91  lpms_message setRate = {.id = lpmsInfo->unitID,
92  .command = LPMS_MSG_SET_FREQ,
93  .length = sizeof(uint32_t),
94  .data = rateData};
95  lpms_send_command(lpmsInfo->handle, &setRate);
96  usleep(125000);
97  lpms_message getRate = {.id = lpmsInfo->unitID, .command = LPMS_MSG_GET_FREQ};
98  lpms_send_command(lpmsInfo->handle, &getRate);
99  usleep(10000);
100 
101  const uint32_t outputs =
102  ((1U << LPMS_IMU_ACCEL_RAW) + (1U << LPMS_IMU_ACCEL_CAL) +
103  (1U << LPMS_IMU_GYRO_RAW) + (1U << LPMS_IMU_GYRO_CAL) +
104  (1U << LPMS_IMU_GYRO_ALIGN) + (1U << LPMS_IMU_OMEGA) + (1U << LPMS_IMU_EULER) +
105  (1U << LPMS_IMU_ACCEL_LINEAR) + (1U << LPMS_IMU_ALTITUDE));
106  uint8_t outputData[4] = {(outputs & 0xFF), (outputs & 0xFF00) >> 8,
107  (outputs & 0xFF0000) >> 16, (outputs & 0xFF000000) >> 24};
108 
109  lpms_message setTransmitted = {.id = lpmsInfo->unitID,
110  .command = LPMS_MSG_SET_OUTPUTS,
111  .length = sizeof(uint32_t),
112  .data = outputData};
113  lpms_send_command(lpmsInfo->handle, &setTransmitted);
114  usleep(125000);
115 
116  lpms_message getTransmitted = {.id = lpmsInfo->unitID, .command = LPMS_MSG_GET_OUTPUTS};
117  lpms_send_command(lpmsInfo->handle, &getTransmitted);
118  usleep(10000);
119  log_info(args->pstate, 1, "[LPMS:%s] Initial setup commands sent", args->tag);
120  return NULL;
121 }
122 
136 inline bool lpms_queue_message(msgqueue *Q, const uint8_t src, const uint8_t chan,
137  const float val) {
138  msg_t *m = msg_new_float(src, chan, val);
139  if (m == NULL) { return false; }
140  if (!queue_push(Q, m)) {
141  msg_destroy(m);
142  return false;
143  }
144  return true;
145 }
146 
159 void *lpms_logging(void *ptargs) {
161  log_thread_args_t *args = (log_thread_args_t *)ptargs;
162  lpms_params *lpmsInfo = (lpms_params *)args->dParams;
163 
164  log_info(args->pstate, 1, "[LPMS:%s] Logging thread started", args->tag);
165  lpms_send_stream_mode(lpmsInfo->handle);
166 
167  uint8_t *buf = calloc(LPMS_BUFF, sizeof(uint8_t));
168  size_t lpms_hw = 0;
169  size_t lpms_end = 0;
170  uint32_t outputs = 0;
171  bool unitMismatch = false;
172  unsigned int pendingCount = 0;
173  unsigned int missingCount = 0;
174  while (!shutdownFlag) {
175  lpms_data d = {.present = outputs};
176  lpms_message *m = calloc(1, sizeof(lpms_message));
177  if (!m) {
178  perror("lpms_message calloc");
179  args->returnCode = -1;
180  pthread_exit(&(args->returnCode));
181  return NULL;
182  }
183  bool r = lpms_readMessage_buf(lpmsInfo->handle, m, buf, &lpms_end, &lpms_hw);
184  if (r) {
185  uint16_t cs = 0;
186  if (!(lpms_checksum(m, &cs) && cs == m->checksum)) {
187  free(m->data);
188  free(m);
189  continue;
190  }
191  if ((m->id != lpmsInfo->unitID) && !unitMismatch) {
192  log_warning(
193  args->pstate,
194  "[LPMS:%s] Unexpected unit ID (got 0x%02x, expected 0x%02x)",
195  args->tag, m->id, lpmsInfo->unitID);
196  unitMismatch = true;
197  }
198  if (m->command == LPMS_MSG_GET_OUTPUTS) {
199  d.present = (uint32_t)m->data[0] + ((uint32_t)m->data[1] << 8) +
200  ((uint32_t)m->data[2] << 16) +
201  ((uint32_t)m->data[3] << 24);
202  outputs = d.present;
203  log_info(args->pstate, 1,
204  "[LPMS:%s] Output configuration received for unit 0x%02x",
205  args->tag, m->id);
206  } else if (m->command == LPMS_MSG_GET_SENSORMODEL) {
207  log_info(args->pstate, 1,
208  "[LPMS:%s] Unit 0x%02x: Sensor model: %-24s", args->tag,
209  m->id, (char *)m->data);
210  char *lm = NULL;
211  int sl = asprintf(&lm, "LPMS Unit 0x%02x: Sensor model: %-24s",
212  m->id, (char *)m->data);
214  sl, lm);
215  free(lm);
216  lm = NULL;
217  if (!queue_push(args->logQ, sm)) {
218  log_error(args->pstate,
219  "[LPMS:%s] Error pushing message to queue",
220  args->tag);
221  msg_destroy(sm);
222  args->returnCode = -1;
223  pthread_exit(&(args->returnCode));
224  }
225  } else if (m->command == LPMS_MSG_GET_SERIALNUM) {
226  log_info(args->pstate, 1,
227  "[LPMS:%s] Unit 0x%02x: Serial number: %-24s", args->tag,
228  m->id, (char *)m->data);
229  char *lm = NULL;
230  int sl = asprintf(&lm, "LPMS Unit 0x%02x: Serial number: %-24s",
231  m->id, (char *)m->data);
233  sl, lm);
234  free(lm);
235  lm = NULL;
236  if (!queue_push(args->logQ, sm)) {
237  log_error(args->pstate,
238  "[LPMS:%s] Error pushing message to queue",
239  args->tag);
240  msg_destroy(sm);
241  args->returnCode = -1;
242  pthread_exit(&(args->returnCode));
243  }
244  } else if (m->command == LPMS_MSG_GET_FIRMWAREVER) {
245  log_info(args->pstate, 1,
246  "[LPMS:%s] Unit 0x%02x: Firmware version: %-24s",
247  args->tag, m->id, (char *)m->data);
248  char *lm = NULL;
249  int sl = asprintf(&lm, "LPMS Unit 0x%02x: Firmware version: %-24s",
250  m->id, (char *)m->data);
252  sl, lm);
253  free(lm);
254  lm = NULL;
255  if (!queue_push(args->logQ, sm)) {
256  log_error(args->pstate,
257  "[LPMS:%s] Error pushing message to queue",
258  args->tag);
259  msg_destroy(sm);
260  args->returnCode = -1;
261  pthread_exit(&(args->returnCode));
262  }
263  } else if (m->command == LPMS_MSG_GET_FREQ) {
264  uint32_t rate = m->data[0] + ((uint32_t)m->data[1] << 8) +
265  ((uint32_t)m->data[2] << 16) +
266  ((uint32_t)m->data[3] << 24);
267  log_info(args->pstate, 1,
268  "[LPMS:%s] Unit 0x%02x: Message rate %dHz", args->tag,
269  m->id, rate);
270  } else if (m->command == LPMS_MSG_GET_IMUDATA) {
271  if (!d.present) {
272  // Can't extract data until configuration has appeared
273  if ((pendingCount++ % 100) == 0) {
274  log_warning(
275  args->pstate,
276  "[LPMS:%s] No output configuration received - skipping messages (%d skipped so far)",
277  args->tag, pendingCount);
278  log_info(args->pstate, 3,
279  "[LPMS:%s] Repeating GET_OUTPUTS request",
280  args->tag);
281  lpms_message getTransmitted = {
282  .id = lpmsInfo->unitID,
283  .command = LPMS_MSG_GET_OUTPUTS};
284  lpms_send_command(lpmsInfo->handle,
285  &getTransmitted);
286  }
287  free(m->data);
288  free(m);
289  continue;
290  }
291  if (lpms_imu_set_timestamp(m, &d)) {
292  msg_t *ts = msg_new_timestamp(lpmsInfo->sourceNum,
294  if (!queue_push(args->logQ, ts)) {
295  log_error(
296  args->pstate,
297  "[LPMS:%s] Error pushing message to queue",
298  args->tag);
299  msg_destroy(ts);
300  free(m->data);
301  free(m);
302  args->returnCode = -1;
303  pthread_exit(&(args->returnCode));
304  }
305  } else {
306  log_warning(args->pstate,
307  "[LPMS:%s] Unit 0x%02x: Timestamp invalid",
308  args->tag, m->id);
309  }
310  bool dataSet = true;
311  dataSet &= lpms_imu_set_accel_raw(m, &d);
312  dataSet &= lpms_imu_set_accel_cal(m, &d);
313  dataSet &= lpms_imu_set_gyro_raw(m, &d);
314  dataSet &= lpms_imu_set_gyro_cal(m, &d);
315  dataSet &= lpms_imu_set_gyro_aligned(m, &d);
316  dataSet &= lpms_imu_set_omega(m, &d);
317  dataSet &= lpms_imu_set_euler_angles(m, &d);
318  dataSet &= lpms_imu_set_accel_linear(m, &d);
319  dataSet &= lpms_imu_set_altitude(m, &d);
320  /*
321  Not currently included in outputs
322  dataSet |= lpms_imu_set_mag_raw(m, &d);
323  dataSet |= lpms_imu_set_mag_cal(m, &d);
324  dataSet |= lpms_imu_set_pressure(m, &d);
325  dataSet |= lpms_imu_set_temperature(m, &d);
326  */
327  if (!dataSet) {
328  if (missingCount == 0) {
329  log_warning(
330  args->pstate,
331  "[LPMS:%s] Unit 0x%02x: Some data missing in update",
332  args->tag, m->id);
333  }
334  missingCount++;
335  } else {
336  missingCount = 0;
337  }
338  // Create output messages and push to queue
339  bool rs = true;
340  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
341  CHAN_ACC_RAW_X, d.accel_raw[0]);
342  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
343  CHAN_ACC_RAW_Y, d.accel_raw[1]);
344  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
345  CHAN_ACC_RAW_Z, d.accel_raw[2]);
346  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
347  CHAN_ACC_CAL_X, d.accel_cal[0]);
348  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
349  CHAN_ACC_CAL_Y, d.accel_cal[1]);
350  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
351  CHAN_ACC_CAL_Z, d.accel_cal[2]);
352  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
353  CHAN_G_RAW_X, d.gyro_raw[0]);
354  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
355  CHAN_G_RAW_Y, d.gyro_raw[1]);
356  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
357  CHAN_G_RAW_Z, d.gyro_raw[2]);
358  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
359  CHAN_G_CAL_X, d.gyro_cal[0]);
360  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
361  CHAN_G_CAL_Y, d.gyro_cal[1]);
362  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
363  CHAN_G_CAL_Z, d.gyro_cal[2]);
364  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
365  CHAN_G_ALIGN_X, d.gyro_aligned[0]);
366  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
367  CHAN_G_ALIGN_Y, d.gyro_aligned[1]);
368  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
369  CHAN_G_ALIGN_Z, d.gyro_aligned[2]);
370  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
371  CHAN_OMEGA_X, d.omega[0]);
372  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
373  CHAN_OMEGA_Y, d.omega[1]);
374  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
375  CHAN_OMEGA_Z, d.omega[2]);
376  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
377  CHAN_ROLL, d.euler_angles[0]);
378  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
379  CHAN_PITCH, d.euler_angles[1]);
380  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum, CHAN_YAW,
381  d.euler_angles[2]);
382  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
383  CHAN_ACC_LIN_X, d.accel_linear[0]);
384  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
385  CHAN_ACC_LIN_Y, d.accel_linear[1]);
386  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
387  CHAN_ACC_LIN_Z, d.accel_linear[2]);
388  rs &= lpms_queue_message(args->logQ, lpmsInfo->sourceNum,
389  CHAN_ALTITUDE, d.altitude);
390  if (!rs) {
391  log_error(
392  args->pstate,
393  "[LPMS:%s] Unable to allocate and/or queue all messages (%d)",
394  args->tag, errno);
395  free(m->data);
396  free(m);
397  args->returnCode = -1;
398  pthread_exit(&(args->returnCode));
399  }
400  } else {
401  log_info(args->pstate, 2,
402  "[LPSM:%s] Unhandled message type: 0x%02x [%02d bytes]",
403  args->tag, m->command, m->length);
404  }
405  free(m->data);
406  free(m);
407  } else {
408  // No message available, so sleep
409  usleep(1E5);
410  }
411  }
412  free(buf);
413  pthread_exit(NULL);
414  return NULL; // Superfluous, as returning zero via pthread_exit above
415 }
416 
423 void *lpms_shutdown(void *ptargs) {
424  log_thread_args_t *args = (log_thread_args_t *)ptargs;
425  lpms_params *lpmsInfo = (lpms_params *)args->dParams;
426 
427  if (lpmsInfo->handle >= 0) { // Admittedly 0 is unlikely
428  close(lpmsInfo->handle);
429  }
430  lpmsInfo->handle = -1;
431  if (lpmsInfo->portName) {
432  free(lpmsInfo->portName);
433  lpmsInfo->portName = NULL;
434  }
435  return NULL;
436 }
437 
443  .logging = &lpms_logging,
444  .shutdown = &lpms_shutdown,
445  .channels = &lpms_channels};
446  return cb;
447 }
448 
453  lpms_params mp = {
454  .portName = NULL, .baudRate = 921600, .handle = -1, .unitID = 1, .pollFreq = 10};
455  return mp;
456 }
457 
466 void *lpms_channels(void *ptargs) {
467  log_thread_args_t *args = (log_thread_args_t *)ptargs;
468  lpms_params *lpmsInfo = (lpms_params *)args->dParams;
469 
470  msg_t *m_sn = msg_new_string(lpmsInfo->sourceNum, SLCHAN_NAME,
471  strlen(lpmsInfo->sourceName), lpmsInfo->sourceName);
472 
473  if (!queue_push(args->logQ, m_sn)) {
474  log_error(args->pstate, "[LPMS:%s] Error pushing channel name to queue",
475  args->tag);
476  msg_destroy(m_sn);
477  args->returnCode = -1;
478  pthread_exit(&(args->returnCode));
479  }
480 
481  strarray *channels = sa_new(29);
482  sa_create_entry(channels, SLCHAN_NAME, 4, "Name");
483  sa_create_entry(channels, SLCHAN_MAP, 8, "Channels");
484  sa_create_entry(channels, SLCHAN_TSTAMP, 9, "Timestamp");
485  sa_create_entry(channels, SLCHAN_RAW, 8, "Raw Data");
486  sa_create_entry(channels, CHAN_ACC_RAW_X, 17, "AccelerationRaw_X");
487  sa_create_entry(channels, CHAN_ACC_RAW_Y, 17, "AccelerationRaw_Y");
488  sa_create_entry(channels, CHAN_ACC_RAW_Z, 17, "AccelerationRaw_Z");
489  sa_create_entry(channels, CHAN_ACC_CAL_X, 17, "AccelerationCal_X");
490  sa_create_entry(channels, CHAN_ACC_CAL_Y, 17, "AccelerationCal_Y");
491  sa_create_entry(channels, CHAN_ACC_CAL_Z, 17, "AccelerationCal_Z");
492  sa_create_entry(channels, CHAN_G_RAW_X, 9, "GyroRaw_X");
493  sa_create_entry(channels, CHAN_G_RAW_Y, 9, "GyroRaw_Y");
494  sa_create_entry(channels, CHAN_G_RAW_Z, 9, "GyroRaw_Z");
495  sa_create_entry(channels, CHAN_G_CAL_X, 9, "GyroCal_X");
496  sa_create_entry(channels, CHAN_G_CAL_Y, 9, "GyroCal_Y");
497  sa_create_entry(channels, CHAN_G_CAL_Z, 9, "GyroCal_Z");
498  sa_create_entry(channels, CHAN_G_ALIGN_X, 11, "GyroAlign_X");
499  sa_create_entry(channels, CHAN_G_ALIGN_Y, 11, "GyroAlign_Y");
500  sa_create_entry(channels, CHAN_G_ALIGN_Z, 11, "GyroAlign_Z");
501  sa_create_entry(channels, CHAN_OMEGA_X, 12, "AngularVel X");
502  sa_create_entry(channels, CHAN_OMEGA_Y, 12, "AngularVel Y");
503  sa_create_entry(channels, CHAN_OMEGA_Z, 12, "AngularVel Z");
504  sa_create_entry(channels, CHAN_ROLL, 4, "Roll");
505  sa_create_entry(channels, CHAN_PITCH, 5, "Pitch");
506  sa_create_entry(channels, CHAN_YAW, 3, "Yaw");
507  sa_create_entry(channels, CHAN_ACC_LIN_X, 17, "AccelerationLin_X");
508  sa_create_entry(channels, CHAN_ACC_LIN_Y, 17, "AccelerationLin_Y");
509  sa_create_entry(channels, CHAN_ACC_LIN_Z, 17, "AccelerationLin_Z");
510  sa_create_entry(channels, CHAN_ALTITUDE, 8, "Altitude");
511  msg_t *m_cmap = msg_new_string_array(lpmsInfo->sourceNum, SLCHAN_MAP, channels);
512 
513  if (!queue_push(args->logQ, m_cmap)) {
514  log_error(args->pstate, "[LPMS:%s] Error pushing channel map to queue", args->tag);
515  msg_destroy(m_cmap);
516  sa_destroy(channels);
517  free(channels);
518  args->returnCode = -1;
519  pthread_exit(&(args->returnCode));
520  }
521 
522  sa_destroy(channels);
523  free(channels);
524  return NULL;
525 }
526 
533  if (lta->dParams) {
534  log_error(lta->pstate, "[LPMS:%s] Refusing to reconfigure", lta->tag);
535  return false;
536  }
537 
538  lpms_params *lpmsInfo = calloc(1, sizeof(lpms_params));
539  if (!lpmsInfo) {
540  log_error(lta->pstate, "[LPMS:%s] Unable to allocate memory for device parameters",
541  lta->tag);
542  return false;
543  }
544  (*lpmsInfo) = lpms_getParams();
545 
546  config_kv *t = NULL;
547  if ((t = config_get_key(s, "port"))) { lpmsInfo->portName = config_qstrdup(t->value); }
548  t = NULL;
549 
550  if ((t = config_get_key(s, "name"))) {
551  lpmsInfo->sourceName = config_qstrdup(t->value);
552  } else {
553  // Must set a name, so nick the tag value
554  lpmsInfo->sourceName = strdup(lta->tag);
555  }
556  t = NULL;
557 
558  if ((t = config_get_key(s, "sourcenum"))) {
559  errno = 0;
560  int sn = strtol(t->value, NULL, 0);
561  if (errno) {
562  log_error(lta->pstate, "[LPMS:%s] Error parsing source number: %s",
563  lta->tag, strerror(errno));
564  free(lpmsInfo);
565  return false;
566  }
567  if (sn < 0) {
568  log_error(lta->pstate, "[LPMS:%s] Invalid source number (%s)", lta->tag,
569  t->value);
570  free(lpmsInfo);
571  return false;
572  }
573  if (sn < 10) {
574  lpmsInfo->sourceNum += sn;
575  } else {
576  lpmsInfo->sourceNum = sn;
577  if (sn < SLSOURCE_ADC || sn > (SLSOURCE_ADC + 0x0F)) {
578  log_warning(
579  lta->pstate,
580  "[LPMS:%s] Unexpected Source ID number (0x%02x)- this may cause analysis problems",
581  lta->tag, sn);
582  }
583  }
584  } else {
585  lpmsInfo->sourceNum = SLSOURCE_ADC + 0x0E;
586  log_warning(lta->pstate, "[LPMS:%s] Source number not provided - 0x%02x assigned",
587  lta->tag, lpmsInfo->sourceNum);
588  }
589 
590  if ((t = config_get_key(s, "baud"))) {
591  errno = 0;
592  lpmsInfo->baudRate = strtol(t->value, NULL, 0);
593  if (errno) {
594  log_error(lta->pstate, "[LPMS:%s] Error parsing baud rate: %s", lta->tag,
595  strerror(errno));
596  free(lpmsInfo);
597  return false;
598  }
599  }
600  t = NULL;
601 
602  if ((t = config_get_key(s, "unit"))) {
603  errno = 0;
604  lpmsInfo->unitID = strtol(t->value, NULL, 0);
605  if (errno) {
606  log_error(lta->pstate, "[LPMS:%s] Error parsing unit ID: %s", lta->tag,
607  strerror(errno));
608  free(lpmsInfo);
609  return false;
610  }
611  if ((lpmsInfo->unitID <= 0) || (lpmsInfo->unitID > 255)) {
612  log_error(lta->pstate,
613  "[LPMS:%s] Unit ID specified (%d not in range 1-255)", lta->tag,
614  lpmsInfo->unitID);
615  free(lpmsInfo);
616  return false;
617  }
618  }
619  t = NULL;
620 
621  if ((t = config_get_key(s, "frequency"))) {
622  errno = 0;
623  lpmsInfo->pollFreq = strtol(t->value, NULL, 0);
624  if (errno) {
625  log_error(lta->pstate,
626  "[LPMS:%s] Error parsing minimum poll frequency: %s", lta->tag,
627  strerror(errno));
628  free(lpmsInfo);
629  return false;
630  }
631 
632  switch (lpmsInfo->pollFreq) {
633  case 5:
634  case 10:
635  case 50:
636  case 100:
637  case 250:
638  case 500:
639  break;
640  default:
641  log_error(
642  lta->pstate,
643  "[LPMS:%s] Invalid minimum poll frequency (%d is not greater than zero)",
644  lta->tag, lpmsInfo->pollFreq);
645  free(lpmsInfo);
646  return false;
647  }
648  }
649  t = NULL;
650  lta->dParams = lpmsInfo;
651  return true;
652 }
bool lpms_readMessage_buf(int handle, lpms_message *out, uint8_t buf[LPMS_BUFF], size_t *index, size_t *hw)
Read data from handle, and parse message if able.
bool lpms_send_command(const int handle, lpms_message *m)
Write command defined by structure to handle.
#define LPMS_BUFF
Default serial buffer allocation size.
int lpms_openConnection(const char *device, const int baud)
Open connection to an LPMS serial device.
bool lpms_send_stream_mode(const int handle)
Shortcut: Send LPMS_MSG_MODE_STREAM.
bool lpms_send_command_mode(const int handle)
Shortcut: Send LPMS_MSG_MODE_CMD.
bool lpms_checksum(const lpms_message *msg, uint16_t *csum)
Calculate checksum for LPMS message packet.
Definition: LPMSMessages.c:135
bool lpms_imu_set_gyro_aligned(const lpms_message *msg, lpms_data *d)
Extract gyro_aligned from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:258
bool lpms_imu_set_altitude(const lpms_message *msg, lpms_data *d)
Extract altitude from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:470
bool lpms_imu_set_euler_angles(const lpms_message *msg, lpms_data *d)
Extract euler_angles from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:383
bool lpms_imu_set_gyro_raw(const lpms_message *msg, lpms_data *d)
Extract gyro_raw from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:214
bool lpms_imu_set_accel_raw(const lpms_message *msg, lpms_data *d)
Extract accel_raw from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:178
bool lpms_imu_set_timestamp(const lpms_message *msg, lpms_data *d)
Extract timestamp from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:161
bool lpms_imu_set_accel_cal(const lpms_message *msg, lpms_data *d)
Extract accel_cal from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:194
bool lpms_imu_set_gyro_cal(const lpms_message *msg, lpms_data *d)
Extract gyro_cal from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:235
bool lpms_imu_set_accel_linear(const lpms_message *msg, lpms_data *d)
Extract accel_linear from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:411
bool lpms_imu_set_omega(const lpms_message *msg, lpms_data *d)
Extract omega from lpms_message into lpms_data, if available.
Definition: LPMSMessages.c:330
char * config_qstrdup(const char *c)
Duplicate string, stripping optional leading/trailing quote marks.
Definition: LoggerConfig.c:271
config_kv * config_get_key(const config_section *cs, const char *kn)
Find configugration key within specific section, by name.
Definition: LoggerConfig.c:204
void signalHandlersBlock(void)
Block signals that we have handlers for.
#define LPMS_IMU_EULER
euler[] will contain data
Definition: LPMSTypes.h:109
#define LPMS_IMU_ACCEL_LINEAR
accel_linear[] will contain data
Definition: LPMSTypes.h:110
#define LPMS_IMU_OMEGA
omega[] will contain data
Definition: LPMSTypes.h:107
#define LPMS_IMU_ACCEL_CAL
accel_cal[] will contain data
Definition: LPMSTypes.h:101
#define LPMS_IMU_GYRO_ALIGN
gyro_align[] will contain data
Definition: LPMSTypes.h:104
#define LPMS_IMU_ALTITUDE
altitude will contain data
Definition: LPMSTypes.h:112
#define LPMS_IMU_GYRO_CAL
gyro_cal[] will contain data
Definition: LPMSTypes.h:103
#define LPMS_IMU_GYRO_RAW
gyro_raw[] will contain data
Definition: LPMSTypes.h:102
#define LPMS_IMU_ACCEL_RAW
accel_raw[] will contain data
Definition: LPMSTypes.h:100
#define LPMS_MSG_SET_OUTPUTS
Configure fields included in IMUDATA messages.
Definition: LPMSMessages.h:63
#define LPMS_MSG_GET_OUTPUTS
Get fields configured for IMUDATA messages.
Definition: LPMSMessages.h:64
#define LPMS_MSG_GET_SERIALNUM
Get serial number as 24 character string.
Definition: LPMSMessages.h:61
#define LPMS_MSG_GET_FREQ
Get current streaming output rate.
Definition: LPMSMessages.h:68
#define LPMS_MSG_GET_IMUDATA
IMU data, as configured by LPMS_MSG_SET_OUTPUTS.
Definition: LPMSMessages.h:58
#define LPMS_MSG_GET_FIRMWAREVER
Get firmware version as 24 character string.
Definition: LPMSMessages.h:60
#define LPMS_MSG_GET_SENSORMODEL
Get hardware model as 24 character string.
Definition: LPMSMessages.h:59
#define LPMS_MSG_SET_FREQ
Set streaming output rate (Hz)
Definition: LPMSMessages.h:67
msg_t * msg_new_string(const uint8_t source, const uint8_t type, const size_t len, const char *str)
Create a new message with a single string embedded.
Definition: messages.c:84
void msg_destroy(msg_t *msg)
Destroy a message.
Definition: messages.c:349
msg_t * msg_new_string_array(const uint8_t source, const uint8_t type, const strarray *array)
Create a new message containing an array of strings.
Definition: messages.c:116
msg_t * msg_new_timestamp(const uint8_t source, const uint8_t type, const uint32_t ts)
Create a timestamp message.
Definition: messages.c:57
msg_t * msg_new_float(const uint8_t source, const uint8_t type, const float val)
Create new message with a single numeric value.
Definition: messages.c:38
#define SLCHAN_TSTAMP
Source timestamp (milliseconds, arbitrary epoch)
Definition: sources.h:99
#define SLCHAN_RAW
Raw device data (Not mandatory)
Definition: sources.h:100
#define SLCHAN_LOG_INFO
Information messages.
Definition: sources.h:101
#define SLCHAN_MAP
Channel name map (excludes log channels)
Definition: sources.h:98
#define SLCHAN_NAME
Name of source device.
Definition: sources.h:97
bool lpms_parseConfig(log_thread_args_t *lta, config_section *s)
Take a configuration section and parse parameters.
Definition: LoggerLPMS.c:532
void * lpms_logging(void *ptargs)
Serial source main logging loop.
Definition: LoggerLPMS.c:159
bool lpms_queue_message(msgqueue *Q, const uint8_t src, const uint8_t chan, const float val)
Helper function: Create and queue data messages, with error handling.
Definition: LoggerLPMS.c:136
void * lpms_channels(void *ptargs)
Serial source channel map.
Definition: LoggerLPMS.c:466
void * lpms_setup(void *ptargs)
Generic serial connection setup.
Definition: LoggerLPMS.c:58
device_callbacks lpms_getCallbacks()
Fill out device callback functions for logging.
Definition: LoggerLPMS.c:441
lpms_params lpms_getParams()
Fill out default MP source parameters.
Definition: LoggerLPMS.c:452
void * lpms_shutdown(void *ptargs)
Serial source shutdown.
Definition: LoggerLPMS.c:423
#define SLSOURCE_ADC
Generic analogue inputs.
Definition: sources.h:63
atomic_bool shutdownFlag
Trigger clean software shutdown.
Definition: LoggerSignals.c:34
void log_info(const program_state *s, const int level, const char *format,...)
Output formatted information message at a given level.
Definition: logging.c:125
void log_warning(const program_state *s, const char *format,...)
Output formatted warning message.
Definition: logging.c:86
void log_error(const program_state *s, const char *format,...)
Output formatted error message.
Definition: logging.c:47
bool queue_push(msgqueue *queue, msg_t *msg)
Add a message to the tail of the queue.
Definition: queue.c:103
void sa_destroy(strarray *sa)
Destroy array and contents.
Definition: strarray.c:182
strarray * sa_new(int entries)
Allocate storage for a new array.
Definition: strarray.c:37
bool sa_create_entry(strarray *array, const int index, const size_t len, const char *src)
Create an string in a given position from a character array and length.
Definition: strarray.c:149
Represent a key=value pair.
Definition: LoggerConfig.h:42
char * value
Configuration item value.
Definition: LoggerConfig.h:44
Configuration file section.
Definition: LoggerConfig.h:54
Device specific function information.
Definition: Logger.h.in:72
device_fn startup
Called serially at startup, opens devices etc.
Definition: Logger.h.in:73
Logging thread information.
Definition: Logger.h.in:86
msgqueue * logQ
Main message queue. Pushed to by threads, consumed by main()
Definition: Logger.h.in:89
char * tag
Tag/source name for messages etc.
Definition: Logger.h.in:87
void * dParams
Device/Thread specific data.
Definition: Logger.h.in:92
program_state * pstate
Current program state, used for logging.
Definition: Logger.h.in:90
int returnCode
Thread return code (output)
Definition: Logger.h.in:93
LPMS IMU data packet.
Definition: LPMSTypes.h:72
float euler_angles[3]
Orientation as Euler roll angles [X].
Definition: LPMSTypes.h:83
float altitude
Altitude [m].
Definition: LPMSTypes.h:86
float gyro_aligned[3]
Calibrated and aligned gyroscope values [X/s].
Definition: LPMSTypes.h:78
float gyro_raw[3]
Raw gyroscope values [X/s].
Definition: LPMSTypes.h:76
float accel_linear[3]
Linear acceleration (only) [g].
Definition: LPMSTypes.h:84
uint32_t timestamp
Counted in 0.002s increments.
Definition: LPMSTypes.h:73
float accel_cal[3]
Calibrated accelerometer values [g].
Definition: LPMSTypes.h:75
float gyro_cal[3]
Calibrated gyroscope values [X/s].
Definition: LPMSTypes.h:77
float accel_raw[3]
Raw accelerometer values [g].
Definition: LPMSTypes.h:74
float omega[3]
Angular velocity [X/s].
Definition: LPMSTypes.h:81
uint32_t present
Bitmask indicating set/valid members.
Definition: LPMSTypes.h:88
Represent LPMS message.
Definition: LPMSTypes.h:48
uint16_t length
Length of data, in bytes.
Definition: LPMSTypes.h:51
uint16_t checksum
Sum of all preceding message bytes.
Definition: LPMSTypes.h:52
uint8_t * data
Pointer to data array.
Definition: LPMSTypes.h:53
uint16_t id
Source/Destination Sensor ID.
Definition: LPMSTypes.h:49
uint16_t command
Message type.
Definition: LPMSTypes.h:50
Serial device specific parameters.
Definition: LoggerLPMS.h:44
int unitID
LPMS Sensor Address.
Definition: LoggerLPMS.h:48
int pollFreq
Desired number of measurements per second.
Definition: LoggerLPMS.h:51
int baudRate
Baud rate for operations (Default 921600)
Definition: LoggerLPMS.h:49
int handle
Handle for currently opened device.
Definition: LoggerLPMS.h:50
char * sourceName
User defined name for this source.
Definition: LoggerLPMS.h:45
char * portName
Target port name.
Definition: LoggerLPMS.h:47
uint8_t sourceNum
Source ID for messages.
Definition: LoggerLPMS.h:46
Queuable message.
Definition: messages.h:71
Represent a simple FIFO message queue.
Definition: queue.h:51
Array of strings.
Definition: strarray.h:43