mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-29 22:24:32 +02:00
218 lines
6.3 KiB
C++
218 lines
6.3 KiB
C++
#include "telem_serial.h"
|
|
|
|
#include "la-log.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#define UNUSED __attribute__((unused))
|
|
|
|
void Telem_Serial::init()
|
|
{
|
|
open_serial_port();
|
|
}
|
|
|
|
void Telem_Serial::pack_select_fds(fd_set &fds_read, fd_set &fds_write UNUSED, fd_set &fds_err,
|
|
uint8_t &nfds)
|
|
{
|
|
FD_SET(fd, &fds_read);
|
|
FD_SET(fd, &fds_err);
|
|
|
|
if (fd >= nfds) {
|
|
nfds = fd + 1;
|
|
}
|
|
}
|
|
|
|
void Telem_Serial::handle_select_fds(fd_set &fds_read, fd_set &fds_write UNUSED, fd_set &fds_err,
|
|
uint8_t &nfds UNUSED)
|
|
{
|
|
/* check for packets from telem_forwarder */
|
|
if (FD_ISSET(fd, &fds_err)) {
|
|
FD_CLR(fd, &fds_err);
|
|
la_log(LOG_ERR, "select(%d): %s", fd, strerror(errno));
|
|
}
|
|
|
|
if (FD_ISSET(fd, &fds_read)) {
|
|
FD_CLR(fd, &fds_read);
|
|
_recv_buflen_content = handle_read();
|
|
// ::fprintf(stderr, "received %u bytes\n", _buflen_content);
|
|
}
|
|
}
|
|
|
|
// from serial_setup in telem_forward
|
|
void Telem_Serial::open_serial_port()
|
|
{
|
|
fd = open(serialPortName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
|
|
|
if (fd < 0) {
|
|
la_log(LOG_ERR, "unable to open serial port %s", serialPortName.c_str());
|
|
abort();
|
|
}
|
|
|
|
// Configure port for 8N1 transmission
|
|
struct termios options;
|
|
|
|
tcgetattr(fd, &options); // Gets the current options for the port
|
|
// Set the output baud rate
|
|
switch (serialBaud) {
|
|
case 1200:
|
|
cfsetspeed(&options, B1200);
|
|
break;
|
|
case 2400:
|
|
cfsetspeed(&options, B2400);
|
|
break;
|
|
case 4800:
|
|
cfsetspeed(&options, B4800);
|
|
break;
|
|
case 9600:
|
|
cfsetspeed(&options, B9600);
|
|
break;
|
|
case 19200:
|
|
cfsetspeed(&options, B19200);
|
|
break;
|
|
case 38400:
|
|
cfsetspeed(&options, B38400);
|
|
break;
|
|
case 57600:
|
|
cfsetspeed(&options, B57600);
|
|
break;
|
|
case 115200:
|
|
cfsetspeed(&options, B115200);
|
|
break;
|
|
case 500000:
|
|
cfsetspeed(&options, B500000);
|
|
break;
|
|
case 921600:
|
|
cfsetspeed(&options, B921600);
|
|
break;
|
|
case 1500000:
|
|
cfsetspeed(&options, B1500000);
|
|
break;
|
|
default:
|
|
syslog(LOG_ERR, "unsupported baud rate %d", serialBaud);
|
|
return;
|
|
}
|
|
options.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
|
|
options.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST);
|
|
options.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
|
options.c_cflag &= ~(CSIZE | PARENB);
|
|
options.c_cflag |= (CS8 | CLOCAL);
|
|
|
|
if (serialFlow) {
|
|
options.c_cflag |= CRTSCTS; // hardware flow control
|
|
} else {
|
|
options.c_cflag &= ~(CRTSCTS); // no hardware flow control
|
|
}
|
|
|
|
// At 115k (87 us per char), reading 1 char at a time results in increased
|
|
// CPU usage, since we actually can keep up with getting a small number of
|
|
// characters per loop. At 921k (11 us per char), we get more characters
|
|
// each time through the loop, so there is less advantage to setting VMIN
|
|
// to more than 1.
|
|
//
|
|
// CPU Usage at
|
|
// VMIN 115k 921k
|
|
// 1 7.0% 1.8%
|
|
// 10 2.7% 1.6%
|
|
// 100 1.2% 1.2%
|
|
//
|
|
// The problem with asking for more than 1 character per read is that each
|
|
// message will usually not be received until some bytes in the following
|
|
// message are available. That is often not a problem, but there are
|
|
// sometimes gaps of several 10s of milliseconds in the telemetry stream,
|
|
// and it is preferable to process messages as soon as they are available.
|
|
if (serialBaud <= 115200) {
|
|
options.c_cc[VMIN] = 10;
|
|
} else {
|
|
options.c_cc[VMIN] = 1;
|
|
}
|
|
options.c_cc[VTIME] = 0;
|
|
|
|
tcsetattr(fd, TCSANOW, &options); // Set the new options for the port "NOW"
|
|
|
|
la_log(LOG_INFO, "opened serial port %s", serialPortName.c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
uint32_t Telem_Serial::handle_read()
|
|
{
|
|
ssize_t res = read(fd, _recv_buf, _recv_buflen);
|
|
if (res == -1) {
|
|
la_log(LOG_INFO, "Failed read: %s", strerror(errno));
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void Telem_Serial::configure(INIReader *config)
|
|
{
|
|
serialPortName = config->Get("solo", "telemDev", "/dev/ttymxc1");
|
|
serialBaud = config->GetInteger("solo", "telemBaud", 57600);
|
|
serialFlow = config->GetBoolean("solo", "telemFlow", true);
|
|
}
|
|
|
|
void Telem_Serial::do_writer_sends()
|
|
{
|
|
while (_send_buf_start != _send_buf_stop) { // FIXME: use file descriptors!
|
|
bool tail_first = false;
|
|
if (_send_buf_stop < _send_buf_start) {
|
|
tail_first = true;
|
|
}
|
|
uint32_t bytes_to_send =
|
|
tail_first ? (send_buf_size() - _send_buf_start) : (_send_buf_stop - _send_buf_start);
|
|
|
|
int32_t sent = ::write(fd, (const char *)&_send_buf[_send_buf_start], bytes_to_send);
|
|
if (sent < 0) {
|
|
// cry
|
|
break;
|
|
} else if (sent == 0) {
|
|
break;
|
|
} else {
|
|
_send_buf_start += sent;
|
|
if (_send_buf_start == send_buf_size()) {
|
|
_send_buf_start = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Telem_Serial::send_message(const mavlink_message_t &msg)
|
|
{
|
|
char sendbuf[1024]; // large enough...
|
|
|
|
uint16_t messageLen = mavlink_msg_to_send_buffer((uint8_t *)sendbuf, &msg);
|
|
if (send_buffer_space_free() < messageLen) {
|
|
// dropped_packets++;
|
|
return false;
|
|
}
|
|
if (_send_buf_stop >= _send_buf_start) {
|
|
uint16_t to_copy = send_buf_size() - _send_buf_stop;
|
|
if (to_copy > messageLen) {
|
|
to_copy = messageLen;
|
|
}
|
|
memcpy(&_send_buf[_send_buf_stop], sendbuf, to_copy);
|
|
_send_buf_stop += to_copy;
|
|
if (_send_buf_stop >= send_buf_size()) {
|
|
_send_buf_stop = 0;
|
|
}
|
|
to_copy = messageLen - to_copy;
|
|
if (to_copy) {
|
|
memcpy(&_send_buf[_send_buf_stop], &sendbuf[messageLen - to_copy], to_copy);
|
|
_send_buf_stop += to_copy;
|
|
if (_send_buf_stop >= send_buf_size()) {
|
|
_send_buf_stop = 0;
|
|
}
|
|
}
|
|
} else {
|
|
memcpy(&_send_buf[_send_buf_stop], &sendbuf[0], messageLen);
|
|
_send_buf_stop += messageLen;
|
|
if (_send_buf_stop >= send_buf_size()) {
|
|
_send_buf_stop = 0;
|
|
}
|
|
}
|
|
return true;
|
|
}
|