2020-09-28 01:31:38 -03:00
# include "AP_Frsky_SPort_Passthrough.h"
2020-09-28 07:34:33 -03:00
# include <AP_AHRS/AP_AHRS.h>
# include <AP_BattMonitor/AP_BattMonitor.h>
# include <AP_GPS/AP_GPS.h>
# include <AP_HAL/utility/RingBuffer.h>
# include <AP_InertialSensor/AP_InertialSensor.h>
# include <AP_Notify/AP_Notify.h>
# include <AP_RangeFinder/AP_RangeFinder.h>
# include <GCS_MAVLink/GCS.h>
2020-09-28 18:53:17 -03:00
/*
2020-09-28 07:34:33 -03:00
for FrSky SPort Passthrough
*/
// data bits preparation
// for parameter data
# define PARAM_ID_OFFSET 24
# define PARAM_VALUE_LIMIT 0xFFFFFF
// for gps status data
# define GPS_SATS_LIMIT 0xF
# define GPS_STATUS_LIMIT 0x3
# define GPS_STATUS_OFFSET 4
# define GPS_HDOP_OFFSET 6
# define GPS_ADVSTATUS_OFFSET 14
# define GPS_ALTMSL_OFFSET 22
// for battery data
# define BATT_VOLTAGE_LIMIT 0x1FF
# define BATT_CURRENT_OFFSET 9
# define BATT_TOTALMAH_LIMIT 0x7FFF
# define BATT_TOTALMAH_OFFSET 17
// for autopilot status data
# define AP_CONTROL_MODE_LIMIT 0x1F
# define AP_SIMPLE_OFFSET 5
# define AP_SSIMPLE_OFFSET 6
# define AP_FLYING_OFFSET 7
# define AP_ARMED_OFFSET 8
# define AP_BATT_FS_OFFSET 9
# define AP_EKF_FS_OFFSET 10
# define AP_IMU_TEMP_MIN 19.0f
# define AP_IMU_TEMP_MAX 82.0f
# define AP_IMU_TEMP_OFFSET 26
// for home position related data
# define HOME_ALT_OFFSET 12
# define HOME_BEARING_LIMIT 0x7F
# define HOME_BEARING_OFFSET 25
// for velocity and yaw data
# define VELANDYAW_XYVEL_OFFSET 9
# define VELANDYAW_YAW_LIMIT 0x7FF
# define VELANDYAW_YAW_OFFSET 17
// for attitude (roll, pitch) and range data
# define ATTIANDRNG_ROLL_LIMIT 0x7FF
# define ATTIANDRNG_PITCH_LIMIT 0x3FF
# define ATTIANDRNG_PITCH_OFFSET 11
# define ATTIANDRNG_RNGFND_OFFSET 21
2020-09-28 01:31:38 -03:00
bool AP_Frsky_SPort_Passthrough : : init ( )
{
if ( ! AP_RCTelemetry : : init ( ) ) {
return false ;
}
return AP_Frsky_SPort : : init ( ) ;
}
bool AP_Frsky_SPort_Passthrough : : init_serial_port ( )
{
if ( _use_external_data ) {
return true ;
}
return AP_Frsky_SPort : : init_serial_port ( ) ;
}
void AP_Frsky_SPort_Passthrough : : send_sport_frame ( uint8_t frame , uint16_t appid , uint32_t data )
{
if ( _use_external_data ) {
external_data . frame = frame ;
external_data . appid = appid ;
external_data . data = data ;
external_data . pending = true ;
return ;
}
return AP_Frsky_SPort : : send_sport_frame ( frame , appid , data ) ;
}
2020-09-28 07:34:33 -03:00
/*
setup ready for passthrough telem
*/
void AP_Frsky_SPort_Passthrough : : setup_wfq_scheduler ( void )
{
// initialize packet weights for the WFQ scheduler
// priority[i] = 1/_scheduler.packet_weight[i]
// rate[i] = LinkRate * ( priority[i] / (sum(priority[1-n])) )
set_scheduler_entry ( TEXT , 35 , 28 ) ; // 0x5000 status text (dynamic)
set_scheduler_entry ( ATTITUDE , 50 , 38 ) ; // 0x5006 Attitude and range (dynamic)
set_scheduler_entry ( GPS_LAT , 550 , 280 ) ; // 0x800 GPS lat
set_scheduler_entry ( GPS_LON , 550 , 280 ) ; // 0x800 GPS lon
set_scheduler_entry ( VEL_YAW , 400 , 250 ) ; // 0x5005 Vel and Yaw
set_scheduler_entry ( AP_STATUS , 700 , 500 ) ; // 0x5001 AP status
set_scheduler_entry ( GPS_STATUS , 700 , 500 ) ; // 0x5002 GPS status
set_scheduler_entry ( HOME , 400 , 500 ) ; // 0x5004 Home
set_scheduler_entry ( BATT_2 , 1300 , 500 ) ; // 0x5008 Battery 2 status
set_scheduler_entry ( BATT_1 , 1300 , 500 ) ; // 0x5008 Battery 1 status
set_scheduler_entry ( PARAM , 1700 , 1000 ) ; // 0x5007 parameters
}
void AP_Frsky_SPort_Passthrough : : adjust_packet_weight ( bool queue_empty )
{
if ( ! queue_empty ) {
_scheduler . packet_weight [ TEXT ] = 45 ; // messages
_scheduler . packet_weight [ ATTITUDE ] = 80 ; // attitude
} else {
_scheduler . packet_weight [ TEXT ] = 5000 ; // messages
_scheduler . packet_weight [ ATTITUDE ] = 45 ; // attitude
}
}
// WFQ scheduler
bool AP_Frsky_SPort_Passthrough : : is_packet_ready ( uint8_t idx , bool queue_empty )
{
bool packet_ready = false ;
switch ( idx ) {
2020-09-28 18:53:17 -03:00
case TEXT :
packet_ready = ! queue_empty ;
break ;
case AP_STATUS :
packet_ready = gcs ( ) . vehicle_initialised ( ) ;
break ;
case BATT_2 :
packet_ready = AP : : battery ( ) . num_instances ( ) > 1 ;
break ;
default :
packet_ready = true ;
break ;
2020-09-28 07:34:33 -03:00
}
return packet_ready ;
}
/*
* WFQ scheduler
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
void AP_Frsky_SPort_Passthrough : : process_packet ( uint8_t idx )
{
// send packet
switch ( idx ) {
2020-09-28 18:53:17 -03:00
case TEXT : // 0x5000 status text
if ( get_next_msg_chunk ( ) ) {
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID , _msg_chunk . chunk ) ;
}
break ;
case ATTITUDE : // 0x5006 Attitude and range
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 6 , calc_attiandrng ( ) ) ;
break ;
case GPS_LAT : // 0x800 GPS lat
// sample both lat and lon at the same time
2020-09-30 22:24:10 -03:00
send_sport_frame ( SPORT_DATA_FRAME , GPS_LONG_LATI_FIRST_ID , calc_gps_latlng ( _passthrough . send_latitude ) ) ; // gps latitude or longitude
_passthrough . gps_lng_sample = calc_gps_latlng ( _passthrough . send_latitude ) ;
2020-09-28 18:53:17 -03:00
// force the scheduler to select GPS lon as packet that's been waiting the most
// this guarantees that gps coords are sent at max
// _scheduler.avg_polling_period*number_of_downlink_sensors time separation
_scheduler . packet_timer [ GPS_LON ] = _scheduler . packet_timer [ GPS_LAT ] - 10000 ;
break ;
case GPS_LON : // 0x800 GPS lon
send_sport_frame ( SPORT_DATA_FRAME , GPS_LONG_LATI_FIRST_ID , _passthrough . gps_lng_sample ) ; // gps longitude
break ;
case VEL_YAW : // 0x5005 Vel and Yaw
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 5 , calc_velandyaw ( ) ) ;
break ;
case AP_STATUS : // 0x5001 AP status
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 1 , calc_ap_status ( ) ) ;
break ;
case GPS_STATUS : // 0x5002 GPS Status
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 2 , calc_gps_status ( ) ) ;
break ;
case HOME : // 0x5004 Home
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 4 , calc_home ( ) ) ;
break ;
case BATT_2 : // 0x5008 Battery 2 status
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 8 , calc_batt ( 1 ) ) ;
break ;
case BATT_1 : // 0x5003 Battery 1 status
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 3 , calc_batt ( 0 ) ) ;
break ;
case PARAM : // 0x5007 parameters
send_sport_frame ( SPORT_DATA_FRAME , DIY_FIRST_ID + 7 , calc_param ( ) ) ;
break ;
2020-09-28 07:34:33 -03:00
}
}
/*
* send telemetry data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
void AP_Frsky_SPort_Passthrough : : send ( void )
{
int16_t numc ;
numc = _port - > available ( ) ;
// check if available is negative
if ( numc < 0 ) {
return ;
}
// this is the constant for hub data frame
if ( _port - > txspace ( ) < 19 ) {
return ;
}
// keep only the last two bytes of the data found in the serial buffer, as we shouldn't respond to old poll requests
uint8_t prev_byte = 0 ;
for ( int16_t i = 0 ; i < numc ; i + + ) {
prev_byte = _passthrough . new_byte ;
_passthrough . new_byte = _port - > read ( ) ;
}
if ( prev_byte = = FRAME_HEAD ) {
if ( _passthrough . new_byte = = SENSOR_ID_27 ) { // byte 0x7E is the header of each poll request
run_wfq_scheduler ( ) ;
}
}
}
/*
* grabs one " chunk " ( 4 bytes ) of the queued message to be transmitted
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
bool AP_Frsky_SPort_Passthrough : : get_next_msg_chunk ( void )
{
if ( ! _statustext . available ) {
WITH_SEMAPHORE ( _statustext . sem ) ;
if ( ! _statustext . queue . pop ( _statustext . next ) ) {
return false ;
}
_statustext . available = true ;
}
if ( _msg_chunk . repeats = = 0 ) { // if it's the first time get_next_msg_chunk is called for a given chunk
uint8_t character = 0 ;
_msg_chunk . chunk = 0 ; // clear the 4 bytes of the chunk buffer
for ( int i = 3 ; i > - 1 & & _msg_chunk . char_index < sizeof ( _statustext . next . text ) ; i - - ) {
character = _statustext . next . text [ _msg_chunk . char_index + + ] ;
if ( ! character ) {
break ;
}
_msg_chunk . chunk | = character < < i * 8 ;
}
if ( ! character | | ( _msg_chunk . char_index = = sizeof ( _statustext . next . text ) ) ) { // we've reached the end of the message (string terminated by '\0' or last character of the string has been processed)
_msg_chunk . char_index = 0 ; // reset index to get ready to process the next message
// add severity which is sent as the MSB of the last three bytes of the last chunk (bits 24, 16, and 8) since a character is on 7 bits
_msg_chunk . chunk | = ( _statustext . next . severity & 0x4 ) < < 21 ;
_msg_chunk . chunk | = ( _statustext . next . severity & 0x2 ) < < 14 ;
_msg_chunk . chunk | = ( _statustext . next . severity & 0x1 ) < < 7 ;
}
}
// repeat each message chunk 3 times to ensure transmission
// on slow links reduce the number of duplicate chunks
uint8_t extra_chunks = 2 ;
if ( _scheduler . avg_packet_rate < 20 ) {
// with 3 or more extra frsky sensors on the bus
// send messages only once
extra_chunks = 0 ;
} else if ( _scheduler . avg_packet_rate < 30 ) {
// with 1 or 2 extra frsky sensors on the bus
// send messages twice
extra_chunks = 1 ;
}
2020-09-28 18:53:17 -03:00
2020-09-28 07:34:33 -03:00
if ( _msg_chunk . repeats + + > extra_chunks ) {
_msg_chunk . repeats = 0 ;
if ( _msg_chunk . char_index = = 0 ) {
// we're ready for the next message
_statustext . available = false ;
}
}
return true ;
}
/*
* prepare parameter data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_param ( void )
{
const AP_BattMonitor & _battery = AP : : battery ( ) ;
uint32_t param = 0 ;
uint8_t last_param = AP : : battery ( ) . num_instances ( ) > 1 ? BATT_CAPACITY_2 : BATT_CAPACITY_1 ;
// cycle through paramIDs
if ( _paramID > = last_param ) {
_paramID = 0 ;
}
_paramID + + ;
2020-09-28 18:53:17 -03:00
switch ( _paramID ) {
2020-09-28 07:34:33 -03:00
case FRAME_TYPE :
param = gcs ( ) . frame_type ( ) ; // see MAV_TYPE in Mavlink definition file common.h
break ;
case BATT_FS_VOLTAGE : // was used to send the battery failsafe voltage, lend slot to next param
case BATT_FS_CAPACITY : // was used to send the battery failsafe capacity in mAh, lend slot to next param
case BATT_CAPACITY_1 :
_paramID = 4 ;
param = ( uint32_t ) roundf ( _battery . pack_capacity_mah ( 0 ) ) ; // battery pack capacity in mAh
break ;
case BATT_CAPACITY_2 :
param = ( uint32_t ) roundf ( _battery . pack_capacity_mah ( 1 ) ) ; // battery pack capacity in mAh
break ;
}
//Reserve first 8 bits for param ID, use other 24 bits to store parameter value
param = ( _paramID < < PARAM_ID_OFFSET ) | ( param & PARAM_VALUE_LIMIT ) ;
return param ;
}
/*
* prepare gps status data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_gps_status ( void )
{
const AP_GPS & gps = AP : : gps ( ) ;
// number of GPS satellites visible (limit to 15 (0xF) since the value is stored on 4 bits)
2020-09-30 22:24:10 -03:00
uint32_t gps_status = ( gps . num_sats ( ) < GPS_SATS_LIMIT ) ? gps . num_sats ( ) : GPS_SATS_LIMIT ;
2020-09-28 07:34:33 -03:00
// GPS receiver status (limit to 0-3 (0x3) since the value is stored on 2 bits: NO_GPS = 0, NO_FIX = 1, GPS_OK_FIX_2D = 2, GPS_OK_FIX_3D or GPS_OK_FIX_3D_DGPS or GPS_OK_FIX_3D_RTK_FLOAT or GPS_OK_FIX_3D_RTK_FIXED = 3)
gps_status | = ( ( gps . status ( ) < GPS_STATUS_LIMIT ) ? gps . status ( ) : GPS_STATUS_LIMIT ) < < GPS_STATUS_OFFSET ;
// GPS horizontal dilution of precision in dm
2020-09-28 18:53:17 -03:00
gps_status | = prep_number ( roundf ( gps . get_hdop ( ) * 0.1f ) , 2 , 1 ) < < GPS_HDOP_OFFSET ;
2020-09-28 07:34:33 -03:00
// GPS receiver advanced status (0: no advanced fix, 1: GPS_OK_FIX_3D_DGPS, 2: GPS_OK_FIX_3D_RTK_FLOAT, 3: GPS_OK_FIX_3D_RTK_FIXED)
gps_status | = ( ( gps . status ( ) > GPS_STATUS_LIMIT ) ? gps . status ( ) - GPS_STATUS_LIMIT : 0 ) < < GPS_ADVSTATUS_OFFSET ;
// Altitude MSL in dm
const Location & loc = gps . location ( ) ;
2020-09-28 18:53:17 -03:00
gps_status | = prep_number ( roundf ( loc . alt * 0.1f ) , 2 , 2 ) < < GPS_ALTMSL_OFFSET ;
2020-09-28 07:34:33 -03:00
return gps_status ;
}
/*
* prepare battery data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_batt ( uint8_t instance )
{
const AP_BattMonitor & _battery = AP : : battery ( ) ;
float current , consumed_mah ;
if ( ! _battery . current_amps ( current , instance ) ) {
current = 0 ;
}
if ( ! _battery . consumed_mah ( consumed_mah , instance ) ) {
consumed_mah = 0 ;
}
2020-09-28 18:53:17 -03:00
2020-09-28 07:34:33 -03:00
// battery voltage in decivolts, can have up to a 12S battery (4.25Vx12S = 51.0V)
2020-09-30 22:24:10 -03:00
uint32_t batt = ( ( ( uint16_t ) roundf ( _battery . voltage ( instance ) * 10.0f ) ) & BATT_VOLTAGE_LIMIT ) ;
2020-09-28 07:34:33 -03:00
// battery current draw in deciamps
batt | = prep_number ( roundf ( current * 10.0f ) , 2 , 1 ) < < BATT_CURRENT_OFFSET ;
// battery current drawn since power on in mAh (limit to 32767 (0x7FFF) since value is stored on 15 bits)
batt | = ( ( consumed_mah < BATT_TOTALMAH_LIMIT ) ? ( ( uint16_t ) roundf ( consumed_mah ) & BATT_TOTALMAH_LIMIT ) : BATT_TOTALMAH_LIMIT ) < < BATT_TOTALMAH_OFFSET ;
return batt ;
}
/*
* prepare various autopilot status data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_ap_status ( void )
{
// IMU temperature: offset -19, 0 means temp =< 19°, 63 means temp => 82°
uint8_t imu_temp = ( uint8_t ) roundf ( constrain_float ( AP : : ins ( ) . get_temperature ( 0 ) , AP_IMU_TEMP_MIN , AP_IMU_TEMP_MAX ) - AP_IMU_TEMP_MIN ) ;
// control/flight mode number (limit to 31 (0x1F) since the value is stored on 5 bits)
2020-09-30 22:24:10 -03:00
uint32_t ap_status = ( uint8_t ) ( ( gcs ( ) . custom_mode ( ) + 1 ) & AP_CONTROL_MODE_LIMIT ) ;
2020-09-28 07:34:33 -03:00
// simple/super simple modes flags
ap_status | = ( uint8_t ) ( gcs ( ) . simple_input_active ( ) ) < < AP_SIMPLE_OFFSET ;
ap_status | = ( uint8_t ) ( gcs ( ) . supersimple_input_active ( ) ) < < AP_SSIMPLE_OFFSET ;
// is_flying flag
ap_status | = ( uint8_t ) ( AP_Notify : : flags . flying ) < < AP_FLYING_OFFSET ;
// armed flag
ap_status | = ( uint8_t ) ( AP_Notify : : flags . armed ) < < AP_ARMED_OFFSET ;
// battery failsafe flag
ap_status | = ( uint8_t ) ( AP_Notify : : flags . failsafe_battery ) < < AP_BATT_FS_OFFSET ;
// bad ekf flag
ap_status | = ( uint8_t ) ( AP_Notify : : flags . ekf_bad ) < < AP_EKF_FS_OFFSET ;
// IMU temperature
ap_status | = imu_temp < < AP_IMU_TEMP_OFFSET ;
return ap_status ;
}
/*
* prepare home position related data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_home ( void )
{
uint32_t home = 0 ;
Location loc ;
Location home_loc ;
2020-09-30 22:24:10 -03:00
bool got_position = false ;
2020-09-28 07:34:33 -03:00
float _relative_home_altitude = 0 ;
{
AP_AHRS & _ahrs = AP : : ahrs ( ) ;
WITH_SEMAPHORE ( _ahrs . get_semaphore ( ) ) ;
2020-09-30 22:24:10 -03:00
got_position = _ahrs . get_position ( loc ) ;
2020-09-28 07:34:33 -03:00
home_loc = _ahrs . get_home ( ) ;
}
2020-09-30 22:24:10 -03:00
if ( got_position ) {
2020-09-28 07:34:33 -03:00
// check home_loc is valid
if ( home_loc . lat ! = 0 | | home_loc . lng ! = 0 ) {
// distance between vehicle and home_loc in meters
home = prep_number ( roundf ( home_loc . get_distance ( loc ) ) , 3 , 2 ) ;
// angle from front of vehicle to the direction of home_loc in 3 degree increments (just in case, limit to 127 (0x7F) since the value is stored on 7 bits)
home | = ( ( ( uint8_t ) roundf ( loc . get_bearing_to ( home_loc ) * 0.00333f ) ) & HOME_BEARING_LIMIT ) < < HOME_BEARING_OFFSET ;
}
// altitude between vehicle and home_loc
_relative_home_altitude = loc . alt ;
if ( ! loc . relative_alt ) {
// loc.alt has home altitude added, remove it
_relative_home_altitude - = home_loc . alt ;
}
}
// altitude above home in decimeters
home | = prep_number ( roundf ( _relative_home_altitude * 0.1f ) , 3 , 2 ) < < HOME_ALT_OFFSET ;
return home ;
}
/*
* prepare velocity and yaw data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_velandyaw ( void )
{
float vspd = get_vspeed_ms ( ) ;
// vertical velocity in dm/s
uint32_t velandyaw = prep_number ( roundf ( vspd * 10 ) , 2 , 1 ) ;
AP_AHRS & _ahrs = AP : : ahrs ( ) ;
WITH_SEMAPHORE ( _ahrs . get_semaphore ( ) ) ;
// horizontal velocity in dm/s (use airspeed if available and enabled - even if not used - otherwise use groundspeed)
const AP_Airspeed * aspeed = _ahrs . get_airspeed ( ) ;
2020-09-28 18:53:17 -03:00
if ( aspeed & & aspeed - > enabled ( ) ) {
2020-09-28 07:34:33 -03:00
velandyaw | = prep_number ( roundf ( aspeed - > get_airspeed ( ) * 10 ) , 2 , 1 ) < < VELANDYAW_XYVEL_OFFSET ;
} else { // otherwise send groundspeed estimate from ahrs
velandyaw | = prep_number ( roundf ( _ahrs . groundspeed ( ) * 10 ) , 2 , 1 ) < < VELANDYAW_XYVEL_OFFSET ;
}
// yaw from [0;36000] centidegrees to .2 degree increments [0;1800] (just in case, limit to 2047 (0x7FF) since the value is stored on 11 bits)
velandyaw | = ( ( uint16_t ) roundf ( _ahrs . yaw_sensor * 0.05f ) & VELANDYAW_YAW_LIMIT ) < < VELANDYAW_YAW_OFFSET ;
return velandyaw ;
}
/*
* prepare attitude ( roll , pitch ) and range data
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint32_t AP_Frsky_SPort_Passthrough : : calc_attiandrng ( void )
{
const RangeFinder * _rng = RangeFinder : : get_singleton ( ) ;
AP_AHRS & _ahrs = AP : : ahrs ( ) ;
// roll from [-18000;18000] centidegrees to unsigned .2 degree increments [0;1800] (just in case, limit to 2047 (0x7FF) since the value is stored on 11 bits)
2020-09-30 22:24:10 -03:00
uint32_t attiandrng = ( ( uint16_t ) roundf ( ( _ahrs . roll_sensor + 18000 ) * 0.05f ) & ATTIANDRNG_ROLL_LIMIT ) ;
2020-09-28 07:34:33 -03:00
// pitch from [-9000;9000] centidegrees to unsigned .2 degree increments [0;900] (just in case, limit to 1023 (0x3FF) since the value is stored on 10 bits)
attiandrng | = ( ( uint16_t ) roundf ( ( _ahrs . pitch_sensor + 9000 ) * 0.05f ) & ATTIANDRNG_PITCH_LIMIT ) < < ATTIANDRNG_PITCH_OFFSET ;
// rangefinder measurement in cm
attiandrng | = prep_number ( _rng ? _rng - > distance_cm_orient ( ROTATION_PITCH_270 ) : 0 , 3 , 1 ) < < ATTIANDRNG_RNGFND_OFFSET ;
return attiandrng ;
}
/*
fetch Sport data for an external transport , such as FPort
*/
bool AP_Frsky_SPort_Passthrough : : get_telem_data ( uint8_t & frame , uint16_t & appid , uint32_t & data )
{
run_wfq_scheduler ( ) ;
if ( ! external_data . pending ) {
return false ;
}
frame = external_data . frame ;
appid = external_data . appid ;
data = external_data . data ;
external_data . pending = false ;
return true ;
}
/*
* prepare value for transmission through FrSky link
* for FrSky SPort Passthrough ( OpenTX ) protocol ( X - receivers )
*/
uint16_t AP_Frsky_SPort_Passthrough : : prep_number ( int32_t number , uint8_t digits , uint8_t power )
{
uint16_t res = 0 ;
uint32_t abs_number = abs ( number ) ;
if ( ( digits = = 2 ) & & ( power = = 1 ) ) { // number encoded on 8 bits: 7 bits for digits + 1 for 10^power
if ( abs_number < 100 ) {
res = abs_number < < 1 ;
} else if ( abs_number < 1270 ) {
res = ( ( uint8_t ) roundf ( abs_number * 0.1f ) < < 1 ) | 0x1 ;
} else { // transmit max possible value (0x7F x 10^1 = 1270)
res = 0xFF ;
}
if ( number < 0 ) { // if number is negative, add sign bit in front
res | = 0x1 < < 8 ;
}
} else if ( ( digits = = 2 ) & & ( power = = 2 ) ) { // number encoded on 9 bits: 7 bits for digits + 2 for 10^power
if ( abs_number < 100 ) {
res = abs_number < < 2 ;
} else if ( abs_number < 1000 ) {
res = ( ( uint8_t ) roundf ( abs_number * 0.1f ) < < 2 ) | 0x1 ;
} else if ( abs_number < 10000 ) {
res = ( ( uint8_t ) roundf ( abs_number * 0.01f ) < < 2 ) | 0x2 ;
} else if ( abs_number < 127000 ) {
res = ( ( uint8_t ) roundf ( abs_number * 0.001f ) < < 2 ) | 0x3 ;
} else { // transmit max possible value (0x7F x 10^3 = 127000)
res = 0x1FF ;
}
if ( number < 0 ) { // if number is negative, add sign bit in front
res | = 0x1 < < 9 ;
}
} else if ( ( digits = = 3 ) & & ( power = = 1 ) ) { // number encoded on 11 bits: 10 bits for digits + 1 for 10^power
if ( abs_number < 1000 ) {
res = abs_number < < 1 ;
} else if ( abs_number < 10240 ) {
res = ( ( uint16_t ) roundf ( abs_number * 0.1f ) < < 1 ) | 0x1 ;
} else { // transmit max possible value (0x3FF x 10^1 = 10240)
res = 0x7FF ;
}
if ( number < 0 ) { // if number is negative, add sign bit in front
res | = 0x1 < < 11 ;
}
} else if ( ( digits = = 3 ) & & ( power = = 2 ) ) { // number encoded on 12 bits: 10 bits for digits + 2 for 10^power
if ( abs_number < 1000 ) {
res = abs_number < < 2 ;
} else if ( abs_number < 10000 ) {
res = ( ( uint16_t ) roundf ( abs_number * 0.1f ) < < 2 ) | 0x1 ;
} else if ( abs_number < 100000 ) {
res = ( ( uint16_t ) roundf ( abs_number * 0.01f ) < < 2 ) | 0x2 ;
} else if ( abs_number < 1024000 ) {
res = ( ( uint16_t ) roundf ( abs_number * 0.001f ) < < 2 ) | 0x3 ;
} else { // transmit max possible value (0x3FF x 10^3 = 127000)
res = 0xFFF ;
}
if ( number < 0 ) { // if number is negative, add sign bit in front
res | = 0x1 < < 12 ;
}
}
return res ;
}