MSP OSD Revision

This commit is contained in:
Daniel M. Sahu 2022-09-16 12:02:41 -04:00 committed by Daniel Agar
parent 770f8080c0
commit 779e738143
19 changed files with 1435 additions and 330 deletions

View File

@ -24,7 +24,7 @@ CONFIG_DRIVERS_IRLOCK=y
CONFIG_COMMON_LIGHT=y
CONFIG_COMMON_MAGNETOMETER=y
CONFIG_COMMON_OPTICAL_FLOW=y
CONFIG_DRIVERS_OSD=y
CONFIG_COMMON_OSD=y
CONFIG_DRIVERS_PCA9685=y
CONFIG_DRIVERS_PCA9685_PWM_OUT=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y

View File

@ -23,7 +23,7 @@ CONFIG_DRIVERS_IRLOCK=y
CONFIG_COMMON_LIGHT=y
CONFIG_COMMON_MAGNETOMETER=y
CONFIG_COMMON_OPTICAL_FLOW=y
CONFIG_DRIVERS_OSD=y
CONFIG_COMMON_OSD=y
CONFIG_DRIVERS_PCA9685=y
CONFIG_DRIVERS_PCA9685_PWM_OUT=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y

View File

@ -27,7 +27,7 @@ CONFIG_COMMON_LIGHT=y
CONFIG_DRIVERS_LIGHTS_RGBLED_PWM=y
CONFIG_COMMON_MAGNETOMETER=y
CONFIG_COMMON_OPTICAL_FLOW=y
CONFIG_DRIVERS_OSD_MSP_OSD=y
CONFIG_COMMON_OSD=y
CONFIG_DRIVERS_PCA9685=y
CONFIG_DRIVERS_PCA9685_PWM_OUT=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y

View File

@ -29,7 +29,7 @@ CONFIG_DRIVERS_IRLOCK=y
CONFIG_COMMON_LIGHT=y
CONFIG_COMMON_MAGNETOMETER=y
CONFIG_COMMON_OPTICAL_FLOW=y
CONFIG_DRIVERS_OSD=y
CONFIG_COMMON_OSD=y
CONFIG_DRIVERS_PCA9685=y
CONFIG_DRIVERS_PCA9685_PWM_OUT=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y

View File

@ -28,6 +28,7 @@ CONFIG_DRIVERS_MAGNETOMETER_RM3100=y
CONFIG_COMMON_LIGHT=y
CONFIG_COMMON_MAGNETOMETER=y
CONFIG_COMMON_OPTICAL_FLOW=y
CONFIG_COMMON_OSD=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y
CONFIG_DRIVERS_POWER_MONITOR_INA228=y
CONFIG_DRIVERS_POWER_MONITOR_INA238=y

View File

@ -3,6 +3,7 @@ CONFIG_BOARD_TESTING=y
CONFIG_BOARD_ETHERNET=y
CONFIG_DRIVERS_CAMERA_TRIGGER=y
CONFIG_DRIVERS_GPS=y
CONFIG_DRIVERS_OSD_MSP_OSD=y
CONFIG_DRIVERS_TONE_ALARM=y
CONFIG_MODULES_AIRSHIP_ATT_CONTROL=y
CONFIG_MODULES_AIRSPEED_SELECTOR=y

View File

@ -31,15 +31,18 @@
#
############################################################################
add_subdirectory(MessageDisplay)
px4_add_module(
MODULE drivers__osd__msp_osd
MAIN msp_osd
SRCS
msp_defines.h
msp_osd.cpp
msp_osd.hpp
MspV1.cpp
MspV1.hpp
uorb_to_msp.cpp
MODULE_CONFIG
module.yaml
DEPENDS
message_display
px4_work_queue
)

View File

@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2019 PX4 Development Team. All rights reserved.
# Copyright (c) 2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@ -31,4 +31,12 @@
#
############################################################################
add_subdirectory(atxxxx)
add_compile_options(-Wno-stringop-truncation) # full message intentional string truncation
px4_add_library(message_display
MessageDisplay.cpp
MessageDisplay.hpp
)
# unit testing
px4_add_unit_gtest(SRC MessageDisplayTest.cpp LINKLIBS message_display)

View File

@ -0,0 +1,157 @@
/****************************************************************************
*
* Copyright (c) 2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/* Implementation of MessageDisplay class.
*/
#include <math.h>
#include "MessageDisplay.hpp"
namespace msp_osd
{
void MessageDisplay::set(const MessageDisplayType mode, const char *string)
{
switch (mode) {
case MessageDisplayType::WARNING:
if (strcmp(warning_msg, string) != 0) {
warning_msg[MSG_BUFFER_SIZE - 1] = '\0';
strncpy(warning_msg, string, MSG_BUFFER_SIZE - 1);
updated_ = true;
}
break;
case MessageDisplayType::FLIGHT_MODE:
if (strcmp(flight_mode_msg, string) != 0) {
flight_mode_msg[MSG_BUFFER_SIZE - 1] = '\0';
strncpy(flight_mode_msg, string, MSG_BUFFER_SIZE - 1);
updated_ = true;
}
break;
case MessageDisplayType::ARMING:
if (strcmp(arming_msg, string) != 0) {
arming_msg[MSG_BUFFER_SIZE - 1] = '\0';
strncpy(arming_msg, string, MSG_BUFFER_SIZE - 1);
updated_ = true;
}
break;
case MessageDisplayType::STATUS:
if (strcmp(status_msg, string) != 0) {
status_msg[MSG_BUFFER_SIZE - 1] = '\0';
strncpy(status_msg, string, MSG_BUFFER_SIZE - 1);
updated_ = true;
}
break;
case MessageDisplayType::HEADING:
if (strcmp(heading_msg, string) != 0) {
heading_msg[MSG_BUFFER_SIZE - 1] = '\0';
strncpy(heading_msg, string, MSG_BUFFER_SIZE - 1);
updated_ = true;
}
break;
default:
// PX4_ERR("Received unsupported message display mode.");
break;
}
}
void MessageDisplay::get(char *string, const uint32_t current_time)
{
// clear input sting
string[0] = '\0';
// check if we should update the full message (and reset display)
if (updated_) {
// full_message = "Flight Mode: " + flight_mode_msg + " - ARMED: " + arming_msg + " - STATUS: " + status_msg + " - WARNING: " + warning_msg + " ";
// reset and construct full message
strcpy(full_message, "");
strncat(full_message, flight_mode_msg, 3); // first three characters of Flight Mode
strcat(full_message, "|");
strncat(full_message, arming_msg, 4); // first four characters of Arming Message
strcat(full_message, "|");
strncat(full_message, heading_msg, 2); // first two characters of Heading
// add a warning message, if it's not empty
if (strlen(warning_msg) != 0) {
// copy as much of warning message as we can fit
strcat(full_message, " WARN: ");
const size_t chars_used {20};
strncat(full_message, warning_msg, MSG_BUFFER_SIZE - chars_used - FULL_MSG_LENGTH - 1);
// pad with one full length of terminal whitespace
for (unsigned i = 0; i != FULL_MSG_LENGTH; ++i) {
strcat(full_message, " ");
}
}
// reset display variables
index = 0;
updated_ = false;
}
// handle edge case where full message is short
if (strlen(full_message) < FULL_MSG_LENGTH) {
strncpy(string, full_message, FULL_MSG_LENGTH);
string[FULL_MSG_LENGTH] = '\0';
return;
}
// check if we should update the sub-message (giving extra time to the beginning)
uint32_t dt = current_time - last_update_;
if ((index == 0 && dt >= dwell_) || (index != 0 && dt >= period_)) {
// scroll through message by updating index
if (++index > strlen(full_message) - FULL_MSG_LENGTH) {
index = 0;
}
// save timestamp
last_update_ = current_time;
}
// reset update flag and return latest message
strncpy(string, full_message + index, FULL_MSG_LENGTH);
string[FULL_MSG_LENGTH] = '\0';
}
} // namespace msp_osd

View File

