PX4 Firmware
PX4 Autopilot Software http://px4.io
IridiumSBD.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2016-2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 #include "IridiumSBD.h"
35 
36 #include <px4_platform_common/tasks.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <poll.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <termios.h>
45 #include <pthread.h>
46 
47 #include <systemlib/err.h>
48 #include <parameters/param.h>
49 
50 static constexpr const char *satcom_state_string[4] = {"STANDBY", "SIGNAL CHECK", "SBD SESSION", "TEST"};
51 
52 #define VERBOSE_INFO(...) if (_verbose) { PX4_INFO(__VA_ARGS__); }
53 
54 #define IRIDIUMSBD_DEVICE_PATH "/dev/iridium"
55 
58 
61 {
62 }
63 
64 ///////////////////////////////////////////////////////////////////////
65 // public functions //
66 ///////////////////////////////////////////////////////////////////////
67 
68 int IridiumSBD::start(int argc, char *argv[])
69 {
70  PX4_INFO("starting");
71 
72  if (IridiumSBD::instance != nullptr) {
73  PX4_WARN("already started");
74  return PX4_ERROR;
75  }
76 
77  IridiumSBD::instance = new IridiumSBD();
78 
79  IridiumSBD::task_handle = px4_task_spawn_cmd("iridiumsbd", SCHED_DEFAULT,
80  SCHED_PRIORITY_SLOW_DRIVER, 1350, (main_t)&IridiumSBD::main_loop_helper, argv);
81 
82  int counter = 0;
83  IridiumSBD::instance->_start_completed = false;
84  IridiumSBD::instance->_task_should_exit = false;
85 
86  // give the driver 6 seconds to start up
87  while (!IridiumSBD::instance->_start_completed && (IridiumSBD::task_handle != -1) && counter < 60) {
88  counter++;
89  usleep(100000);
90  }
91 
92  if (IridiumSBD::instance->_start_completed && (IridiumSBD::task_handle != -1)) {
93  return PX4_OK;
94 
95  } else {
96  // the driver failed to start so make sure it is shut down before exiting
97  IridiumSBD::instance->_task_should_exit = true;
98 
99  for (int i = 0; (i < 10 + 1) && (IridiumSBD::task_handle != -1); i++) {
100  sleep(1);
101  }
102 
103  return PX4_ERROR;
104  }
105 }
106 
108 {
109  if (IridiumSBD::instance == nullptr) {
110  PX4_WARN("not started");
111  return PX4_ERROR;
112  }
113 
114  if (IridiumSBD::instance->_cdev_used) {
115  PX4_WARN("device is used. Stop all users (MavLink)");
116  return PX4_ERROR;
117  }
118 
119  PX4_WARN("stopping...");
120 
121  IridiumSBD::instance->_task_should_exit = true;
122 
123  // give it enough time to stop
124  //param_timeout_s = 10;
125 
126  // TODO
127  for (int i = 0; (i < 10 + 1) && (IridiumSBD::task_handle != -1); i++) {
128  sleep(1);
129  }
130 
131  // well, kill it anyway, though this may crash
132  if (IridiumSBD::task_handle != -1) {
133  PX4_WARN("killing task forcefully");
134 
135  ::close(IridiumSBD::instance->uart_fd);
136  task_delete(IridiumSBD::task_handle);
138  delete IridiumSBD::instance;
139  IridiumSBD::instance = nullptr;
140  }
141 
142  return OK;
143 }
144 
146 {
147  if (IridiumSBD::instance == nullptr) {
148  PX4_WARN("not started");
149  return;
150  }
151 
152  PX4_INFO("started");
153  PX4_INFO("state: %s", satcom_state_string[instance->_state]);
154 
155  PX4_INFO("TX buf written: %d", instance->_tx_buf_write_idx);
156  PX4_INFO("Signal quality: %d", instance->_signal_quality);
157  PX4_INFO("TX session pending: %d", instance->_tx_session_pending);
158  PX4_INFO("RX session pending: %d", instance->_rx_session_pending);
159  PX4_INFO("RX read pending: %d", instance->_rx_read_pending);
160  PX4_INFO("Time since last signal check: %lld", hrt_absolute_time() - instance->_last_signal_check);
161  PX4_INFO("Last heartbeat: %lld", instance->_last_heartbeat);
162 }
163 
164 void IridiumSBD::test(int argc, char *argv[])
165 {
166  if (instance == nullptr) {
167  PX4_WARN("not started");
168  return;
169  }
170 
172  PX4_WARN("MODEM BUSY!");
173  return;
174  }
175 
176  if (argc > 2) {
177  strncpy(instance->_test_command, argv[2], sizeof(instance->_test_command));
178  instance->_test_command[sizeof(instance->_test_command) - 1] = 0;
179 
180  } else {
181  instance->_test_command[0] = 0;
182  }
183 
185 }
186 
187 int IridiumSBD::ioctl(struct file *filp, int cmd, unsigned long arg)
188 {
189  switch (cmd) {
190  case FIONREAD: {
191  int count = _rx_msg_end_idx - _rx_msg_read_idx;
192  *(int *)arg = count;
193 
194  return OK;
195  }
196 
197  case FIONSPACE: {
199  *(int *)arg = count;
200 
201  return OK;
202  }
203 
204  default: {
205 
206  /* see if the parent class can make any use of it */
207  return CDev::ioctl(filp, cmd, arg);
208  }
209  }
210 }
211 
212 ///////////////////////////////////////////////////////////////////////
213 // private functions //
214 ///////////////////////////////////////////////////////////////////////
215 
216 void IridiumSBD::main_loop_helper(int argc, char *argv[])
217 {
218  // start the main loop and stay in it
219  IridiumSBD::instance->main_loop(argc, argv);
220 
221  // tear down everything after the main loop exits
222  ::close(IridiumSBD::instance->uart_fd);
224  delete IridiumSBD::instance;
225  IridiumSBD::instance = nullptr;
226 
227  PX4_WARN("stopped");
228 }
229 
230 void IridiumSBD::main_loop(int argc, char *argv[])
231 {
232  CDev::init();
233 
234  pthread_mutex_init(&_tx_buf_mutex, NULL);
235  pthread_mutex_init(&_rx_buf_mutex, NULL);
236 
237  int arg_i = 3;
238  int arg_uart_name = 0;
239 
240  while (arg_i < argc) {
241  if (!strcmp(argv[arg_i], "-d")) {
242  arg_i++;
243  arg_uart_name = arg_i;
244 
245  } else if (!strcmp(argv[arg_i], "-v")) {
246  PX4_WARN("verbose mode ON");
247  _verbose = true;
248  }
249 
250  arg_i++;
251  }
252 
253  if (arg_uart_name == 0) {
254  PX4_ERR("no Iridium SBD modem UART port provided!");
255  _task_should_exit = true;
256  return;
257  }
258 
259 
260  bool command_executed = false;
261 
262  for (int counter = 0; (counter < 20) && !command_executed; counter++) {
263  if (open_uart(argv[arg_uart_name]) == SATCOM_UART_OK) {
264  command_executed = true;
265 
266  } else {
267  usleep(100000);
268  }
269  }
270 
271  if (!command_executed) {
272  PX4_ERR("failed to open UART port!");
273  _task_should_exit = true;
274  return;
275  }
276 
277  // disable flow control
278  command_executed = false;
279 
280  for (int counter = 0; (counter < 20) && !command_executed; counter++) {
281  write_at("AT&K0");
282 
284  usleep(100000);
285 
286  } else {
287  command_executed = true;
288  }
289  }
290 
291  if (!command_executed) {
292  PX4_ERR("modem not responding");
293  _task_should_exit = true;
294  return;
295  }
296 
297  // disable command echo
298  command_executed = false;
299 
300  for (int counter = 0; (counter < 10) && !command_executed; counter++) {
301  write_at("ATE0");
302 
304  usleep(100000);
305 
306  } else {
307  command_executed = true;
308  }
309  }
310 
311  if (!command_executed) {
312  PX4_ERR("modem not responding");
313  _task_should_exit = true;
314  return;
315  }
316 
317  param_t param_pointer;
318 
319  param_pointer = param_find("ISBD_READ_INT");
320  param_get(param_pointer, &_param_read_interval_s);
321 
322  param_pointer = param_find("ISBD_SBD_TIMEOUT");
323  param_get(param_pointer, &_param_session_timeout_s);
324 
325  if (_param_session_timeout_s < 0) {
327  }
328 
329  param_pointer = param_find("ISBD_STACK_TIME");
330  param_get(param_pointer, &_param_stacking_time_ms);
331 
332  if (_param_stacking_time_ms < 0) {
334  }
335 
336  VERBOSE_INFO("read interval: %d s", _param_read_interval_s);
337  VERBOSE_INFO("SBD session timeout: %d s", _param_session_timeout_s);
338  VERBOSE_INFO("SBD stack time: %d ms", _param_stacking_time_ms);
339 
340  _start_completed = true;
341 
342  while (!_task_should_exit) {
343  switch (_state) {
345  standby_loop();
346  break;
347 
348  case SATCOM_STATE_CSQ:
349  csq_loop();
350  break;
351 
353  sbdsession_loop();
354  break;
355 
356  case SATCOM_STATE_TEST:
357  test_loop();
358  break;
359  }
360 
362 
363  if (_new_state != _state) {
364  VERBOSE_INFO("SWITCHING STATE FROM %s TO %s", satcom_state_string[_state], satcom_state_string[_new_state]);
365  _state = _new_state;
367 
368  } else {
370  usleep(100000); // 100ms
371  }
372  }
373 }
374 
376 {
377  if (_test_pending) {
378  _test_pending = false;
379 
380  if (!strcmp(_test_command, "s")) {
381  write(0, "kreczmer", 8);
382 
383  } else if (!strcmp(_test_command, "read")) {
384  _rx_session_pending = true;
385 
386  } else {
388  start_test();
389  return;
390  }
391  }
392 
393  // check for incoming SBDRING, handled inside read_at_command()
394  read_at_command();
395 
396  if (_param_read_interval_s > 0
397  && ((hrt_absolute_time() - _last_read_time) > (uint64_t)_param_read_interval_s * 1000000)) {
398  _rx_session_pending = true;
399  }
400 
401  // write the MO buffer when the message stacking time expires
403  write_tx_buf();
404  }
405 
406  // do not start an SBD session if there is still data in the MT buffer, or it will be lost
408  if (_signal_quality > 0) {
409  // clear the MO buffer if we only want to read a message
411  if (clear_mo_buffer()) {
413  return;
414  }
415 
416  } else {
418  return;
419  }
420 
421  } else {
422  start_csq();
423  return;
424  }
425  }
426 
427  // start a signal check if requested and not a switch to another mode is scheduled
430  start_csq();
431  return;
432  }
433 
434  // only read the MT buffer if the higher layer (mavlink app) read the previous message
436  read_rx_buf();
437  return;
438  }
439 }
440 
442 {
443  int res = read_at_command();
444 
445  if (res == SATCOM_RESULT_NA) {
446  return;
447  }
448 
449  if (res != SATCOM_RESULT_OK) {
450  VERBOSE_INFO("UPDATE SIGNAL QUALITY: ERROR");
451 
453  return;
454  }
455 
456  if (strncmp((const char *)_rx_command_buf, "+CSQ:", 5)) {
457  VERBOSE_INFO("UPDATE SIGNAL QUALITY: WRONG ANSWER:");
458  VERBOSE_INFO("%s", _rx_command_buf);
459 
461  return;
462  }
463 
464  _signal_quality = _rx_command_buf[5] - 48;
465 
466  VERBOSE_INFO("SIGNAL QUALITY: %d", _signal_quality);
467 
469 }
470 
472 {
473  int res = read_at_command();
474 
475  if (res == SATCOM_RESULT_NA) {
476  if ((_param_session_timeout_s > 0)
478  > (uint64_t)_param_session_timeout_s * 1000000)) {
479 
480  PX4_WARN("SBD SESSION: TIMEOUT!");
483  pthread_mutex_unlock(&_tx_buf_mutex);
484  }
485 
486  return;
487  }
488 
489  if (res != SATCOM_RESULT_OK) {
490  VERBOSE_INFO("SBD SESSION: ERROR. RESULT: %d", res);
491 
494  pthread_mutex_unlock(&_tx_buf_mutex);
495  return;
496  }
497 
498  if (strncmp((const char *)_rx_command_buf, "+SBDIX:", 7)) {
499 
500  VERBOSE_INFO("SBD SESSION: WRONG ANSWER: %s", _rx_command_buf);
501 
504  pthread_mutex_unlock(&_tx_buf_mutex);
505  return;
506  }
507 
508  int mo_status, mt_status, mt_len, mt_queued;
509  const char *p = (const char *)_rx_command_buf + 7;
510  char **rx_buf_parse = (char **)&p;
511 
512  mo_status = strtol(*rx_buf_parse, rx_buf_parse, 10);
513  (*rx_buf_parse)++;
514  strtol(*rx_buf_parse, rx_buf_parse, 10); // MOMSN, ignore it
515  (*rx_buf_parse)++;
516  mt_status = strtol(*rx_buf_parse, rx_buf_parse, 10);
517  (*rx_buf_parse)++;
518  strtol(*rx_buf_parse, rx_buf_parse, 10); // MTMSN, ignore it
519  (*rx_buf_parse)++;
520  mt_len = strtol(*rx_buf_parse, rx_buf_parse, 10);
521  (*rx_buf_parse)++;
522  mt_queued = strtol(*rx_buf_parse, rx_buf_parse, 10);
523 
524  VERBOSE_INFO("MO ST: %d, MT ST: %d, MT LEN: %d, MT QUEUED: %d", mo_status, mt_status, mt_len, mt_queued);
525  VERBOSE_INFO("SBD session duration: %.2f", (hrt_absolute_time() - _session_start_time) / 1000000.0);
526 
527  switch (mo_status) {
528  case 0:
529  case 2:
530  case 3:
531  case 4:
532  VERBOSE_INFO("SBD SESSION: SUCCESS (%d)", mo_status);
533 
534  _ring_pending = false;
535  _tx_session_pending = false;
539 
540  if (mt_queued > 0) {
541  _rx_session_pending = true;
542 
543  } else {
544  _rx_session_pending = false;
545 
546  }
547 
548  if (mt_len > 0) {
549  _rx_read_pending = true;
550  }
551 
552  // after a successful session reset the tx buffer
553  _tx_buf_write_idx = 0;
554  break;
555 
556  case 1:
557  VERBOSE_INFO("SBD SESSION: MO SUCCESS, MT FAIL");
559 
560  // after a successful session reset the tx buffer
561  _tx_buf_write_idx = 0;
563 
564  _tx_session_pending = false;
565  break;
566 
567  case 32:
568  VERBOSE_INFO("SBD SESSION: NO NETWORK SIGNAL");
569 
571  _signal_quality = 0;
572  break;
573 
574  default:
576  VERBOSE_INFO("SBD SESSION: FAILED (%d)", mo_status);
577  }
578 
580  pthread_mutex_unlock(&_tx_buf_mutex);
581 }
582 
584 {
585  int res = read_at_command();
586 
587  if (res != SATCOM_RESULT_NA) {
588  PX4_INFO("TEST RESULT: %d, LENGTH %d\nDATA:\n%s", res, _rx_command_len, _rx_command_buf);
589  PX4_INFO("TEST DONE, TOOK %lld MS", (hrt_absolute_time() - _test_timer) / 1000);
591  }
592 
593  // timeout after 60 s in the test state
594  if ((hrt_absolute_time() - _test_timer) > 60000000) {
595  PX4_WARN("TEST TIMEOUT AFTER %lld S", (hrt_absolute_time() - _test_timer) / 1000000);
597  }
598 }
599 
601 {
603  VERBOSE_INFO("CANNOT ENTER CSQ STATE");
604  return;
605 
606  } else {
607  VERBOSE_INFO("UPDATING SIGNAL QUALITY");
608  }
609 
611 
612  if (!is_modem_ready()) {
613  VERBOSE_INFO("UPDATE SIGNAL QUALITY: MODEM NOT READY!");
614  return;
615  }
616 
617  write_at("AT+CSQ");
619 }
620 
622 {
624  VERBOSE_INFO("CANNOT ENTER SBD SESSION STATE");
625  return;
626 
627  } else {
628  VERBOSE_INFO("STARTING SBD SESSION");
629  }
630 
631  if (!is_modem_ready()) {
632  VERBOSE_INFO("SBD SESSION: MODEM NOT READY!");
633  return;
634  }
635 
636  if (_ring_pending) {
637  write_at("AT+SBDIXA");
638 
639  } else {
640  write_at("AT+SBDIX");
641  }
642 
644 
645  pthread_mutex_lock(&_tx_buf_mutex);
646 
648 }
649 
651 {
653  PX4_INFO("CANNOT ENTER TEST STATE");
654  return;
655  }
656 
657  int res = read_at_command();
658 
659  if (res != SATCOM_RESULT_NA) {
660  PX4_WARN("SOMETHING WAS IN BUFFER");
661  printf("TEST RESULT: %d, LENGTH %d\nDATA:\n%s\nRAW DATA:\n", res, _rx_command_len, _rx_command_buf);
662 
663  for (int i = 0; i < _rx_command_len; i++) {
664  printf("%d ", _rx_command_buf[i]);
665  }
666 
667  printf("\n");
668  }
669 
670  if (!is_modem_ready()) {
671  PX4_WARN("MODEM NOT READY!");
672  return;
673  }
674 
675  if (strlen(_test_command) != 0) {
676  if ((strstr(_test_command, "AT") != nullptr) || (strstr(_test_command, "at") != nullptr)) {
677  PX4_INFO("TEST %s", _test_command);
680 
681  } else {
682  PX4_WARN("The test command does not include AT or at: %s, ignoring it.", _test_command);
684  }
685 
686  } else {
687  PX4_INFO("TEST DONE");
688  }
689 }
690 
691 ssize_t IridiumSBD::write(struct file *filp, const char *buffer, size_t buflen)
692 {
693  // general check if the incoming message would be too large (the buffer should not reset in that case)
694  if ((ssize_t)buflen > SATCOM_TX_BUF_LEN) {
695  return PX4_ERROR;
696  }
697 
698  pthread_mutex_lock(&_tx_buf_mutex);
699 
700  // parsing the size of the message to write
702  if (buflen < 3) {
703  _packet_length = buflen;
704 
705  } else if ((unsigned char)buffer[0] == 253 && (buflen == 10)) { // mavlink 2
706  const uint8_t payload_len = buffer[1];
707  const uint8_t incompat_flags = buffer[2];
708  _packet_length = payload_len + 12;
710 
711  if (incompat_flags & 0x1) { //signing
712  _packet_length += 13;
713  }
714 
715  } else if ((unsigned char)buffer[0] == 254 && (buflen == 6)) { // mavlink 1
716  const uint8_t payload_len = buffer[1];
717  _packet_length = payload_len + 8;
719 
720  } else {
721  _packet_length = buflen;
722  }
723  }
724 
725  // check if there is enough space to write the message
727  _tx_buf_write_idx = 0;
729  }
730 
731  // keep track of the remaining packet length and if the full message is written
732  _packet_length -= buflen;
733 
734  if (_packet_length == 0) {
735  _writing_mavlink_packet = false;
736  }
737 
738  VERBOSE_INFO("WRITE: LEN %d, TX WRITTEN: %d", buflen, _tx_buf_write_idx);
739 
740  memcpy(_tx_buf + _tx_buf_write_idx, buffer, buflen);
741 
742  _tx_buf_write_idx += buflen;
744  _tx_buf_write_pending = true;
745 
746  pthread_mutex_unlock(&_tx_buf_mutex);
747 
748  return buflen;
749 }
750 
751 ssize_t IridiumSBD::read(struct file *filp, char *buffer, size_t buflen)
752 {
753  pthread_mutex_lock(&_rx_buf_mutex);
754  VERBOSE_INFO("READ: LEN %d, RX: %d RX END: %d", buflen, _rx_msg_read_idx, _rx_msg_end_idx);
755 
757  size_t bytes_to_copy = _rx_msg_end_idx - _rx_msg_read_idx;
758 
759  if (bytes_to_copy > buflen) {
760  bytes_to_copy = buflen;
761  }
762 
763  memcpy(buffer, &_rx_msg_buf[_rx_msg_read_idx], bytes_to_copy);
764 
765  _rx_msg_read_idx += bytes_to_copy;
766 
767  pthread_mutex_unlock(&_rx_buf_mutex);
768  return bytes_to_copy;
769 
770  } else {
771  pthread_mutex_unlock(&_rx_buf_mutex);
772  return -EAGAIN;
773  }
774 }
775 
777 {
778  if (!is_modem_ready()) {
779  VERBOSE_INFO("WRITE SBD: MODEM NOT READY!");
780  return;
781  }
782 
783  pthread_mutex_lock(&_tx_buf_mutex);
784 
785  char command[13];
786  sprintf(command, "AT+SBDWB=%d", _tx_buf_write_idx);
787  write_at(command);
788 
790  VERBOSE_INFO("WRITE SBD: MODEM NOT RESPONDING!");
791  pthread_mutex_unlock(&_tx_buf_mutex);
792  return;
793  }
794 
795  int sum = 0;
796 
797  int written = 0;
798 
799  while (written != _tx_buf_write_idx) {
800  written += ::write(uart_fd, _tx_buf + written, _tx_buf_write_idx - written);
801  }
802 
803  for (int i = 0; i < _tx_buf_write_idx; i++) {
804  sum += _tx_buf[i];
805  }
806 
807  uint8_t checksum[2] = {(uint8_t)(sum / 256), (uint8_t)(sum & 255)};
808  ::write(uart_fd, checksum, 2);
809 
810 
811  VERBOSE_INFO("SEND SBD: CHECKSUM %d %d", checksum[0], checksum[1]);
812 
813  if (read_at_command(250) != SATCOM_RESULT_OK) {
814  VERBOSE_INFO("WRITE SBD: ERROR WHILE WRITING DATA TO MODEM!");
815 
816  pthread_mutex_unlock(&_tx_buf_mutex);
817  return;
818  }
819 
820  if (_rx_command_buf[0] != '0') {
821 
822  VERBOSE_INFO("WRITE SBD: ERROR WHILE WRITING DATA TO MODEM! (%d)", _rx_command_buf[0] - '0');
823 
824  pthread_mutex_unlock(&_tx_buf_mutex);
825  return;
826  }
827 
828  VERBOSE_INFO("WRITE SBD: DATA WRITTEN TO MODEM");
829 
830  _tx_buf_write_pending = false;
831 
832  pthread_mutex_unlock(&_tx_buf_mutex);
833 
834  _tx_session_pending = true;
835 }
836 
838 {
839  if (!is_modem_ready()) {
840  VERBOSE_INFO("READ SBD: MODEM NOT READY!");
841  return;
842  }
843 
844  pthread_mutex_lock(&_rx_buf_mutex);
845 
846 
847  write_at("AT+SBDRB");
848 
849  if (read_at_msg() != SATCOM_RESULT_OK) {
850  VERBOSE_INFO("READ SBD: MODEM NOT RESPONDING!");
852  pthread_mutex_unlock(&_rx_buf_mutex);
853  return;
854  }
855 
856  int data_len = (_rx_msg_buf[0] << 8) + _rx_msg_buf[1];
857 
858  // rx_buf contains 2 byte length, data, 2 byte checksum and /r/n delimiter
859  if (data_len != _rx_msg_end_idx - 6) {
860  PX4_ERR("READ SBD: WRONG DATA LENGTH");
862  pthread_mutex_unlock(&_rx_buf_mutex);
863  return;
864  }
865 
866  int checksum = 0;
867 
868  for (int i = 2; i < data_len + 2; i++) {
869  checksum += _rx_msg_buf[i];
870  }
871 
872  if ((checksum / 256 != _rx_msg_buf[_rx_msg_end_idx - 4]) || ((checksum & 255) != _rx_msg_buf[_rx_msg_end_idx - 3])) {
873  PX4_ERR("READ SBD: WRONG DATA CHECKSUM");
875  pthread_mutex_unlock(&_rx_buf_mutex);
876  return;
877  }
878 
879  _rx_msg_read_idx = 2; // ignore the length
880  _rx_msg_end_idx -= 4; // ignore the checksum and delimiter
881  _rx_read_pending = false;
882 
883  pthread_mutex_unlock(&_rx_buf_mutex);
884  VERBOSE_INFO("READ SBD: SUCCESS, LEN: %d", data_len);
885 }
886 
887 void IridiumSBD::write_at(const char *command)
888 {
889  VERBOSE_INFO("WRITING AT COMMAND: %s", command);
890 
891  ::write(uart_fd, command, strlen(command));
892  ::write(uart_fd, "\r", 1);
893 }
894 
896 {
897  return read_at(_rx_command_buf, &_rx_command_len, timeout);
898 }
899 
901 {
902  return read_at(_rx_msg_buf, &_rx_msg_end_idx, timeout);
903 }
904 
905 satcom_result_code IridiumSBD::read_at(uint8_t *rx_buf, int *rx_len, int16_t timeout)
906 {
907  struct pollfd fds[1];
908  fds[0].fd = uart_fd;
909  fds[0].events = POLLIN;
910 
911  uint8_t buf = 0;
912  int last_rn_idx = 0;
913  int rx_buf_pos = 0;
914  *rx_len = 0;
915 
916  while (1) {
917  if (::poll(&fds[0], 1, timeout) > 0) {
918  if (::read(uart_fd, &buf, 1) > 0) {
919  if (rx_buf_pos == 0 && (buf == '\r' || buf == '\n')) {
920  // ignore the leading \r\n
921  continue;
922  }
923 
924  rx_buf[rx_buf_pos++] = buf;
925 
926  if (rx_buf[rx_buf_pos - 1] == '\n' && rx_buf[rx_buf_pos - 2] == '\r') {
927  // found the \r\n delimiter
928  if (rx_buf_pos == last_rn_idx + 2) {
929  //if (verbose) { PX4_INFO("second in a row, ignore it");}
930  ; // second in a row, ignore it
931 
932  } else if (!strncmp((const char *)&rx_buf[last_rn_idx], "OK\r\n", 4)) {
933  rx_buf[*rx_len] = 0; // null terminator after the information response for printing purposes
934  return SATCOM_RESULT_OK;
935 
936  } else if (!strncmp((const char *)&rx_buf[last_rn_idx], "ERROR\r\n", 7)) {
937  return SATCOM_RESULT_ERROR;
938 
939  } else if (!strncmp((const char *)&rx_buf[last_rn_idx], "SBDRING\r\n", 9)) {
940  _ring_pending = true;
941  _rx_session_pending = true;
942 
943  VERBOSE_INFO("GET SBDRING");
944 
945  return SATCOM_RESULT_SBDRING;
946 
947  } else if (!strncmp((const char *)&rx_buf[last_rn_idx], "READY\r\n", 7)) {
948  return SATCOM_RESULT_READY;
949 
950  } else if (!strncmp((const char *)&rx_buf[last_rn_idx], "HARDWARE FAILURE", 16)) {
951  PX4_WARN("HARDWARE FAILURE!");
952  return SATCOM_RESULT_HWFAIL;
953 
954  } else {
955  *rx_len = rx_buf_pos; // that was the information response, result code incoming
956  }
957 
958  last_rn_idx = rx_buf_pos;
959  }
960  }
961 
962  } else {
963  break;
964  }
965  }
966 
967  return SATCOM_RESULT_NA;
968 }
969 
971 {
972  _test_pending = true;
973 }
974 
976 {
977  write_at("AT+SBDD0");
978 
979  if (read_at_command() != SATCOM_RESULT_OK || _rx_command_buf[0] != '0') {
980  VERBOSE_INFO("CLEAR MO BUFFER: ERROR");
981  return false;
982  }
983 
984  return true;
985 }
986 
988 {
989  VERBOSE_INFO("opening Iridium SBD modem UART: %s", uart_name);
990 
991  uart_fd = ::open(uart_name, O_RDWR | O_BINARY);
992 
993  if (uart_fd < 0) {
994  VERBOSE_INFO("UART open failed!");
995  return SATCOM_UART_OPEN_FAIL;
996  }
997 
998  // set the UART speed to 115200
999  struct termios uart_config;
1000  tcgetattr(uart_fd, &uart_config);
1001  cfsetspeed(&uart_config, 115200);
1002  tcsetattr(uart_fd, TCSANOW, &uart_config);
1003 
1004  VERBOSE_INFO("UART opened");
1005 
1006  return SATCOM_UART_OK;
1007 }
1008 
1010 {
1011  write_at("AT");
1012 
1013  if (read_at_command() == SATCOM_RESULT_OK) {
1014  return true;
1015 
1016  } else {
1017  return false;
1018  }
1019 }
1020 
1021 pollevent_t IridiumSBD::poll_state(struct file *filp)
1022 {
1023  pollevent_t pollstate = 0;
1024 
1026  pollstate |= POLLIN;
1027  }
1028 
1030  pollstate |= POLLOUT;
1031  }
1032 
1033  return pollstate;
1034 }
1035 
1037 {
1038  bool need_to_publish = false;
1039 
1041  need_to_publish = true;
1043  }
1044 
1046  need_to_publish = true;
1048  }
1049 
1051  need_to_publish = true;
1053  }
1054 
1056  need_to_publish = true;
1058  }
1059 
1061  need_to_publish = true;
1063  }
1064 
1066  need_to_publish = true;
1068  }
1069 
1071  need_to_publish = true;
1073  }
1074 
1076  need_to_publish = true;
1078  }
1079 
1080  if (_status.state != _state) {
1081  need_to_publish = true;
1082  _status.state = _state;
1083  }
1084 
1086  need_to_publish = true;
1088  }
1089 
1091  need_to_publish = true;
1093  }
1094 
1096  need_to_publish = true;
1098  }
1099 
1101  need_to_publish = true;
1103  }
1104 
1106  need_to_publish = true;
1108  }
1109 
1111 
1112  // publish the status if it changed
1113  if (need_to_publish) {
1115  }
1116 }
1117 
1119 {
1120  const bool present = true;
1121  const bool enabled = true;
1122  const bool ok = _status.last_heartbeat > 0; // maybe at some point here an additional check should be made
1123 
1124  if ((_info.present != present) || (_info.enabled != enabled) || (_info.ok != ok)) {
1126  _info.subsystem_type = subsystem_info_s::SUBSYSTEM_TYPE_SATCOM;
1127  _info.present = present;
1128  _info.enabled = enabled;
1129  _info.ok = ok;
1130 
1132  }
1133 }
1134 
1135 
1136 int IridiumSBD::open_first(struct file *filep)
1137 {
1138  _cdev_used = true;
1139  return CDev::open_first(filep);
1140 }
1141 
1142 int IridiumSBD::close_last(struct file *filep)
1143 {
1144  _cdev_used = false;
1145  return CDev::close_last(filep);
1146 }
1147 
1148 int iridiumsbd_main(int argc, char *argv[])
1149 {
1150  if (argc < 2) {
1151  goto out_error;
1152  }
1153 
1154  if (!strcmp(argv[1], "start")) {
1155  return IridiumSBD::start(argc, argv);
1156 
1157  } else if (!strcmp(argv[1], "stop")) {
1158  return IridiumSBD::stop();
1159 
1160  } else if (!strcmp(argv[1], "status")) {
1162  return OK;
1163 
1164  } else if (!strcmp(argv[1], "test")) {
1165  IridiumSBD::test(argc, argv);
1166  return OK;
1167  }
1168 
1169 out_error:
1170  PX4_INFO("usage: iridiumsbd {start|stop|status|test} [-d uart_device]");
1171 
1172  return PX4_ERROR;
1173 }
void test_loop(void)
Definition: IridiumSBD.cpp:583
int _rx_msg_end_idx
Definition: IridiumSBD.h:319
virtual int open_first(struct file *filep) override
Notification of the first open of CDev.
pollevent_t poll_state(struct file *filp)
bool _ring_pending
Definition: IridiumSBD.h:326
hrt_abstime _test_timer
Definition: IridiumSBD.h:313
void write_tx_buf()
Definition: IridiumSBD.cpp:776
satcom_state _state
Definition: IridiumSBD.h:338
void sbdsession_loop(void)
Definition: IridiumSBD.cpp:471
static void test(int argc, char *argv[])
Definition: IridiumSBD.cpp:164
virtual int open(file_t *filep)
Handle an open of the device.
Definition: CDev.cpp:141
bool _rx_session_pending
Definition: IridiumSBD.h:327
void schedule_test(void)
Definition: IridiumSBD.cpp:970
void read_rx_buf()
Definition: IridiumSBD.cpp:837
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
hrt_abstime _session_start_time
Definition: IridiumSBD.h:336
bool _tx_buf_write_pending
Definition: IridiumSBD.h:325
hrt_abstime _last_write_time
Definition: IridiumSBD.h:333
void csq_loop(void)
Definition: IridiumSBD.cpp:441
void standby_loop(void)
Definition: IridiumSBD.cpp:375
hrt_abstime _last_heartbeat
Definition: IridiumSBD.h:335
bool clear_mo_buffer()
Definition: IridiumSBD.cpp:975
bool publish(const T &data)
Publish the struct.
int _rx_msg_read_idx
Definition: IridiumSBD.h:320
int iridiumsbd_main(int argc, char *argv[])
#define SATCOM_MAX_MESSAGE_LENGTH
Definition: IridiumSBD.h:89
uint64_t subsystem_type
void publish_iridium_status(void)
virtual int close_last(struct file *filep) override
Notification of the last close of CDev.
static constexpr const char * satcom_state_string[4]
Definition: IridiumSBD.cpp:50
satcom_result_code read_at_command(int16_t timeout=100)
Definition: IridiumSBD.cpp:895
uint16_t _packet_length
Definition: IridiumSBD.h:306
bool _tx_session_pending
Definition: IridiumSBD.h:329
uORB::Publication< iridiumsbd_status_s > _iridiumsbd_status_pub
Definition: IridiumSBD.h:308
bool _writing_mavlink_packet
Definition: IridiumSBD.h:305
virtual int close(file_t *filep)
Handle a close of the device.
Definition: CDev.cpp:166
uint8_t _signal_quality
Definition: IridiumSBD.h:300
subsystem_info_s _info
Definition: IridiumSBD.h:347
#define VERBOSE_INFO(...)
Definition: IridiumSBD.cpp:52
uint8_t _tx_buf[SATCOM_TX_BUF_LEN]
Definition: IridiumSBD.h:322
static int task_handle
Definition: IridiumSBD.h:290
Global flash based parameter store.
static int stop()
Definition: IridiumSBD.cpp:107
bool _cdev_used
Definition: IridiumSBD.h:331
bool _start_completed
Definition: IridiumSBD.h:292
int _tx_buf_write_idx
Definition: IridiumSBD.h:323
void main_loop(int argc, char *argv[])
Definition: IridiumSBD.cpp:230
uint16_t _failed_sbd_sessions
Definition: IridiumSBD.h:301
ssize_t write(struct file *filp, const char *buffer, size_t buflen)
Definition: IridiumSBD.cpp:691
bool publish(const T &data)
Publish the struct.
Definition: Publication.hpp:68
The driver for the Rockblock 9602 and 9603 RockBlock module for satellite communication over the Irid...
Definition: IridiumSBD.h:102
void init()
Activates/configures the hardware registers.
uint16_t successful_sbd_sessions
#define SATCOM_SIGNAL_REFRESH_DELAY
Definition: IridiumSBD.h:92
hrt_abstime _last_signal_check
Definition: IridiumSBD.h:299
bool _test_pending
Definition: IridiumSBD.h:311
int _rx_command_len
Definition: IridiumSBD.h:316
uint16_t _num_tx_buf_reset
Definition: IridiumSBD.h:303
satcom_uart_status
Definition: IridiumSBD.h:53
ssize_t read(struct file *filp, char *buffer, size_t buflen)
Definition: IridiumSBD.cpp:751
satcom_uart_status open_uart(char *uart_name)
Definition: IridiumSBD.cpp:987
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
satcom_result_code read_at(uint8_t *rx_buf, int *rx_len, int16_t timeout=100)
Definition: IridiumSBD.cpp:905
static unsigned counter
Definition: safety.c:56
int32_t _param_stacking_time_ms
Definition: IridiumSBD.h:297
bool _verbose
Definition: IridiumSBD.h:344
bool _rx_read_pending
Definition: IridiumSBD.h:328
void start_test(void)
Definition: IridiumSBD.cpp:650
bool is_modem_ready(void)
#define IRIDIUMSBD_DEVICE_PATH
Definition: IridiumSBD.cpp:54
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
char _test_command[32]
Definition: IridiumSBD.h:312
uint16_t _successful_sbd_sessions
Definition: IridiumSBD.h:302
pthread_mutex_t _rx_buf_mutex
Definition: IridiumSBD.h:342
satcom_result_code read_at_msg(int16_t timeout=100)
Definition: IridiumSBD.cpp:900
struct @83::@85::@87 file
#define SATCOM_TX_BUF_LEN
Definition: IridiumSBD.h:88
static IridiumSBD * instance
Definition: IridiumSBD.h:289
static int start(int argc, char *argv[])
Definition: IridiumSBD.cpp:68
int32_t _param_read_interval_s
Definition: IridiumSBD.h:295
void start_sbd_session(void)
Definition: IridiumSBD.cpp:621
void start_csq(void)
Definition: IridiumSBD.cpp:600
#define OK
Definition: uavcan_main.cpp:71
bool _task_should_exit
Definition: IridiumSBD.h:291
void write_at(const char *command)
Definition: IridiumSBD.cpp:887
static void status()
Definition: IridiumSBD.cpp:145
iridiumsbd_status_s _status
Definition: IridiumSBD.h:346
uint8_t _rx_msg_buf[SATCOM_RX_MSG_BUF_LEN]
Definition: IridiumSBD.h:318
static void main_loop_helper(int argc, char *argv[])
Definition: IridiumSBD.cpp:216
uORB::PublicationQueued< subsystem_info_s > _subsystem_pub
Definition: IridiumSBD.h:309
satcom_state _new_state
Definition: IridiumSBD.h:339
uint8_t _rx_command_buf[SATCOM_RX_COMMAND_BUF_LEN]
Definition: IridiumSBD.h:315
int32_t _param_session_timeout_s
Definition: IridiumSBD.h:296
void publish_subsystem_status()
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uint32_t param_t
Parameter handle.
Definition: param.h:98
virtual int poll(file_t *filep, px4_pollfd_struct_t *fds, bool setup)
Perform a poll setup/teardown operation.
Definition: CDev.cpp:213
pthread_mutex_t _tx_buf_mutex
Definition: IridiumSBD.h:341
hrt_abstime _last_read_time
Definition: IridiumSBD.h:334
int ioctl(struct file *filp, int cmd, unsigned long arg)
Definition: IridiumSBD.cpp:187
satcom_result_code
Definition: IridiumSBD.h:64