diff --git a/libraries/AP_Proximity/AP_Proximity.cpp b/libraries/AP_Proximity/AP_Proximity.cpp index b91403d088..dd2cac8837 100644 --- a/libraries/AP_Proximity/AP_Proximity.cpp +++ b/libraries/AP_Proximity/AP_Proximity.cpp @@ -17,6 +17,7 @@ #include "AP_Proximity_LightWareSF40C.h" #include "AP_Proximity_RPLidarA2.h" #include "AP_Proximity_TeraRangerTower.h" +#include "AP_Proximity_TeraRangerTowerEvo.h" #include "AP_Proximity_RangeFinder.h" #include "AP_Proximity_MAV.h" #include "AP_Proximity_SITL.h" @@ -30,7 +31,7 @@ const AP_Param::GroupInfo AP_Proximity::var_info[] = { // @Param: _TYPE // @DisplayName: Proximity type // @Description: What type of proximity sensor is connected - // @Values: 0:None,1:LightWareSF40C,2:MAVLink,3:TeraRangerTower,4:RangeFinder,5:RPLidarA2 + // @Values: 0:None,1:LightWareSF40C,2:MAVLink,3:TeraRangerTower,4:RangeFinder,5:RPLidarA2,6:TeraRangerTowerEvo // @RebootRequired: True // @User: Standard AP_GROUPINFO("_TYPE", 1, AP_Proximity, _type[0], 0), @@ -150,7 +151,7 @@ const AP_Param::GroupInfo AP_Proximity::var_info[] = { // @Param: 2_TYPE // @DisplayName: Second Proximity type // @Description: What type of proximity sensor is connected - // @Values: 0:None,1:LightWareSF40C,2:MAVLink,3:TeraRangerTower,4:RangeFinder,5:RPLidarA2 + // @Values: 0:None,1:LightWareSF40C,2:MAVLink,3:TeraRangerTower,4:RangeFinder,5:RPLidarA2,6:TeraRangerTowerEvo // @User: Advanced // @RebootRequired: True AP_GROUPINFO("2_TYPE", 16, AP_Proximity, _type[1], 0), @@ -305,6 +306,13 @@ void AP_Proximity::detect_instance(uint8_t instance) return; } } + if (type == Proximity_Type_TRTOWEREVO) { + if (AP_Proximity_TeraRangerTowerEvo::detect(serial_manager)) { + state[instance].instance = instance; + drivers[instance] = new AP_Proximity_TeraRangerTowerEvo(*this, state[instance], serial_manager); + return; + } + } if (type == Proximity_Type_RangeFinder) { state[instance].instance = instance; drivers[instance] = new AP_Proximity_RangeFinder(*this, state[instance]); diff --git a/libraries/AP_Proximity/AP_Proximity.h b/libraries/AP_Proximity/AP_Proximity.h index 5be475b6f6..bfa330b5ef 100644 --- a/libraries/AP_Proximity/AP_Proximity.h +++ b/libraries/AP_Proximity/AP_Proximity.h @@ -47,6 +47,7 @@ public: Proximity_Type_TRTOWER = 3, Proximity_Type_RangeFinder = 4, Proximity_Type_RPLidarA2 = 5, + Proximity_Type_TRTOWEREVO = 6, Proximity_Type_SITL = 10, }; diff --git a/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.cpp b/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.cpp new file mode 100644 index 0000000000..f5c83aa093 --- /dev/null +++ b/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.cpp @@ -0,0 +1,182 @@ +/* + 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 . + */ + +#include +#include "AP_Proximity_TeraRangerTowerEvo.h" +#include +#include +#include +#include + +extern const AP_HAL::HAL& hal; + +/* + The constructor also initialises the proximity sensor. Note that this + constructor is not called until detect() returns true, so we + already know that we should setup the proximity sensor +*/ +AP_Proximity_TeraRangerTowerEvo::AP_Proximity_TeraRangerTowerEvo(AP_Proximity &_frontend, + AP_Proximity::Proximity_State &_state, + AP_SerialManager &serial_manager) : + AP_Proximity_Backend(_frontend, _state) +{ + uart = serial_manager.find_serial(AP_SerialManager::SerialProtocol_Lidar360, 0); + if (uart != nullptr) { + uart->begin(serial_manager.find_baudrate(AP_SerialManager::SerialProtocol_Lidar360, 0)); + } + // _last_request_sent_ms = AP_HAL::millis(); +} + +// detect if a TeraRanger Tower proximity sensor is connected by looking for a configured serial port +bool AP_Proximity_TeraRangerTowerEvo::detect(AP_SerialManager &serial_manager) +{ + AP_HAL::UARTDriver *uart = nullptr; + uart = serial_manager.find_serial(AP_SerialManager::SerialProtocol_Lidar360, 0); + return uart != nullptr; +} + +// update the state of the sensor +void AP_Proximity_TeraRangerTowerEvo::update(void) +{ + if (uart == nullptr) { + return; + } + + //initialize the sensor + if(_current_init_state != InitState::InitState_Finished) + { + initialise_modes(); + } + + // process incoming messages + read_sensor_data(); + + // check for timeout and set health status + if ((_last_distance_received_ms == 0) || (AP_HAL::millis() - _last_distance_received_ms > PROXIMITY_TRTOWER_TIMEOUT_MS)) { + set_status(AP_Proximity::Proximity_NoData); + } else { + set_status(AP_Proximity::Proximity_Good); + } +} + +// get maximum and minimum distances (in meters) of primary sensor +float AP_Proximity_TeraRangerTowerEvo::distance_max() const +{ + return 8.0f; +} +float AP_Proximity_TeraRangerTowerEvo::distance_min() const +{ + return 0.75f; +} + +void AP_Proximity_TeraRangerTowerEvo::initialise_modes() +{ + if((AP_HAL::millis() - _last_request_sent_ms) < _mode_request_delay) { + return; + } + + if (_current_init_state == InitState_Printout) { + set_mode(BINARY_MODE, 4); + _current_init_state = InitState_Sequence; + } + if (_current_init_state == InitState_Sequence) { + //set tower mode - 4 sensors are triggered at once with 90 deg angle between each sensor + //if this mode is not set the tower will default to sequential mode + set_mode(SEQUENCE_MODE, 4); + _current_init_state = InitState_Rate; + } else if (_current_init_state == InitState_Rate) { + //set update rate of the sensor. If it's not set it will default to ASAP mode + set_mode(REFRESH_50_HZ, 5); + _current_init_state = InitState_StreamStart; + } else if (_current_init_state == InitState_StreamStart) { + set_mode(ACTIVATE_STREAM, 5); + _current_init_state = InitState_Finished; + } +} + +void AP_Proximity_TeraRangerTowerEvo::set_mode(const uint8_t *c, int length) +{ + uart->write(c, length); + _last_request_sent_ms = AP_HAL::millis(); +} + +// check for replies from sensor, returns true if at least one message was processed +bool AP_Proximity_TeraRangerTowerEvo::read_sensor_data() +{ + if (uart == nullptr) { + return false; + } + + uint16_t message_count = 0; + int16_t nbytes = uart->available(); + + while (nbytes-- > 0) { + char c = uart->read(); + if (c == 'T' ) { + buffer_count = 0; + } + buffer[buffer_count++] = c; + + // we should always read 19 bytes THxxxxxxxxxxxxxxxxMC + if (buffer_count >= 20){ + buffer_count = 0; + + //check if message has right CRC + if (crc_crc8(buffer, 19) == buffer[19]){ + uint16_t d1 = process_distance(buffer[2], buffer[3]); + uint16_t d2 = process_distance(buffer[4], buffer[5]); + uint16_t d3 = process_distance(buffer[6], buffer[7]); + uint16_t d4 = process_distance(buffer[8], buffer[9]); + uint16_t d5 = process_distance(buffer[10], buffer[11]); + uint16_t d6 = process_distance(buffer[12], buffer[13]); + uint16_t d7 = process_distance(buffer[14], buffer[15]); + uint16_t d8 = process_distance(buffer[16], buffer[17]); + + update_sector_data(0, d1); + update_sector_data(45, d8); + update_sector_data(90, d7); + update_sector_data(135, d6); + update_sector_data(180, d5); + update_sector_data(225, d4); + update_sector_data(270, d3); + update_sector_data(315, d2); + + message_count++; + } + } + } + return (message_count > 0); +} + +uint16_t AP_Proximity_TeraRangerTowerEvo::process_distance(uint8_t buf1, uint8_t buf2) +{ + return (buf1 << 8) + buf2; +} + +// process reply +void AP_Proximity_TeraRangerTowerEvo::update_sector_data(int16_t angle_deg, uint16_t distance_cm) +{ + uint8_t sector; + if (convert_angle_to_sector(angle_deg, sector)) { + _angle[sector] = angle_deg; + _distance[sector] = ((float) distance_cm) / 1000; + + //check for target too far, target too close and sensor not connected + _distance_valid[sector] = distance_cm != 0xffff; //&& distance_cm != 0x0000 && distance_cm != 0x0001; + _last_distance_received_ms = AP_HAL::millis(); + // update boundary used for avoidance + update_boundary_for_sector(sector); + } +} diff --git a/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.h b/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.h new file mode 100644 index 0000000000..48525324c5 --- /dev/null +++ b/libraries/AP_Proximity/AP_Proximity_TeraRangerTowerEvo.h @@ -0,0 +1,63 @@ +#pragma once + +#include "AP_Proximity.h" +#include "AP_Proximity_Backend.h" + +#define PROXIMITY_TRTOWER_TIMEOUT_MS 300 // requests timeout after 0.3 seconds + +class AP_Proximity_TeraRangerTowerEvo : public AP_Proximity_Backend { + +public: + // constructor + AP_Proximity_TeraRangerTowerEvo(AP_Proximity &_frontend, AP_Proximity::Proximity_State &_state, AP_SerialManager &serial_manager); + + // static detection function + static bool detect(AP_SerialManager &serial_manager); + + // update state + void update(void); + + // get maximum and minimum distances (in meters) of sensor + float distance_max() const; + float distance_min() const; + +private: + + // check and process replies from sensor + void initialise_modes(); + bool read_sensor_data(); + void update_sector_data(int16_t angle_deg, uint16_t distance_cm); + uint16_t process_distance(uint8_t buf1, uint8_t buf2); + void set_mode(const uint8_t *c, int length); + + enum InitState { + InitState_Printout = 0, + InitState_Sequence, + InitState_Rate, + InitState_StreamStart, + InitState_Finished + }; + + // reply related variables + AP_HAL::UARTDriver *uart = nullptr; + uint8_t buffer[20]; // buffer where to store data from serial + uint8_t buffer_count; + + // request related variables + uint32_t _last_distance_received_ms; // system time of last distance measurement received from sensor + uint32_t _last_request_sent_ms; // system time of last command set + const uint16_t _mode_request_delay = 400; + enum InitState _current_init_state = InitState_Printout; + + // tower evo operating modes + + const uint8_t BINARY_MODE[4] = {(uint8_t)0x00, (uint8_t)0x11, (uint8_t)0x02, (uint8_t)0x4C}; + const uint8_t TOWER_MODE[4] = {(uint8_t)0x00, (uint8_t)0x31, (uint8_t)0x03, (uint8_t)0xE5}; + const uint8_t SEQUENCE_MODE[4] = {(uint8_t)0x00, (uint8_t)0x31, (uint8_t)0x02, (uint8_t)0xE2}; + const uint8_t ACTIVATE_STREAM[5] = {(uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x02, (uint8_t)0x01, (uint8_t)0xDF}; + const uint8_t REFRESH_50_HZ[5] = { (uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x03, (uint8_t)0x02, (uint8_t)0xC3}; + const uint8_t REFRESH_100_HZ[5] = { (uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x03, (uint8_t)0x03, (uint8_t)0xC4}; + const uint8_t REFRESH_250_HZ[5] = { (uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x03, (uint8_t)0x04, (uint8_t)0xD1}; + const uint8_t REFRESH_500_HZ[5] = { (uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x03, (uint8_t)0x05, (uint8_t)0xD6}; + const uint8_t REFRESH_600_HZ[5] = { (uint8_t)0x00, (uint8_t)0x52, (uint8_t)0x03, (uint8_t)0x06, (uint8_t)0xDF}; +};