/* 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 . */ /* GHST protocol decoder based on betaflight implementation Code by Andy Piper */ #include "AP_RCProtocol_config.h" #if AP_RCPROTOCOL_GHST_ENABLED #define CRSF_BAUDRATE 416666U #include "AP_RCProtocol.h" #include "AP_RCProtocol_GHST.h" #include #include #include #include #include #include /* * GHST protocol * * GHST protocol uses a single wire half duplex uart connection. * * 420000 baud * not inverted * 8 Bit * 1 Stop bit * Big endian * Max frame size is 14 bytes * * Every frame has the structure: * * * Device address: (uint8_t) * Frame length: length in bytes including Type (uint8_t) * Type: (uint8_t) * CRC: (uint8_t) * */ extern const AP_HAL::HAL& hal; //#define GHST_DEBUG //#define GHST_DEBUG_CHARS #ifdef GHST_DEBUG # define debug(fmt, args...) hal.console->printf("GHST: " fmt "\n", ##args) static const char* get_frame_type(uint8_t byte, uint8_t subtype = 0) { switch(byte) { case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_5TO8: case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_12_5TO8: return "RC5_8"; case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_9TO12: case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_12_9TO12: return "RC9_12"; case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_13TO16: case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_HS4_12_13TO16: return "RC13_16"; case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_RSSI: case AP_RCProtocol_GHST::GHST_UL_RC_CHANS_12_RSSI: return "RSSI"; case AP_RCProtocol_GHST::GHST_UL_RC_VTX_CTRL: return "VTX_CTRL"; case AP_RCProtocol_GHST::GHST_UL_VTX_SETUP: return "VTX_SETUP"; } return "UNKNOWN"; } #else # define debug(fmt, args...) do {} while(0) #endif #define GHST_MAX_FRAME_TIME_US 500U // 14 bytes @ 420k = ~450us #define GHST_FRAME_TIMEOUT_US 10000U // 10ms to account for scheduling delays #define GHST_INTER_FRAME_TIME_US 2000U // At fastest, frames are sent by the transmitter every 2 ms, 500 Hz #define GHST_HEADER_TYPE_LEN (GHST_HEADER_LEN + 1) // header length including type const uint16_t AP_RCProtocol_GHST::RF_MODE_RATES[RFMode::RF_MODE_MAX_MODES] = { 55, 160, 250, 19, 250, 500, 150, 250, }; AP_RCProtocol_GHST* AP_RCProtocol_GHST::_singleton; AP_RCProtocol_GHST::AP_RCProtocol_GHST(AP_RCProtocol &_frontend) : AP_RCProtocol_Backend(_frontend) { #if !APM_BUILD_TYPE(APM_BUILD_UNKNOWN) if (_singleton != nullptr) { AP_HAL::panic("Duplicate GHST handler"); } _singleton = this; #else if (_singleton == nullptr) { _singleton = this; } #endif } AP_RCProtocol_GHST::~AP_RCProtocol_GHST() { _singleton = nullptr; } // get the protocol string const char* AP_RCProtocol_GHST::get_protocol_string() const { return "GHST"; } // return the link rate as defined by the LinkStatistics uint16_t AP_RCProtocol_GHST::get_link_rate() const { return RF_MODE_RATES[_link_status.rf_mode - GHST_RF_MODE_NORMAL]; } void AP_RCProtocol_GHST::_process_byte(uint32_t timestamp_us, uint8_t byte) { //debug("process_byte(0x%x)", byte); // we took too long decoding, start again - the RX will only send complete frames so this is unlikely to fail, // however thread scheduling can introduce longer delays even when the data has been received if (_frame_ofs > 0 && (timestamp_us - _start_frame_time_us) > GHST_FRAME_TIMEOUT_US) { _frame_ofs = 0; } // overflow check if (_frame_ofs >= GHST_FRAMELEN_MAX) { _frame_ofs = 0; } // start of a new frame if (_frame_ofs == 0) { _start_frame_time_us = timestamp_us; } add_to_buffer(_frame_ofs++, byte); // need a header to get the length if (_frame_ofs < GHST_HEADER_TYPE_LEN) { return; } // parse the length if (_frame_ofs == GHST_HEADER_TYPE_LEN) { _frame_crc = crc8_dvb_s2(0, _frame.type); // check for garbage frame if (_frame.length > GHST_FRAME_PAYLOAD_MAX) { _frame_ofs = 0; } return; } // update crc if (_frame_ofs < _frame.length + GHST_HEADER_LEN) { _frame_crc = crc8_dvb_s2(_frame_crc, byte); } // overflow check if (_frame_ofs > _frame.length + GHST_HEADER_LEN) { _frame_ofs = 0; return; } // decode whatever we got and expect if (_frame_ofs == _frame.length + GHST_HEADER_LEN) { log_data(AP_RCProtocol::GHST, timestamp_us, (const uint8_t*)&_frame, _frame_ofs - GHST_HEADER_LEN); // we consumed the partial frame, reset _frame_ofs = 0; // bad CRC (payload start is +1 from frame start, so need to subtract that from frame length to get index) if (_frame_crc != _frame.payload[_frame.length - 2]) { return; } _last_frame_time_us = _last_rx_frame_time_us = timestamp_us; // decode here if (decode_ghost_packet()) { _last_tx_frame_time_us = timestamp_us; // we have received a frame from the transmitter add_input(MAX_CHANNELS, _channels, false, _link_status.rssi, _link_status.link_quality); } } } void AP_RCProtocol_GHST::update(void) { } // write out a frame of any type bool AP_RCProtocol_GHST::write_frame(Frame* frame) { AP_HAL::UARTDriver *uart = get_current_UART(); if (!uart) { return false; } // check that we haven't been too slow in responding to the new UART data. If we respond too late then we will // corrupt the next incoming control frame. incoming packets at max 126bits @500Hz @420k baud gives total budget of 2ms // per packet of which we need 300us to receive a packet. outgoing packets are 126bits which require 300us to send // leaving at most 1.4ms of delay that can be tolerated uint64_t tend = uart->receive_time_constraint_us(1); uint64_t now = AP_HAL::micros64(); uint64_t tdelay = now - tend; if (tdelay > 1000) { // we've been too slow in responding return false; } // calculate crc uint8_t crc = crc8_dvb_s2(0, frame->type); for (uint8_t i = 0; i < frame->length - 2; i++) { crc = crc8_dvb_s2(crc, frame->payload[i]); } frame->payload[frame->length - 2] = crc; uart->write((uint8_t*)frame, frame->length + 2); uart->flush(); #ifdef GHST_DEBUG hal.console->printf("GHST: writing %s:", get_frame_type(frame->type, frame->payload[0])); for (uint8_t i = 0; i < frame->length + 2; i++) { uint8_t val = ((uint8_t*)frame)[i]; #ifdef GHST_DEBUG_CHARS if (val >= 32 && val <= 126) { hal.console->printf(" 0x%x '%c'", val, (char)val); } else { #endif hal.console->printf(" 0x%x", val); #ifdef GHST_DEBUG_CHARS } #endif } hal.console->printf("\n"); #endif return true; } bool AP_RCProtocol_GHST::decode_ghost_packet() { #ifdef GHST_DEBUG hal.console->printf("GHST: received %s:", get_frame_type(_frame.type)); uint8_t* fptr = (uint8_t*)&_frame; for (uint8_t i = 0; i < _frame.length + 2; i++) { #ifdef GHST_DEBUG_CHARS if (fptr[i] >= 32 && fptr[i] <= 126) { hal.console->printf(" 0x%x '%c'", fptr[i], (char)fptr[i]); } else { #endif hal.console->printf(" 0x%x", fptr[i]); #ifdef GHST_DEBUG_CHARS } #endif } hal.console->printf("\n"); #endif const RadioFrame* radio_frame = (const RadioFrame*)(&_frame.payload); const Channels12Bit_4Chan* channels = &(radio_frame->channels); const uint8_t* lowres_channels = radio_frame->lowres_channels; // Scaling from Betaflight // Scaling 12bit channels (8bit channels in brackets) // OpenTx RC PWM (BF) // min -1024 0( 0) 988us // ctr 0 2048(128) 1500us // max 1024 4096(256) 2012us // // Scaling legacy (nearly 10bit) // derived from original SBus scaling, with slight correction for offset // now symmetrical around OpenTx 0 value // scaling is: // OpenTx RC PWM (BF) // min -1024 172( 22) 988us // ctr 0 992(124) 1500us // max 1024 1811(226) 2012us #define CHANNEL_RESCALE(x) (((5 * x) >> 2) - 430) #define CHANNEL_SCALE(x) (int32_t(x) / 4 + 988) #define CHANNEL_SCALE_LEGACY(x) CHANNEL_SCALE(CHANNEL_RESCALE(x)) // legacy scaling if (_frame.type >= GHST_UL_RC_CHANS_HS4_5TO8 && _frame.type <= GHST_UL_RC_CHANS_RSSI) { _channels[0] = CHANNEL_SCALE_LEGACY(channels->ch0); _channels[1] = CHANNEL_SCALE_LEGACY(channels->ch1); _channels[2] = CHANNEL_SCALE_LEGACY(channels->ch2); _channels[3] = CHANNEL_SCALE_LEGACY(channels->ch3); } else { _channels[0] = CHANNEL_SCALE(channels->ch0); _channels[1] = CHANNEL_SCALE(channels->ch1); _channels[2] = CHANNEL_SCALE(channels->ch2); _channels[3] = CHANNEL_SCALE(channels->ch3); } #define CHANNEL_LR_RESCALE(x) (5 * x - 108) #define CHANNEL_LR_SCALE(x) (int32_t(x) * 2 + 988) #define CHANNEL_LR_SCALE_LEGACY(x) (CHANNEL_LR_RESCALE(x) + 988) switch (_frame.type) { case GHST_UL_RC_CHANS_HS4_5TO8: case GHST_UL_RC_CHANS_HS4_9TO12: case GHST_UL_RC_CHANS_HS4_13TO16: { uint8_t offset = (_frame.type - GHST_UL_RC_CHANS_HS4_5TO8 + 1) * 4; _channels[offset++] = CHANNEL_LR_SCALE_LEGACY(lowres_channels[0]); _channels[offset++] = CHANNEL_LR_SCALE_LEGACY(lowres_channels[1]); _channels[offset++] = CHANNEL_LR_SCALE_LEGACY(lowres_channels[2]); _channels[offset++] = CHANNEL_LR_SCALE_LEGACY(lowres_channels[3]); break; } case GHST_UL_RC_CHANS_HS4_12_5TO8: case GHST_UL_RC_CHANS_HS4_12_9TO12: case GHST_UL_RC_CHANS_HS4_12_13TO16: { uint8_t offset = (_frame.type - GHST_UL_RC_CHANS_HS4_12_5TO8 + 1) * 4; _channels[offset++] = CHANNEL_LR_SCALE(lowres_channels[0]); _channels[offset++] = CHANNEL_LR_SCALE(lowres_channels[1]); _channels[offset++] = CHANNEL_LR_SCALE(lowres_channels[2]); _channels[offset++] = CHANNEL_LR_SCALE(lowres_channels[3]); break; } case GHST_UL_RC_CHANS_RSSI: case GHST_UL_RC_CHANS_12_RSSI: process_link_stats_frame((uint8_t*)&_frame.payload); break; default: break; } #if AP_GHST_TELEM_ENABLED if (AP_GHST_Telem::process_frame(FrameType(_frame.type), (uint8_t*)&_frame.payload)) { process_telemetry(); } #endif return true; } // send out telemetry bool AP_RCProtocol_GHST::process_telemetry(bool check_constraint) { AP_HAL::UARTDriver *uart = get_current_UART(); if (!uart) { return false; } if (!telem_available) { #if AP_GHST_TELEM_ENABLED && !APM_BUILD_TYPE(APM_BUILD_iofirmware) if (AP_GHST_Telem::get_telem_data(&_telemetry_frame, is_tx_active())) { telem_available = true; } else { return false; } #else return false; #endif } if (!write_frame(&_telemetry_frame)) { return false; } // get fresh telem_data in the next call telem_available = false; return true; } // process link statistics to get RSSI void AP_RCProtocol_GHST::process_link_stats_frame(const void* data) { const LinkStatisticsFrame* link = (const LinkStatisticsFrame*)data; uint8_t rssi_dbm; rssi_dbm = link->rssi_dbm; _link_status.link_quality = link->link_quality; if (_use_lq_for_rssi) { _link_status.rssi = derive_scaled_lq_value(link->link_quality); } else{ // AP rssi: -1 for unknown, 0 for no link, 255 for maximum link if (rssi_dbm < 50) { _link_status.rssi = 255; } else if (rssi_dbm > 120) { _link_status.rssi = 0; } else { // this is an approximation recommended by Remo from TBS _link_status.rssi = int16_t(roundf((1.0f - (rssi_dbm - 50.0f) / 70.0f) * 255.0f)); } } _link_status.rf_mode = link->protocol; } bool AP_RCProtocol_GHST::is_telemetry_supported() const { return _link_status.rf_mode == AP_RCProtocol_GHST::GHST_RF_MODE_NORMAL || _link_status.rf_mode == AP_RCProtocol_GHST::GHST_RF_MODE_RACE || _link_status.rf_mode == AP_RCProtocol_GHST::GHST_RF_MODE_LR || _link_status.rf_mode == AP_RCProtocol_GHST::GHST_RF_MODE_RACE250; } // process a byte provided by a uart void AP_RCProtocol_GHST::process_byte(uint8_t byte, uint32_t baudrate) { // reject RC data if we have been configured for standalone mode if (baudrate != CRSF_BAUDRATE && baudrate != GHST_BAUDRATE) { return; } _process_byte(AP_HAL::micros(), byte); } // change the bootstrap baud rate to ELRS standard if configured void AP_RCProtocol_GHST::process_handshake(uint32_t baudrate) { AP_HAL::UARTDriver *uart = get_current_UART(); // only change the baudrate if we are bootstrapping CRSF if (uart == nullptr || baudrate != CRSF_BAUDRATE || baudrate == GHST_BAUDRATE || uart->get_baud_rate() == GHST_BAUDRATE || (get_rc_protocols_mask() & ((1U<<(uint8_t(AP_RCProtocol::GHST)+1))+1)) == 0) { return; } uart->begin(GHST_BAUDRATE); } //returns uplink link quality on 0-255 scale int16_t AP_RCProtocol_GHST::derive_scaled_lq_value(uint8_t uplink_lq) { return int16_t(roundf(constrain_float(uplink_lq*2.5f,0,255))); } namespace AP { AP_RCProtocol_GHST* ghost() { return AP_RCProtocol_GHST::get_singleton(); } }; #endif // AP_RCPROTOCOL_GHST_ENABLED