LCOV - code coverage report
Current view: top level - AP_Mount - AP_Mount_Topotek.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 13 14 92.9 %
Date: 2026-09-09 11:02:08 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :   Topotek gimbal driver using custom serial protocol
       3             : 
       4             :   Packet format (courtesy of Topotek's SDK document)
       5             : 
       6             :   -------------------------------------------------------------------------------------------
       7             :   Field                 Index   Bytes       Description
       8             :   -------------------------------------------------------------------------------------------
       9             :   Frame Header          0       3           type of command
      10             :   Address Bit           3       2           the source address comes first, and the destination address comes last
      11             :   Data_Len              5       1           data length
      12             :   Control Bit           6       1           r -> query w -> setup and control
      13             :   Identification Bit    7       3           identification function
      14             :   Data                  10      Data_Len
      15             :   Check Bit                     2           the frame header is converted to HEX before reaching the check bit,
      16             :                                             the sum is done, and the result is converted to ASC-II. Two bytes, the high one first
      17             :  */
      18             : 
      19             : #pragma once
      20             : 
      21             : #include "AP_Mount_config.h"
      22             : 
      23             : #if HAL_MOUNT_TOPOTEK_ENABLED
      24             : 
      25             : #include "AP_Mount_Backend_Serial.h"
      26             : #include <AP_HAL/AP_HAL.h>
      27             : #include <AP_Math/AP_Math.h>
      28             : #include <AP_Common/AP_Common.h>
      29             : 
      30             : #define AP_MOUNT_TOPOTEK_PACKETLEN_MAX              36          // maximum number of bytes in a packet sent to or received from the gimbal
      31             : #define AP_MOUNT_RECV_GIMBAL_CMD_CATEGORIES_NUM     7           // parse the number of gimbal command types
      32             : 
      33             : class AP_Mount_Topotek : public AP_Mount_Backend_Serial
      34             : {
      35             : 
      36             : public:
      37             :     // Constructor
      38             :     using AP_Mount_Backend_Serial::AP_Mount_Backend_Serial;
      39             : 
      40             :     // Do not allow copies
      41             :     CLASS_NO_COPY(AP_Mount_Topotek);
      42             : 
      43             :     // update mount position - should be called periodically
      44             :     void update() override;
      45             : 
      46             :     // return true if healthy
      47             :     bool healthy() const override;
      48             : 
      49             :     // has_pan_control - returns true if this mount can control its pan (required for multicopters)
      50           7 :     bool has_pan_control() const override { return yaw_range_valid(); };
      51             : 
      52             :     //
      53             :     // camera controls for gimbals
      54             :     //
      55             : 
      56             :     // take a picture.  returns true on success
      57             :     bool take_picture() override;
      58             : 
      59             :     // start or stop video recording
      60             :     // set start_recording = true to start record, false to stop recording
      61             :     bool record_video(bool start_recording) override;
      62             : 
      63             :     // set zoom specified as a rate
      64             :     bool set_zoom(ZoomType zoom_type, float zoom_value) override;
      65             : 
      66             :     // set focus specified as rate or auto
      67             :     // focus in = -1, focus hold = 0, focus out = 1
      68             :     SetFocusResult set_focus(FocusType focus_type, float focus_value) override;
      69             : 
      70             :     // set tracking to none, point or rectangle (see TrackingType enum)
      71             :     // if POINT only p1 is used, if RECTANGLE then p1 is top-left, p2 is bottom-right
      72             :     // p1,p2 are in range 0 to 1.  0 is left or top, 1 is right or bottom
      73             :     bool set_tracking(TrackingType tracking_type, const Vector2f& p1, const Vector2f& p2) override;
      74             : 
      75             :     // send command to gimbal to cancel tracking (if necessary)
      76             :     // returns true on success, false on failure to send message
      77             :     bool cancel_tracking();
      78             : 
      79             :     // set camera picture-in-picture mode
      80             :     bool set_lens(uint8_t lens) override;
      81             : 
      82             : #if HAL_MOUNT_SET_CAMERA_SOURCE_ENABLED
      83             :     // set_camera_source is functionally the same as set_lens except primary and secondary lenses are specified by type
      84             :     // primary and secondary sources use the AP_Camera::CameraSource enum cast to uint8_t
      85             :     bool set_camera_source(uint8_t primary_source, uint8_t secondary_source) override;
      86             : #endif
      87             : 
      88           2 :     bool has_camera_information() const override { return true; }
      89             :     // return camera vendor name
      90           2 :     void get_camera_vendor_name(char *buf, uint8_t buflen) const override { strncpy(buf, "Topotek", buflen); }
      91             :     // return camera model name
      92           2 :     void get_camera_model_name(char *buf, uint8_t buflen) const override {
      93           2 :         if (!_got_gimbal_model_name) {
      94           0 :             return;
      95             :         }
      96           2 :         strncpy(buf, _model_name, buflen);
      97             :     }
      98             :     // return camera firmware version
      99           2 :     uint32_t get_camera_firmware_version() const override { return _firmware_ver; }
     100             :     // return camera capability flags
     101           2 :     uint32_t get_camera_cap_flags() const override {
     102             :         return (CAMERA_CAP_FLAGS_CAPTURE_VIDEO |
     103             :                 CAMERA_CAP_FLAGS_CAPTURE_IMAGE |
     104             :                 CAMERA_CAP_FLAGS_HAS_BASIC_ZOOM |
     105             :                 CAMERA_CAP_FLAGS_HAS_BASIC_FOCUS |
     106             :                 CAMERA_CAP_FLAGS_HAS_TRACKING_POINT |
     107           2 :                 CAMERA_CAP_FLAGS_HAS_TRACKING_RECTANGLE);
     108             :     }
     109             : 
     110             :     // send camera settings message to GCS
     111             :     void send_camera_settings(mavlink_channel_t chan) const override;
     112             : 
     113             :     //
     114             :     // rangefinder
     115             :     //
     116             : 
     117             :     // get rangefinder distance.  Returns true on success
     118             :     bool get_rangefinder_distance(float& distance_m) const override;
     119             : 
     120             :     // enable/disable rangefinder.  Returns true on success
     121             :     bool set_rangefinder_enable(bool enable) override;
     122             : 
     123             : protected:
     124             : 
     125             :     // get attitude as a quaternion.  returns true on success
     126             :     bool get_attitude_quaternion(Quaternion& att_quat) override;
     127             : 
     128             :     // Topotek can send either rates or angles
     129        4717 :     uint8_t natively_supported_mount_target_types() const override {
     130        4717 :         return NATIVE_ANGLES_AND_RATES_ONLY;
     131             :     };
     132             : 
     133             : private:
     134             : 
     135             :     // header type (fixed or variable length)
     136             :     // first three bytes of packet determined by this value
     137             :     enum class HeaderType : uint8_t {
     138             :         FIXED_LEN = 0x00,       // #TP will be sent
     139             :         VARIABLE_LEN = 0x01,    // #tp will be sent
     140             :     };
     141             : 
     142             :     // address (2nd and 3rd bytes of packet)
     143             :     // first byte is always U followed by one of the other options
     144             :     enum class AddressByte : uint8_t {
     145             :         SYSTEM_AND_IMAGE = 68,      // 'D'
     146             :         AUXILIARY_EQUIPMENT = 69,   // 'E'
     147             :         GIMBAL = 71,                // 'G'
     148             :         LENS = 77,                  // 'M'
     149             :         NETWORK = 80,               // 'P'
     150             :         UART = 85,                  // 'U'
     151             :     };
     152             : 
     153             :     // control byte (read or write)
     154             :     // sent as 7th byte of packet
     155             :     enum class ControlByte : uint8_t {
     156             :         READ = 114,     // 'r'
     157             :         WRITE = 119,    // 'w'
     158             :     };
     159             : 
     160             :     // parsing state
     161             :     enum class ParseState : uint8_t {
     162             :         WAITING_FOR_HEADER1 = 0,// #
     163             :         WAITING_FOR_HEADER2,    // T or t
     164             :         WAITING_FOR_HEADER3,    // P or p
     165             :         WAITING_FOR_ADDR1,      // normally U
     166             :         WAITING_FOR_ADDR2,      // M, D, E, P, G
     167             :         WAITING_FOR_DATALEN,
     168             :         WAITING_FOR_CONTROL,    // r or w
     169             :         WAITING_FOR_ID1,        // e.g. 'G'
     170             :         WAITING_FOR_ID2,        // e.g. 'A'
     171             :         WAITING_FOR_ID3,        // e.g. 'C'
     172             :         WAITING_FOR_DATA,       // normally hex numbers in char form (e.g. '0A')
     173             :         WAITING_FOR_CRC_LOW,
     174             :         WAITING_FOR_CRC_HIGH,
     175             :     };
     176             : 
     177             :     // tracking status
     178             :     enum class TrackingStatus : uint8_t {
     179             :         STOPPED_TRACKING = 0x30,                // not tracking
     180             :         WAITING_FOR_TRACKING = 0x31,            // wait to track command status
     181             :         TRACKING_IN_PROGRESS = 0x32,            // the status is being tracked
     182             :         LENS_UNSUPPORT_TRACK = 0x34,            // this lens does not support tracking
     183             :     };
     184             : 
     185             :     // identifier bytes
     186             :     typedef char Identifier[3];
     187             : 
     188             :     // send text prefix string
     189             :     static const char* send_message_prefix;
     190             : 
     191             :     // reading incoming packets from gimbal and confirm they are of the correct format
     192             :     void read_incoming_packets();
     193             : 
     194             :     // request gimbal attitude
     195             :     void request_gimbal_attitude();
     196             : 
     197             :     // request gimbal memory card information
     198             :     void request_gimbal_sdcard_info();
     199             : 
     200             :     // request gimbal tracking status
     201             :     void request_track_status();
     202             : 
     203             :     // request gimbal version
     204             :     void request_gimbal_version();
     205             : 
     206             :     // request gimbal model name
     207             :     void request_gimbal_model_name();
     208             : 
     209             :     // send angle target in radians to gimbal
     210             :     void send_target_angles(const MountAngleTarget& angle_rad) override;
     211             : 
     212             :     // send rate target in rad/s to gimbal
     213             :     void send_target_rates(const MountRateTarget& rate_rads) override;
     214             : 
     215             :     // send time and date to gimbal
     216             :     bool send_time_to_gimbal();
     217             : 
     218             :     // send GPS-related information to the gimbal
     219             :     bool send_location_info();
     220             : 
     221             :     // attitude information analysis of gimbal
     222             :     void gimbal_angle_analyse();
     223             : 
     224             :     // gimbal video information analysis
     225             :     void gimbal_record_analyse();
     226             : 
     227             :     // information analysis of gimbal storage card
     228             :     void gimbal_sdcard_analyse();
     229             : 
     230             :     // gimbal tracking information analysis
     231             :     void gimbal_track_analyse();
     232             : 
     233             :     // gimbal basic information analysis
     234             :     void gimbal_version_analyse();
     235             : 
     236             :     // gimbal model name message analysis
     237             :     void gimbal_model_name_analyse();
     238             : 
     239             :     // gimbal distance information analysis
     240             :     void gimbal_dist_info_analyse();
     241             : 
     242             :     // return the address to send as the source of our packets.  The
     243             :     // gimbal sends its replies out of the interface named here, so this
     244             :     // must be the interface we are actually connected to.  This is one
     245             :     // of the few places it is legitimate to ask a port how it is
     246             :     // connected; the protocol itself carries that information
     247       14764 :     AddressByte source_address() const {
     248       14764 :         return _uart->is_network_port() ? AddressByte::NETWORK : AddressByte::UART;
     249             :     }
     250             : 
     251             :     // calculate checksum
     252             :     uint8_t calculate_crc(const uint8_t *cmd, uint8_t len) const;
     253             : 
     254             :     // hexadecimal to character conversion
     255             :     uint8_t hex2char(uint8_t data) const;
     256             : 
     257             :     // send a fixed length packet to gimbal
     258             :     // returns true on success, false if serial port initialization failed
     259             :     bool send_fixedlen_packet(AddressByte address, const Identifier id, bool write, uint8_t value);
     260             : 
     261             :     // send a variable length packet to gimbal
     262             :     // returns true on success, false if serial port initialization failed
     263             :     bool send_variablelen_packet(HeaderType header, AddressByte address, const Identifier id, bool write, const uint8_t* databuff, uint8_t databuff_len);
     264             : 
     265             :     // set gimbal's lock vs follow mode
     266             :     // lock should be true if gimbal should maintain an earth-frame target
     267             :     // lock is false to follow / maintain a body-frame target
     268             :     bool set_gimbal_lock(bool lock);
     269             : 
     270             :     // members
     271             :     bool _recording;                                            // recording status (received from gimbal)
     272             :     bool _is_tracking;                                          // whether to enable the tracking state
     273             :     TrackingStatus _last_tracking_state = TrackingStatus::STOPPED_TRACKING; // last tracking state received from gimbal
     274             :     uint8_t _last_mode;                                         // mode during latest update, used to detect mode changes and cancel tracking
     275             :     bool _sdcard_status;                                        // memory card status (received from gimbal)
     276             :     bool _last_lock;                                            // last lock mode sent to gimbal
     277             :     bool _got_gimbal_version;                                   // true if gimbal's version has been received
     278             :     bool _got_gimbal_model_name;                                // true if gimbal's model name has been received
     279             :     bool _last_zoom_stop;                                       // true if zoom has been stopped (used to re-send in order to handle lost packets)
     280             :     bool _last_focus_stop;                                      // true if focus has been stopped (used to re-sent in order to handle lost packets)
     281             :     char _model_name[16];                                       // gimbal model name, always null-terminated
     282             :     uint8_t _sent_time_count;                                   // count of current time messages sent to gimbal
     283             :     uint32_t _firmware_ver;                                     // firmware version
     284             :     Vector3f _current_angle_rad;                                // current angles in radians received from gimbal (x=roll, y=pitch, z=yaw)
     285             :     uint32_t _last_current_angle_ms;                            // system time (in milliseconds) that angle information received from the gimbal
     286             :     uint32_t _last_req_current_info_ms;                         // system time that this driver last requested current gimbal infomation
     287             :     uint8_t _last_req_step;                                     // 10hz request loop step (different requests are sent at various steps)
     288             :     uint8_t _stop_order_count;                                  // number of stop commands sent since target rates became zero
     289             :     float _measure_dist_m = -1.0f;                              // latest rangefinder distance (in meters)
     290             :     uint8_t _msg_buff[AP_MOUNT_TOPOTEK_PACKETLEN_MAX];          // buffer holding bytes from latest packet received.  only used to calculate crc
     291             :     uint8_t _msg_buff_len;                                      // number of bytes in the msg buffer
     292             :     struct {
     293             :         ParseState state;                                       // parser state
     294             :         uint8_t data_len;                                       // expected number of data bytes
     295             :     } _parser;
     296             : 
     297             :     // mapping from received message key to member function pointer to consume the message
     298             :     typedef struct {
     299             :         uint8_t uart_cmd_key[4];                                // gimbal message key;
     300             :         void (AP_Mount_Topotek::*func)(void);                       // member function to consume messager
     301             :     } UartCmdFunctionHandler;
     302             : 
     303             :     // stores command ID and corresponding member functions that are compared with the command received by the gimbal
     304             :     UartCmdFunctionHandler uart_recv_cmd_compare_list[AP_MOUNT_RECV_GIMBAL_CMD_CATEGORIES_NUM] = {
     305             :         {{"GIA"}, &AP_Mount_Topotek::gimbal_angle_analyse},
     306             :         {{"REC"}, &AP_Mount_Topotek::gimbal_record_analyse},
     307             :         {{"SDC"}, &AP_Mount_Topotek::gimbal_sdcard_analyse},
     308             :         {{"LRF"}, &AP_Mount_Topotek::gimbal_dist_info_analyse},
     309             :         {{"TRC"}, &AP_Mount_Topotek::gimbal_track_analyse},
     310             :         {{"VSN"}, &AP_Mount_Topotek::gimbal_version_analyse},
     311             :         {{"PA2"}, &AP_Mount_Topotek::gimbal_model_name_analyse}
     312             :     };
     313             : };
     314             : 
     315             : #endif // HAL_MOUNT_TOPOTEK_ENABLED

Generated by: LCOV version 1.14