You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
9.2 KiB
250 lines
9.2 KiB
/* |
|
This program 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 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <AP_HAL/AP_HAL.h> |
|
#include <GCS_MAVLink/GCS.h> |
|
#include "AP_Beacon_Nooploop.h" |
|
#include <ctype.h> |
|
#include <stdio.h> |
|
|
|
#define NOOPLOOP_INVALID_VAL -8388000 // indicates data unavailable or invalid |
|
#define NOOPLOOP_SF0_SZ 128 // setting_frame0 packet size |
|
|
|
#define NOOPLOOP_HEADER 0x55 // message header |
|
#define NOOPLOOP_FUNCTION_MARK_NODE_FRAME2 4 |
|
#define NOOPLOOP_NODE_FRAME2_FRAMELEN_MAX 4096 // frames should be less than 4k bytes |
|
#define NOOPLOOP_NODE_FRAME2_SYSTIME 6 // start of 4 bytes holding system time in ms |
|
#define NOOPLOOP_NODE_FRAME2_PRECISION_X 10 // start of 1 byte holding precision in m*100 in x axis |
|
#define NOOPLOOP_NODE_FRAME2_PRECISION_Y 11 // start of 1 byte holding precision in m*100 in y axis |
|
#define NOOPLOOP_NODE_FRAME2_PRECISION_Z 12 // start of 1 byte holding precision in m*100 in y axis |
|
#define NOOPLOOP_NODE_FRAME2_POSX 13 // start of 3 bytes holding x position in m*1000 |
|
#define NOOPLOOP_NODE_FRAME2_POSY 16 // start of 3 bytes holding y position in m*1000 |
|
#define NOOPLOOP_NODE_FRAME2_POSZ 19 // start of 3 bytes holding z position in m*1000 |
|
#define NOOPLOOP_NODE_FRAME2_VALID_NODES 118 |
|
#define NOOPLOOP_NODE_FRAME2_NODE_BLOCK 119 |
|
|
|
#define NOOPLOOP_HEADER2 0x54 // message header |
|
#define NOOPLOOP_FUNCTION_MARK_SETTING_FRAME0 0 |
|
#define NOOPLOOP_SETTING_FRAME0_A0 37 |
|
|
|
|
|
extern const AP_HAL::HAL& hal; |
|
|
|
// return true if sensor is basically healthy (we are receiving data) |
|
bool AP_Beacon_Nooploop::healthy() |
|
{ |
|
// healthy if we have parsed a message within the past 300ms |
|
return ((AP_HAL::millis() - _last_update_ms) < AP_BEACON_TIMEOUT_MS); |
|
} |
|
|
|
// update the state of the sensor |
|
void AP_Beacon_Nooploop::update(void) |
|
{ |
|
// return immediately if not serial port |
|
if (uart == nullptr) { |
|
return; |
|
} |
|
|
|
// check uart for any incoming messages |
|
uint32_t nbytes = MIN(uart->available(), 1024U); |
|
while (nbytes-- > 0) { |
|
int16_t b = uart->read(); |
|
if (b >= 0 ) { |
|
MsgType type = parse_byte((uint8_t)b); |
|
if (type == MsgType::NODE_FRAME2) { |
|
if (_anchor_pos_avail) { |
|
parse_node_frame2(); |
|
} else if (AP_HAL::millis() - _last_request_setting_ms > 2000) { |
|
_last_request_setting_ms = AP_HAL::millis(); |
|
request_setting(); |
|
} |
|
} else if (type == MsgType::SETTING_FRAME0) { |
|
parse_setting_frame0(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void AP_Beacon_Nooploop::request_setting() |
|
{ |
|
//send setting_frame0 to tag, tag will fill anchor position and ack |
|
uart->write((uint8_t)0x54); |
|
uart->write((uint8_t)0); |
|
uart->write((uint8_t)1); |
|
for (uint8_t i = 0; i < 124; i++) { |
|
uart->write((uint8_t)0); //manual states filled with any char, but in fact only 0 works |
|
} |
|
uart->write((uint8_t)0x55); |
|
} |
|
|
|
// process one byte received on serial port |
|
// message is stored in _msgbuf |
|
AP_Beacon_Nooploop::MsgType AP_Beacon_Nooploop::parse_byte(uint8_t b) |
|
{ |
|
// process byte depending upon current state |
|
switch (_state) { |
|
|
|
case ParseState::HEADER: |
|
if (b == NOOPLOOP_HEADER) { |
|
_msgbuf[0] = b; |
|
_msg_len = 1; |
|
_crc_expected = b; |
|
_state = ParseState::H55_FUNCTION_MARK; |
|
} else if (b == NOOPLOOP_HEADER2) { |
|
_msgbuf[0] = b; |
|
_msg_len = 1; |
|
_crc_expected = b; |
|
_state = ParseState::H54_FUNCTION_MARK; |
|
} |
|
break; |
|
|
|
case ParseState::H55_FUNCTION_MARK: |
|
if (b == NOOPLOOP_FUNCTION_MARK_NODE_FRAME2) { |
|
_msgbuf[1] = b; |
|
_msg_len++; |
|
_crc_expected += b; |
|
_state = ParseState::LEN_L; |
|
} else { |
|
_state = ParseState::HEADER; |
|
} |
|
break; |
|
|
|
case ParseState::H54_FUNCTION_MARK: |
|
if (b == NOOPLOOP_FUNCTION_MARK_SETTING_FRAME0) { |
|
_msgbuf[1] = b; |
|
_msg_len++; |
|
_crc_expected += b; |
|
_state = ParseState::SF0_PAYLOAD; |
|
} else { |
|
_state = ParseState::HEADER; |
|
} |
|
break; |
|
|
|
case ParseState::LEN_L: |
|
_msgbuf[2] = b; |
|
_msg_len++; |
|
_crc_expected += b; |
|
_state = ParseState::LEN_H; |
|
break; |
|
|
|
case ParseState::LEN_H: |
|
// extract and sanity check frame length |
|
_frame_len = UINT16_VALUE(b, _msgbuf[2]); |
|
if (_frame_len > NOOPLOOP_NODE_FRAME2_FRAMELEN_MAX) { |
|
_state = ParseState::HEADER; |
|
} else { |
|
_msgbuf[3] = b; |
|
_msg_len++; |
|
_crc_expected += b; |
|
_state = ParseState::NF2_PAYLOAD; |
|
} |
|
break; |
|
|
|
case ParseState::NF2_PAYLOAD: |
|
// add byte to buffer if there is room |
|
if (_msg_len < NOOPLOOP_MSG_BUF_MAX) { |
|
_msgbuf[_msg_len] = b; |
|
} |
|
_msg_len++; |
|
if (_msg_len >= _frame_len) { |
|
_state = ParseState::HEADER; |
|
// check crc |
|
if (b == _crc_expected) { |
|
return MsgType::NODE_FRAME2; |
|
} |
|
} else { |
|
_crc_expected += b; |
|
} |
|
break; |
|
|
|
case ParseState::SF0_PAYLOAD: |
|
// add byte to buffer if there is room |
|
if (_msg_len < NOOPLOOP_MSG_BUF_MAX) { |
|
_msgbuf[_msg_len] = b; |
|
} |
|
_msg_len++; |
|
if (_msg_len >= NOOPLOOP_SF0_SZ) { |
|
_state = ParseState::HEADER; |
|
// check crc |
|
if (b == _crc_expected) { |
|
return MsgType::SETTING_FRAME0; |
|
} |
|
} else { |
|
_crc_expected += b; |
|
} |
|
break; |
|
} |
|
|
|
return MsgType::INVALID; |
|
} |
|
|
|
void AP_Beacon_Nooploop::parse_node_frame2() |
|
{ |
|
// a message has been received |
|
_last_update_ms = AP_HAL::millis(); |
|
|
|
// estimated precision for x,y position in meters |
|
const float precision_x = _msgbuf[NOOPLOOP_NODE_FRAME2_PRECISION_X] * 0.01; |
|
const float precision_y = _msgbuf[NOOPLOOP_NODE_FRAME2_PRECISION_Y] * 0.01; |
|
//EKF's estimate goes very bad if the error value sent into the EKF is unrealistically low. ensure it's never less than a reasonable value |
|
const float pos_err = MAX(0.1f, sqrtf(sq(precision_x)+sq(precision_y))); |
|
|
|
// x,y,z position in m*1000 in ENU frame |
|
const int32_t pos_x = ((int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSX+2] << 24 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSX+1] << 16 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSX] << 8) >> 8; |
|
const int32_t pos_y = ((int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSY+2] << 24 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSY+1] << 16 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSY] << 8) >> 8; |
|
const int32_t pos_z = ((int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSZ+2] << 24 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSZ+1] << 16 | (int32_t)_msgbuf[NOOPLOOP_NODE_FRAME2_POSZ] << 8) >> 8; |
|
|
|
// position scaled to meters and changed to NED |
|
const Vector3f pos_m {pos_y * 0.001f, pos_x * 0.001f, -pos_z * 0.001f}; |
|
|
|
set_vehicle_position(pos_m, pos_err); |
|
|
|
const uint8_t valid_nodes = _msgbuf[NOOPLOOP_NODE_FRAME2_VALID_NODES]; |
|
for (uint8_t i = 0; i < valid_nodes; i++) { |
|
uint16_t offset = NOOPLOOP_NODE_FRAME2_NODE_BLOCK + i * 13; |
|
uint8_t id = _msgbuf[offset+1]; //nooploop id starts from 0, increments clockwise, 0 -> 1 define Y axis. |
|
const int32_t dist = ((int32_t)_msgbuf[offset+2+2] << 24 | (int32_t)_msgbuf[offset+2+1] << 16 | (int32_t)_msgbuf[offset+2] << 8) >> 8; |
|
set_beacon_distance(id, dist * 0.001f); |
|
} |
|
} |
|
|
|
void AP_Beacon_Nooploop::parse_setting_frame0() |
|
{ |
|
for (uint8_t i = 0; i < 4; i++) { |
|
uint16_t offset = NOOPLOOP_SETTING_FRAME0_A0 + i * 9; |
|
|
|
// x,y,z position in m*1000 in ENU frame |
|
const int32_t pos_x = ((int32_t)_msgbuf[offset+2] << 24 | (int32_t)_msgbuf[offset+1] << 16 | (int32_t)_msgbuf[offset] << 8) >> 8; |
|
if (pos_x == NOOPLOOP_INVALID_VAL) { //anchor position not available |
|
return; |
|
} |
|
offset+=3; |
|
const int32_t pos_y = ((int32_t)_msgbuf[offset+2] << 24 | (int32_t)_msgbuf[offset+1] << 16 | (int32_t)_msgbuf[offset] << 8) >> 8; |
|
if (pos_y == NOOPLOOP_INVALID_VAL) { |
|
return; |
|
} |
|
offset+=3; |
|
const int32_t pos_z = ((int32_t)_msgbuf[offset+2] << 24 | (int32_t)_msgbuf[offset+1] << 16 | (int32_t)_msgbuf[offset] << 8) >> 8; |
|
if (pos_z == NOOPLOOP_INVALID_VAL) { |
|
return; |
|
} |
|
|
|
// position scaled to meters and changed to NED |
|
const Vector3f pos_m {pos_y * 0.001f, pos_x * 0.001f, -pos_z * 0.001f}; |
|
|
|
set_beacon_position(i, pos_m); |
|
} |
|
_anchor_pos_avail = true; |
|
}
|
|
|