/*
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 .
*/
/*
driver for UPixel UPFLOW optical flow sensor
UPFLOW serial packet description
byte0: header0 (0xFE)
byte1: header1 (0x0A)
byte2: x-motion low byte;
byte3: x-motion high byte;
byte4: y-motion low byte;
byte5: y-motion high byte;
byte6: dt low byte;
byte7: dt high byte;
byte8: reserved;
byte9: reserved;
byte10: surface quality
byte11: hardware version
byte12:checksum
byte13:footer (0x55)
*/
#include "AP_OpticalFlow_UPFLOW.h"
#if AP_OPTICALFLOW_UPFLOW_ENABLED
#include
#include
#include
#include
#include "AP_OpticalFlow.h"
#include
#define UPFLOW_HEADER0 (uint8_t)0xFE
#define UPFLOW_HEADER1 (uint8_t)0x0A
#define UPFLOW_FOOTER (uint8_t)0x55
#define UPFLOW_PIXEL_SCALING (1e-4)
#define UPFLOW_TIMEOUT_SEC 0.3f
extern const AP_HAL::HAL& hal;
// constructor
AP_OpticalFlow_UPFLOW::AP_OpticalFlow_UPFLOW(AP_OpticalFlow &_frontend, AP_HAL::UARTDriver *_uart) :
OpticalFlow_backend(_frontend),
uart(_uart)
{
}
// detect the device
AP_OpticalFlow_UPFLOW *AP_OpticalFlow_UPFLOW::detect(AP_OpticalFlow &_frontend)
{
AP_SerialManager *serial_manager = AP::serialmanager().get_singleton();
if (serial_manager == nullptr) {
return nullptr;
}
// look for first serial driver with protocol defined as OpticalFlow
AP_HAL::UARTDriver *uart = serial_manager->find_serial(AP_SerialManager::SerialProtocol_OpticalFlow, 0);
if (uart == nullptr) {
return nullptr;
}
// we have found a serial port so use it
AP_OpticalFlow_UPFLOW *sensor = new AP_OpticalFlow_UPFLOW(_frontend, uart);
return sensor;
}
// initialise the sensor
void AP_OpticalFlow_UPFLOW::init()
{
// sanity check uart
if (uart == nullptr) {
return;
}
// open serial port with baud rate of 19200
uart->begin(19200);
}
// read latest values from sensor and fill in x,y and totals.
void AP_OpticalFlow_UPFLOW::update(void)
{
// sanity check uart
if (uart == nullptr) {
return;
}
// record gyro values as long as they are being used
// the sanity check of dt below ensures old gyro values are not used
if (gyro_sum_count >= 1000) {
gyro_sum.zero();
gyro_sum_count = 0;
}
const Vector3f& gyro = AP::ahrs().get_gyro();
gyro_sum.x += gyro.x;
gyro_sum.y += gyro.y;
gyro_sum_count++;
bool phrased = false;
// read any available characters in the serial buffer
uint32_t nbytes = MIN(uart->available(), 1024u);
while (nbytes-- > 0) {
int16_t r = uart->read();
if (r < 0) {
break;
}
uint8_t c = (uint8_t)r;
if (recv_count == 0) {
//Header0
if (c == UPFLOW_HEADER0) {
recv_count++;
sum = 0;
}
} else if (recv_count == 1) {
//Header1
if (c != UPFLOW_HEADER1) {
recv_count = 0;
} else {
recv_count++;
}
} else if (recv_count < 12) {
//actual data
((uint8_t*)&updata)[recv_count - 2] = c;
sum ^= c;
recv_count++;
} else if (recv_count == 12) {
//checksum
if (sum != c) {
recv_count = 0;
} else {
recv_count++;
}
} else { //footer
if (c == UPFLOW_FOOTER) {
phrased=true;
}
recv_count = 0;
}
}
// return without updating state if no readings
if (phrased == false) {
return;
}
struct AP_OpticalFlow::OpticalFlow_state state {};
state.surface_quality = updata.quality;
float dt = updata.integration_timespan * 1.0e-6;
// sanity check dt
if (is_positive(dt) && (dt < UPFLOW_TIMEOUT_SEC)) {
// calculate flow values
const Vector2f flowScaler = _flowScaler();
Vector2f flowScaleFactor = Vector2f(1.0f, 1.0f) + flowScaler * 0.001f;
// copy flow rates to state structure
state.flowRate = Vector2f((float)(-updata.flow_x_integral) * flowScaleFactor.x,
(float)(-updata.flow_y_integral) * flowScaleFactor.y);
state.flowRate *= UPFLOW_PIXEL_SCALING / dt;
// copy average body rate to state structure
state.bodyRate = Vector2f{gyro_sum.x / gyro_sum_count, gyro_sum.y / gyro_sum_count};
// we only apply yaw to flowRate as body rate comes from AHRS
_applyYaw(state.flowRate);
} else {
// first frame received in some time so cannot calculate flow values
state.flowRate.zero();
state.bodyRate.zero();
}
_update_frontend(state);
// reset gyro sum
gyro_sum.zero();
gyro_sum_count = 0;
}
#endif // AP_OPTICALFLOW_UPFLOW_ENABLED