@ -0,0 +1,104 @@
/****************************************************************************
*
* Copyright (c) 2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/* Class for displaying messages to a limited screen.
*/
#pragma once
#include <string.h>
#include <stdint.h>
namespace msp_osd
{
// character size limitations
#define MSG_BUFFER_SIZE 250
// size of available characters, accounting for null terminator
// note: the craft_name seems to think it has 15 chars. From testing
// that seems incorrect
#define FULL_MSG_LENGTH 12
#define FULL_MSG_BUFFER 13
// supported message types
enum MessageDisplayType {
WARNING,
FLIGHT_MODE,
ARMING,
STATUS,
HEADING
};
// display information
class MessageDisplay
{
// working information
char warning_msg[MSG_BUFFER_SIZE] {""};
char flight_mode_msg[MSG_BUFFER_SIZE] {"???"};
char arming_msg[MSG_BUFFER_SIZE] {"????"};
char heading_msg[MSG_BUFFER_SIZE] {"??"};
// currently unused:
char status_msg[MSG_BUFFER_SIZE] {""};
// the full message and the part we're currently displaying
char full_message[MSG_BUFFER_SIZE] {"INITIALIZING"};
// current index we're displaying
uint16_t index{0};
// last update timestamp
uint64_t last_update_{0};
bool updated_{false};
// dwell duration update period (us)
uint64_t period_{125'000};
uint64_t dwell_{500'000};
public:
MessageDisplay() = default;
MessageDisplay(const uint64_t period, const uint64_t dwell) : period_(period), dwell_(dwell) {}
// set the given string
void set(const MessageDisplayType mode, const char *string);
// get the latest subsection of the message
void get(char *string, const uint32_t current_time);
// update local parameters
void set_period(const uint64_t period) { period_ = period; }
void set_dwell(const uint64_t dwell) { dwell_ = dwell; }
};
} // namespace msp_osd

View File

@ -0,0 +1,164 @@
/****************************************************************************
*
* Copyright (C) 2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include <gtest/gtest.h>
#include "MessageDisplay.hpp"
// default parameters
#define PERIOD 125000
#define DWELL 500000
// use base namespace
using namespace msp_osd;
// core test setup class
class MessageDisplayTest : public ::testing::Test
{
public:
// test copy of the message display object
std::unique_ptr<MessageDisplay> md_;
// setup a new test
void SetUp() override
{
// construct new default display
md_ = std::make_unique<MessageDisplay>(PERIOD, DWELL);
return;
}
};
// check basic construction
TEST_F(MessageDisplayTest, testMessageDisplayConstruction)
{
// make sure we have an object!
ASSERT_TRUE(static_cast<bool>(md_));
// verify default message
char message[FULL_MSG_LENGTH];
md_->get(message, 0);
EXPECT_STREQ(message, "INITIALIZING");
}
// check setting and getting various status messages
TEST_F(MessageDisplayTest, testMessageDisplayStatic)
{
// make sure we have an object!
ASSERT_TRUE(static_cast<bool>(md_));
// initialize output string
char message[FULL_MSG_BUFFER];
// set a random flight mode that's too short
md_->set(MessageDisplayType::FLIGHT_MODE, "S");
md_->get(message, 0);
EXPECT_STREQ(message, "S|????|??");
// set a random flight mode that's too long
md_->set(MessageDisplayType::FLIGHT_MODE, "SOSSOS");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|????|??");
// set a random flight mode that's just right
md_->set(MessageDisplayType::FLIGHT_MODE, "SOS");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|????|??");
// set a random arming status that's too short
md_->set(MessageDisplayType::ARMING, "DS");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DS|??");
// set a random arming status that's too long
md_->set(MessageDisplayType::ARMING, "DISARM");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DISA|??");
// set a random arming status that's just right
md_->set(MessageDisplayType::ARMING, "DSRM");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DSRM|??");
// set a random heading that's too short
md_->set(MessageDisplayType::HEADING, "N");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DSRM|N");
// set a random heading that's too short
md_->set(MessageDisplayType::HEADING, "NBNW");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DSRM|NB");
// set a random heading that's too short
md_->set(MessageDisplayType::HEADING, "NW");
md_->get(message, 0);
EXPECT_STREQ(message, "SOS|DSRM|NW");
}
// check setting and getting for a warning message
TEST_F(MessageDisplayTest, testMessageDisplayWarning)
{
// make sure we have an object!
ASSERT_TRUE(static_cast<bool>(md_));
// set just a warning message, but a very long one
md_->set(MessageDisplayType::WARNING,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
// the ground truth message is the following:
const char ground_truth[MSG_BUFFER_SIZE] =
"???|????|?? WARN: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea comm ";
// walk through the message as it scrolls
char message[FULL_MSG_BUFFER]; // output message
char correct[FULL_MSG_BUFFER]; // correct value of output message
uint32_t stamp = DWELL - 1; // start at the end of the DWELL time
for (size_t i = 0; i != MSG_BUFFER_SIZE - FULL_MSG_LENGTH; ++i) {
// get updated message for this time
md_->get(message, stamp);
// get substring that we should be seeing
strncpy(correct, &ground_truth[i], FULL_MSG_LENGTH);
EXPECT_STREQ(message, correct);
// update time
stamp += PERIOD;
}
// verify that we wrap around as expected at the end
md_->get(message, stamp);
strncpy(correct, ground_truth, FULL_MSG_LENGTH);
EXPECT_STREQ(message, correct);
}

View File

@ -62,7 +62,7 @@ struct msp_message_descriptor_t {
uint8_t message_size;
};
#define MSP_DESCRIPTOR_COUNT 12
#define MSP_DESCRIPTOR_COUNT 11
const msp_message_descriptor_t msp_message_descriptors[MSP_DESCRIPTOR_COUNT] = {
{MSP_OSD_CONFIG, true, sizeof(msp_osd_config_t)},
{MSP_NAME, true, sizeof(msp_name_t)},
@ -75,7 +75,6 @@ const msp_message_descriptor_t msp_message_descriptors[MSP_DESCRIPTOR_COUNT] = {
{MSP_COMP_GPS, true, sizeof(msp_comp_gps_t)},
{MSP_ESC_SENSOR_DATA, true, sizeof(msp_esc_sensor_data_dji_t)},
{MSP_MOTOR_TELEMETRY, true, sizeof(msp_motor_telemetry_t)},
{MSP_FC_VARIANT, true, sizeof(msp_fc_variant_t)},
};
#define MSP_FRAME_START_SIZE 5
@ -84,7 +83,7 @@ bool MspV1::Send(const uint8_t message_id, const void *payload)
{
uint32_t payload_size = 0;
msp_message_descriptor_t *desc = NULL;
msp_message_descriptor_t *desc = nullptr;
for (int i = 0; i < MSP_DESCRIPTOR_COUNT; i++) {
if (message_id == msp_message_descriptors[i].message_id) {

View File

@ -41,6 +41,6 @@ public:
bool Send(const uint8_t message_id, const void *payload);
private:
int _fd;
int _fd{-1};
};

View File

@ -0,0 +1,85 @@
module_name: MSP OSD
serial_config:
- command: msp_osd start -d ${SERIAL_DEV}
port_config_param:
name: MSP_OSD_CONFIG
group: OSD
parameters:
- group: OSD
definitions:
# OSD Parameters
OSD_SYMBOLS:
description:
short: OSD Symbol Selection
long: |
Configure / toggle support display options.
type: bitmask
bit:
0: CRAFT_NAME
1: DISARMED
2: GPS_LAT
3: GPS_LON
4: GPS_SATS
5: GPS_SPEED
6: HOME_DIST
7: HOME_DIR
8: MAIN_BATT_VOLTAGE
9: CURRENT_DRAW
10: MAH_DRAWN
11: RSSI_VALUE
12: ALTITUDE
13: NUMERICAL_VARIO
# the following are reserved but not currently functional
14: (unused) FLYMODE
15: (unused) ESC_TMP
16: (unused) PITCH_ANGLE
17: (unused) ROLL_ANGLE
18: (unused) CROSSHAIRS
19: (unused) AVG_CELL_VOLTAGE
20: (unused) HORIZON_SIDEBARS
21: (unused) POWER
default: 16383
# OSD Log Level
OSD_LOG_LEVEL:
description:
short: OSD Warning Level
long: |
Minimum security of log level to display on the OSD.
type: int32
bit:
0: EMERGENCY
1: ALERT
2: CRITICAL
3: ERROR
4: WARNING
5: NOTICE
6: INFO
7: DEBUG
8: NONE
default: 3
# Scroll Speed
OSD_SCROLL_RATE:
description:
short: OSD Scroll Rate (ms)
long: |
Scroll rate in milliseconds for OSD messages longer than available character width.
This is lower-bounded by the nominal loop rate of this module.
type: int32
min: 100
max: 1000
default: 125
# Dwell TIme
OSD_DWELL_TIME:
description:
short: OSD Dwell Time (ms)
long: |
Amount of time in milliseconds to dwell at the beginning of the display, when scrolling.
type: int32
min: 100
max: 10000
default: 500

View File

@ -33,8 +33,6 @@
#pragma once
#define FLIGHT_CONTROLLER_IDENTIFIER_LENGTH 4
// requests & replies
#define MSP_API_VERSION 1
#define MSP_FC_VARIANT 2

View File

@ -31,6 +31,13 @@
*
****************************************************************************/
/* Notes:
* - Currently there's a lot of wasted processing here if certain displays are enabled.
* A relatively low-hanging fruit would be figuring out which display elements require
* information from what UORB topics and disable if the information isn't displayed.
* (this is complicated by the fact that it's not a one-to-one mapping...)
*/
#include "msp_osd.hpp"
#include "msp_defines.h"
@ -54,7 +61,6 @@
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/vehicle_air_data.h>
#include <matrix/math.hpp>
#include <lib/geo/geo.h>
#include "MspV1.hpp"
@ -63,20 +69,17 @@
//in betaflight configurator set OSD elements to your desired positions and in CLI type "set osd" to retreieve the numbers.
//234 -> not visible. Horizontally 2048-2074(spacing 1), vertically 2048-2528(spacing 32). 26 characters X 15 lines
// Currently working elements
// Currently working elements positions (hardcoded)
// Left
const uint16_t osd_gps_lat_pos = 2048;
const uint16_t osd_gps_lon_pos = 2080;
uint16_t osd_gps_sats_pos = 2112;
const uint16_t osd_gps_sats_pos = 2112;
const uint16_t osd_rssi_value_pos = 2176;
const uint16_t osd_flymode_pos = 234;
const uint16_t osd_esc_tmp_pos = 234;
// Center
const uint16_t osd_home_dir_pos = 2093;
const uint16_t osd_craft_name_pos = 2543;
const uint16_t osd_horizon_sidebars_pos = 234;
const uint16_t osd_disarmed_pos = 2125;
// Right
@ -87,56 +90,19 @@ const uint16_t osd_altitude_pos = 2233;
const uint16_t osd_numerical_vario_pos = 2267;
const uint16_t osd_gps_speed_pos = 2299;
const uint16_t osd_home_dist_pos = 2331;
const uint16_t osd_power_pos = 234;
// Disabled
const uint16_t osd_pitch_angle_pos = 234;
const uint16_t osd_roll_angle_pos = 234;
const uint16_t osd_crosshairs_pos = 234;
const uint16_t osd_avg_cell_voltage_pos = 234;
// Not implemented or not available
const uint16_t osd_throttle_pos_pos = 234;
const uint16_t osd_vtx_channel_pos = 234;
const uint16_t osd_roll_pids_pos = 234;
const uint16_t osd_pitch_pids_pos = 234;
const uint16_t osd_yaw_pids_pos = 234;
const uint16_t osd_pidrate_profile_pos = 234;
const uint16_t osd_warnings_pos = 234;
const uint16_t osd_debug_pos = 234;
const uint16_t osd_artificial_horizon_pos = 234;
const uint16_t osd_item_timer_1_pos = 234;
const uint16_t osd_item_timer_2_pos = 234;
const uint16_t osd_main_batt_usage_pos = 234;
const uint16_t osd_numerical_heading_pos = 234;
const uint16_t osd_compass_bar_pos = 234;
const uint16_t osd_esc_rpm_pos = 234;
const uint16_t osd_remaining_time_estimate_pos = 234;
const uint16_t osd_rtc_datetime_pos = 234;
const uint16_t osd_adjustment_range_pos = 234;
const uint16_t osd_core_temperature_pos = 234;
const uint16_t osd_anti_gravity_pos = 234;
const uint16_t osd_g_force_pos = 234;
const uint16_t osd_motor_diag_pos = 234;
const uint16_t osd_log_status_pos = 234;
const uint16_t osd_flip_arrow_pos = 234;
const uint16_t osd_link_quality_pos = 234;
const uint16_t osd_flight_dist_pos = 234;
const uint16_t osd_stick_overlay_left_pos = 234;
const uint16_t osd_stick_overlay_right_pos = 234;
const uint16_t osd_display_name_pos = 234;
const uint16_t osd_esc_rpm_freq_pos = 234;
const uint16_t osd_rate_profile_name_pos = 234;
const uint16_t osd_pid_profile_name_pos = 234;
const uint16_t osd_profile_name_pos = 234;
const uint16_t osd_rssi_dbm_value_pos = 234;
const uint16_t osd_rc_channels_pos = 234;
MspOsd::MspOsd() :
MspOsd::MspOsd(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::test1),
_msp(NULL)
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default)
{
_display.set_period(_param_osd_scroll_rate.get() * 1000ULL);
_display.set_dwell(_param_osd_dwell_time.get() * 1000ULL);
// back up device name for connection later
strcpy(_device, device);
// _is_initialized = true;
PX4_INFO("MSP OSD prepared to run on %s", _device);
}
MspOsd::~MspOsd()
@ -155,7 +121,6 @@ void MspOsd::SendConfig()
msp_osd_config_t msp_osd_config;
msp_osd_config.units = 0;
msp_osd_config.osd_item_count = 56;
msp_osd_config.osd_stat_count = 24;
msp_osd_config.osd_timer_count = 2;
@ -164,63 +129,70 @@ void MspOsd::SendConfig()
msp_osd_config.osdprofileindex = 1; // 1
msp_osd_config.overlay_radio_mode = 0; // 0
msp_osd_config.osd_rssi_value_pos = osd_rssi_value_pos;
msp_osd_config.osd_main_batt_voltage_pos = osd_main_batt_voltage_pos;
msp_osd_config.osd_crosshairs_pos = osd_crosshairs_pos;
msp_osd_config.osd_artificial_horizon_pos = osd_artificial_horizon_pos;
msp_osd_config.osd_horizon_sidebars_pos = osd_horizon_sidebars_pos;
msp_osd_config.osd_item_timer_1_pos = osd_item_timer_1_pos;
msp_osd_config.osd_item_timer_2_pos = osd_item_timer_2_pos;
msp_osd_config.osd_flymode_pos = osd_flymode_pos;
msp_osd_config.osd_craft_name_pos = osd_craft_name_pos;
msp_osd_config.osd_throttle_pos_pos = osd_throttle_pos_pos;
msp_osd_config.osd_vtx_channel_pos = osd_vtx_channel_pos;
msp_osd_config.osd_current_draw_pos = osd_current_draw_pos;
msp_osd_config.osd_mah_drawn_pos = osd_mah_drawn_pos;
msp_osd_config.osd_gps_speed_pos = osd_gps_speed_pos;
msp_osd_config.osd_gps_sats_pos = osd_gps_sats_pos;
msp_osd_config.osd_altitude_pos = osd_altitude_pos;
msp_osd_config.osd_roll_pids_pos = osd_roll_pids_pos;
msp_osd_config.osd_pitch_pids_pos = osd_pitch_pids_pos;
msp_osd_config.osd_yaw_pids_pos = osd_yaw_pids_pos;
msp_osd_config.osd_power_pos = osd_power_pos;
msp_osd_config.osd_pidrate_profile_pos = osd_pidrate_profile_pos;
msp_osd_config.osd_warnings_pos = osd_warnings_pos;
msp_osd_config.osd_avg_cell_voltage_pos = osd_avg_cell_voltage_pos;
msp_osd_config.osd_gps_lon_pos = osd_gps_lon_pos;
msp_osd_config.osd_gps_lat_pos = osd_gps_lat_pos;
msp_osd_config.osd_debug_pos = osd_debug_pos;
msp_osd_config.osd_pitch_angle_pos = osd_pitch_angle_pos;
msp_osd_config.osd_roll_angle_pos = osd_roll_angle_pos;
msp_osd_config.osd_main_batt_usage_pos = osd_main_batt_usage_pos;
msp_osd_config.osd_disarmed_pos = osd_disarmed_pos;
msp_osd_config.osd_home_dir_pos = osd_home_dir_pos;
msp_osd_config.osd_home_dist_pos = osd_home_dist_pos;
msp_osd_config.osd_numerical_heading_pos = osd_numerical_heading_pos;
msp_osd_config.osd_numerical_vario_pos = osd_numerical_vario_pos;
msp_osd_config.osd_compass_bar_pos = osd_compass_bar_pos;
msp_osd_config.osd_esc_tmp_pos = osd_esc_tmp_pos;
msp_osd_config.osd_esc_rpm_pos = osd_esc_rpm_pos;
msp_osd_config.osd_remaining_time_estimate_pos = osd_remaining_time_estimate_pos;
msp_osd_config.osd_rtc_datetime_pos = osd_rtc_datetime_pos;
msp_osd_config.osd_adjustment_range_pos = osd_adjustment_range_pos;
msp_osd_config.osd_core_temperature_pos = osd_core_temperature_pos;
msp_osd_config.osd_anti_gravity_pos = osd_anti_gravity_pos;
msp_osd_config.osd_g_force_pos = osd_g_force_pos;
msp_osd_config.osd_motor_diag_pos = osd_motor_diag_pos;
msp_osd_config.osd_log_status_pos = osd_log_status_pos;
msp_osd_config.osd_flip_arrow_pos = osd_flip_arrow_pos;
msp_osd_config.osd_link_quality_pos = osd_link_quality_pos;
msp_osd_config.osd_flight_dist_pos = osd_flight_dist_pos;
msp_osd_config.osd_stick_overlay_left_pos = osd_stick_overlay_left_pos;
msp_osd_config.osd_stick_overlay_right_pos = osd_stick_overlay_right_pos;
msp_osd_config.osd_display_name_pos = osd_display_name_pos;
msp_osd_config.osd_esc_rpm_freq_pos = osd_esc_rpm_freq_pos;
msp_osd_config.osd_rate_profile_name_pos = osd_rate_profile_name_pos;
msp_osd_config.osd_pid_profile_name_pos = osd_pid_profile_name_pos;
msp_osd_config.osd_profile_name_pos = osd_profile_name_pos;
msp_osd_config.osd_rssi_dbm_value_pos = osd_rssi_dbm_value_pos;
msp_osd_config.osd_rc_channels_pos = osd_rc_channels_pos;
// display conditional elements
msp_osd_config.osd_craft_name_pos = enabled(SymbolIndex::CRAFT_NAME) ? osd_craft_name_pos : LOCATION_HIDDEN;
msp_osd_config.osd_disarmed_pos = enabled(SymbolIndex::DISARMED) ? osd_disarmed_pos : LOCATION_HIDDEN;
msp_osd_config.osd_gps_lat_pos = enabled(SymbolIndex::GPS_LAT) ? osd_gps_lat_pos : LOCATION_HIDDEN;
msp_osd_config.osd_gps_lon_pos = enabled(SymbolIndex::GPS_LON) ? osd_gps_lon_pos : LOCATION_HIDDEN;
msp_osd_config.osd_gps_sats_pos = enabled(SymbolIndex::GPS_SATS) ? osd_gps_sats_pos : LOCATION_HIDDEN;
msp_osd_config.osd_gps_speed_pos = enabled(SymbolIndex::GPS_SPEED) ? osd_gps_speed_pos : LOCATION_HIDDEN;
msp_osd_config.osd_home_dist_pos = enabled(SymbolIndex::HOME_DIST) ? osd_home_dist_pos : LOCATION_HIDDEN;
msp_osd_config.osd_home_dir_pos = enabled(SymbolIndex::HOME_DIR) ? osd_home_dir_pos : LOCATION_HIDDEN;
msp_osd_config.osd_main_batt_voltage_pos = enabled(SymbolIndex::MAIN_BATT_VOLTAGE) ? osd_main_batt_voltage_pos :
LOCATION_HIDDEN;
msp_osd_config.osd_current_draw_pos = enabled(SymbolIndex::CURRENT_DRAW) ? osd_current_draw_pos : LOCATION_HIDDEN;
msp_osd_config.osd_mah_drawn_pos = enabled(SymbolIndex::MAH_DRAWN) ? osd_mah_drawn_pos : LOCATION_HIDDEN;
msp_osd_config.osd_rssi_value_pos = enabled(SymbolIndex::RSSI_VALUE) ? osd_rssi_value_pos : LOCATION_HIDDEN;
msp_osd_config.osd_altitude_pos = enabled(SymbolIndex::ALTITUDE) ? osd_altitude_pos : LOCATION_HIDDEN;
msp_osd_config.osd_numerical_vario_pos = enabled(SymbolIndex::NUMERICAL_VARIO) ? osd_numerical_vario_pos :
LOCATION_HIDDEN;
// possibly available, but not currently used
msp_osd_config.osd_flymode_pos = LOCATION_HIDDEN;
msp_osd_config.osd_esc_tmp_pos = LOCATION_HIDDEN;
msp_osd_config.osd_pitch_angle_pos = LOCATION_HIDDEN;
msp_osd_config.osd_roll_angle_pos = LOCATION_HIDDEN;
msp_osd_config.osd_crosshairs_pos = LOCATION_HIDDEN;
msp_osd_config.osd_avg_cell_voltage_pos = LOCATION_HIDDEN;
msp_osd_config.osd_horizon_sidebars_pos = LOCATION_HIDDEN;
msp_osd_config.osd_power_pos = LOCATION_HIDDEN;
// Not implemented or not available
msp_osd_config.osd_artificial_horizon_pos = LOCATION_HIDDEN;
msp_osd_config.osd_item_timer_1_pos = LOCATION_HIDDEN;
msp_osd_config.osd_item_timer_2_pos = LOCATION_HIDDEN;
msp_osd_config.osd_throttle_pos_pos = LOCATION_HIDDEN;
msp_osd_config.osd_vtx_channel_pos = LOCATION_HIDDEN;
msp_osd_config.osd_roll_pids_pos = LOCATION_HIDDEN;
msp_osd_config.osd_pitch_pids_pos = LOCATION_HIDDEN;
msp_osd_config.osd_yaw_pids_pos = LOCATION_HIDDEN;
msp_osd_config.osd_pidrate_profile_pos = LOCATION_HIDDEN;
msp_osd_config.osd_warnings_pos = LOCATION_HIDDEN;
msp_osd_config.osd_debug_pos = LOCATION_HIDDEN;
msp_osd_config.osd_main_batt_usage_pos = LOCATION_HIDDEN;
msp_osd_config.osd_numerical_heading_pos = LOCATION_HIDDEN;
msp_osd_config.osd_compass_bar_pos = LOCATION_HIDDEN;
msp_osd_config.osd_esc_rpm_pos = LOCATION_HIDDEN;
msp_osd_config.osd_remaining_time_estimate_pos = LOCATION_HIDDEN;
msp_osd_config.osd_rtc_datetime_pos = LOCATION_HIDDEN;
msp_osd_config.osd_adjustment_range_pos = LOCATION_HIDDEN;
msp_osd_config.osd_core_temperature_pos = LOCATION_HIDDEN;
msp_osd_config.osd_anti_gravity_pos = LOCATION_HIDDEN;
msp_osd_config.osd_g_force_pos = LOCATION_HIDDEN;
msp_osd_config.osd_motor_diag_pos = LOCATION_HIDDEN;
msp_osd_config.osd_log_status_pos = LOCATION_HIDDEN;
msp_osd_config.osd_flip_arrow_pos = LOCATION_HIDDEN;
msp_osd_config.osd_link_quality_pos = LOCATION_HIDDEN;
msp_osd_config.osd_flight_dist_pos = LOCATION_HIDDEN;
msp_osd_config.osd_stick_overlay_left_pos = LOCATION_HIDDEN;
msp_osd_config.osd_stick_overlay_right_pos = LOCATION_HIDDEN;
msp_osd_config.osd_display_name_pos = LOCATION_HIDDEN;
msp_osd_config.osd_esc_rpm_freq_pos = LOCATION_HIDDEN;
msp_osd_config.osd_rate_profile_name_pos = LOCATION_HIDDEN;
msp_osd_config.osd_pid_profile_name_pos = LOCATION_HIDDEN;
msp_osd_config.osd_profile_name_pos = LOCATION_HIDDEN;
msp_osd_config.osd_rssi_dbm_value_pos = LOCATION_HIDDEN;
msp_osd_config.osd_rc_channels_pos = LOCATION_HIDDEN;
_msp.Send(MSP_OSD_CONFIG, &msp_osd_config);
}
@ -239,16 +211,16 @@ void MspOsd::Run()
parameter_update_s param_update;
_parameter_update_sub.copy(&param_update);
updateParams(); // update module parameters (in DEFINE_PARAMETERS)
parameters_update();
}
// perform first time initialization, if needed
if (!_is_initialized) {
_is_initialized = true;
struct termios t;
_msp_fd = open("/dev/ttyS3", O_RDWR | O_NONBLOCK);
_msp_fd = open(_device, O_RDWR | O_NONBLOCK);
if (_msp_fd < 0) {
_performance_data.initialization_problems = true;
return;
}
@ -262,200 +234,203 @@ void MspOsd::Run()
_msp = MspV1(_msp_fd);
PX4_WARN("Startup");
_is_initialized = true;
}
msp_battery_state_t battery_state = {0};
msp_name_t name = {0};
msp_status_BF_t status_BF = {0};
msp_analog_t analog = {0};
msp_raw_gps_t raw_gps = {0};
msp_comp_gps_t comp_gps = {0};
msp_attitude_t attitude = {0};
msp_altitude_t altitude = {0};
msp_esc_sensor_data_dji_t esc_sensor_data = {0};
//msp_motor_telemetry_t motor_telemetry = {0};
msp_fc_variant_t variant = {0};
// avoid premature pessimization; if skip processing if we're effectively disabled
if (_param_osd_symbols.get() == 0) {
return;
}
//power_monitor_sub.update(&power_monitor_struct);
_battery_status_sub.update(&_battery_status_struct);
_vehicle_status_sub.update(&_vehicle_status_struct);
_vehicle_gps_position_sub.update(&_vehicle_gps_position_struct);
_airspeed_validated_sub.update(&_airspeed_validated_struct);
_vehicle_air_data_sub.update(&_vehicle_air_data_struct);
_home_position_sub.update(&_home_position_struct);
_vehicle_global_position_sub.update(&_vehicle_global_position_struct);
_vehicle_local_position_sub.update(&_vehicle_local_position_struct);
_vehicle_attitude_sub.update(&_vehicle_attitude_struct);
_estimator_status_sub.update(&_estimator_status_struct);
_input_rc_sub.update(&_input_rc_struct);
// update display message
{
vehicle_status_s vehicle_status{};
_vehicle_status_sub.copy(&vehicle_status);
matrix::Eulerf euler_attitude(matrix::Quatf(_vehicle_attitude_struct.q));
vehicle_attitude_s vehicle_attitude{};
_vehicle_attitude_sub.copy(&vehicle_attitude);
memcpy(variant.flightControlIdentifier, "BTFL", sizeof(variant.flightControlIdentifier));
_msp.Send(MSP_FC_VARIANT, &variant);
log_message_s log_message{};
_log_message_sub.copy(&log_message);
// MSP_NAME
snprintf(name.craft_name, sizeof(name.craft_name), "> %i", _x);
name.craft_name[14] = '\0';
_msp.Send(MSP_NAME, &name);
const auto display_message = msp_osd::construct_display_message(
vehicle_status,
vehicle_attitude,
log_message,
_param_osd_log_level.get(),
_display);
this->Send(MSP_NAME, &display_message);
}
// MSP_FC_VARIANT
{
const auto msg = msp_osd::construct_FC_VARIANT();
this->Send(MSP_FC_VARIANT, &msg);
}
// MSP_STATUS
if (_vehicle_status_struct.arming_state == _vehicle_status_struct.ARMING_STATE_ARMED) {
status_BF.flight_mode_flags |= ARM_ACRO_BF;
{
vehicle_status_s vehicle_status{};
_vehicle_status_sub.copy(&vehicle_status);
switch (_vehicle_status_struct.nav_state) {
case _vehicle_status_struct.NAVIGATION_STATE_MANUAL:
status_BF.flight_mode_flags |= 0;
break;
case _vehicle_status_struct.NAVIGATION_STATE_ACRO:
status_BF.flight_mode_flags |= 0;
break;
case _vehicle_status_struct.NAVIGATION_STATE_STAB:
status_BF.flight_mode_flags |= STAB_BF;
break;
case _vehicle_status_struct.NAVIGATION_STATE_AUTO_RTL:
status_BF.flight_mode_flags |= RESC_BF;
break;
case _vehicle_status_struct.NAVIGATION_STATE_TERMINATION:
status_BF.flight_mode_flags |= FS_BF;
break;
default:
status_BF.flight_mode_flags = 0;
break;
}
const auto msg = msp_osd::construct_STATUS(vehicle_status);
this->Send(MSP_STATUS, &msg);
}
status_BF.arming_disable_flags_count = 1;
status_BF.arming_disable_flags = !(_vehicle_status_struct.arming_state == _vehicle_status_struct.ARMING_STATE_ARMED);
_msp.Send(MSP_STATUS, &status_BF);
// MSP_ANALOG
analog.vbat = _battery_status_struct.voltage_v * 10; // bottom right... v * 10
analog.rssi = (uint16_t)((_input_rc_struct.rssi * 1023.0f) / 100.0f);
analog.amperage = _battery_status_struct.current_a * 100; // main amperage
analog.mAhDrawn = _battery_status_struct.discharged_mah; // unused
_msp.Send(MSP_ANALOG, &analog);
{
battery_status_s battery_status{};
_battery_status_sub.copy(&battery_status);
input_rc_s input_rc{};
_input_rc_sub.copy(&input_rc);
const auto msg = msp_osd::construct_ANALOG(
battery_status,
input_rc);
this->Send(MSP_ANALOG, &msg);
}
// MSP_BATTERY_STATE
battery_state.amperage = _battery_status_struct.current_a; // not used?
battery_state.batteryVoltage = (uint16_t)(_battery_status_struct.voltage_v * 400.0f); // OK
battery_state.mAhDrawn = _battery_status_struct.discharged_mah ; // OK
battery_state.batteryCellCount = _battery_status_struct.cell_count;
battery_state.batteryCapacity = _battery_status_struct.capacity; // not used?
{
battery_status_s battery_status{};
_battery_status_sub.copy(&battery_status);
// Voltage color 0==white, 1==red
if (_battery_status_struct.voltage_v < 14.4f) {
battery_state.batteryState = 1;
} else {
battery_state.batteryState = 0;
const auto msg = msp_osd::construct_BATTERY_STATE(battery_status);
this->Send(MSP_BATTERY_STATE, &msg);
}
battery_state.legacyBatteryVoltage = _battery_status_struct.voltage_v * 10;
_msp.Send(MSP_BATTERY_STATE, &battery_state);
// MSP_RAW_GPS
if (_vehicle_gps_position_struct.fix_type >= 2) {
raw_gps.lat = _vehicle_gps_position_struct.lat;
raw_gps.lon = _vehicle_gps_position_struct.lon;
raw_gps.alt = _vehicle_gps_position_struct.alt / 10;
//raw_gps.groundCourse = vehicle_gps_position_struct
altitude.estimatedActualPosition = _vehicle_gps_position_struct.alt / 10;
{
sensor_gps_s vehicle_gps_position{};
_vehicle_gps_position_sub.copy(&vehicle_gps_position);
} else {
raw_gps.lat = 0;
raw_gps.lon = 0;
raw_gps.alt = 0;
altitude.estimatedActualPosition = 0;
}
airspeed_validated_s airspeed_validated{};
_airspeed_validated_sub.copy(&airspeed_validated);
if (_vehicle_gps_position_struct.fix_type == 0
|| _vehicle_gps_position_struct.fix_type == 1) {
raw_gps.fixType = MSP_GPS_NO_FIX;
} else if (_vehicle_gps_position_struct.fix_type == 2) {
raw_gps.fixType = MSP_GPS_FIX_2D;
} else if (_vehicle_gps_position_struct.fix_type >= 3 && _vehicle_gps_position_struct.fix_type <= 5) {
raw_gps.fixType = MSP_GPS_FIX_3D;
} else {
raw_gps.fixType = MSP_GPS_NO_FIX;
}
//raw_gps.hdop = vehicle_gps_position_struct.hdop
raw_gps.numSat = _vehicle_gps_position_struct.satellites_used;
if (_airspeed_validated_struct.airspeed_sensor_measurement_valid
&& _airspeed_validated_struct.indicated_airspeed_m_s != NAN
&& _airspeed_validated_struct.indicated_airspeed_m_s > 0) {
raw_gps.groundSpeed = _airspeed_validated_struct.indicated_airspeed_m_s * 100;
} else {
raw_gps.groundSpeed = 0;
}
//PX4_WARN("%f\r\n", (double)_battery_status_struct.current_a);
_msp.Send(MSP_RAW_GPS, &raw_gps);
// Calculate distance and direction to home
if (_home_position_struct.valid_hpos
&& _home_position_struct.valid_lpos
&& _estimator_status_struct.solution_status_flags & (1 << 4)) {
float bearing_to_home = get_bearing_to_next_waypoint(_vehicle_global_position_struct.lat,
_vehicle_global_position_struct.lon,
_home_position_struct.lat, _home_position_struct.lon);
float distance_to_home = get_distance_to_next_waypoint(_vehicle_global_position_struct.lat,
_vehicle_global_position_struct.lon,
_home_position_struct.lat, _home_position_struct.lon);
comp_gps.distanceToHome = (int16_t)distance_to_home; // meters
comp_gps.directionToHome = bearing_to_home; // degrees
} else {
comp_gps.distanceToHome = 0; // meters
comp_gps.directionToHome = 0; // degrees
const auto msg = msp_osd::construct_RAW_GPS(
vehicle_gps_position,
airspeed_validated);
this->Send(MSP_RAW_GPS, &msg);
}
// MSP_COMP_GPS
comp_gps.heartbeat = _heartbeat;
_heartbeat = !_heartbeat;
_msp.Send(MSP_COMP_GPS, &comp_gps);
{
// update heartbeat
_heartbeat = !_heartbeat;
// MSP_ATTITUDE
attitude.pitch = math::degrees(euler_attitude.theta()) * 10;
attitude.roll = math::degrees(euler_attitude.phi()) * 10;
attitude.yaw = math::degrees(euler_attitude.psi()) * 10;
_msp.Send(MSP_ATTITUDE, &attitude);
home_position_s home_position{};
_home_position_sub.copy(&home_position);
// MSP_ALTITUDE
if (_estimator_status_struct.solution_status_flags & (1 << 5)) {
altitude.estimatedActualVelocity = -_vehicle_local_position_struct.vz * 10; //m/s to cm/s
estimator_status_s estimator_status{};
_estimator_status_sub.copy(&estimator_status);
} else {
altitude.estimatedActualVelocity = 0;
vehicle_global_position_s vehicle_global_position{};
_vehicle_global_position_sub.copy(&vehicle_global_position);
// construct and send message
const auto msg = msp_osd::construct_COMP_GPS(
home_position,
estimator_status,
vehicle_global_position,
_heartbeat);
this->Send(MSP_COMP_GPS, &msg);
}
_msp.Send(MSP_ALTITUDE, &altitude);
// MSP_ATTITUDE
{
vehicle_attitude_s vehicle_attitude{};
_vehicle_attitude_sub.copy(&vehicle_attitude);
const auto msg = msp_osd::construct_ATTITUDE(vehicle_attitude);
this->Send(MSP_ATTITUDE, &msg);
}
// MSP_ALTITUDE
{
sensor_gps_s vehicle_gps_position{};
_vehicle_gps_position_sub.copy(&vehicle_gps_position);
estimator_status_s estimator_status{};
_estimator_status_sub.copy(&estimator_status);
vehicle_local_position_s vehicle_local_position{};
_vehicle_local_position_sub.copy(&vehicle_local_position);
// construct and send message
const auto msg = msp_osd::construct_ALTITUDE(
vehicle_gps_position,
estimator_status,
vehicle_local_position);
this->Send(MSP_ALTITUDE, &msg);
}
// MSP_MOTOR_TELEMETRY
esc_sensor_data.rpm = 0;
esc_sensor_data.temperature = 50;
_msp.Send(MSP_ESC_SENSOR_DATA, &esc_sensor_data);
{
const auto msg = msp_osd::construct_ESC_SENSOR_DATA();
this->Send(MSP_ESC_SENSOR_DATA, &msg);
}
// send full configuration
SendConfig();
}
void MspOsd::Send(const unsigned int message_type, const void *payload)
{
if (_msp.Send(message_type, payload)) {
_performance_data.successful_sends++;
} else {
_performance_data.unsuccessful_sends++;
}
}
void MspOsd::parameters_update()
{
// update our display rate and dwell time
_display.set_period(hrt_abstime(_param_osd_scroll_rate.get() * 1000ULL));
_display.set_dwell(hrt_abstime(_param_osd_dwell_time.get() * 1000ULL));
}
bool MspOsd::enabled(const SymbolIndex &symbol)
{
return _param_osd_symbols.get() & (1u << symbol);
}
int MspOsd::task_spawn(int argc, char *argv[])
{
MspOsd *instance = new MspOsd();
// initialize device
const char *device = nullptr;
bool error_flag = false;
// loop through input arguments
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device = myoptarg;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return PX4_ERROR;
}
if (!device) {
PX4_ERR("Missing device");
return PX4_ERROR;
}
MspOsd *instance = new MspOsd(device);
if (instance) {
_object.store(instance);
@ -478,7 +453,17 @@ int MspOsd::task_spawn(int argc, char *argv[])
int MspOsd::print_status()
{
PX4_INFO("Running");
PX4_INFO("Running on %s", _device);
PX4_INFO("\tinitialized: %d", _is_initialized);
PX4_INFO("\tinitialization issues: %d", _performance_data.initialization_problems);
PX4_INFO("\tscroll rate: %d", static_cast<int>(_param_osd_scroll_rate.get()));
PX4_INFO("\tsuccessful sends: %lu", _performance_data.successful_sends);
PX4_INFO("\tunsuccessful sends: %lu", _performance_data.unsuccessful_sends);
// print current display string
char msg[FULL_MSG_BUFFER];
_display.get(msg, hrt_absolute_time());
PX4_INFO("Current message: \n\t%s", msg);
return 0;
}

View File

@ -41,29 +41,67 @@
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/estimator_status.h>
#include <uORB/topics/home_position.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/log_message.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/power_monitor.h>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/vehicle_air_data.h>
#include <uORB/topics/home_position.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/estimator_status.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/vehicle_status.h>
#include "MspV1.hpp"
#include "MessageDisplay/MessageDisplay.hpp"
#include "uorb_to_msp.hpp"
using namespace time_literals;
// location to "hide" unused display elements
#define LOCATION_HIDDEN 234;
struct PerformanceData {
bool initialization_problems{false};
long unsigned int successful_sends{0};
long unsigned int unsuccessful_sends{0};
};
// mapping from symbol name to bit in the parameter bitmask
// @TODO investigate params; it seems like this should be available directly?
enum SymbolIndex : uint8_t {
CRAFT_NAME = 0,
DISARMED = 1,
GPS_LAT = 2,
GPS_LON = 3,
GPS_SATS = 4,
GPS_SPEED = 5,
HOME_DIST = 6,
HOME_DIR = 7,
MAIN_BATT_VOLTAGE = 8,
CURRENT_DRAW = 9,
MAH_DRAWN = 10,
RSSI_VALUE = 11,
ALTITUDE = 12,
NUMERICAL_VARIO = 13,
FLYMODE = 14,
ESC_TMP = 15,
PITCH_ANGLE = 16,
ROLL_ANGLE = 17,
CROSSHAIRS = 18,
AVG_CELL_VOLTAGE = 19,
HORIZON_SIDEBARS = 20,
POWER = 21
};
class MspOsd : public ModuleBase<MspOsd>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
MspOsd();
MspOsd(const char *device);
~MspOsd() override;
@ -84,44 +122,55 @@ public:
private:
void Run() override;
MspV1 _msp;
int _msp_fd{-1};
bool _is_initialized{false};
//uORB::Subscription power_monitor_sub(ORB_ID(power_monitor));
uORB::Subscription _battery_status_sub{ORB_ID(battery_status)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::Subscription _vehicle_gps_position_sub{ORB_ID(vehicle_gps_position)};
uORB::Subscription _airspeed_validated_sub{ORB_ID(airspeed_validated)};
uORB::Subscription _vehicle_air_data_sub{ORB_ID(vehicle_air_data)};
uORB::Subscription _home_position_sub{ORB_ID(home_position)};
uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
uORB::Subscription _estimator_status_sub{ORB_ID(estimator_status)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _input_rc_sub{ORB_ID(input_rc)};
struct battery_status_s _battery_status_struct = {0};
struct vehicle_status_s _vehicle_status_struct;
struct sensor_gps_s _vehicle_gps_position_struct = {0};
struct airspeed_validated_s _airspeed_validated_struct = {0};
struct vehicle_air_data_s _vehicle_air_data_struct = {0};
struct home_position_s _home_position_struct = {0};
struct vehicle_global_position_s _vehicle_global_position_struct = {0};
struct vehicle_attitude_s _vehicle_attitude_struct = {0};
struct estimator_status_s _estimator_status_struct = {0};
struct vehicle_local_position_s _vehicle_local_position_struct = {0};
struct input_rc_s _input_rc_struct = {0};
// update a single display element in the display
void Send(const unsigned int message_type, const void *payload);
// send full configuration to MSP (triggers the actual update)
void SendConfig();
void SendTelemetry();
uint8_t _x{0};
bool _heartbeat{false};
// perform actions required for local updates
void parameters_update();
// convenience function to check if a given symbol is enabled
bool enabled(const SymbolIndex &symbol);
MspV1 _msp{0};
int _msp_fd{-1};
msp_osd::MessageDisplay _display{};
bool _is_initialized{false};
// subscriptions to desired vehicle display information
uORB::Subscription _airspeed_validated_sub{ORB_ID(airspeed_validated)};
uORB::Subscription _battery_status_sub{ORB_ID(battery_status)};
uORB::Subscription _estimator_status_sub{ORB_ID(estimator_status)};
uORB::Subscription _home_position_sub{ORB_ID(home_position)};
uORB::Subscription _input_rc_sub{ORB_ID(input_rc)};
uORB::Subscription _log_message_sub{ORB_ID(log_message)};
uORB::Subscription _vehicle_air_data_sub{ORB_ID(vehicle_air_data)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position)};
uORB::Subscription _vehicle_gps_position_sub{ORB_ID(vehicle_gps_position)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
// local heartbeat
bool _heartbeat{false};
// parameters
DEFINE_PARAMETERS(
(ParamInt<px4::params::OSD_SYMBOLS>) _param_osd_symbols,
(ParamInt<px4::params::OSD_SCROLL_RATE>) _param_osd_scroll_rate,
(ParamInt<px4::params::OSD_DWELL_TIME>) _param_osd_dwell_time,
(ParamInt<px4::params::OSD_LOG_LEVEL>) _param_osd_log_level
)
// metadata
char _device[64] {};
PerformanceData _performance_data{};
};

View File

@ -0,0 +1,439 @@
/****************************************************************************
*
* Copyright (c) 2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/* uorb_to_msp.cpp
*
* Implementation file for UORB -> MSP conversion functions.
*/
// includes for mathematical manipulation
#include <math.h>
#include <matrix/math.hpp>
#include <lib/geo/geo.h>
// clock access
#include <px4_platform_common/defines.h>
using namespace time_literals;
#include "uorb_to_msp.hpp"
namespace msp_osd
{
msp_name_t construct_display_message(const vehicle_status_s &vehicle_status,
const vehicle_attitude_s &vehicle_attitude,
const log_message_s &log_message,
const int log_level,
MessageDisplay &display)
{
// initialize result
msp_name_t display_message {0};
const auto now = hrt_absolute_time();
static uint64_t last_warning_stamp {0};
// update arming state, flight mode, and warnings, if current
if (vehicle_status.timestamp < (now - 1_s)) {
display.set(MessageDisplayType::ARMING, "???");
display.set(MessageDisplayType::FLIGHT_MODE, "???");
} else {
// display armed / disarmed
if (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED) {
display.set(MessageDisplayType::ARMING, "ARM");
} else {
display.set(MessageDisplayType::ARMING, "DSRM");
}
// display flight mode
switch (vehicle_status.nav_state) {
case vehicle_status_s::NAVIGATION_STATE_MANUAL:
display.set(MessageDisplayType::FLIGHT_MODE, "MANUAL");
break;
case vehicle_status_s::NAVIGATION_STATE_ALTCTL:
display.set(MessageDisplayType::FLIGHT_MODE, "ALTCTL");
break;
case vehicle_status_s::NAVIGATION_STATE_POSCTL:
display.set(MessageDisplayType::FLIGHT_MODE, "POSCTL");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_MISSION");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_LOITER:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_LOITER");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_RTL");
break;
case vehicle_status_s::NAVIGATION_STATE_UNUSED:
display.set(MessageDisplayType::FLIGHT_MODE, "UNUSED");
break;
case vehicle_status_s::NAVIGATION_STATE_ACRO:
display.set(MessageDisplayType::FLIGHT_MODE, "ACRO");
break;
case vehicle_status_s::NAVIGATION_STATE_UNUSED1:
display.set(MessageDisplayType::FLIGHT_MODE, "UNUSED1");
break;
case vehicle_status_s::NAVIGATION_STATE_DESCEND:
display.set(MessageDisplayType::FLIGHT_MODE, "DESCEND");
break;
case vehicle_status_s::NAVIGATION_STATE_TERMINATION:
display.set(MessageDisplayType::FLIGHT_MODE, "TERMINATION");
break;
case vehicle_status_s::NAVIGATION_STATE_OFFBOARD:
display.set(MessageDisplayType::FLIGHT_MODE, "OFFBOARD");
break;
case vehicle_status_s::NAVIGATION_STATE_STAB:
display.set(MessageDisplayType::FLIGHT_MODE, "STAB");
break;
case vehicle_status_s::NAVIGATION_STATE_UNUSED2:
display.set(MessageDisplayType::FLIGHT_MODE, "UNUSED2");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_TAKEOFF:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_TAKEOFF");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_LAND:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_LAND");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_FOLLOW_TARGET:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_FOLLOW_TARGET");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_PRECLAND:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_PRECLAND");
break;
case vehicle_status_s::NAVIGATION_STATE_ORBIT:
display.set(MessageDisplayType::FLIGHT_MODE, "ORBIT");
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_VTOL_TAKEOFF:
display.set(MessageDisplayType::FLIGHT_MODE, "AUTO_VTOL_TAKEOFF");
break;
case vehicle_status_s::NAVIGATION_STATE_MAX:
display.set(MessageDisplayType::FLIGHT_MODE, "MAX");
break;
default:
display.set(MessageDisplayType::FLIGHT_MODE, "???");
}
}
// display, if updated
if (log_message.severity <= log_level) {
display.set(MessageDisplayType::WARNING, log_message.text);
last_warning_stamp = now;
} else if (now - last_warning_stamp > 30_s) {
// clear warning after timeout
display.set(MessageDisplayType::WARNING, "");
last_warning_stamp = now;
}
// update heading, if relatively recent
if (vehicle_attitude.timestamp < (now - 1_s)) {
display.set(MessageDisplayType::HEADING, "N?");
} else {
// convert to YAW
matrix::Eulerf euler_attitude(matrix::Quatf(vehicle_attitude.q));
const auto yaw = math::degrees(euler_attitude.psi());
// display north direction
if (yaw <= 22.5f) {
display.set(MessageDisplayType::HEADING, "N");
} else if (yaw <= 67.5f) {
display.set(MessageDisplayType::HEADING, "NE");
} else if (yaw <= 112.5f) {
display.set(MessageDisplayType::HEADING, "E");
} else if (yaw <= 157.5f) {
display.set(MessageDisplayType::HEADING, "SE");
} else if (yaw <= 202.5f) {
display.set(MessageDisplayType::HEADING, "S");
} else if (yaw <= 247.5f) {
display.set(MessageDisplayType::HEADING, "SW");
} else if (yaw <= 292.5f) {
display.set(MessageDisplayType::HEADING, "W");
} else if (yaw <= 337.5f) {
display.set(MessageDisplayType::HEADING, "NW");
} else if (yaw <= 360.0f) {
display.set(MessageDisplayType::HEADING, "N");
}
}
// update message and return
display.get(display_message.craft_name, hrt_absolute_time());
return display_message;
}
msp_fc_variant_t construct_FC_VARIANT()
{
// initialize result
msp_fc_variant_t variant{};
memcpy(variant.flightControlIdentifier, "BTFL", sizeof(variant.flightControlIdentifier));
return variant;
}
msp_status_BF_t construct_STATUS(const vehicle_status_s &vehicle_status)
{
// initialize result
msp_status_BF_t status_BF = {0};
if (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED) {
status_BF.flight_mode_flags |= ARM_ACRO_BF;
switch (vehicle_status.nav_state) {
case vehicle_status_s::NAVIGATION_STATE_MANUAL:
status_BF.flight_mode_flags |= 0;
break;
case vehicle_status_s::NAVIGATION_STATE_ACRO:
status_BF.flight_mode_flags |= 0;
break;
case vehicle_status_s::NAVIGATION_STATE_STAB:
status_BF.flight_mode_flags |= STAB_BF;
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
status_BF.flight_mode_flags |= RESC_BF;
break;
case vehicle_status_s::NAVIGATION_STATE_TERMINATION:
status_BF.flight_mode_flags |= FS_BF;
break;
default:
status_BF.flight_mode_flags = 0;
break;
}
}
status_BF.arming_disable_flags_count = 1;
status_BF.arming_disable_flags = !(vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
return status_BF;
}
msp_analog_t construct_ANALOG(const battery_status_s &battery_status, const input_rc_s &input_rc)
{
// initialize result
msp_analog_t analog {0};
analog.vbat = battery_status.voltage_v * 10; // bottom right... v * 10
analog.rssi = (uint16_t)((input_rc.rssi * 1023.0f) / 100.0f);
analog.amperage = battery_status.current_a * 100; // main amperage
analog.mAhDrawn = battery_status.discharged_mah; // unused
return analog;
}
msp_battery_state_t construct_BATTERY_STATE(const battery_status_s &battery_status)
{
// initialize result
msp_battery_state_t battery_state = {0};
// MSP_BATTERY_STATE
battery_state.amperage = battery_status.current_a; // not used?
battery_state.batteryVoltage = (uint16_t)(battery_status.voltage_v * 400.0f); // OK
battery_state.mAhDrawn = battery_status.discharged_mah ; // OK
battery_state.batteryCellCount = battery_status.cell_count;
battery_state.batteryCapacity = battery_status.capacity; // not used?
// Voltage color 0==white, 1==red
if (battery_status.voltage_v < 14.4f) {
battery_state.batteryState = 1;
} else {
battery_state.batteryState = 0;
}
battery_state.legacyBatteryVoltage = battery_status.voltage_v * 10;
return battery_state;
}
msp_raw_gps_t construct_RAW_GPS(const sensor_gps_s &vehicle_gps_position,
const airspeed_validated_s &airspeed_validated)
{
// initialize result
msp_raw_gps_t raw_gps {0};
if (vehicle_gps_position.fix_type >= 2) {
raw_gps.lat = vehicle_gps_position.lat;
raw_gps.lon = vehicle_gps_position.lon;
raw_gps.alt = vehicle_gps_position.alt / 10;
//raw_gps.groundCourse = vehicle_gps_position_struct
} else {
raw_gps.lat = 0;
raw_gps.lon = 0;
raw_gps.alt = 0;
}
if (vehicle_gps_position.fix_type == 0
|| vehicle_gps_position.fix_type == 1) {
raw_gps.fixType = MSP_GPS_NO_FIX;
} else if (vehicle_gps_position.fix_type == 2) {
raw_gps.fixType = MSP_GPS_FIX_2D;
} else if (vehicle_gps_position.fix_type >= 3 && vehicle_gps_position.fix_type <= 5) {
raw_gps.fixType = MSP_GPS_FIX_3D;
} else {
raw_gps.fixType = MSP_GPS_NO_FIX;
}
//raw_gps.hdop = vehicle_gps_position_struct.hdop
raw_gps.numSat = vehicle_gps_position.satellites_used;
if (airspeed_validated.airspeed_sensor_measurement_valid
&& airspeed_validated.indicated_airspeed_m_s != NAN
&& airspeed_validated.indicated_airspeed_m_s > 0) {
raw_gps.groundSpeed = airspeed_validated.indicated_airspeed_m_s * 100;
} else {
raw_gps.groundSpeed = 0;
}
return raw_gps;
}
msp_comp_gps_t construct_COMP_GPS(const home_position_s &home_position,
const estimator_status_s &estimator_status,
const vehicle_global_position_s &vehicle_global_position,
const bool heartbeat)
{
// initialize result
msp_comp_gps_t comp_gps {0};
// Calculate distance and direction to home
if (home_position.valid_hpos
&& home_position.valid_lpos
&& estimator_status.solution_status_flags & (1 << 4)) {
float bearing_to_home = get_bearing_to_next_waypoint(vehicle_global_position.lat,
vehicle_global_position.lon,
home_position.lat, home_position.lon);
float distance_to_home = get_distance_to_next_waypoint(vehicle_global_position.lat,
vehicle_global_position.lon,
home_position.lat, home_position.lon);
comp_gps.distanceToHome = (int16_t)distance_to_home; // meters
comp_gps.directionToHome = bearing_to_home; // degrees
} else {
comp_gps.distanceToHome = 0; // meters
comp_gps.directionToHome = 0; // degrees
}
comp_gps.heartbeat = heartbeat;
return comp_gps;
}
msp_attitude_t construct_ATTITUDE(const vehicle_attitude_s &vehicle_attitude)
{
// initialize results
msp_attitude_t attitude {0};
// convert from quaternion to RPY
matrix::Eulerf euler_attitude(matrix::Quatf(vehicle_attitude.q));
attitude.pitch = math::degrees(euler_attitude.theta()) * 10;
attitude.roll = math::degrees(euler_attitude.phi()) * 10;
attitude.yaw = math::degrees(euler_attitude.psi()) * 10;
return attitude;
}
msp_altitude_t construct_ALTITUDE(const sensor_gps_s &vehicle_gps_position,
const estimator_status_s &estimator_status,
const vehicle_local_position_s &vehicle_local_position)
{
// initialize result
msp_altitude_t altitude {0};
if (vehicle_gps_position.fix_type >= 2) {
altitude.estimatedActualPosition = vehicle_gps_position.alt / 10;
} else {
altitude.estimatedActualPosition = 0;
}
if (estimator_status.solution_status_flags & (1 << 5)) {
altitude.estimatedActualVelocity = -vehicle_local_position.vz * 10; //m/s to cm/s
} else {
altitude.estimatedActualVelocity = 0;
}
return altitude;
}
msp_esc_sensor_data_dji_t construct_ESC_SENSOR_DATA()
{
// initialize result
msp_esc_sensor_data_dji_t esc_sensor_data {0};
esc_sensor_data.rpm = 0;
esc_sensor_data.temperature = 50;
return esc_sensor_data;
}
} // namespace msp_osd

View File

@ -0,0 +1,112 @@
/****************************************************************************
*
* Copyright (c) 2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/* uorb_to_msp.hpp
*
* Declaration of functions which translate UORB messages into MSP specific structures.
*/
#pragma once
// basic types
#include <cmath>
// UORB topic structs
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/power_monitor.h>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/vehicle_air_data.h>
#include <uORB/topics/home_position.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/estimator_status.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/log_message.h>
// PX4 events interface
#include <px4_platform_common/events.h>
// MSP structs
#include "msp_defines.h"
#include "MessageDisplay/MessageDisplay.hpp"
namespace msp_osd
{
// construct an MSP_NAME struct
// note: this is actually how we display _all_ string information
msp_name_t construct_display_message(const vehicle_status_s &vehicle_status,
const vehicle_attitude_s &vehicle_attitude,
const log_message_s &log_message,
const int log_level,
MessageDisplay &display);
// construct an MSP_FC_VARIANT struct
msp_fc_variant_t construct_FC_VARIANT();
// construct an MSP_STATUS struct
msp_status_BF_t construct_STATUS(const vehicle_status_s &vehicle_status);
// construct an MSP_ANALOG struct
msp_analog_t construct_ANALOG(const battery_status_s &battery_status, const input_rc_s &input_rc);
// construct an MSP_BATTERY_STATE struct
msp_battery_state_t construct_BATTERY_STATE(const battery_status_s &battery_status);
// construct an MSP_RAW_GPS struct
msp_raw_gps_t construct_RAW_GPS(const sensor_gps_s &vehicle_gps_position,
const airspeed_validated_s &airspeed_validated);
// construct an MSP_COMP_GPS struct
msp_comp_gps_t construct_COMP_GPS(const home_position_s &home_position,
const estimator_status_s &estimator_status,
const vehicle_global_position_s &vehicle_global_position,
const bool heartbeat);
// construct an MSP_ATTITUDE struct
msp_attitude_t construct_ATTITUDE(const vehicle_attitude_s &vehicle_attitude);
// construct an MSP_ALTITUDE struct
msp_altitude_t construct_ALTITUDE(const sensor_gps_s &vehicle_gps_position,
const estimator_status_s &estimator_status,
const vehicle_local_position_s &vehicle_local_position);
// construct an MSP_ESC_SENSOR_DATA struct
msp_esc_sensor_data_dji_t construct_ESC_SENSOR_DATA();
} // namespace msp_osd