LCOV - code coverage report
Current view: top level - AP_Generator - AP_Generator_RichenPower.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 6 6 100.0 %
Date: 2026-09-09 11:02:08 Functions: 2 2 100.0 %

          Line data    Source code
       1             : // -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
       2             : #pragma once
       3             : 
       4             : #include "AP_Generator_config.h"
       5             : 
       6             : #if AP_GENERATOR_RICHENPOWER_ENABLED
       7             : 
       8             : #include "AP_Generator_Backend.h"
       9             : 
      10             : #include <AP_Logger/AP_Logger_config.h>
      11             : #include <AP_Common/AP_Common.h>
      12             : #include <stdint.h>
      13             : #include <stdio.h>
      14             : 
      15             : 
      16             : /*
      17             :  *  Example setup:
      18             :  *  param set SERIAL2_PROTOCOL 30  # Generator protocol
      19             :  *  param set SERIAL2_BAUD 9600
      20             :  *  param set RC9_OPTION 85        # pilot directive for generator
      21             :  *  param set SERVO8_FUNCTION 42   # autopilot directive to generator
      22             :  */
      23             : 
      24             : class AP_Generator_RichenPower : public AP_Generator_Backend
      25             : {
      26             : 
      27             : public:
      28             :     // constructor
      29             :     using AP_Generator_Backend::AP_Generator_Backend;
      30             : 
      31             :     // init should be called at vehicle startup to get the generator library ready
      32             :     __INITFUNC__ void init(void) override;
      33             :     // update should be called regularly to update the generator state
      34             :     void update(void) override;
      35             : 
      36             :     // methods to control the generator state:
      37             :     bool stop(void) override;
      38             :     bool idle(void) override;
      39             :     bool run(void) override;
      40             : 
      41             :     // method to send a GENERATOR_STATUS mavlink message
      42             :     void send_generator_status(const GCS_MAVLINK &channel) override;
      43             : 
      44             :     // prearm checks to ensure generator is good for arming.  Note
      45             :     // that if the generator has never sent us a message then these
      46             :     // automatically pass!
      47             :     bool pre_arm_check(char *failmsg, uint8_t failmsg_len) const override;
      48             : 
      49             :     // Update front end with voltage, current, and rpm values
      50             :     void update_frontend_readings(void);
      51             : 
      52             :     // healthy returns true if the generator is not present, or it is
      53             :     // present, providing telemetry and not indicating an errors.
      54             :     bool healthy() const override;
      55             : 
      56             : private:
      57             : 
      58             :     // read - read serial port, return true if a new reading has been found
      59             :     bool get_reading();
      60             :     AP_HAL::UARTDriver *uart;
      61             : 
      62             :     // methods and state to record pilot desired runstate and actual runstate:
      63             :     enum class RunState {
      64             :         STOP = 17,
      65             :         IDLE = 18,
      66             :         RUN = 19,
      67             :     };
      68             :     RunState pilot_desired_runstate = RunState::STOP;
      69             :     RunState commanded_runstate = RunState::STOP;  // output is based on this
      70           9 :     void set_pilot_desired_runstate(RunState newstate) {
      71             :         // GCS_SEND_TEXT(MAV_SEVERITY_INFO, "RichenPower: Moving to state (%u) from (%u)", (unsigned)newstate, (unsigned)runstate);
      72           9 :         pilot_desired_runstate = newstate;
      73           9 :     }
      74             :     void update_runstate();
      75             : 
      76             :     // boolean so we can emit the RichenPower protocol version once
      77             :     bool protocol_information_anounced;
      78             : 
      79             :     // reported mode from the generator:
      80             :     enum class Mode {
      81             :         IDLE = 0,
      82             :         RUN = 1,
      83             :         CHARGE = 2,
      84             :         BALANCE = 3,
      85             :         OFF = 4,
      86             :     };
      87             : 
      88             :     // un-packed data from the generator:
      89             :     struct Reading {
      90             :         uint32_t    runtime; //seconds
      91             :         uint32_t    seconds_until_maintenance;
      92             :         uint16_t    errors;
      93             :         uint16_t    rpm;
      94             :         float       output_voltage;
      95             :         float       output_current;
      96             :         Mode        mode;
      97             :     };
      98             : 
      99             : #if HAL_LOGGING_ENABLED
     100             :     // method and state to write and entry to the onboard log:
     101             :     void Log_Write();
     102             :     uint32_t last_logged_reading_ms;
     103             : #endif
     104             : 
     105             :     struct Reading last_reading;
     106             :     uint32_t last_reading_ms;
     107             : 
     108             :     static constexpr uint8_t HEADER_MAGIC1 = 0xAA;
     109             :     static constexpr uint8_t HEADER_MAGIC2 = 0x55;
     110             : 
     111             :     static constexpr uint8_t FOOTER_MAGIC1 = 0x55;
     112             :     static constexpr uint8_t FOOTER_MAGIC2 = 0xAA;
     113             : 
     114             :     // reported errors from the generator:
     115             :     enum class Errors { // bitmask
     116             :         MaintenanceRequired = 0,
     117             :         StartDisabled = 1,
     118             :         Overload = 2,
     119             :         LowVoltageOutput = 3,
     120             :         LowBatteryVoltage = 4,
     121             :         LAST
     122             :     };
     123             : 
     124             :     const char *error_strings[5] = {
     125             :         "MaintenanceRequired",
     126             :         "StartDisabled",
     127             :         "Overload",
     128             :         "LowVoltageOutput",
     129             :         "LowBatteryVoltage",
     130             :     };
     131             :     static_assert(ARRAY_SIZE(error_strings) == (uint8_t)Errors::LAST,
     132             :                   "have error string for each error");
     133             : 
     134             :     // RichenPower data packet format:
     135             :     struct PACKED RichenPacket {
     136             :         uint8_t headermagic1;
     137             :         uint8_t headermagic2;
     138             :         uint16_t version;
     139             :         uint8_t runtime_minutes;
     140             :         uint8_t runtime_seconds;
     141             :         uint16_t runtime_hours;
     142             :         uint16_t seconds_until_maintenance_high;
     143             :         uint16_t seconds_until_maintenance_low;
     144             :         uint16_t errors;
     145             :         uint16_t rpm;
     146             :         uint16_t throttle;
     147             :         uint16_t idle_throttle;
     148             :         uint16_t output_voltage;
     149             :         uint16_t output_current;
     150             :         uint16_t dynamo_current;
     151             :         uint8_t unknown1;
     152             :         uint8_t mode;
     153             :         uint8_t unknown6[38]; // "data"?!
     154             :         uint16_t checksum;
     155             :         uint8_t footermagic1;
     156             :         uint8_t footermagic2;
     157             :     };
     158             : 
     159             :     union RichenUnion {
     160             :         uint8_t parse_buffer[70];
     161             :         struct RichenPacket packet;
     162             :     };
     163             :     RichenUnion u;
     164             : 
     165             :     // number of bytes currently in the buffer
     166             :     uint8_t body_length;
     167             : 
     168             :     // move the expected header bytes into &buffer[0], adjusting
     169             :     // body_length as appropriate.
     170             :     void move_header_in_buffer(uint8_t initial_offset);
     171             : 
     172             :     // a simple heat model to avoid the motor moving to run too fast
     173             :     // or being stopped before cooldown.  The generator itself does
     174             :     // not supply temperature via telemetry, so we fake one based on
     175             :     // RPM.
     176             :     uint32_t last_heat_update_ms;
     177             :     float heat;
     178             :     void update_heat();
     179             : 
     180             :     // returns true if the generator should be allowed to move into
     181             :     // the "run" (high-RPM) state:
     182             :     bool generator_ok_to_run() const;
     183             :     // returns an amount of synthetic heat required for the generator
     184             :     // to move into the "run" state:
     185             :     static constexpr float heat_required_for_run();
     186             : 
     187             :     // approximate run and idle speeds for the generator:
     188             :     static const uint16_t RUN_RPM = 15000;
     189             :     static const uint16_t IDLE_RPM = 6000;
     190             : 
     191             :     static constexpr float heat_environment_loss_factor = 0.005f;
     192             :     // powf is not constexpr, so we create a const for it:
     193             :     // powf(1.0f-heat_environment_loss_factor, 30)
     194             :     static constexpr float heat_environment_loss_30s = 0.860384;
     195             :     static constexpr float heat_environment_loss_60s = 0.740261;
     196             : 
     197             :     // boolean so we can announce we've stopped the generator due to a
     198             :     // crash just once:
     199             :     bool vehicle_was_crashed;
     200             : 
     201             :     // data and methods to handle time-in-idle-state:
     202             :     uint32_t idle_state_start_ms;
     203             : 
     204           9 :     uint32_t time_in_idle_state_ms() const {
     205           9 :         if (idle_state_start_ms == 0) {
     206             :             return 0;
     207             :         }
     208           9 :         return AP_HAL::millis() - idle_state_start_ms;
     209             :     }
     210             : 
     211             :     // check if the generator requires maintenance and send a message if it does:
     212             :     void check_maintenance_required();
     213             :     // if we are emitting warnings about the generator requiring
     214             :     // maintenamce, this is the last time we sent the warning:
     215             :     uint32_t last_maintenance_warning_ms;
     216             : };
     217             : #endif  // AP_GENERATOR_RICHENPOWER_ENABLED

Generated by: LCOV version 1.14