mirror of
https://github.com/azaion/autopilot.git
synced 2026-04-22 11:16:34 +00:00
Refactored a8 codes and added remote testing app a8_remote.
This commit is contained in:
+21
-2
@@ -18,8 +18,27 @@ linux-g++ {
|
|||||||
QMAKE_CC = clang
|
QMAKE_CC = clang
|
||||||
}
|
}
|
||||||
|
|
||||||
SOURCES += *.cpp
|
SOURCES += \
|
||||||
HEADERS += *.h
|
config.cpp \
|
||||||
|
localControl.cpp \
|
||||||
|
main.cpp \
|
||||||
|
remoteControl.cpp \
|
||||||
|
serialCommand.cpp \
|
||||||
|
serialPort.cpp \
|
||||||
|
serialResponse.cpp \
|
||||||
|
utilsCRC16.cpp \
|
||||||
|
utilsTargetLocation.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
config.hpp \
|
||||||
|
defines.hpp \
|
||||||
|
localControl.h \
|
||||||
|
remoteControl.h \
|
||||||
|
serialCommand.hpp \
|
||||||
|
serialPort.hpp \
|
||||||
|
serialResponse.hpp \
|
||||||
|
utilsCRC16.hpp \
|
||||||
|
utilsTargetLocation.hpp
|
||||||
|
|
||||||
# When using FFmpeg
|
# When using FFmpeg
|
||||||
#INCLUDEPATH += /usr/include/x86_64-linux-gnu
|
#INCLUDEPATH += /usr/include/x86_64-linux-gnu
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#include "config.hpp"
|
||||||
|
#include "defines.hpp"
|
||||||
|
#include "serialResponse.hpp"
|
||||||
|
|
||||||
|
SerialPort *Config::mSerial = nullptr;
|
||||||
|
SerialCommand *Config::mCommand = nullptr;
|
||||||
|
uint16_t Config::mResolutionX = 0;
|
||||||
|
uint16_t Config::mResolutionY = 0;
|
||||||
|
float Config::mMaxZoom = 0.0f;
|
||||||
|
float Config::mCurrentYaw = 0.0f;
|
||||||
|
float Config::mCurrentPitch = 0.0f;
|
||||||
|
float Config::mCurrentRoll = 0.0f;
|
||||||
|
float Config::mCurrentZoom = 0.0f;
|
||||||
|
|
||||||
|
void Config::sanityCheck()
|
||||||
|
{
|
||||||
|
if (mSerial == nullptr) {
|
||||||
|
qCritical().noquote().nospace() << "Serial connection not available.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCommand == nullptr) {
|
||||||
|
qCritical().noquote().nospace() << "Commands not available.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::setInitalValues(SerialPort *serial, SerialCommand *command)
|
||||||
|
{
|
||||||
|
mSerial = serial;
|
||||||
|
mCommand = command;
|
||||||
|
|
||||||
|
sanityCheck();
|
||||||
|
|
||||||
|
// Setup some values that might be needed from camera
|
||||||
|
QByteArray tempCommand;
|
||||||
|
QByteArray tempResponse;
|
||||||
|
QHash<QString, QVariant> responseValues;
|
||||||
|
|
||||||
|
// Get resolution to reduce calls to camera
|
||||||
|
tempCommand = mCommand->getCommandForInternal(COMMAND_ID::ACQUIRE_CAMERA_CODEC_SPECS);
|
||||||
|
mSerial->sendCommand(tempCommand);
|
||||||
|
tempResponse = mSerial->readResponse();
|
||||||
|
responseValues = SerialResponse::getResponceValues(tempResponse);
|
||||||
|
mResolutionX = responseValues["width"].toInt();
|
||||||
|
mResolutionY = responseValues["height"].toInt();
|
||||||
|
|
||||||
|
// Get max zoom value to reduce calls to camera
|
||||||
|
tempCommand = mCommand->getCommandForInternal(COMMAND_ID::ACQUIRE_MAX_ZOOM_VALUE);
|
||||||
|
mSerial->sendCommand(tempCommand);
|
||||||
|
tempResponse = mSerial->readResponse();
|
||||||
|
responseValues = SerialResponse::getResponceValues(tempResponse);
|
||||||
|
mMaxZoom = responseValues["zoom"].toInt();
|
||||||
|
|
||||||
|
// Debug printing
|
||||||
|
qDebug().noquote().nospace() << "Camera resolution: " << mResolutionX << "*" << mResolutionY;
|
||||||
|
qDebug().noquote().nospace() << "Camera max zoom: " << mMaxZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::updateState()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
QHash<QString, QVariant> responseValues;
|
||||||
|
QByteArray tempCommand;
|
||||||
|
QByteArray tempResponse;
|
||||||
|
|
||||||
|
// Get current angles
|
||||||
|
tempCommand = mCommand->getCommandForInternal(COMMAND_ID::ACQUIRE_ATTITUDE_DATA);
|
||||||
|
Config::getSerial()->sendCommand(tempCommand);
|
||||||
|
tempResponse = Config::getSerial()->readResponse();
|
||||||
|
responseValues = SerialResponse::getResponceValues(tempResponse);
|
||||||
|
mCurrentYaw = responseValues["yaw"].toFloat();
|
||||||
|
mCurrentPitch = responseValues["pitch"].toFloat();
|
||||||
|
mCurrentRoll = responseValues["roll"].toFloat();
|
||||||
|
|
||||||
|
// Get current zoom
|
||||||
|
tempCommand = mCommand->getCommandForInternal(COMMAND_ID::ACQUIRE_CURRENT_ZOOM);
|
||||||
|
Config::getSerial()->sendCommand(tempCommand);
|
||||||
|
tempResponse = Config::getSerial()->readResponse();
|
||||||
|
responseValues = SerialResponse::getResponceValues(tempResponse);
|
||||||
|
mCurrentZoom = responseValues["zoom"].toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
SerialPort *Config::getSerial()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerialCommand *Config::getCommand()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Config::getResolutionWidth()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mResolutionX;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Config::getResolutionHeight()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mResolutionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Config::getMaxZoom()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mMaxZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Config::getCurrentYaw()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mCurrentYaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Config::getCurrentPitch()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mCurrentPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Config::getCurrentRoll()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return mCurrentRoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Config::getCurrentZoom()
|
||||||
|
{
|
||||||
|
sanityCheck();
|
||||||
|
return (mCurrentZoom == 0.0f ? 1.0f : mCurrentZoom);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "serialCommand.hpp"
|
||||||
|
#include "serialPort.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void setInitalValues(SerialPort *serial, SerialCommand *command);
|
||||||
|
static void updateState(void);
|
||||||
|
|
||||||
|
static SerialPort *getSerial();
|
||||||
|
static SerialCommand *getCommand(void);
|
||||||
|
static uint16_t getResolutionWidth(void);
|
||||||
|
static uint16_t getResolutionHeight(void);
|
||||||
|
static float getMaxZoom(void);
|
||||||
|
|
||||||
|
static float getCurrentYaw(void);
|
||||||
|
static float getCurrentPitch(void);
|
||||||
|
static float getCurrentRoll(void);
|
||||||
|
static float getCurrentZoom(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void sanityCheck(void);
|
||||||
|
|
||||||
|
static SerialPort *mSerial;
|
||||||
|
static SerialCommand *mCommand;
|
||||||
|
|
||||||
|
// Get these only once
|
||||||
|
static uint16_t mResolutionX;
|
||||||
|
static uint16_t mResolutionY;
|
||||||
|
static float mMaxZoom;
|
||||||
|
|
||||||
|
// Update these before every command
|
||||||
|
static float mCurrentYaw;
|
||||||
|
static float mCurrentPitch;
|
||||||
|
static float mCurrentRoll;
|
||||||
|
static float mCurrentZoom;
|
||||||
|
};
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
enum MESSAGE_IDX { STX = 0, CTRL = 2, Data_len = 3, SEQ = 5, CMD_ID = 7, DATA = 8 };
|
enum MESSAGE_IDX { STX = 0, CTRL = 2, Data_len = 3, SEQ = 5, CMD_ID = 7, DATA = 8 };
|
||||||
|
|
||||||
enum COMMAND_ID {
|
enum COMMAND_ID {
|
||||||
TURN_TO_X = 2, // Set first to 2, because 0 is reserved for EXIT_PROGRAM and 1 for running target location test
|
TURN_TO_DEGREES = 1,
|
||||||
|
TURN_TO_PIXEL,
|
||||||
ZOOM_TO_X,
|
ZOOM_TO_X,
|
||||||
ACQUIRE_CAMERA_CODEC_SPECS,
|
ACQUIRE_CAMERA_CODEC_SPECS,
|
||||||
ACQUIRE_CURRENT_ZOOM,
|
ACQUIRE_CURRENT_ZOOM,
|
||||||
@@ -33,11 +37,22 @@ enum COMMAND_ID {
|
|||||||
ENABLE_CVBS,
|
ENABLE_CVBS,
|
||||||
DISABLE_HDMI_CVBS,
|
DISABLE_HDMI_CVBS,
|
||||||
ACQUIRE_RANGE_DATA,
|
ACQUIRE_RANGE_DATA,
|
||||||
|
RUN_TARGET_LOCATION_TEST
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CAMERA_FIELD_OF_VIEW_DIAGONAL 93.0
|
#define CAMERA_ASPECT_RATIO 1.777777778f
|
||||||
#define CAMERA_FIELD_OF_VIEW_HORIZONTAL 81.0
|
#define CAMERA_FIELD_OF_VIEW_DIAGONAL 93.0f
|
||||||
#define CAMERA_RESOLUTION_HEIGHT 1080
|
#define CAMERA_FIELD_OF_VIEW_HORIZONTAL 81.0f
|
||||||
#define CAMERA_RESOLUTION_WIDTH 1920
|
#define CAMERA_FIELD_OF_VIEW_VERTICAL 62.0f
|
||||||
|
#define CAMERA_FOCAL_LENGTH 21
|
||||||
|
#define CAMERA_RESOLUTION_WIDTH 1280
|
||||||
|
#define CAMERA_RESOLUTION_HEIGHT 720
|
||||||
|
#define GIMBAL_YAW_MIN -135.0f
|
||||||
|
#define GIMBAL_YAW_MAX 135.0f
|
||||||
|
#define GIMBAL_PITCH_MIN -90.0f
|
||||||
|
#define GIMBAL_PITCH_MAX 25.0f
|
||||||
#define SERIAL_RESPONSE_WAIT_TIME 500
|
#define SERIAL_RESPONSE_WAIT_TIME 500
|
||||||
#define RTSP_ADDRESS "rtsp://192.168.144.25:8554/main.264"
|
#define SERIAL_PORT "/dev/ttyUSB0"
|
||||||
|
#define FIFO_WHO_AM_I "CAM"
|
||||||
|
#define FIFO_TO_GIMBAL "/tmp/fifo_to_a8_gimbal"
|
||||||
|
#define FIFO_FROM_GIMBAL "/tmp/fifo_from_a8_gimbal"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include "localControl.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "serialResponse.hpp"
|
||||||
|
#include "utilsTargetLocation.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
LocalControl::LocalControl()
|
||||||
|
: QObject(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void LocalControl::run()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
Config::getCommand()->printCommands();
|
||||||
|
|
||||||
|
// Get user input
|
||||||
|
qInfo().noquote().nospace() << "Enter a command (0 to exit): ";
|
||||||
|
int16_t number;
|
||||||
|
std::cin >> number;
|
||||||
|
|
||||||
|
// Check if the input is within the valid range for uint8_t
|
||||||
|
if (number < 0 || number > Config::getCommand()->getCommandCount()) {
|
||||||
|
qWarning().noquote().nospace() << "Number (" << qPrintable(QString::number(number)) << ") out of range 0 -" << qPrintable(QString::number(Config::getCommand()->getCommandCount() - 1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit loop if user enters 0
|
||||||
|
if (number == 0) {
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
} else {
|
||||||
|
Config::updateState();
|
||||||
|
COMMAND_ID commandId = (COMMAND_ID) number;
|
||||||
|
|
||||||
|
if (commandId == COMMAND_ID::RUN_TARGET_LOCATION_TEST) {
|
||||||
|
qInfo().noquote().nospace() << "Running target location test";
|
||||||
|
GPSData gpsData = UtilsTargetLocation::getLocation(200.0f, 63.16122286887124f, 23.822053704379698f, 180.0f, 0.0f, 0.0f, 5.0f, 20);
|
||||||
|
qInfo().noquote().nospace() << "Altitude: " << gpsData.altitude;
|
||||||
|
qInfo().noquote().nospace() << "Latitude: " << gpsData.latitude;
|
||||||
|
qInfo().noquote().nospace() << "Longitude: " << gpsData.longitude;
|
||||||
|
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example command to send (replace with actual Siyi A8 mini camera
|
||||||
|
// commands)
|
||||||
|
QByteArray command = Config::getCommand()->getCommandForUI(commandId);
|
||||||
|
Config::getSerial()->sendCommand(command);
|
||||||
|
|
||||||
|
// Read response from the camera
|
||||||
|
QByteArray response = Config::getSerial()->readResponse();
|
||||||
|
SerialResponse::printResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class LocalControl : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LocalControl();
|
||||||
|
void run();
|
||||||
|
};
|
||||||
+21
-56
@@ -1,69 +1,34 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include "serialCommand.h"
|
#include "config.hpp"
|
||||||
#include "serialPort.h"
|
#include "localControl.h"
|
||||||
#include "serialResponse.h"
|
#include "remoteControl.h"
|
||||||
#include "utilsTargetLocation.h"
|
#include "serialCommand.hpp"
|
||||||
#include <iostream>
|
#include "serialPort.hpp"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
SerialCommand serialCommand;
|
||||||
|
SerialPort serialPort;
|
||||||
|
Config::setInitalValues(&serialPort, &serialCommand);
|
||||||
|
|
||||||
// Replace with your actual port name
|
bool useRemoteMode = true;
|
||||||
const QString portName = "/dev/ttyUSB0";
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
if (QString(argv[i]) == "-l") {
|
||||||
// Create SerialPort object
|
useRemoteMode = false;
|
||||||
SerialPort serial(portName);
|
|
||||||
|
|
||||||
// Open the serial port
|
|
||||||
if (!serial.openPort()) {
|
|
||||||
qDebug() << "Failed to open serial port";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SerialCommand commands;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
commands.printCommands();
|
|
||||||
|
|
||||||
// Get user input
|
|
||||||
qInfo() << "Enter a command (0 to exit, 1 to run test): ";
|
|
||||||
int16_t number;
|
|
||||||
std::cin >> number;
|
|
||||||
|
|
||||||
// Check if the input is within the valid range for uint8_t
|
|
||||||
if (number < 0 || number > commands.getCommandCount() - 1) {
|
|
||||||
qWarning() << "Number (" << qPrintable(QString::number(number)) << ") out of range 0 -" << qPrintable(QString::number(commands.getCommandCount() - 1));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit loop if user enters 0
|
|
||||||
if (number == 0) {
|
|
||||||
break;
|
break;
|
||||||
} else if (number == 1) {
|
|
||||||
qInfo() << "Running target location test";
|
|
||||||
|
|
||||||
UtilsTargetLocation locateTarget(&serial, &commands);
|
|
||||||
GPSData gpsData = locateTarget.getLocation(200.0, 63.16122286887124, 23.822053704379698, 180.0, 0.0, 0.0, 5.0, 20);
|
|
||||||
qInfo() << "Altitude: " << gpsData.altitude;
|
|
||||||
qInfo() << "Latitude: " << gpsData.latitude;
|
|
||||||
qInfo() << "Longitude: " << gpsData.longitude;
|
|
||||||
} else {
|
|
||||||
COMMAND_ID commandId = (COMMAND_ID) number;
|
|
||||||
|
|
||||||
// Example command to send (replace with actual Siyi A8 mini camera commands)
|
|
||||||
QByteArray command = commands.getCommandByID(commandId);
|
|
||||||
serial.sendCommand(command);
|
|
||||||
|
|
||||||
// Read response from the camera
|
|
||||||
QByteArray response = serial.readResponse();
|
|
||||||
SerialResponse::printResponse(response);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the serial port
|
// Remote mode will read commands from pipe
|
||||||
serial.closePort();
|
if (useRemoteMode == true) {
|
||||||
|
RemoteControl remoteControl;
|
||||||
|
remoteControl.run();
|
||||||
|
} else {
|
||||||
|
LocalControl localControl;
|
||||||
|
localControl.run();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
#include "remoteControl.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "defines.hpp"
|
||||||
|
#include "utilsTargetLocation.hpp"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
RemoteControl::RemoteControl()
|
||||||
|
: QObject(nullptr)
|
||||||
|
{
|
||||||
|
openNamedPipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteControl::~RemoteControl()
|
||||||
|
{
|
||||||
|
if (mFifoFdIn != -1) {
|
||||||
|
close(mFifoFdIn);
|
||||||
|
}
|
||||||
|
if (mFifoFdOut != -1) {
|
||||||
|
close(mFifoFdOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::openNamedPipe()
|
||||||
|
{
|
||||||
|
struct stat statBuf;
|
||||||
|
|
||||||
|
// Open incoming pipe (READ ONLY)
|
||||||
|
if (stat(FIFO_TO_GIMBAL, &statBuf) == 0) {
|
||||||
|
if (S_ISFIFO(statBuf.st_mode)) {
|
||||||
|
qInfo().noquote().nospace() << "Named pipe already exists: " << FIFO_TO_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qCritical().noquote().nospace() << FIFO_TO_GIMBAL << " exists but is not a FIFO";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mkfifo(FIFO_TO_GIMBAL, 0666) == -1) {
|
||||||
|
perror("mkfifo");
|
||||||
|
qCritical().noquote().nospace() << "Failed to create named pipe: " << FIFO_TO_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qInfo().noquote().nospace() << "Created named pipe: " << FIFO_TO_GIMBAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFifoFdIn = open(FIFO_TO_GIMBAL, O_RDONLY | O_NONBLOCK);
|
||||||
|
if (mFifoFdIn == -1) {
|
||||||
|
qCritical().noquote().nospace() << "Error opening pipe for reading: " << FIFO_TO_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qInfo().noquote().nospace() << "Opened pipe: " << FIFO_TO_GIMBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open outgoing pipe (WRITE ONLY)
|
||||||
|
if (stat(FIFO_FROM_GIMBAL, &statBuf) == 0) {
|
||||||
|
if (S_ISFIFO(statBuf.st_mode)) {
|
||||||
|
qInfo().noquote().nospace() << "Named pipe already exists: " << FIFO_FROM_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qCritical().noquote().nospace() << FIFO_FROM_GIMBAL << " exists but is not a FIFO";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mkfifo(FIFO_FROM_GIMBAL, 0666) == -1) {
|
||||||
|
perror("mkfifo");
|
||||||
|
qCritical().noquote().nospace() << "Failed to create named pipe: " << FIFO_FROM_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qInfo().noquote().nospace() << "Created named pipe: " << FIFO_FROM_GIMBAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFifoFdOut = open(FIFO_FROM_GIMBAL, O_WRONLY);
|
||||||
|
if (mFifoFdOut == -1) {
|
||||||
|
qCritical().noquote().nospace() << "Error opening pipe for writing: " << FIFO_FROM_GIMBAL;
|
||||||
|
} else {
|
||||||
|
qInfo().noquote().nospace() << "Opened pipe: " << FIFO_FROM_GIMBAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::whenDone(void)
|
||||||
|
{
|
||||||
|
// All is done, no it's time to rest
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::restoreOrientation(void)
|
||||||
|
{
|
||||||
|
QByteArray serialCommandAngle = Config::getCommand()->getCommandForInternal(COMMAND_ID::TURN_TO_DEGREES);
|
||||||
|
|
||||||
|
int16_t degreesVal = Config::getCurrentYaw() * 10;
|
||||||
|
serialCommandAngle[8] = degreesVal & 0xFF;
|
||||||
|
serialCommandAngle[9] = degreesVal >> 8;
|
||||||
|
degreesVal = Config::getCurrentPitch() * 10;
|
||||||
|
serialCommandAngle[10] = degreesVal & 0xFF;
|
||||||
|
serialCommandAngle[11] = degreesVal >> 8;
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << serialCommandAngle;
|
||||||
|
Config::getSerial()->sendCommand(serialCommandAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::restoreZoom(void)
|
||||||
|
{
|
||||||
|
QByteArray serialCommandZoom = Config::getCommand()->getCommandForInternal(COMMAND_ID::ZOOM_TO_X);
|
||||||
|
|
||||||
|
uint8_t integerPart = static_cast<uint8_t>(Config::getCurrentZoom());
|
||||||
|
float fractionalPart = Config::getCurrentZoom() - integerPart;
|
||||||
|
uint8_t scaledFractional = uint8_t(fractionalPart * 10);
|
||||||
|
|
||||||
|
serialCommandZoom[8] = integerPart;
|
||||||
|
serialCommandZoom[9] = scaledFractional;
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << serialCommandZoom.toStdString();
|
||||||
|
Config::getSerial()->sendCommand(serialCommandZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject RemoteControl::calculateTargetPosition(QJsonObject &commandObject, QJsonObject &responseObject)
|
||||||
|
{
|
||||||
|
qDebug().noquote().nospace() << "Calculating?";
|
||||||
|
|
||||||
|
float latitude = commandObject["latitude"].toDouble();
|
||||||
|
float longitude = commandObject["longitude"].toDouble();
|
||||||
|
float altitude = commandObject["altitude"].toDouble();
|
||||||
|
float yaw = commandObject["yaw"].toDouble();
|
||||||
|
float pitch = commandObject["pitch"].toDouble();
|
||||||
|
float targetPixelWidth = commandObject["target_pixel_width"].toInt();
|
||||||
|
//float targetPixelHeight = commandObject["target_pixel_height"].toInt();
|
||||||
|
float targetRealWidth = commandObject["target_real_width"].toDouble();
|
||||||
|
//float targetRealHeight = commandObject["target_real_height"].toDouble();
|
||||||
|
|
||||||
|
GPSData gpsData = UtilsTargetLocation::getLocation(altitude, latitude, longitude, yaw, pitch, 0.0f, targetRealWidth, targetPixelWidth);
|
||||||
|
responseObject["altitude"] = gpsData.altitude;
|
||||||
|
responseObject["latitude"] = gpsData.latitude;
|
||||||
|
responseObject["longitude"] = gpsData.longitude;
|
||||||
|
|
||||||
|
return responseObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::turnToTarget(QJsonObject &commandObject)
|
||||||
|
{
|
||||||
|
qDebug().noquote().nospace() << "Turning?";
|
||||||
|
|
||||||
|
uint16_t targetX = commandObject["target_x"].toInt();
|
||||||
|
uint16_t targetY = commandObject["target_y"].toInt();
|
||||||
|
|
||||||
|
float resultYaw = 0.0f;
|
||||||
|
float resultPitch = 0.0f;
|
||||||
|
UtilsTargetLocation::getAnglesToOnScreenTarget(targetX, targetY, resultYaw, resultPitch);
|
||||||
|
|
||||||
|
QByteArray serialCommandTurn = Config::getCommand()->getCommandForInternal(COMMAND_ID::TURN_TO_PIXEL);
|
||||||
|
|
||||||
|
int16_t degreesVal = resultYaw * 10;
|
||||||
|
serialCommandTurn[8] = degreesVal & 0xFF;
|
||||||
|
serialCommandTurn[9] = degreesVal >> 8;
|
||||||
|
|
||||||
|
degreesVal = resultPitch * 10;
|
||||||
|
serialCommandTurn[10] = degreesVal & 0xFF;
|
||||||
|
serialCommandTurn[11] = degreesVal >> 8;
|
||||||
|
|
||||||
|
Config::getSerial()->sendCommand(serialCommandTurn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::zoomToTarget(QJsonObject &commandObject)
|
||||||
|
{
|
||||||
|
qDebug().noquote().nospace() << "Zooming?";
|
||||||
|
|
||||||
|
float fillRatio = 0.5;
|
||||||
|
float targetPixelWidth = commandObject["target_pixel_width"].toInt();
|
||||||
|
float targetPixelHeight = commandObject["target_pixel_height"].toInt();
|
||||||
|
float adjustedObjectWidth = targetPixelWidth / fillRatio;
|
||||||
|
float adjustedObjectHeight = targetPixelHeight / fillRatio;
|
||||||
|
float zoomWidth = static_cast<double>(Config::getResolutionWidth()) / adjustedObjectWidth;
|
||||||
|
float zoomHeight = static_cast<double>(Config::getResolutionHeight()) / adjustedObjectHeight;
|
||||||
|
float zoom = std::min(zoomWidth, zoomHeight);
|
||||||
|
if (zoom < 1.0f) {
|
||||||
|
zoom = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray serialCommandNewZoom = Config::getCommand()->getCommandForInternal(COMMAND_ID::ZOOM_TO_X);
|
||||||
|
|
||||||
|
uint8_t integerPart = static_cast<uint8_t>(zoom);
|
||||||
|
float fractionalPart = zoom - integerPart;
|
||||||
|
uint8_t scaledFractional = uint8_t(fractionalPart * 10);
|
||||||
|
|
||||||
|
serialCommandNewZoom[8] = integerPart;
|
||||||
|
serialCommandNewZoom[9] = scaledFractional;
|
||||||
|
|
||||||
|
Config::getSerial()->sendCommand(serialCommandNewZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteControl::run()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
char buffer[1024];
|
||||||
|
ssize_t bytesRead = read(mFifoFdIn, buffer, sizeof(buffer) - 1);
|
||||||
|
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
buffer[bytesRead] = '\0';
|
||||||
|
|
||||||
|
QJsonDocument commandDoc = QJsonDocument::fromJson(buffer);
|
||||||
|
|
||||||
|
// Ignore non json messages
|
||||||
|
if (commandDoc.isNull() == false) {
|
||||||
|
QJsonObject commandObject = commandDoc.object();
|
||||||
|
|
||||||
|
// Ignore own messages and messages that don't have sender
|
||||||
|
if (commandObject.contains("sender") == false || commandObject["sender"] == FIFO_WHO_AM_I) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString message = QString::fromUtf8(buffer);
|
||||||
|
qDebug().noquote().nospace() << "Received: " << message;
|
||||||
|
|
||||||
|
// Exit with exit message
|
||||||
|
if (commandObject.contains("EXIT") == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare responce object
|
||||||
|
QJsonObject responseObject;
|
||||||
|
responseObject["sender"] = FIFO_WHO_AM_I;
|
||||||
|
responseObject["status"] = "OK";
|
||||||
|
|
||||||
|
// Get current orientation and zoom
|
||||||
|
Config::updateState();
|
||||||
|
|
||||||
|
// Turn to target
|
||||||
|
if (commandObject.contains("target_x") == true && commandObject.contains("target_y") == true) {
|
||||||
|
QTimer::singleShot(100, this, [this, commandObject]() mutable { turnToTarget(commandObject); });
|
||||||
|
QTimer::singleShot(1000, this, &RemoteControl::whenDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate target location
|
||||||
|
if (commandObject.contains("latitude") == true && commandObject.contains("longitude") == true && commandObject.contains("altitude") == true && commandObject.contains("yaw") == true && commandObject.contains("pitch") == true && commandObject.contains("target_pixel_width") == true && commandObject.contains("target_pixel_height") == true && commandObject.contains("target_real_width") == true && commandObject.contains("target_real_height") == true) {
|
||||||
|
responseObject = calculateTargetPosition(commandObject, responseObject);
|
||||||
|
QTimer::singleShot(1000, this, &RemoteControl::whenDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zoom to target
|
||||||
|
if (commandObject.contains("target_pixel_width") == true && commandObject.contains("target_pixel_height") == true) {
|
||||||
|
QTimer::singleShot(100, this, [this, commandObject]() mutable { zoomToTarget(commandObject); });
|
||||||
|
QTimer::singleShot(1000, this, &RemoteControl::whenDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore previous zoom and orientation
|
||||||
|
QTimer::singleShot(100, this, &RemoteControl::restoreZoom);
|
||||||
|
QTimer::singleShot(1000, this, &RemoteControl::whenDone);
|
||||||
|
|
||||||
|
QTimer::singleShot(100, this, &RemoteControl::restoreOrientation);
|
||||||
|
QTimer::singleShot(1000, this, &RemoteControl::whenDone);
|
||||||
|
|
||||||
|
// Respond after doing camera things
|
||||||
|
QJsonDocument responseDocument(responseObject);
|
||||||
|
std::string response = responseDocument.toJson(QJsonDocument::Compact).toStdString();
|
||||||
|
write(mFifoFdOut, response.c_str(), response.size());
|
||||||
|
qDebug().noquote().nospace() << "Sent: " << response;
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for a while
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class RemoteControl : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RemoteControl();
|
||||||
|
~RemoteControl();
|
||||||
|
void openNamedPipe(void);
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
QJsonObject calculateTargetPosition(QJsonObject &commandObject, QJsonObject &responseObject);
|
||||||
|
void turnToTarget(QJsonObject &commandObject);
|
||||||
|
void zoomToTarget(QJsonObject &commandObject);
|
||||||
|
void restoreOrientation(void);
|
||||||
|
void restoreZoom(void);
|
||||||
|
void whenDone(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int mFifoFdIn;
|
||||||
|
int mFifoFdOut;
|
||||||
|
};
|
||||||
@@ -1,33 +1,35 @@
|
|||||||
#include "serialCommand.h"
|
#include "serialCommand.hpp"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "utilsCRC16.h"
|
#include "config.hpp"
|
||||||
|
#include "utilsTargetLocation.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
SerialCommand::SerialCommand()
|
SerialCommand::SerialCommand()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Field Index Bytes Description
|
Field Index Bytes Description
|
||||||
STX 0 2 0x6655: starting mark. Low byte in the front
|
STX 0 2 0x6655: starting mark. Low byte in the front
|
||||||
CTRL 2 1 0: need_ack (if the current data pack need “ack”)
|
CTRL 2 1 0: need_ack (if the current data pack need “ack”)
|
||||||
1: ack_pack (if the current data pack is an “ack” package) 2-7: reserved
|
1: ack_pack (if the current data pack is an “ack” package) 2-7: reserved
|
||||||
Data_len 3 2 Data field byte length. Low byte in the front
|
Data_len 3 2 Data field byte length. Low byte in the front
|
||||||
SEQ 5 2 Frame sequence (0 ~ 65535). Low byte in the front
|
SEQ 5 2 Frame sequence (0 ~ 65535). Low byte in the front
|
||||||
CMD_ID 7 1 Command ID
|
CMD_ID 7 1 Command ID
|
||||||
DATA 8 Data_len Data
|
DATA 8 Data_len Data
|
||||||
CRC16 2 CRC16 check to the complete data package. Low
|
CRC16 2 CRC16 check to the complete data package. Low
|
||||||
byte in the front
|
byte in the front
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mSerialCommands.push_back({COMMAND_ID::TURN_TO_X, createByteArray({0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}), "Turn to X"});
|
mSerialCommands.push_back({COMMAND_ID::TURN_TO_DEGREES, createByteArray({0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}), "Turn to degrees"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::TURN_TO_PIXEL, createByteArray({0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}), "Turn to pixel"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ZOOM_TO_X, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00}), "Zoom to X"});
|
mSerialCommands.push_back({COMMAND_ID::ZOOM_TO_X, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00}), "Zoom to X"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_CAMERA_CODEC_SPECS, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00}), "Acquire Camera Codec Specs"});
|
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_CURRENT_ZOOM, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18}), "Acquire current zoom"});
|
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_ATTITUDE_DATA, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0d}), "Acquire Attitude Data"});
|
|
||||||
mSerialCommands.push_back({COMMAND_ID::AUTO_CENTER, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x01}), "Auto Centering"});
|
mSerialCommands.push_back({COMMAND_ID::AUTO_CENTER, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x01}), "Auto Centering"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_ATTITUDE_DATA, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0D}), "Acquire Attitude Data"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_CURRENT_ZOOM, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18}), "Acquire current zoom"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_CAMERA_CODEC_SPECS, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00}), "Acquire Camera Codec Specs"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ZOOM_MOST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x05, 0x01}), "Zoom +1"});
|
mSerialCommands.push_back({COMMAND_ID::ZOOM_MOST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x05, 0x01}), "Zoom +1"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ZOOM_LEAST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x05, 0xFF}), "Zoom -1"});
|
mSerialCommands.push_back({COMMAND_ID::ZOOM_LEAST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x05, 0xFF}), "Zoom -1"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::FOCUS_MOST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01}), "Manual Focus +1"});
|
mSerialCommands.push_back({COMMAND_ID::FOCUS_MOST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01}), "Manual Focus +1"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::FOCUS_LEAST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0xff}), "Manual Focus -1"});
|
mSerialCommands.push_back({COMMAND_ID::FOCUS_LEAST, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF}), "Manual Focus -1"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::FOCUS_AUTO, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01}), "Auto Focus"});
|
mSerialCommands.push_back({COMMAND_ID::FOCUS_AUTO, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01}), "Auto Focus"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ROTATE_UP, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2D}), "Rotate Up"});
|
mSerialCommands.push_back({COMMAND_ID::ROTATE_UP, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2D}), "Rotate Up"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ROTATE_DOWN, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, -0x2D}), "Rotate Down"});
|
mSerialCommands.push_back({COMMAND_ID::ROTATE_DOWN, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, -0x2D}), "Rotate Down"});
|
||||||
@@ -35,33 +37,41 @@ byte in the front
|
|||||||
mSerialCommands.push_back({COMMAND_ID::ROTATE_LEFT, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, -0x2D, 0x00}), "Rotate Left"});
|
mSerialCommands.push_back({COMMAND_ID::ROTATE_LEFT, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, -0x2D, 0x00}), "Rotate Left"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ROTATE_STOP, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00}), "Stop rotation"});
|
mSerialCommands.push_back({COMMAND_ID::ROTATE_STOP, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00}), "Stop rotation"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_MAX_ZOOM_VALUE, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16}), "Acquire the Max Zoom Value"});
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_MAX_ZOOM_VALUE, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16}), "Acquire the Max Zoom Value"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::TAKE_PICTURES, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00}), "Take Pictures"});
|
mSerialCommands.push_back({COMMAND_ID::TAKE_PICTURES, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00}), "Take Pictures"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::TAKE_VIDEO, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x02}), "Record Video"});
|
mSerialCommands.push_back({COMMAND_ID::TAKE_VIDEO, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x02}), "Record Video"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ROTATE_100_100, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x64, 0x64}), "Rotate 100 100"});
|
mSerialCommands.push_back({COMMAND_ID::ROTATE_100_100, createByteArray({0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x64, 0x64}), "Rotate 100 100"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_GIMBAL_STATUS, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a}), "Gimbal Status Information"});
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_GIMBAL_STATUS, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A}), "Gimbal Status Information"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_HW_INFO, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02}), "Acquire Hardware ID"});
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_HW_INFO, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02}), "Acquire Hardware ID"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_FIRMWARE_VERSION, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01}), "Acquire Firmware Version"});
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_FIRMWARE_VERSION, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01}), "Acquire Firmware Version"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::MODE_LOCK, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x03}), "Lock Mode"});
|
mSerialCommands.push_back({COMMAND_ID::MODE_LOCK, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x03}), "Lock Mode"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::MODE_FOLLOW, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x04}), "Follow Mode"});
|
mSerialCommands.push_back({COMMAND_ID::MODE_FOLLOW, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x04}), "Follow Mode"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::MODE_FPV, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x05}), "FPV Mode"});
|
mSerialCommands.push_back({COMMAND_ID::MODE_FPV, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x05}), "FPV Mode"});
|
||||||
mSerialCommands.push_back({COMMAND_ID::ENABLE_HDMI, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x06}), "Set Video Output as HDMI (Only available on A8 mini, restart to take effect)"});
|
mSerialCommands.push_back({COMMAND_ID::ENABLE_HDMI,
|
||||||
mSerialCommands.push_back({COMMAND_ID::ENABLE_CVBS, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x07}), "Set Video Output as CVBS (Only available on A8 mini, restart to take effect)"});
|
createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x06}),
|
||||||
mSerialCommands.push_back({COMMAND_ID::DISABLE_HDMI_CVBS, createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x08}), "Turn Off both CVBS and HDMI Output (Only available on A8 mini, restart to take effect)"});
|
"Set Video Output as HDMI (Only available on A8 mini, restart to take "
|
||||||
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_RANGE_DATA, createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x15}), "Read Range from Laser Rangefinder(Low byte in the front, high byte in the back, available on ZT30)"});
|
"effect)"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::ENABLE_CVBS,
|
||||||
|
createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x07}),
|
||||||
|
"Set Video Output as CVBS (Only available on A8 mini, restart to take "
|
||||||
|
"effect)"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::DISABLE_HDMI_CVBS,
|
||||||
|
createByteArray({0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x08}),
|
||||||
|
"Turn Off both CVBS and HDMI Output (Only available on A8 mini, restart "
|
||||||
|
"to take effect)"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::ACQUIRE_RANGE_DATA,
|
||||||
|
createByteArray({0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x15}),
|
||||||
|
"Read Range from Laser Rangefinder(Low byte in the front, high byte in "
|
||||||
|
"the back, available on ZT30)"});
|
||||||
|
mSerialCommands.push_back({COMMAND_ID::RUN_TARGET_LOCATION_TEST, createByteArray({0x00, 0x00}), "TEST target location calculations"});
|
||||||
|
|
||||||
// Sort vector by COMMAND_ID
|
// Sort vector by COMMAND_ID
|
||||||
std::sort(mSerialCommands.begin(), mSerialCommands.end(), [](const Command& a, const Command& b) { return a.id < b.id; });
|
std::sort(mSerialCommands.begin(), mSerialCommands.end(), [](const Command &a, const Command &b) { return a.id < b.id; });
|
||||||
}
|
}
|
||||||
|
|
||||||
SerialCommand::~SerialCommand()
|
QByteArray SerialCommand::createByteArray(const std::initializer_list<int> &bytes)
|
||||||
{
|
|
||||||
// Do something?
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SerialCommand::createByteArray(const std::initializer_list<int>& bytes)
|
|
||||||
{
|
{
|
||||||
QByteArray byteArray;
|
QByteArray byteArray;
|
||||||
for (int byte : bytes) {
|
for (int8_t byte : bytes) {
|
||||||
byteArray.append(static_cast<char>(byte));
|
byteArray.append(static_cast<char>(byte));
|
||||||
}
|
}
|
||||||
return byteArray;
|
return byteArray;
|
||||||
@@ -69,8 +79,10 @@ QByteArray SerialCommand::createByteArray(const std::initializer_list<int>& byte
|
|||||||
|
|
||||||
void SerialCommand::printCommands()
|
void SerialCommand::printCommands()
|
||||||
{
|
{
|
||||||
|
qInfo().noquote().nospace() << " ";
|
||||||
|
|
||||||
for (uint8_t i = 0; i < mSerialCommands.size(); i++) {
|
for (uint8_t i = 0; i < mSerialCommands.size(); i++) {
|
||||||
qInfo().noquote() << QString::number(mSerialCommands.at(i).id) + ": " + mSerialCommands.at(i).description;
|
qInfo().noquote().nospace() << QString::number(mSerialCommands.at(i).id) + ": " + mSerialCommands.at(i).description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,14 +91,14 @@ void SerialCommand::setExtraValues(COMMAND_ID commandId)
|
|||||||
uint16_t commandIndex = getCommandIndex(commandId);
|
uint16_t commandIndex = getCommandIndex(commandId);
|
||||||
QByteArray command = mSerialCommands.at(commandIndex).command;
|
QByteArray command = mSerialCommands.at(commandIndex).command;
|
||||||
|
|
||||||
if (commandId == COMMAND_ID::TURN_TO_X) {
|
if (commandId == COMMAND_ID::TURN_TO_DEGREES) {
|
||||||
float degrees;
|
float degrees;
|
||||||
|
|
||||||
qInfo() << "Enter yaw degrees (-135.0 -> 135.0): ";
|
qInfo().noquote().nospace() << "Enter yaw degrees (" << GIMBAL_YAW_MIN << " -> " << GIMBAL_YAW_MAX << "): ";
|
||||||
std::cin >> degrees;
|
std::cin >> degrees;
|
||||||
|
|
||||||
if (std::cin.fail() || degrees < -135.0f || degrees > 135.0f) {
|
if (std::cin.fail() || degrees < GIMBAL_YAW_MIN || degrees > GIMBAL_YAW_MAX) {
|
||||||
qWarning() << "Invalid value, using 0.0";
|
qWarning().noquote().nospace() << "Invalid value, using 0.0";
|
||||||
degrees = 0.0f;
|
degrees = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,31 +106,61 @@ void SerialCommand::setExtraValues(COMMAND_ID commandId)
|
|||||||
command[8] = degreesVal & 0xFF;
|
command[8] = degreesVal & 0xFF;
|
||||||
command[9] = degreesVal >> 8;
|
command[9] = degreesVal >> 8;
|
||||||
|
|
||||||
qInfo() << "Enter pitch degrees (-90.0 -> 25.0): ";
|
qInfo().noquote().nospace() << "Enter pitch degrees (" << GIMBAL_PITCH_MIN << " -> " << GIMBAL_PITCH_MAX << "): ";
|
||||||
std::cin >> degrees;
|
std::cin >> degrees;
|
||||||
|
|
||||||
if (std::cin.fail() || degrees < -90.0f || degrees > 25.0f) {
|
if (std::cin.fail() || degrees < GIMBAL_PITCH_MIN || degrees > GIMBAL_PITCH_MAX) {
|
||||||
qWarning() << "Invalid value, using 0.0";
|
qWarning().noquote().nospace() << "Invalid value, using 0.0";
|
||||||
degrees = 0.0f;
|
degrees = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
degreesVal = degrees * 10;
|
degreesVal = degrees * 10;
|
||||||
command[10] = degreesVal & 0xFF;
|
command[10] = degreesVal & 0xFF;
|
||||||
command[11] = degreesVal >> 8;
|
command[11] = degreesVal >> 8;
|
||||||
|
} else if (commandId == COMMAND_ID::TURN_TO_PIXEL) {
|
||||||
|
float targetX, targetY;
|
||||||
|
|
||||||
|
qInfo().noquote().nospace() << "Enter left_to_right (1 - " << Config::getResolutionWidth() << "): ";
|
||||||
|
std::cin >> targetX;
|
||||||
|
|
||||||
|
if (std::cin.fail() || targetX < 1 || targetX > Config::getResolutionWidth()) {
|
||||||
|
qWarning().noquote().nospace() << "Invalid value, using " << Config::getResolutionWidth() / 2;
|
||||||
|
targetX = Config::getResolutionWidth() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo().noquote().nospace() << "Enter top_to_bottom (1 -> " << Config::getResolutionHeight() << "): ";
|
||||||
|
std::cin >> targetY;
|
||||||
|
|
||||||
|
if (std::cin.fail() || targetY < 1 || targetY > Config::getResolutionHeight()) {
|
||||||
|
qWarning().noquote().nospace() << "Invalid value, using " << Config::getResolutionHeight() / 2;
|
||||||
|
targetY = Config::getResolutionHeight() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float resultYaw = 0.0f;
|
||||||
|
float resultPitch = 0.0f;
|
||||||
|
UtilsTargetLocation::getAnglesToOnScreenTarget(targetX, targetY, resultYaw, resultPitch);
|
||||||
|
|
||||||
|
int16_t degreesVal = resultYaw * 10;
|
||||||
|
command[8] = degreesVal & 0xFF;
|
||||||
|
command[9] = degreesVal >> 8;
|
||||||
|
|
||||||
|
degreesVal = resultPitch * 10;
|
||||||
|
command[10] = degreesVal & 0xFF;
|
||||||
|
command[11] = degreesVal >> 8;
|
||||||
} else if (commandId == COMMAND_ID::ZOOM_TO_X) {
|
} else if (commandId == COMMAND_ID::ZOOM_TO_X) {
|
||||||
float zoom;
|
float zoom;
|
||||||
|
|
||||||
qInfo() << "Enter zoom (1.0 -> 6.0): ";
|
qInfo().noquote().nospace() << "Enter zoom (1.0 -> " << Config::getMaxZoom() << "): ";
|
||||||
std::cin >> zoom;
|
std::cin >> zoom;
|
||||||
|
|
||||||
if (std::cin.fail() || zoom < 1.0 || zoom > 6.0) {
|
if (std::cin.fail() || zoom < 1.0f || zoom > Config::getMaxZoom()) {
|
||||||
qWarning() << "Invalid value, using 1.0";
|
qWarning().noquote().nospace() << "Invalid value, using 1.0";
|
||||||
zoom = 1.0f;
|
zoom = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t integerPart = static_cast<uint8_t>(zoom);
|
uint8_t integerPart = static_cast<uint8_t>(zoom);
|
||||||
float fractionalPart = zoom - integerPart;
|
float fractionalPart = zoom - integerPart;
|
||||||
uint8_t scaledFractional = int(fractionalPart * 10);
|
uint8_t scaledFractional = uint8_t(fractionalPart * 10);
|
||||||
command[8] = integerPart;
|
command[8] = integerPart;
|
||||||
command[9] = scaledFractional;
|
command[9] = scaledFractional;
|
||||||
}
|
}
|
||||||
@@ -142,25 +184,35 @@ int16_t SerialCommand::getCommandIndex(COMMAND_ID commandId)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SerialCommand::getCommandByID(COMMAND_ID commandId)
|
QByteArray SerialCommand::getCommandForUI(COMMAND_ID commandId)
|
||||||
{
|
{
|
||||||
setExtraValues(commandId);
|
setExtraValues(commandId);
|
||||||
|
|
||||||
uint16_t commandIndex = getCommandIndex(commandId);
|
int16_t commandIndex = getCommandIndex(commandId);
|
||||||
|
if (commandIndex == -1) {
|
||||||
|
qCritical().noquote().nospace() << "Command not found for command: " << commandId;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray command = mSerialCommands.at(commandIndex).command;
|
QByteArray command = mSerialCommands.at(commandIndex).command;
|
||||||
|
|
||||||
int8_t crcBytes[2];
|
|
||||||
UtilsCRC16::getCRCBytes(command, crcBytes);
|
|
||||||
|
|
||||||
command.resize(command.size() + 2); // Increase array size to accommodate CRC bytes
|
|
||||||
command[command.size() - 2] = crcBytes[0]; // Set LSB
|
|
||||||
command[command.size() - 1] = crcBytes[1]; // Set MSB
|
|
||||||
|
|
||||||
QString commandStr;
|
QString commandStr;
|
||||||
for (int i = 0; i < command.size(); i++) {
|
for (int i = 0; i < command.size(); i++) {
|
||||||
commandStr += QString("%1").arg(command.at(i), 2, 16, QChar('0')).toUpper();
|
if (i > 0) {
|
||||||
|
commandStr += ",";
|
||||||
|
}
|
||||||
|
commandStr += QString("0x%1").arg(command.at(i), 2, 16, QChar('0')).toUpper();
|
||||||
|
commandStr.replace("0X", "0x");
|
||||||
}
|
}
|
||||||
qDebug() << "Command: " << commandStr;
|
qDebug().noquote().nospace() << "Command: " << commandStr;
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray SerialCommand::getCommandForInternal(COMMAND_ID commandId)
|
||||||
|
{
|
||||||
|
int16_t commandIndex = getCommandIndex(commandId);
|
||||||
|
if (commandIndex == -1) {
|
||||||
|
qCritical().noquote().nospace() << "Command not found for command: " << commandId;
|
||||||
|
}
|
||||||
|
return mSerialCommands.at(commandIndex).command;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include "defines.h"
|
#include "defines.hpp"
|
||||||
|
|
||||||
struct Command {
|
struct Command
|
||||||
|
{
|
||||||
COMMAND_ID id;
|
COMMAND_ID id;
|
||||||
QByteArray command;
|
QByteArray command;
|
||||||
QString description;
|
QString description;
|
||||||
@@ -14,9 +15,9 @@ struct Command {
|
|||||||
class SerialCommand {
|
class SerialCommand {
|
||||||
public:
|
public:
|
||||||
SerialCommand();
|
SerialCommand();
|
||||||
~SerialCommand();
|
|
||||||
void printCommands(void);
|
void printCommands(void);
|
||||||
QByteArray getCommandByID(COMMAND_ID commandId);
|
QByteArray getCommandForUI(COMMAND_ID commandId);
|
||||||
|
QByteArray getCommandForInternal(COMMAND_ID commandId);
|
||||||
uint8_t getCommandCount();
|
uint8_t getCommandCount();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1,60 +1,87 @@
|
|||||||
#include "serialPort.h"
|
#include "serialPort.hpp"
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "defines.h"
|
#include "defines.hpp"
|
||||||
|
#include "utilsCRC16.hpp"
|
||||||
|
|
||||||
SerialPort::SerialPort(const QString &portName)
|
SerialPort::SerialPort()
|
||||||
: mSerialPort(portName)
|
: QObject(nullptr)
|
||||||
{
|
{
|
||||||
mSerialPort.setPortName(portName);
|
mSerialPort = new QSerialPort();
|
||||||
mSerialPort.setBaudRate(QSerialPort::Baud115200);
|
mSerialPort->setPortName(SERIAL_PORT);
|
||||||
mSerialPort.setDataBits(QSerialPort::Data8);
|
mSerialPort->setBaudRate(QSerialPort::Baud115200);
|
||||||
mSerialPort.setStopBits(QSerialPort::OneStop);
|
mSerialPort->setDataBits(QSerialPort::Data8);
|
||||||
mSerialPort.setFlowControl(QSerialPort::NoFlowControl);
|
mSerialPort->setStopBits(QSerialPort::OneStop);
|
||||||
|
mSerialPort->setFlowControl(QSerialPort::NoFlowControl);
|
||||||
|
|
||||||
|
// Open the serial port
|
||||||
|
if (openPort() == false) {
|
||||||
|
qCritical() << "SerialPort(): Unable to open port";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SerialPort::~SerialPort()
|
SerialPort::~SerialPort()
|
||||||
{
|
{
|
||||||
closePort(); // Close port if open on destruction
|
closePort();
|
||||||
|
delete mSerialPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SerialPort::openPort()
|
bool SerialPort::openPort()
|
||||||
{
|
{
|
||||||
if (mSerialPort.isOpen()) {
|
if (mSerialPort->isOpen()) {
|
||||||
qDebug() << "Port already open";
|
qDebug().noquote().nospace() << "Port already open";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mSerialPort.open(QIODevice::ReadWrite);
|
return mSerialPort->open(QIODevice::ReadWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialPort::closePort()
|
void SerialPort::closePort()
|
||||||
{
|
{
|
||||||
if (mSerialPort.isOpen()) {
|
if (mSerialPort->isOpen()) {
|
||||||
mSerialPort.close();
|
mSerialPort->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialPort::sendCommand(const QByteArray &command)
|
void SerialPort::sendCommand(const QByteArray &command)
|
||||||
{
|
{
|
||||||
if (!mSerialPort.isOpen()) {
|
QByteArray toSend = command;
|
||||||
qDebug() << "Error: Port not open";
|
int8_t crcBytes[2];
|
||||||
|
UtilsCRC16::getCRCBytes(toSend, crcBytes);
|
||||||
|
toSend.resize(toSend.size() + 2); // Increase array size to accommodate CRC bytes
|
||||||
|
toSend[toSend.size() - 2] = crcBytes[0]; // Set LSB
|
||||||
|
toSend[toSend.size() - 1] = crcBytes[1]; // Set MSB
|
||||||
|
|
||||||
|
QString commandStr;
|
||||||
|
for (int i = 0; i < toSend.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
commandStr += ",";
|
||||||
|
}
|
||||||
|
commandStr += QString("0x%1").arg(toSend.at(i), 2, 16, QChar('0')).toUpper();
|
||||||
|
commandStr.replace("0X", "0x");
|
||||||
|
}
|
||||||
|
qDebug().noquote().nospace() << "Command: " << commandStr;
|
||||||
|
|
||||||
|
if (!mSerialPort->isOpen()) {
|
||||||
|
qCritical().noquote().nospace() << "Error: Port not open (sendCommand)";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSerialPort.write(command);
|
mSerialPort->write(toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SerialPort::readResponse()
|
QByteArray SerialPort::readResponse()
|
||||||
{
|
{
|
||||||
if (!mSerialPort.isOpen()) {
|
if (!mSerialPort->isOpen()) {
|
||||||
qDebug() << "Error: Port not open";
|
qDebug().noquote().nospace() << "Error: Port not open (readResponse)";
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read data from serial port until timeout or specific criteria met
|
// Read data from serial port until timeout or specific criteria met
|
||||||
QByteArray response;
|
QByteArray response;
|
||||||
while (mSerialPort.waitForReadyRead(SERIAL_RESPONSE_WAIT_TIME)) { // Adjust timeout as needed
|
while (mSerialPort->waitForReadyRead(SERIAL_RESPONSE_WAIT_TIME)) { // Adjust timeout as needed
|
||||||
response.append(mSerialPort.readAll());
|
response.append(mSerialPort->readAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QSerialPort>
|
|
||||||
|
|
||||||
class SerialPort {
|
|
||||||
public:
|
|
||||||
SerialPort(const QString& portName);
|
|
||||||
~SerialPort();
|
|
||||||
|
|
||||||
bool openPort();
|
|
||||||
void closePort();
|
|
||||||
void sendCommand(const QByteArray& command);
|
|
||||||
QByteArray readResponse();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QSerialPort mSerialPort;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSerialPort>
|
||||||
|
|
||||||
|
class SerialPort : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SerialPort();
|
||||||
|
~SerialPort();
|
||||||
|
|
||||||
|
bool openPort();
|
||||||
|
void closePort();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sendCommand(const QByteArray &command);
|
||||||
|
QByteArray readResponse();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSerialPort *mSerialPort;
|
||||||
|
};
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
#include "serialResponse.h"
|
#include "serialResponse.hpp"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "defines.h"
|
#include "defines.hpp"
|
||||||
#include "utilsCRC16.h"
|
#include "utilsCRC16.hpp"
|
||||||
|
|
||||||
void SerialResponse::printResponse(QByteArray response)
|
void SerialResponse::printResponse(QByteArray response)
|
||||||
{
|
{
|
||||||
if (response.size() == 0) {
|
|
||||||
qWarning().noquote() << "Response is empty";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<QString, QVariant> results = getResponceValues(response);
|
QHash<QString, QVariant> results = getResponceValues(response);
|
||||||
|
|
||||||
QList<QString> keys = results.keys();
|
QList<QString> keys = results.keys();
|
||||||
@@ -37,6 +32,12 @@ void SerialResponse::printResponse(QByteArray response)
|
|||||||
|
|
||||||
QHash<QString, QVariant> SerialResponse::getResponceValues(QByteArray response)
|
QHash<QString, QVariant> SerialResponse::getResponceValues(QByteArray response)
|
||||||
{
|
{
|
||||||
|
QHash<QString, QVariant> results;
|
||||||
|
|
||||||
|
if (response.size() == 0) {
|
||||||
|
qCritical().noquote().nospace() << "Response is empty, exiting...";
|
||||||
|
}
|
||||||
|
|
||||||
// Check response data validity
|
// Check response data validity
|
||||||
int8_t crcCheck[2];
|
int8_t crcCheck[2];
|
||||||
uint8_t desiredLength = response.size() - 2;
|
uint8_t desiredLength = response.size() - 2;
|
||||||
@@ -49,61 +50,57 @@ QHash<QString, QVariant> SerialResponse::getResponceValues(QByteArray response)
|
|||||||
|
|
||||||
// Data not OK
|
// Data not OK
|
||||||
if (crcCheck[0] != crcOriginal[0] || crcCheck[1] != crcOriginal[1]) {
|
if (crcCheck[0] != crcOriginal[0] || crcCheck[1] != crcOriginal[1]) {
|
||||||
qWarning() << "Response data INVALID";
|
qWarning().noquote().nospace() << "Response data INVALID";
|
||||||
QString responseCRC = QString("0x%1,0x%2").arg(crcOriginal[0], 2, 16, QLatin1Char('0')).arg(crcOriginal[1], 2, 16, QLatin1Char('0')).toUpper();
|
QString responseCRC = QString("0x%1,0x%2").arg(crcOriginal[0], 2, 16, QLatin1Char('0')).arg(crcOriginal[1], 2, 16, QLatin1Char('0')).toUpper();
|
||||||
QString recalcCRC = QString("0x%1,0x%2").arg(crcCheck[0], 2, 16, QLatin1Char('0')).arg(crcCheck[1], 2, 16, QLatin1Char('0')).toUpper();
|
QString recalcCRC = QString("0x%1,0x%2").arg(crcCheck[0], 2, 16, QLatin1Char('0')).arg(crcCheck[1], 2, 16, QLatin1Char('0')).toUpper();
|
||||||
qWarning().noquote() << responseCRC << "!=" << recalcCRC;
|
qWarning().noquote().nospace() << responseCRC << "!=" << recalcCRC;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QString, QVariant> results;
|
|
||||||
uint8_t command = response.at(MESSAGE_IDX::CMD_ID);
|
uint8_t command = response.at(MESSAGE_IDX::CMD_ID);
|
||||||
|
|
||||||
if (command == 0x0E) {
|
if (command == 0x0E) {
|
||||||
int16_t yaw = ((uint8_t) response.at(9) << 8) | (uint8_t) response.at(8);
|
int16_t yaw = ((uint8_t) response.at(9) << 8) | (uint8_t) response.at(8);
|
||||||
results.insert("yaw", (float) (yaw / 10));
|
|
||||||
|
|
||||||
int16_t pitch = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
int16_t pitch = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
||||||
results.insert("pitch", (float) (pitch / 10));
|
|
||||||
|
|
||||||
int16_t roll = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
int16_t roll = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
||||||
|
results.insert("yaw", (float) (yaw / 10));
|
||||||
|
results.insert("pitch", (float) (pitch / 10));
|
||||||
results.insert("roll", (float) (roll / 10));
|
results.insert("roll", (float) (roll / 10));
|
||||||
} else if (command == 0x0D) {
|
} else if (command == 0x0D) {
|
||||||
int16_t yaw = ((uint8_t) response.at(9) << 8) | (uint8_t) response.at(8);
|
int16_t yaw = ((uint8_t) response.at(9) << 8) | (uint8_t) response.at(8);
|
||||||
results.insert("yaw", (float) (yaw / 10));
|
|
||||||
|
|
||||||
int16_t pitch = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
int16_t pitch = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
||||||
results.insert("pitch", (float) (pitch / 10));
|
|
||||||
|
|
||||||
int16_t roll = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
int16_t roll = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
||||||
|
int16_t yawSpeed = ((uint8_t) response.at(15) << 8) | (uint8_t) response.at(14);
|
||||||
|
int16_t pitchSpeed = ((uint8_t) response.at(17) << 8) | (uint8_t) response.at(16);
|
||||||
|
int16_t rollSpeed = ((uint8_t) response.at(19) << 8) | (uint8_t) response.at(18);
|
||||||
|
results.insert("yaw", (float) (yaw / 10));
|
||||||
|
results.insert("pitch", (float) (pitch / 10));
|
||||||
results.insert("roll", (float) (roll / 10));
|
results.insert("roll", (float) (roll / 10));
|
||||||
|
results.insert("yaw_speed", (float) (yawSpeed / 10));
|
||||||
int16_t yaw_speed = ((uint8_t) response.at(15) << 8) | (uint8_t) response.at(14);
|
results.insert("pitch_speed", (float) (pitchSpeed / 10));
|
||||||
results.insert("yaw_speed", (float) (yaw_speed / 10));
|
results.insert("roll_speed", (float) (rollSpeed / 10));
|
||||||
|
|
||||||
int16_t pitch_speed = ((uint8_t) response.at(17) << 8) | (uint8_t) response.at(16);
|
|
||||||
results.insert("pitch_speed", (float) (pitch_speed / 10));
|
|
||||||
|
|
||||||
int16_t roll_speed = ((uint8_t) response.at(19) << 8) | (uint8_t) response.at(18);
|
|
||||||
results.insert("roll_speed", (float) (roll_speed / 10));
|
|
||||||
} else if (command == 0x0F) {
|
} else if (command == 0x0F) {
|
||||||
int8_t zoom = (int8_t) response.at(8);
|
int8_t zoom = (int8_t) response.at(8);
|
||||||
results.insert("zoom", zoom);
|
results.insert("zoom", zoom);
|
||||||
} else if (command == 0x18) {
|
} else if (command == 0x16 || command == 0x18) {
|
||||||
float zoomInt = (float) response.at(8);
|
float zoomInt = (float) response.at(8);
|
||||||
float zoomFloat = (float) ((float) response.at(9) / 10);
|
float zoomFloat = (float) ((float) response.at(9) / 10);
|
||||||
results.insert("zoom", zoomInt + zoomFloat);
|
results.insert("zoom", zoomInt + zoomFloat);
|
||||||
} else if (command == 0x20) {
|
} else if (command == 0x20) {
|
||||||
uint16_t width = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
uint16_t width = ((uint8_t) response.at(11) << 8) | (uint8_t) response.at(10);
|
||||||
results.insert("width", width);
|
|
||||||
|
|
||||||
uint16_t height = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
uint16_t height = ((uint8_t) response.at(13) << 8) | (uint8_t) response.at(12);
|
||||||
|
results.insert("width", width);
|
||||||
results.insert("height", height);
|
results.insert("height", height);
|
||||||
} else {
|
} else {
|
||||||
|
qWarning().noquote().nospace() << "Getting responce values not implemented yet for command " << QString("0x%1").arg(command, 2, 16, QLatin1Char('0'));
|
||||||
QString responseStr;
|
QString responseStr;
|
||||||
for (int i = 0; i < response.size(); i++) {
|
for (int i = 0; i < response.size(); i++) {
|
||||||
responseStr += QString("%1").arg(response.at(i), 2, 16, QChar('0')).toUpper();
|
if (i > 0) {
|
||||||
|
responseStr += ",";
|
||||||
|
}
|
||||||
|
responseStr += QString("0x%1").arg(response.at(i), 2, 16, QChar('0')).toUpper();
|
||||||
|
responseStr.replace("0X", "0x");
|
||||||
}
|
}
|
||||||
results.insert("Response", responseStr);
|
qWarning().noquote().nospace() << "Responce byte array: " << responseStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "utilsCRC16.h"
|
#include "utilsCRC16.hpp"
|
||||||
|
|
||||||
static const uint16_t crc16Table[256] = {0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x630,
|
static const uint16_t crc16Table[256] = {0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x630,
|
||||||
0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
|
0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
|
||||||
|
|||||||
@@ -1,48 +1,12 @@
|
|||||||
#include "utilsTargetLocation.h"
|
#include "utilsTargetLocation.hpp"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include "serialCommand.h"
|
#include "config.hpp"
|
||||||
#include "serialResponse.h"
|
#include "defines.hpp"
|
||||||
|
|
||||||
#define USE_OPENCV
|
|
||||||
//#define USE_FFMPEG
|
|
||||||
|
|
||||||
#ifdef USE_OPENCV
|
|
||||||
#include <opencv2/opencv.hpp>
|
#include <opencv2/opencv.hpp>
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
extern "C" {
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
#include <libavutil/imgutils.h>
|
|
||||||
#include <libswscale/swscale.h>
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
UtilsTargetLocation::UtilsTargetLocation(SerialPort *serial, SerialCommand *command)
|
|
||||||
: mSerial(serial)
|
|
||||||
, mCommand(command)
|
|
||||||
{
|
|
||||||
// ... Optional: Additional initialization logic here
|
|
||||||
}
|
|
||||||
|
|
||||||
GPSData UtilsTargetLocation::getLocation(float altitude, float latitude, float lognitude, float yaw, float pitch, float roll, float targetTrueSize, uint16_t targetPixelSize)
|
GPSData UtilsTargetLocation::getLocation(float altitude, float latitude, float lognitude, float yaw, float pitch, float roll, float targetTrueSize, uint16_t targetPixelSize)
|
||||||
{
|
{
|
||||||
if (mSerial == nullptr) {
|
|
||||||
qWarning() << "Cannot get geo location: Serial connection not available.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mCommand == nullptr) {
|
|
||||||
qWarning() << "Cannot get geo location: Commands not available.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture image from rtsp stream
|
|
||||||
captureImageFromStream();
|
|
||||||
|
|
||||||
// TODO?
|
|
||||||
// Send image to AI for target identification and/or target choosing
|
|
||||||
|
|
||||||
// From the drone and camera
|
// From the drone and camera
|
||||||
CameraData cameraData = getCameraData();
|
CameraData cameraData = getCameraData();
|
||||||
GPSData gpsData = {latitude, lognitude, altitude};
|
GPSData gpsData = {latitude, lognitude, altitude};
|
||||||
@@ -60,33 +24,12 @@ GPSData UtilsTargetLocation::getLocation(float altitude, float latitude, float l
|
|||||||
|
|
||||||
CameraData UtilsTargetLocation::getCameraData()
|
CameraData UtilsTargetLocation::getCameraData()
|
||||||
{
|
{
|
||||||
uint16_t height = CAMERA_RESOLUTION_HEIGHT;
|
uint16_t height = Config::getResolutionHeight();
|
||||||
uint16_t width = CAMERA_RESOLUTION_WIDTH;
|
uint16_t width = Config::getResolutionWidth();
|
||||||
float pitch = 0;
|
float yaw = Config::getCurrentYaw();
|
||||||
float yaw = 0;
|
float pitch = Config::getCurrentPitch();
|
||||||
float fov = CAMERA_FIELD_OF_VIEW_HORIZONTAL;
|
float zoom = Config::getCurrentZoom();
|
||||||
|
float fov = CAMERA_FIELD_OF_VIEW_HORIZONTAL / zoom;
|
||||||
QByteArray response;
|
|
||||||
QHash<QString, QVariant> responseValues;
|
|
||||||
mSerial->sendCommand(mCommand->getCommandByID(COMMAND_ID::ACQUIRE_CURRENT_ZOOM)); // Field of view in degrees
|
|
||||||
response = mSerial->readResponse();
|
|
||||||
responseValues = SerialResponse::getResponceValues(response);
|
|
||||||
SerialResponse::printResponse(response);
|
|
||||||
fov = CAMERA_FIELD_OF_VIEW_HORIZONTAL / (responseValues["zoom"].toFloat() == 0.0 ? 1.0 : responseValues["zoom"].toFloat());
|
|
||||||
|
|
||||||
mSerial->sendCommand(mCommand->getCommandByID(COMMAND_ID::ACQUIRE_ATTITUDE_DATA)); // Yaw and pitch in degrees
|
|
||||||
response = mSerial->readResponse();
|
|
||||||
responseValues = SerialResponse::getResponceValues(response);
|
|
||||||
SerialResponse::printResponse(response);
|
|
||||||
yaw = responseValues["yaw"].toFloat();
|
|
||||||
pitch = responseValues["pitch"].toFloat();
|
|
||||||
|
|
||||||
mSerial->sendCommand(mCommand->getCommandByID(COMMAND_ID::ACQUIRE_CAMERA_CODEC_SPECS)); // Camera resolution in pixels
|
|
||||||
response = mSerial->readResponse();
|
|
||||||
responseValues = SerialResponse::getResponceValues(response);
|
|
||||||
SerialResponse::printResponse(response);
|
|
||||||
height = responseValues["height"].toUInt();
|
|
||||||
width = responseValues["width"].toUInt();
|
|
||||||
|
|
||||||
return {height, width, pitch, yaw, fov};
|
return {height, width, pitch, yaw, fov};
|
||||||
}
|
}
|
||||||
@@ -107,7 +50,7 @@ float UtilsTargetLocation::degreesToRadians(float degrees)
|
|||||||
// Function to calculate the new GPS location
|
// Function to calculate the new GPS location
|
||||||
GPSData UtilsTargetLocation::calculateTargetLocation(DroneData drone, CameraData camera, float distance, float bearing)
|
GPSData UtilsTargetLocation::calculateTargetLocation(DroneData drone, CameraData camera, float distance, float bearing)
|
||||||
{
|
{
|
||||||
constexpr float R = 6371000.0; // Earth radius in meters
|
constexpr float R = 6371000.0f; // Earth radius in meters
|
||||||
|
|
||||||
float bearingRad = degreesToRadians(bearing);
|
float bearingRad = degreesToRadians(bearing);
|
||||||
float latRad = degreesToRadians(drone.gps.latitude);
|
float latRad = degreesToRadians(drone.gps.latitude);
|
||||||
@@ -116,7 +59,7 @@ GPSData UtilsTargetLocation::calculateTargetLocation(DroneData drone, CameraData
|
|||||||
float newLatRad = asin(sin(latRad) * cos(distance / R) + cos(latRad) * sin(distance / R) * cos(bearingRad));
|
float newLatRad = asin(sin(latRad) * cos(distance / R) + cos(latRad) * sin(distance / R) * cos(bearingRad));
|
||||||
float newLonRad = lonRad + atan2(sin(bearingRad) * sin(distance / R) * cos(latRad), cos(distance / R) - sin(latRad) * sin(newLatRad));
|
float newLonRad = lonRad + atan2(sin(bearingRad) * sin(distance / R) * cos(latRad), cos(distance / R) - sin(latRad) * sin(newLatRad));
|
||||||
|
|
||||||
float angleRad = camera.pitch * M_PI / 180.0;
|
float angleRad = camera.pitch * M_PI / 180.0f;
|
||||||
float horizontalDistance = distance * cos(angleRad);
|
float horizontalDistance = distance * cos(angleRad);
|
||||||
float newAltitude = drone.gps.altitude + (horizontalDistance * tan(angleRad));
|
float newAltitude = drone.gps.altitude + (horizontalDistance * tan(angleRad));
|
||||||
|
|
||||||
@@ -128,157 +71,27 @@ GPSData UtilsTargetLocation::calculateTargetLocation(DroneData drone, CameraData
|
|||||||
return newLocation;
|
return newLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UtilsTargetLocation::captureImageFromStream()
|
void UtilsTargetLocation::getAnglesToOnScreenTarget(uint16_t targetX, uint16_t targetY, float &resultYaw, float &resultPitch)
|
||||||
{
|
{
|
||||||
#ifdef USE_OPENCV
|
resultYaw = Config::getCurrentYaw();
|
||||||
cv::VideoCapture cap;
|
resultPitch = Config::getCurrentPitch();
|
||||||
cap.open(RTSP_ADDRESS);
|
|
||||||
|
|
||||||
if (!cap.isOpened()) {
|
// Normalize target pixel location to [-0.5, 0.5] range
|
||||||
qWarning() << "Error: Could not open RTSP stream";
|
double normPixelX = (targetX - Config::getResolutionWidth() / 2.0f) / (Config::getResolutionWidth() / 2.0f);
|
||||||
return;
|
double normPixelY = (targetY - Config::getResolutionHeight() / 2.0f) / (Config::getResolutionHeight() / 2.0f);
|
||||||
}
|
|
||||||
|
|
||||||
// Hack to capture proper image
|
// Adjust horizontal field of view for zoom
|
||||||
// For some reason first frames get corrupted
|
double horizontalFov = CAMERA_FIELD_OF_VIEW_HORIZONTAL * (1.0f + (Config::getCurrentZoom() - 1.0f) / 5.0f);
|
||||||
uint8_t frameCount = 0;
|
|
||||||
|
|
||||||
while (true) {
|
// Calculate image plane dimensions based on focal length and aspect ratio
|
||||||
cv::Mat frame;
|
double imagePlaneWidth = 2.0f * CAMERA_FOCAL_LENGTH * tan(horizontalFov / 2.0f * M_PI / 180.0f);
|
||||||
|
double imagePlaneHeight = imagePlaneWidth / CAMERA_ASPECT_RATIO;
|
||||||
|
|
||||||
if (cap.grab() && cap.retrieve(frame)) {
|
// Calculate angle offsets based on normalized pixel location and image plane
|
||||||
if (frame.empty() || frame.total() == 0) {
|
// dimensions
|
||||||
qInfo() << "Warning: Invalid frame captured, retrying...";
|
float turnX = atan2(normPixelX * imagePlaneWidth / 2.0f, CAMERA_FOCAL_LENGTH) * 180.0f / M_PI;
|
||||||
} else {
|
float turnY = atan2(normPixelY * imagePlaneHeight / 2.0f, CAMERA_FOCAL_LENGTH) * 180.0f / M_PI;
|
||||||
// Convert the frame to BGR if needed
|
|
||||||
if (frame.channels() == 1) {
|
|
||||||
qInfo() << "Captured frame is grayscale, converting to BGR";
|
|
||||||
cv::cvtColor(frame, frame, cv::COLOR_GRAY2BGR);
|
|
||||||
} else if (frame.channels() == 4) {
|
|
||||||
qInfo() << "Captured frame is RGBA, converting to BGR";
|
|
||||||
cv::cvtColor(frame, frame, cv::COLOR_RGBA2BGR);
|
|
||||||
}
|
|
||||||
|
|
||||||
frameCount++;
|
resultYaw -= turnX;
|
||||||
if (frameCount > 10) {
|
resultPitch -= turnY;
|
||||||
// Save the captured frame as an image
|
|
||||||
std::string filename = "output_opencv.jpg";
|
|
||||||
if (cv::imwrite(filename, frame)) {
|
|
||||||
qInfo() << "Image captured and saved as" << QString::fromStdString(filename);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
qInfo() << "Error: Failed to save the captured image";
|
|
||||||
}
|
|
||||||
} else if (frameCount > 100) {
|
|
||||||
qInfo() << "Error: Tried 100 times and still fails, giving up";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qInfo() << "Warning: Could not grab or retrieve frame, retrying...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release the video capture object
|
|
||||||
cap.release();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
// Initialize FFmpeg
|
|
||||||
avformat_network_init();
|
|
||||||
|
|
||||||
// Open RTSP stream
|
|
||||||
AVFormatContext *formatContext = avformat_alloc_context();
|
|
||||||
if (avformat_open_input(&formatContext, RTSP_ADDRESS, NULL, NULL) != 0) {
|
|
||||||
qDebug() << "Error: Couldn't open RTSP stream";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find stream info
|
|
||||||
if (avformat_find_stream_info(formatContext, NULL) < 0) {
|
|
||||||
qDebug() << "Error: Couldn't find stream info";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find video stream
|
|
||||||
int videoStreamIndex = -1;
|
|
||||||
for (uint i = 0; i < formatContext->nb_streams; i++) {
|
|
||||||
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
|
||||||
videoStreamIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (videoStreamIndex == -1) {
|
|
||||||
qDebug() << "Error: Couldn't find video stream";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize video decoder
|
|
||||||
AVCodecParameters *codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
|
|
||||||
AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);
|
|
||||||
AVCodecContext *codecContext = avcodec_alloc_context3(codec);
|
|
||||||
avcodec_parameters_to_context(codecContext, codecParameters);
|
|
||||||
avcodec_open2(codecContext, codec, NULL);
|
|
||||||
|
|
||||||
// Allocate frame
|
|
||||||
AVFrame *frame = av_frame_alloc();
|
|
||||||
|
|
||||||
bool shouldBreak = false;
|
|
||||||
|
|
||||||
// Read frames
|
|
||||||
AVPacket packet;
|
|
||||||
|
|
||||||
while (shouldBreak == false && av_read_frame(formatContext, &packet) >= 0) {
|
|
||||||
if (packet.stream_index == videoStreamIndex) {
|
|
||||||
if (avcodec_send_packet(codecContext, &packet) >= 0) {
|
|
||||||
while (avcodec_receive_frame(codecContext, frame) >= 0) {
|
|
||||||
// Convert frame to RGB if necessary
|
|
||||||
if (codecParameters->format != AV_PIX_FMT_RGB24) {
|
|
||||||
AVFrame *rgbFrame = av_frame_alloc();
|
|
||||||
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecParameters->width, codecParameters->height, 1);
|
|
||||||
uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
|
|
||||||
if (av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, codecParameters->width, codecParameters->height, 1) > 0) {
|
|
||||||
// Convert format to enum AVPixelFormat
|
|
||||||
const AVPixFmtDescriptor *pixDesc = av_pix_fmt_desc_get((AVPixelFormat) codecParameters->format);
|
|
||||||
if (!pixDesc) {
|
|
||||||
qDebug() << "Error: Unsupported pixel format";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the AVPixelFormat enum value directly from the
|
|
||||||
descriptor AVPixelFormat pixelFormat = (AVPixelFormat) codecParameters->format;
|
|
||||||
|
|
||||||
// Create the sws context
|
|
||||||
struct SwsContext *swsContext = sws_getContext(codecParameters->width, codecParameters->height, pixelFormat, codecParameters->width, codecParameters->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);
|
|
||||||
sws_scale(swsContext, frame->data, frame->linesize, 0, codecParameters->height, rgbFrame->data, rgbFrame->linesize);
|
|
||||||
sws_freeContext(swsContext);
|
|
||||||
|
|
||||||
// Save frame as JPEG
|
|
||||||
QImage image(rgbFrame->data[0], codecParameters->width, codecParameters->height, QImage::Format_RGB888);
|
|
||||||
image.save("output_ffmpeg.jpg");
|
|
||||||
|
|
||||||
av_free(buffer);
|
|
||||||
av_frame_free(&rgbFrame);
|
|
||||||
shouldBreak = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Save frame as JPEG
|
|
||||||
QImage image(frame->data[0], codecParameters->width, codecParameters->height, QImage::Format_RGB888);
|
|
||||||
image.save("output_ffmpeg.jpg");
|
|
||||||
shouldBreak = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
av_packet_unref(&packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
avformat_close_input(&formatContext);
|
|
||||||
avcodec_free_context(&codecContext);
|
|
||||||
av_frame_free(&frame);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "serialCommand.h"
|
|
||||||
#include "serialPort.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
struct GPSData
|
|
||||||
{
|
|
||||||
float latitude; // Decimal degrees
|
|
||||||
float longitude; // Decimal degrees
|
|
||||||
float altitude; // Meters
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CameraData
|
|
||||||
{
|
|
||||||
uint16_t height; // Pixels
|
|
||||||
uint16_t width; // Pixels
|
|
||||||
float pitch; // Degrees
|
|
||||||
float yaw; // Degrees
|
|
||||||
float fow; // Degrees
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DroneData
|
|
||||||
{
|
|
||||||
GPSData gps;
|
|
||||||
float yaw; // Degrees
|
|
||||||
float pitch; // Degrees
|
|
||||||
float roll; // Degrees
|
|
||||||
};
|
|
||||||
|
|
||||||
class UtilsTargetLocation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UtilsTargetLocation(SerialPort *serial, SerialCommand *command);
|
|
||||||
GPSData getLocation(float altitude, float latitude, float lognitude, float yaw, float pitch, float roll, float targetTrueSize, uint16_t targetPixelSize);
|
|
||||||
|
|
||||||
private:
|
|
||||||
CameraData getCameraData();
|
|
||||||
float calculateTargetDistance(float targetSize, uint16_t targetPixelSize, uint16_t imageWidth, float fov);
|
|
||||||
float degreesToRadians(float degrees);
|
|
||||||
GPSData calculateTargetLocation(DroneData drone, CameraData camera, float distance, float bearing);
|
|
||||||
void captureImageFromStream();
|
|
||||||
|
|
||||||
SerialPort *mSerial;
|
|
||||||
SerialCommand *mCommand;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct GPSData
|
||||||
|
{
|
||||||
|
float latitude; // Decimal degrees
|
||||||
|
float longitude; // Decimal degrees
|
||||||
|
float altitude; // Meters
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraData
|
||||||
|
{
|
||||||
|
uint16_t height; // Pixels
|
||||||
|
uint16_t width; // Pixels
|
||||||
|
float pitch; // Degrees
|
||||||
|
float yaw; // Degrees
|
||||||
|
float fow; // Degrees
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DroneData
|
||||||
|
{
|
||||||
|
GPSData gps;
|
||||||
|
float yaw; // Degrees
|
||||||
|
float pitch; // Degrees
|
||||||
|
float roll; // Degrees
|
||||||
|
};
|
||||||
|
|
||||||
|
class UtilsTargetLocation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static GPSData getLocation(float altitude, float latitude, float lognitude, float yaw, float pitch, float roll, float targetTrueSize, uint16_t targetPixelSize);
|
||||||
|
static void getAnglesToOnScreenTarget(uint16_t targetX, uint16_t targetY, float &resultYaw, float &resultPitch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static CameraData getCameraData();
|
||||||
|
static float calculateTargetDistance(float targetSize, uint16_t targetPixelSize, uint16_t imageWidth, float fov);
|
||||||
|
static float degreesToRadians(float degrees);
|
||||||
|
static GPSData calculateTargetLocation(DroneData drone, CameraData camera, float distance, float bearing);
|
||||||
|
};
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
QT += core
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
CONFIG += c++17 console
|
||||||
|
|
||||||
|
TARGET = a8_remote
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS = -O0 -g -ggdb -fsanitize=undefined,bounds,float-divide-by-zero,integer-divide-by-zero,null,return,signed-integer-overflow,unreachable,shift,alignment,nonnull-attribute,returns-nonnull-attribute,enum
|
||||||
|
QMAKE_LFLAGS = -O0 -g -ggdb -fsanitize=undefined,bounds,float-divide-by-zero,integer-divide-by-zero,null,return,signed-integer-overflow,unreachable,shift,alignment,nonnull-attribute,returns-nonnull-attribute,enum
|
||||||
|
|
||||||
|
QMAKE_CXX = clang++
|
||||||
|
QMAKE_CC = clang
|
||||||
|
|
||||||
|
# Not nice, but for some reason QtCreator doesn't use /usr/lib/ccache/g++ from the PATH
|
||||||
|
linux-g++ {
|
||||||
|
QMAKE_CXX = clang++
|
||||||
|
QMAKE_CC = clang
|
||||||
|
}
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
remoteControl.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
defines.hpp \
|
||||||
|
remoteControl.hpp
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
QMAKE_CXX.QT_COMPILER_STDCXX = 201402L
|
||||||
|
QMAKE_CXX.QMAKE_CLANG_MAJOR_VERSION = 14
|
||||||
|
QMAKE_CXX.QMAKE_CLANG_MINOR_VERSION = 0
|
||||||
|
QMAKE_CXX.QMAKE_CLANG_PATCH_VERSION = 0
|
||||||
|
QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 4
|
||||||
|
QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 2
|
||||||
|
QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 1
|
||||||
|
QMAKE_CXX.COMPILER_MACROS = \
|
||||||
|
QT_COMPILER_STDCXX \
|
||||||
|
QMAKE_CLANG_MAJOR_VERSION \
|
||||||
|
QMAKE_CLANG_MINOR_VERSION \
|
||||||
|
QMAKE_CLANG_PATCH_VERSION \
|
||||||
|
QMAKE_GCC_MAJOR_VERSION \
|
||||||
|
QMAKE_GCC_MINOR_VERSION \
|
||||||
|
QMAKE_GCC_PATCH_VERSION
|
||||||
|
QMAKE_CXX.INCDIRS = \
|
||||||
|
/usr/include/c++/11 \
|
||||||
|
/usr/include/x86_64-linux-gnu/c++/11 \
|
||||||
|
/usr/include/c++/11/backward \
|
||||||
|
/usr/lib/llvm-14/lib/clang/14.0.0/include \
|
||||||
|
/usr/local/include \
|
||||||
|
/usr/include/x86_64-linux-gnu \
|
||||||
|
/usr/include
|
||||||
|
QMAKE_CXX.LIBDIRS = \
|
||||||
|
/usr/lib/llvm-14/lib/clang/14.0.0 \
|
||||||
|
/usr/lib/gcc/x86_64-linux-gnu/11 \
|
||||||
|
/usr/lib64 \
|
||||||
|
/lib/x86_64-linux-gnu \
|
||||||
|
/lib64 \
|
||||||
|
/usr/lib/x86_64-linux-gnu \
|
||||||
|
/usr/lib/llvm-14/lib \
|
||||||
|
/lib \
|
||||||
|
/usr/lib
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user