ardupilot/libraries/AP_VideoTX/AP_SmartAudio.h
Andy Piper f98ce3dc9e AP_VideoTX: add lookup tables for VTX power settings
correct settings when power set is received
add support for capturing all supported power levels
learn power levels in SmartAudio 2.1
add better support for VTX power levels
don't set power to 0 if in pitmode
add option for iNav compatibility
support non-conforming SmartAudio implementations
re-enable pitmode on SmartAudio 2.0
add support for "blind" VTX setting
2022-10-04 13:38:30 +11:00

272 lines
8.7 KiB
C++

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <AP_HAL/AP_HAL.h>
#ifndef HAL_SMARTAUDIO_ENABLED
#define HAL_SMARTAUDIO_ENABLED !HAL_MINIMIZE_FEATURES
#endif
#if HAL_SMARTAUDIO_ENABLED
#include <AP_Param/AP_Param.h>
#include <AP_SerialManager/AP_SerialManager.h>
#include <AP_HAL/utility/RingBuffer.h>
#include "AP_VideoTX.h"
#define SMARTAUDIO_BUFFER_CAPACITY 5
// SmartAudio Serial Protocol
#define AP_SMARTAUDIO_UART_BAUD 4800
#define AP_SMARTAUDIO_SMARTBAUD_MIN 4560 // -5%
#define AP_SMARTAUDIO_SMARTBAUD_MAX 5040 // +5%
#define AP_SMARTAUDIO_SMARTBAUD_STEP 60
#define AP_SMARTAUDIO_UART_BUFSIZE_RX 16
#define AP_SMARTAUDIO_UART_BUFSIZE_TX 16
#define AP_SMARTAUDIO_MAX_PACKET_SIZE 32
#define SMARTAUDIO_SYNC_BYTE 0xAA
#define SMARTAUDIO_HEADER_BYTE 0x55
#define SMARTAUDIO_START_CODE SMARTAUDIO_SYNC_BYTE + SMARTAUDIO_HEADER_BYTE
#define SMARTAUDIO_GET_PITMODE_FREQ (1 << 14)
#define SMARTAUDIO_SET_PITMODE_FREQ (1 << 15)
#define SMARTAUDIO_FREQUENCY_MASK 0x3FFF
#define SMARTAUDIO_CMD_GET_SETTINGS 0x03
#define SMARTAUDIO_CMD_SET_POWER 0x05
#define SMARTAUDIO_CMD_SET_CHANNEL 0x07
#define SMARTAUDIO_CMD_SET_FREQUENCY 0x09
#define SMARTAUDIO_CMD_SET_MODE 0x0B
#define SMARTAUDIO_COMMAND_FRAME_SIZE (sizeof(FrameHeader) + 1)
#define SMARTAUDIO_U8_COMMAND_FRAME_SIZE (sizeof(FrameHeader) + 2)
#define SMARTAUDIO_U16_COMMAND_FRAME_SIZE (sizeof(FrameHeader) + 3)
#define SMARTAUDIO_RSP_GET_SETTINGS_V1 SMARTAUDIO_CMD_GET_SETTINGS >> 1
#define SMARTAUDIO_RSP_GET_SETTINGS_V2 (SMARTAUDIO_CMD_GET_SETTINGS >> 1) | 0x08
#define SMARTAUDIO_RSP_GET_SETTINGS_V21 (SMARTAUDIO_CMD_GET_SETTINGS >> 1) | 0x10
#define SMARTAUDIO_RSP_SET_POWER SMARTAUDIO_CMD_SET_POWER >> 1
#define SMARTAUDIO_RSP_SET_CHANNEL SMARTAUDIO_CMD_SET_CHANNEL >> 1
#define SMARTAUDIO_RSP_SET_FREQUENCY SMARTAUDIO_CMD_SET_FREQUENCY >> 1
#define SMARTAUDIO_RSP_SET_MODE SMARTAUDIO_CMD_SET_MODE >> 1
#define SMARTAUDIO_BANDCHAN_TO_INDEX(band, channel) (band * VTX_MAX_CHANNELS + (channel))
//#define SA_DEBUG
class AP_SmartAudio
{
public:
enum ProtocolVersion {
SMARTAUDIO_SPEC_PROTOCOL_v1 = 0,
SMARTAUDIO_SPEC_PROTOCOL_v2 = 1,
SMARTAUDIO_SPEC_PROTOCOL_v21 = 2
};
struct Settings {
uint8_t version;
uint8_t mode;
uint8_t channel;
uint8_t power;
uint16_t frequency;
uint8_t band;
uint8_t num_power_levels;
uint8_t power_levels[8];
uint8_t power_in_dbm;
uint16_t pitmodeFrequency;
bool userFrequencyMode; // user is setting freq
bool initialized;
};
struct FrameHeader {
uint8_t syncByte;
uint8_t headerByte;
uint8_t command;
uint8_t length;
void init(uint8_t cmd, uint8_t payloadLength) {
syncByte = SMARTAUDIO_SYNC_BYTE;
headerByte= SMARTAUDIO_HEADER_BYTE;
length = payloadLength;
command = cmd;
}
} PACKED;
struct Frame {
FrameHeader header;
uint8_t payload[3];
} PACKED;
struct U8ResponseFrame {
FrameHeader header;
uint8_t payload;
//uint8_t reserved;
//uint8_t crc;
} PACKED;
struct U16ResponseFrame {
FrameHeader header;
uint16_t payload;
//uint8_t reserved;
//uint8_t crc;
} PACKED;
struct SettingsResponseFrame {
FrameHeader header;
uint8_t channel;
uint8_t power;
uint8_t operationMode;
uint16_t frequency;
//uint8_t crc;
} PACKED;
struct SettingsExtendedResponseFrame {
SettingsResponseFrame settings;
uint8_t power_dbm; // current power
uint8_t num_power_levels;
uint8_t power_levels[8]; // first in the list of dbm levels
//uint8_t crc;
} PACKED;
// v 2.1 additions to response frame
//0x0E (current power in dBm) 0x03 (amount of power levels) 0x00(dBm level 1) 0x0E (dBm level 2) 0x14 (dBm level 3) 0x1A (dBm level 4) 0x01(CRC8)
// request packet to be processed
struct Packet {
Frame frame;
uint8_t frame_size;
} PACKED;
AP_SmartAudio();
static AP_SmartAudio *get_singleton(void)
{
return _singleton;
}
/* Do not allow copies */
AP_SmartAudio(const AP_SmartAudio &other) = delete;
AP_SmartAudio &operator=(const AP_SmartAudio&) = delete;
// init threads and lookup for io uart.
bool init();
private:
// serial interface
AP_HAL::UARTDriver *_port; // UART used to send data to SmartAudio VTX
//Pointer to singleton
static AP_SmartAudio* _singleton;
// response buffer length, permit splitted responses
uint8_t _inline_buffer_length;
// expected packet size
uint8_t _packet_size;
ProtocolVersion _protocol_version;
// statistics
uint16_t _packets_sent;
uint16_t _packets_rcvd;
bool _vtx_freq_change_pending; // a vtx command has been issued but not confirmed by a vtx broadcast frame
bool _vtx_power_change_pending;
bool _vtx_options_change_pending;
bool _vtx_changes_pending;
bool _vtx_use_set_freq; // should frequency set by band/channel or frequency
// value for current baud adjust
int32_t _smartbaud = AP_SMARTAUDIO_UART_BAUD;
int32_t _smartbaud_direction = 1;
// hw vtx state control with 2 elements array use methods _push _peek
uint8_t _vtx_state_idx;
Settings _vtx_states_buffer[2];
Settings *_vtx_current_state;
// ready to go
volatile bool _initialised;
// RingBuffer to store outgoing request.
ObjectBuffer<Packet> requests_queue{SMARTAUDIO_BUFFER_CAPACITY};
// time the last_request is process
uint32_t _last_request_sent_ms;
// loops is waiting a response after a request
bool _is_waiting_response;
#ifdef SA_DEBUG
// utility method for debugging.
void print_bytes_to_hex_string(const char* msg, const uint8_t buf[], uint8_t length);
#endif
void print_settings(const Settings* settings);
void update_vtx_params();
void update_vtx_settings(const Settings& settings);
bool ignore_crc() const { return AP::vtx().has_option(AP_VideoTX::VideoOptions::VTX_SA_IGNORE_CRC); }
// looping over requests
void loop();
// send a frame over the wire
void send_request(const Frame& requestFrame, uint8_t size);
// receive a frame response over the wire
bool read_response(uint8_t *response_buffer);
// parses the response and updates the vtx settings
bool parse_frame_response(const uint8_t *buffer);
bool parse_response_buffer(const uint8_t *buffer);
// get last reading from the fifo queue
bool get_readings(AP_VideoTX *vtx_dest);
// command functions
// request settings
void request_settings();
// change the mode
void set_operation_mode(uint8_t mode);
// change the frequency
void set_frequency(uint16_t frequency, bool isPitModeFreq);
// change the channel
void set_channel(uint8_t chan);
// get the pitmode frequency
void request_pit_mode_frequency();
// set the power
void set_power(uint16_t power_mw, uint16_t dbm);
// set the power using power_level, spec versions 1 and 2 or dbm value for spec version 2.1
void set_power(uint8_t power_level);
// set the band and channel
void set_band_channel(const uint8_t band, const uint8_t channel);
// command functions
void push_command_only_frame(uint8_t command);
void push_uint8_command_frame(uint8_t command, uint8_t data);
void push_uint16_command_frame(uint8_t command, uint16_t data);
static void unpack_frequency(Settings *settings, const uint16_t frequency);
static void unpack_settings(Settings *settings, const SettingsResponseFrame *frame);
static void unpack_settings(Settings *settings, const SettingsExtendedResponseFrame *frame);
// change baud automatically when request-response fails many times
void update_baud_rate();
void set_configuration_pending(bool pending) { _vtx_changes_pending = pending; }
bool is_configuration_pending(){ return _vtx_changes_pending;}
};
#endif