Program Listing for File dispatch.hpp
↰ Return to documentation for file (include/ds_dbw_can/dispatch.hpp
)
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2023, Dataspeed Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Dataspeed Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
#pragma once
#if __cplusplus < 201703L
#warning "The C++ standard must be C++17 or newer"
#endif
#include <stdint.h>
#include <stddef.h> // size_t
#include <stdbool.h> // bool
#include <string.h> // memset()
#include <math.h> // std::round(), std::abs()
#include <algorithm> // std::clamp()
#include <array> // std::array
#include <ds_dbw_can/SAE_J1850_crc.hpp>
namespace ds_dbw_can {
// CRC
static constexpr uint8_t MSG_NULL[7+1] = "\0\0\0\0\0\0\0"; // For static assertions
static constexpr uint8_t crc8(uint16_t id, const uint8_t *data, size_t len) {
return j1850::crc8_can_msg(id, false, data, len);
}
static uint8_t crc8(uint16_t id, const void *ptr, size_t len) {
return j1850::crc8_can_msg(id, false, (const uint8_t *)ptr, len);
}
#pragma pack(push, 1) // Pack structures to a single byte
enum class SystemSyncMode : uint8_t {
None = 0, /* Overrides */
Disengages = 1,
AllOrNone = 2,
AllOrNoneWithBtn = 3,
};
static constexpr const char * systemSyncModeToString(SystemSyncMode x) {
switch (x) {
case SystemSyncMode::None: return "None";
case SystemSyncMode::Disengages: return "Disengages";
case SystemSyncMode::AllOrNone: return "AllOrNone";
case SystemSyncMode::AllOrNoneWithBtn: return "AllOrNoneWithBtn";
default: return "Unknown";
}
}
enum class CmdSrc : uint8_t {
User = 0,
ULC = 1,
Remote = 2,
Button = 3,
_Future = 7,
};
enum class Gear : uint8_t {
None = 0,
Park = 1,
Reverse = 2,
Neutral = 3,
Drive = 4,
Low = 5,
Manual = 6,
Calibrate = 15,
};
enum class TurnSignal : uint8_t {
None = 0,
Left = 1,
Right = 2,
Hazard = 3,
};
enum class Quality : uint8_t {
Ok = 0,
Partial = 1,
NoData = 2,
Fault = 3,
};
struct MsgSteerCmd {
static constexpr size_t TIMEOUT_MS = 100;
enum class CmdType : uint8_t {
None = 0,
Torque = 1, // 0.0078125 Nm
Angle = 2, // 0.1 deg
Curvature = 3, // 0.0000061 1/m
YawRate = 4, // 0.015 deg/s
Percent = 14, // 0.01 %
Calibrate = 15, // 0.1 deg
};
int16_t cmd; // Interpretation changes with cmd_type
CmdType cmd_type :4;
uint8_t enable :1;
uint8_t clear :1;
uint8_t ignore :1;
uint8_t :1;
uint8_t rate; // 4 deg/s, 0 for default, 255 for no limit
uint8_t accel; // 100 deg/s^2, 0 for default, 255 for no limit
uint8_t :8;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCmdTorqueNm(float nm) {
cmd = std::clamp<float>(nm / 0.0078125f, -INT16_MAX, INT16_MAX);
}
void setCmdAngleDeg(float deg, float deg_s = 0, float deg_s2 = 0) {
cmd = std::clamp<float>(deg / 0.1f, -INT16_MAX, INT16_MAX);
if (deg_s < 0 || std::isinf(deg_s)) {
rate = UINT8_MAX; // Unlimited
} else if (deg_s > 0) {
rate = std::clamp<float>(std::round(deg_s / 4), 1, UINT8_MAX - 1);
} else {
rate = 0; // Default
}
if (deg_s2 < 0 || std::isinf(deg_s2)) {
accel = UINT8_MAX; // Unlimited
} else if (deg_s2 > 0) {
accel = std::clamp<float>(std::round(deg_s2 / 100), 1, UINT8_MAX - 1);
} else {
accel = 0; // Default
}
}
void setCmdAngleRad(float rad, float rad_s = 0, float rad_s2 = 0) {
setCmdAngleDeg(rad * (float)(180 / M_PI), rad_s * (float)(180 / M_PI), rad_s2 * (float)(180 / M_PI));
}
void setCmdCurvMDeg(float curv, float deg_s = 0, float deg_s2 = 0) {
cmd = std::clamp<float>(curv / 0.0000061f, -INT16_MAX, INT16_MAX);
if (deg_s < 0 || std::isinf(deg_s)) {
rate = UINT8_MAX; // Unlimited
} else if (deg_s > 0) {
rate = std::clamp<float>(std::round(deg_s / 4), 1, UINT8_MAX - 1);
} else {
rate = 0; // Default
}
if (deg_s2 < 0 || std::isinf(deg_s2)) {
accel = UINT8_MAX; // Unlimited
} else if (deg_s2 > 0) {
accel = std::clamp<float>(std::round(deg_s2 / 100), 1, UINT8_MAX - 1);
} else {
accel = 0; // Default
}
}
void setCmdCurvMRad(float curv, float rad_s = 0, float rad_s2 = 0) {
setCmdCurvMDeg(curv, rad_s * (float)(180 / M_PI), rad_s2 * (float)(180 / M_PI));
}
void setCmdYawRateDegS(float yaw_deg_s, float deg_s = 0, float deg_s2 = 0) {
cmd = std::clamp<float>(yaw_deg_s / 0.015f, -INT16_MAX, INT16_MAX);
if (deg_s < 0 || std::isinf(deg_s)) {
rate = UINT8_MAX; // Unlimited
} else if (deg_s > 0) {
rate = std::clamp<float>(std::round(deg_s / 4), 1, UINT8_MAX - 1);
} else {
rate = 0; // Default
}
if (deg_s2 < 0 || std::isinf(deg_s2)) {
accel = UINT8_MAX; // Unlimited
} else if (deg_s2 > 0) {
accel = std::clamp<float>(std::round(deg_s2 / 100), 1, UINT8_MAX - 1);
} else {
accel = 0; // Default
}
}
void setCmdYawRateRadS(float yaw_rad_s, float rad_s = 0, float rad_s2 = 0) {
setCmdYawRateDegS(yaw_rad_s * (float)(180 / M_PI), rad_s * (float)(180 / M_PI), rad_s2 * (float)(180 / M_PI));
}
void setCmdPercentDeg(float percent, float deg_s = 0, float deg_s2 = 0) {
cmd = std::clamp<float>(percent / 0.01f, -INT16_MAX, INT16_MAX);
if (deg_s < 0 || std::isinf(deg_s)) {
rate = UINT8_MAX; // Unlimited
} else if (deg_s > 0) {
rate = std::clamp<float>(std::round(deg_s / 4), 1, UINT8_MAX - 1);
} else {
rate = 0; // Default
}
if (deg_s2 < 0 || std::isinf(deg_s2)) {
accel = UINT8_MAX; // Unlimited
} else if (deg_s2 > 0) {
accel = std::clamp<float>(std::round(deg_s2 / 100), 1, UINT8_MAX - 1);
} else {
accel = 0; // Default
}
}
void setCmdPercentRad(float percent, float rad_s = 0, float rad_s2 = 0) {
setCmdPercentDeg(percent, rad_s * (float)(180 / M_PI), rad_s2 * (float)(180 / M_PI));
}
float cmdTorqueNm() const {
return cmd * 0.0078125f;
}
float cmdAngleDeg() const {
return cmd * 0.1f;
}
float cmdCurvM() const {
return cmd * 0.0000061f;
}
float cmdYawRateDegS() const {
return cmd * 0.015f;
}
float cmdPercent() const {
return cmd * 0.01f;
}
float cmdAngleRateDegS() const {
if (rate == 0) {
return 0; // Default
} else if (rate < UINT8_MAX) {
return std::max<float>(rate * 4, 100); // Minimum of 100 deg/s
} else {
return INFINITY; // Unlimited
}
}
float cmdAngleAccelDegS2() const {
if (accel == 0) {
return 0; // Default
} else if (accel < UINT8_MAX) {
return std::max<float>(accel * 100, 300); // Minimum of 300 deg/s^2
} else {
return INFINITY; // Unlimited
}
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgSteerCmd));
struct MsgSteerCmdRmt : public MsgSteerCmd {
static constexpr uint32_t ID = 0x200;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgSteerCmdRmt) == sizeof(MsgSteerCmd));
struct MsgSteerCmdUsr : public MsgSteerCmd {
static constexpr uint32_t ID = 0x210;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgSteerCmdUsr) == sizeof(MsgSteerCmd));
struct MsgSteerReport1 {
static constexpr uint32_t ID = 0x100;
static constexpr size_t PERIOD_MIN = 8;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t PERIOD_MAX = 25;
static constexpr size_t TIMEOUT_MS = 100;
enum class CmdType : uint8_t {
None = 0,
Torque = 1,
Angle = 2,
};
int16_t angle :14; // 0.1 deg
uint8_t limiting_value :1;
uint8_t limiting_rate :1;
int16_t cmd :14; // 0.1 deg or 0.0078125 Nm
CmdType cmd_type :2;
int8_t torque; // 0.0625 Nm
uint8_t :4;
uint8_t external_control :1;
uint8_t override_active :1;
uint8_t override_other :1;
uint8_t override_latched :1;
uint8_t ready :1;
uint8_t enabled :1;
uint8_t fault :1;
uint8_t timeout :1;
uint8_t bad_crc :1;
uint8_t bad_rc :1;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setAngleDeg(float deg) {
if (std::isfinite(deg)) {
angle = std::clamp<float>(deg * 10, -INT16_MAX >> 2, INT16_MAX >> 2);
} else {
angle = (uint16_t)0xE000;
}
}
bool angleValid() const {
return (uint16_t)angle != (uint16_t)0xE000;
}
float angleDeg() const {
if (angleValid()) {
return angle * 0.1f;
}
return NAN;
}
float angleRad() const {
return angleDeg() * (float)(M_PI / 180);
}
bool cmdValid() const {
return (uint16_t)cmd != (uint16_t)0xE000;
}
float cmdAngleDeg() const {
if (cmdValid()) {
return cmd * 0.1f;
}
return NAN;
}
float cmdAngleRad() const {
return cmdAngleDeg() * (float)(M_PI / 180);
}
float cmdTorqueNm() const {
if (cmdValid()) {
return cmd * (float)0.0078125;
}
return NAN;
}
void setCmd(MsgSteerCmd::CmdType type, float deg, float nm) {
if (type == MsgSteerCmd::CmdType::Angle && std::isfinite(deg)) {
cmd_type = CmdType::Angle;
cmd = std::clamp<float>(deg * 10, -INT16_MAX >> 2, INT16_MAX >> 2);
} else if (type == MsgSteerCmd::CmdType::Torque && std::isfinite(nm)) {
cmd_type = CmdType::Torque;
cmd = std::clamp<float>(nm * 128, -INT16_MAX >> 2, INT16_MAX >> 2);
} else {
cmd_type = CmdType::None;
cmd = (uint16_t)0xE000;
}
}
void setTorqueNm(float nm) {
if (std::isfinite(nm)) {
torque = std::clamp<float>(nm * 16, -INT8_MAX, INT8_MAX);
} else {
torque = (uint8_t)0x80;
}
}
bool torqueValid() const {
return (uint8_t)torque != (uint8_t)0x80;
}
float torqueNm() const {
if (torqueValid()) {
return torque * (float)0.0625;
}
return NAN;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgSteerReport1));
struct MsgSteerReport2 {
static constexpr uint32_t ID = 0x300;
static constexpr size_t PERIOD_MS = 200;
static constexpr size_t TIMEOUT_MS = 1000;
uint8_t degraded :1;
uint8_t degraded_cmd_type :1;
uint8_t degraded_comms :1;
uint8_t degraded_internal :1;
uint8_t degraded_vehicle :1;
uint8_t degraded_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t fault_power :1;
uint8_t fault_comms :1;
uint8_t fault_internal :1;
uint8_t fault_vehicle :1;
uint8_t fault_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t :8;
uint8_t :8;
uint8_t limit_rate :8; // 4 deg/s, 255=unlimited
uint16_t limit_value :10; // 1 deg, 1023=unlimited
uint8_t :1;
CmdSrc cmd_src :3;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setLimitRateDegS(float deg_s) {
if (std::isfinite(deg_s)) {
limit_rate = std::clamp<float>(std::round(std::abs(deg_s / 4)), 0, 0xFE);
} else {
limit_rate = 0xFF; // Unlimited
}
}
float getLimitRateDegS() const {
if (limit_rate != 0xFF) {
return limit_rate * 4;
}
return INFINITY; // Unlimited
}
void setLimitValueDeg(float deg) {
if (std::isfinite(deg)) {
limit_value = std::clamp<float>(std::round(std::abs(deg)), 0, 0x3FE);
} else {
limit_value = 0x3FF; // Unlimited
}
}
float getLimitValueDeg() const {
if (limit_value != 0x3FF) {
return limit_value;
}
return INFINITY; // Unlimited
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgSteerReport2));
struct MsgSteerReport3 {
static constexpr uint32_t ID = 0x310;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
uint8_t degraded_comms_dbw :1;
uint8_t degraded_comms_dbw_gateway :1;
uint8_t degraded_comms_dbw_brake :1;
uint8_t degraded_comms_dbw_thrtl :1;
uint8_t degraded_comms_dbw_gear :1;
uint8_t :1;
uint8_t degraded_control_performance :1;
uint8_t degraded_param_mismatch :1;
uint8_t degraded_comms_vehicle :1;
uint8_t :3;
uint8_t degraded_comms_actuator :1;
uint8_t :3;
uint8_t degraded_vehicle_speed :1;
uint8_t :6;
uint8_t degraded_calibration :1;
uint8_t fault_comms_dbw :1;
uint8_t fault_comms_dbw_gateway :1;
uint8_t fault_comms_dbw_brake :1;
uint8_t fault_comms_dbw_thrtl :1;
uint8_t fault_comms_dbw_gear :1;
uint8_t :3;
uint8_t fault_comms_vehicle :1;
uint8_t :3;
uint8_t fault_comms_actuator :1;
uint8_t :3;
uint8_t fault_vehicle_speed :1;
uint8_t :3;
uint8_t fault_angle_sensor :1;
uint8_t fault_torque_sensor_1 :1;
uint8_t fault_torque_sensor_2 :1;
uint8_t fault_torque_sensor_mismatch :1;
uint8_t fault_actuator_torque_sensor :1;
uint8_t fault_actuator_config :1;
uint8_t fault_actuator_assist :1;
uint8_t :1;
uint8_t fault_control_performance :1;
uint8_t fault_param_mismatch :1;
uint8_t fault_param_limits :1;
uint8_t fault_calibration :1;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(8 == sizeof(MsgSteerReport3));
struct MsgSteerParamHash {
static constexpr uint32_t ID = 0x330;
static constexpr size_t PERIOD_MS = 5000;
static constexpr size_t TIMEOUT_MS = 17500;
uint32_t hash;
};
static_assert(4 == sizeof(MsgSteerParamHash));
struct MsgBrakeCmd {
static constexpr size_t TIMEOUT_MS = 100;
enum class CmdType : uint8_t {
None = 0, // Command Rate limit
Pressure = 1, // 0.01 bar 10 bar/s
Torque = 2, // 1 Nm 1000 Nm/s
Accel = 8, // 0.001 m/s^2 1 m/s^3 (int16_t, signed)
AccelAcc = 9, // 0.001 m/s^2 1 m/s^3 (int16_t, signed)
AccelAeb = 10, // 0.001 m/s^2 1 m/s^3 (int16_t, signed)
PedalRaw = 13, // 0.01 % 10 %/s
Percent = 14, // 0.01 % 10 %/s
Calibrate = 15,
};
uint16_t cmd; // Interpretation changes with cmd_type
CmdType cmd_type :4;
uint8_t enable :1;
uint8_t clear :1;
uint8_t ignore :1;
uint8_t :1;
uint8_t rate_inc; // See cmd_type, 0 for default, 255 for no limit
uint8_t rate_dec; // See cmd_type, 0 for default, 255 for no limit
uint8_t :8;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCmdPressureBar(float bar, float bar_s_inc = 0, float bar_s_dec = 0) {
cmd = std::clamp<float>(bar / 0.01f, 0, UINT16_MAX);
if (bar_s_inc < 0 || std::isinf(bar_s_inc)) {
rate_inc = UINT8_MAX; // Unlimited
} else if (bar_s_inc > 0) {
rate_inc = std::clamp<float>(std::round(bar_s_inc / 10), 1, UINT8_MAX - 1);
} else {
rate_inc = 0; // Default
}
if (bar_s_dec < 0 || std::isinf(bar_s_dec)) {
rate_dec = UINT8_MAX; // Unlimited
} else if (bar_s_dec > 0) {
rate_dec = std::clamp<float>(std::round(bar_s_dec / 10), 1, UINT8_MAX - 1);
} else {
rate_dec = 0; // Default
}
}
void setCmdTorqueNm(float nm, float nm_s_inc = 0, float nm_s_dec = 0) {
cmd = std::clamp<float>(nm, 0, UINT16_MAX);
if (nm_s_inc < 0 || std::isinf(nm_s_inc)) {
rate_inc = UINT8_MAX; // Unlimited
} else if (nm_s_inc > 0) {
rate_inc = std::clamp<float>(std::round(nm_s_inc / 1000), 1, UINT8_MAX - 1);
} else {
rate_inc = 0; // Default
}
if (nm_s_dec < 0 || std::isinf(nm_s_dec)) {
rate_dec = UINT8_MAX; // Unlimited
} else if (nm_s_dec > 0) {
rate_dec = std::clamp<float>(std::round(nm_s_dec / 1000), 1, UINT8_MAX - 1);
} else {
rate_dec = 0; // Default
}
}
void setCmdAccelMpS(float accel, float jerk_inc = 0, float jerk_dec = 0) {
cmd = (int16_t)std::clamp<float>(accel / 0.001f, -INT16_MAX, INT16_MAX);
if (jerk_inc < 0 || std::isinf(jerk_inc)) {
rate_inc = UINT8_MAX; // Unlimited
} else if (jerk_inc > 0) {
rate_inc = std::clamp<float>(std::round(jerk_inc), 1, UINT8_MAX - 1);
} else {
rate_inc = 0; // Default
}
if (jerk_dec < 0 || std::isinf(jerk_dec)) {
rate_dec = UINT8_MAX; // Unlimited
} else if (jerk_dec > 0) {
rate_dec = std::clamp<float>(std::round(jerk_dec), 1, UINT8_MAX - 1);
} else {
rate_dec = 0; // Default
}
}
void setCmdPercent(float x, float inc = 0, float dec = 0) {
cmd = std::clamp<float>(x / 0.01f, 0, UINT16_MAX);
if (inc < 0 || std::isinf(inc)) {
rate_inc = UINT8_MAX; // Unlimited
} else if (inc > 0) {
rate_inc = std::clamp<float>(std::round(inc / 10), 1, UINT8_MAX - 1);
} else {
rate_inc = 0; // Default
}
if (dec < 0 || std::isinf(dec)) {
rate_dec = UINT8_MAX; // Unlimited
} else if (dec > 0) {
rate_dec = std::clamp<float>(std::round(dec / 10), 1, UINT8_MAX - 1);
} else {
rate_dec = 0; // Default
}
}
float cmdPressureBar() const {
return cmd * 0.01f;
}
uint16_t cmdTorqueNmU16() const {
return cmd;
}
float cmdTorqueNm() const {
return cmd;
}
int16_t cmdAccelMpSx1000() const {
return (int16_t)cmd;
}
float cmdAccelMpS() const {
return (int16_t)cmd * 0.001f;
}
uint16_t cmdPercentU16() const {
constexpr uint16_t MAX = 100 / 0.01;
if (cmd < MAX) {
return (cmd * UINT16_MAX) / MAX;
}
return UINT16_MAX;
}
float cmdPercent() const {
return cmd * 0.01f;
}
float cmdRateIncBarS() const {
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<float>(rate_inc * 10, 20); // Minimum of 20 bar/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateDecBarS() const {
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<float>(rate_dec * 10, 20); // Minimum of 20 bar/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateIncNmS() const {
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<float>(rate_inc * 1e3f, 2e3f); // Minimum of 2 kNm/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateDecNmS() const {
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<float>(rate_dec * 1e3f, 2e3f); // Minimum of 2 kNm/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateIncMS3() const {
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max(rate_inc, (uint8_t)5); // Minimum of 5 m/s^3
} else {
return INFINITY; // Unlimited
}
}
float cmdRateDecMS3() const {
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max(rate_dec, (uint8_t)5); // Minimum of 5 m/s^3
} else {
return INFINITY; // Unlimited
}
}
float cmdRateIncPercentS() const {
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<float>(rate_inc * 10, 50); // Minimum of 50 %/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateDecPercentS() const {
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<float>(rate_dec * 10, 50); // Minimum of 50 %/s
} else {
return INFINITY; // Unlimited
}
}
uint16_t cmdRateIncNmSU16() const { // Nm/ms (0.001Nm/s)
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<uint16_t>(rate_inc, 2); // Minimum of 2 kNm/s
} else {
return UINT16_MAX; // Unlimited
}
}
uint16_t cmdRateDecNmSU16() const { // Nm/ms (0.001Nm/s)
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<uint16_t>(rate_dec, 2); // Minimum of 2 kNm/s
} else {
return UINT16_MAX; // Unlimited
}
}
uint16_t cmdRateIncPercentSU16() const { // %/ms (0.001%/s)
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<uint16_t>(rate_inc * (uint16_t)(10e-2 * 1e-3 * UINT16_MAX), (uint16_t)(50e-2 * 1e-3 * UINT16_MAX)); // Minimum of 50 %/s
} else {
return UINT16_MAX; // Unlimited
}
}
uint16_t cmdRateDecPercentSU16() const { // %/ms (0.001%/s)
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<uint16_t>(rate_dec * (uint16_t)(10e-2 * 1e-3 * UINT16_MAX), (uint16_t)(50e-2 * 1e-3 * UINT16_MAX)); // Minimum of 50 %/s
} else {
return UINT16_MAX; // Unlimited
}
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgBrakeCmd));
struct MsgBrakeCmdRmt : public MsgBrakeCmd {
static constexpr uint32_t ID = 0x201;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgBrakeCmdRmt) == sizeof(MsgBrakeCmd));
struct MsgBrakeCmdUsr : public MsgBrakeCmd {
static constexpr uint32_t ID = 0x211;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgBrakeCmdUsr) == sizeof(MsgBrakeCmd));
struct MsgBrakeCmdUlc : public MsgBrakeCmd {
static constexpr uint32_t ID = 0x221;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgBrakeCmdUlc) == sizeof(MsgBrakeCmd));
struct MsgBrakeReport1 {
static constexpr uint32_t ID = 0x101;
static constexpr size_t PERIOD_MIN = 15;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t PERIOD_MAX = 25;
static constexpr size_t TIMEOUT_MS = 100;
typedef MsgBrakeCmd::CmdType CmdType;
/* Units for input cmd output
* Pressure: 0.05 bar 0.05 bar 0.05 bar
* Torque: 4 Nm 4 Nm 4 Nm
* Accel: 4 Nm 0.005 m/s^2 0.005 m/s^2
* PedalRaw 0.025 % 0.025 % 0.025 %
* Percent 0.025 % 0.025 % 0.025 %
*/
uint16_t input :12; // Interpretation changes with cmd_type, 4095=unknown
uint8_t btsi :1;
uint8_t :1;
uint8_t limiting_value :1;
uint8_t limiting_rate :1;
uint16_t cmd :12; // Interpretation changes with cmd_type, 4095=unknown
CmdType cmd_type :4;
uint16_t output :12; // Interpretation changes with cmd_type, 4095=unknown
uint8_t external_control :1;
uint8_t override_active :1;
uint8_t override_other :1;
uint8_t override_latched :1;
uint8_t ready :1;
uint8_t enabled :1;
uint8_t fault :1;
uint8_t timeout :1;
uint8_t bad_crc :1;
uint8_t bad_rc :1;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setPressureBar(float in_bar, float cmd_bar, float out_bar) {
cmd_type = CmdType::Pressure;
if (std::isfinite(in_bar)) {
input = std::clamp<float>(in_bar / 0.05f, 0, (UINT16_MAX >> 4) - 1);
} else {
input = UINT16_MAX >> 4;
}
if (std::isfinite(cmd_bar)) {
cmd = std::clamp<float>(cmd_bar / 0.05f, 0, (UINT16_MAX >> 4) - 1);
} else {
cmd = UINT16_MAX >> 4;
}
if (std::isfinite(out_bar)) {
output = std::clamp<float>(out_bar / 0.05f, 0, (UINT16_MAX >> 4) - 1);
} else {
output = UINT16_MAX >> 4;
}
}
void setTorqueNm(float in_nm, float cmd_nm, float out_nm) {
cmd_type = CmdType::Torque;
if (std::isfinite(in_nm)) {
input = std::clamp<float>(in_nm / 4, 0, (UINT16_MAX >> 4) - 1);
} else {
input = UINT16_MAX >> 4;
}
if (std::isfinite(cmd_nm)) {
cmd = std::clamp<float>(cmd_nm / 4, 0, (UINT16_MAX >> 4) - 1);
} else {
cmd = UINT16_MAX >> 4;
}
if (std::isfinite(out_nm)) {
output = std::clamp<float>(out_nm / 4, 0, (UINT16_MAX >> 4) - 1);
} else {
output = UINT16_MAX >> 4;
}
}
void setAccel(uint16_t in_nm, int16_t cmd_ms2_x1k, int16_t out_ms2_x1k) {
cmd_type = CmdType::Accel;
if (in_nm != UINT16_MAX) {
input = std::clamp<uint32_t>(in_nm / 4, 0, (UINT16_MAX >> 4) - 1);
} else {
input = UINT16_MAX >> 4;
}
if (std::isfinite(cmd_ms2_x1k)) {
cmd = std::clamp<int16_t>(cmd_ms2_x1k / 5, -INT16_MAX >> 4, INT16_MAX >> 4);
} else {
cmd = (uint16_t)INT16_MIN >> 4;
}
if (std::isfinite(out_ms2_x1k)) {
output = std::clamp<int16_t>(out_ms2_x1k / 5, -INT16_MAX >> 4, INT16_MAX >> 4);
} else {
output = (uint16_t)INT16_MIN >> 4;
}
}
void setAccelAcc(uint16_t in_nm, int16_t cmd_ms2, int16_t out_ms2) {
setAccel(in_nm, cmd_ms2, out_ms2);
cmd_type = CmdType::AccelAcc;
}
void setAccelAeb(uint16_t in_nm, int16_t cmd_ms2, int16_t out_ms2) {
setAccel(in_nm, cmd_ms2, out_ms2);
cmd_type = CmdType::AccelAeb;
}
void setPercent(float in_pc, float cmd_pc, float out_pc) {
cmd_type = CmdType::Percent;
if (std::isfinite(in_pc)) {
input = std::clamp<float>(in_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
input = UINT16_MAX >> 4;
}
if (std::isfinite(cmd_pc)) {
cmd = std::clamp<float>(cmd_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
cmd = UINT16_MAX >> 4;
}
if (std::isfinite(out_pc)) {
output = std::clamp<float>(out_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
output = UINT16_MAX >> 4;
}
}
void setPedalRaw(float in_pc, float cmd_pc, float out_pc) {
setPercent(in_pc, cmd_pc, out_pc);
cmd_type = CmdType::PedalRaw;
}
void setPercentU16(uint16_t in_pc, uint16_t cmd_pc, uint16_t out_pc) {
cmd_type = CmdType::Percent;
constexpr uint16_t MAX = 100 / 0.025;
input = (in_pc * MAX) / UINT16_MAX;
cmd = (cmd_pc * MAX) / UINT16_MAX;
output = (out_pc * MAX) / UINT16_MAX;
}
void setPedalRawU16(uint16_t in_pc, uint16_t cmd_pc, uint16_t out_pc) {
setPercentU16(in_pc, cmd_pc, out_pc);
cmd_type = CmdType::PedalRaw;
}
void setTorqueNmU16(uint16_t in_nm, uint16_t cmd_nm, uint16_t out_nm) {
cmd_type = CmdType::Torque;
input = std::clamp<uint16_t>(in_nm / 4, 0, (UINT16_MAX >> 4) - 1);
cmd = std::clamp<uint16_t>(cmd_nm / 4, 0, (UINT16_MAX >> 4) - 1);
output = std::clamp<uint16_t>(out_nm / 4, 0, (UINT16_MAX >> 4) - 1);
}
int32_t cmdRawSigned() const {
switch (cmd_type) { // No default case, explicitly specify all cases
case MsgBrakeCmd::CmdType::Accel:
case MsgBrakeCmd::CmdType::AccelAcc:
case MsgBrakeCmd::CmdType::AccelAeb:
if (cmd != (uint16_t)(INT16_MIN >> 4)) {
return (int16_t)(cmd << 4) >> 4; // Sign extend bit-field
}
return 0;
case MsgBrakeCmd::CmdType::Pressure:
case MsgBrakeCmd::CmdType::Torque:
case MsgBrakeCmd::CmdType::PedalRaw:
case MsgBrakeCmd::CmdType::Percent:
if (cmd != (uint16_t)(UINT16_MAX >> 4)) {
return cmd;
}
return 0;
case MsgBrakeCmd::CmdType::None:
case MsgBrakeCmd::CmdType::Calibrate:
return 0;
}
return 0;
}
void getPressureBar(float &in_bar, float &cmd_bar, float &out_bar) const {
if (input != UINT16_MAX >> 4) {
in_bar = input * 0.05f;
} else {
in_bar = NAN;
}
if (cmd != UINT16_MAX >> 4) {
cmd_bar = cmd * 0.05f;
} else {
cmd_bar = NAN;
}
if (output != UINT16_MAX >> 4) {
out_bar = output * 0.05f;
} else {
out_bar = NAN;
}
}
void getTorqueNm(float &in_nm, float &cmd_nm, float &out_nm) const {
if (input != UINT16_MAX >> 4) {
in_nm = input * 4.0f;
} else {
in_nm = NAN;
}
if (cmd != UINT16_MAX >> 4) {
cmd_nm = cmd * 4.0f;
} else {
cmd_nm = NAN;
}
if (output != UINT16_MAX >> 4) {
out_nm = output * 4.0f;
} else {
out_nm = NAN;
}
}
void getAccel(float &in_nm, float &cmd_ms2, float &out_ms2) const {
if (input != UINT16_MAX >> 4) {
in_nm = input * 4.0f;
} else {
in_nm = NAN;
}
if (cmd != INT16_MIN >> 4) {
cmd_ms2 = ((int16_t)(cmd << 4) >> 4) * 0.005f;
} else {
cmd_ms2 = NAN;
}
if (output != INT16_MIN >> 4) {
out_ms2 = ((int16_t)(output << 4) >> 4) * 0.005f;
} else {
out_ms2 = NAN;
}
}
void getPercent(float &in_pc, float &cmd_pc, float &out_pc) const {
if (input != UINT16_MAX >> 4) {
in_pc = input * 0.025f;
} else {
in_pc = NAN;
}
if (cmd != UINT16_MAX >> 4) {
cmd_pc = cmd * 0.025f;
} else {
cmd_pc = NAN;
}
if (output != UINT16_MAX >> 4) {
out_pc = output * 0.025f;
} else {
out_pc = NAN;
}
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgBrakeReport1));
struct MsgBrakeReport2 {
static constexpr uint32_t ID = 0x301;
static constexpr size_t PERIOD_MS = 200;
static constexpr size_t TIMEOUT_MS = 1000;
enum class BrkAvlMode : uint8_t {
Unlimited = 0,
SecondsX2 = 1,
MillisecondsX100 = 2,
};
uint8_t degraded :1;
uint8_t degraded_cmd_type :1;
uint8_t degraded_comms :1;
uint8_t degraded_internal :1;
uint8_t degraded_vehicle :1;
uint8_t degraded_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t fault_power :1;
uint8_t fault_comms :1;
uint8_t fault_internal :1;
uint8_t fault_vehicle :1;
uint8_t fault_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t :8;
uint16_t limit_value :10; // 0.1 %, 0.2 bar, 0.02 m/s^2, 1023=unlimited
uint8_t :3;
uint8_t req_park_brake :1;
uint8_t req_shift_park :1;
uint8_t brake_available_full :1;
uint8_t brake_available_duration :8; // 2 sec or 100ms
BrkAvlMode brake_available_mux :2;
uint8_t external_button :1;
CmdSrc cmd_src :3;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setLimitValuePercentU16(uint16_t percent, bool valid) {
if (valid) {
constexpr uint16_t MAX = 100 / 0.1;
limit_value = (percent * MAX) / UINT16_MAX;
} else {
limit_value = 0x3FF; // Unlimited
}
}
void setLimitValuePercent(float percent) {
if (std::isfinite(percent)) {
limit_value = std::clamp<float>(std::round(std::abs(percent) / 0.1f), 0, 0x3FE);
} else {
limit_value = 0x3FF; // Unlimited
}
}
float getLimitValuePercent() const {
if (limit_value != 0x3FF) {
return limit_value * 0.1f;
}
return INFINITY; // Unlimited
}
void setLimitValuePressureBar(float bar) {
if (std::isfinite(bar)) {
limit_value = std::clamp<float>(std::round(std::abs(bar) / 0.2f), 0, 0x3FE);
} else {
limit_value = 0x3FF; // Unlimited
}
}
float getLimitValuePressureBar() const {
if (limit_value != 0x3FF) {
return limit_value * 0.2f;
}
return INFINITY; // Unlimited
}
void setLimitValueDecelMps2x1k(int16_t ms2_x1k) {
if (ms2_x1k != INT16_MIN) {
limit_value = std::clamp<uint16_t>(std::abs(ms2_x1k) / 20, 0, 0x3FE);
} else {
limit_value = 0x3FF; // Unlimited
}
}
float getLimitValueDecelMps2() const {
if (limit_value != 0x3FF) {
return limit_value * 0.02f;
}
return INFINITY; // Unlimited
}
void setBrkAvailDurUnlimited() {
brake_available_mux = BrkAvlMode::Unlimited;
}
void setBrkAvailDurSec(uint16_t seconds, uint16_t seconds_full, uint16_t offset = 0) {
brake_available_mux = BrkAvlMode::SecondsX2;
if (seconds != UINT16_MAX) {
if (seconds > offset) {
brake_available_duration = std::min<uint16_t>((seconds - offset) / 2, UINT8_MAX - 1);
} else {
brake_available_duration = 0;
}
brake_available_full = seconds >= seconds_full;
} else {
brake_available_duration = UINT8_MAX;
brake_available_full = false;
}
}
void setBrkAvailDurMs(uint32_t ms, uint32_t ms_full) {
brake_available_mux = BrkAvlMode::MillisecondsX100;
if (ms != UINT32_MAX) {
brake_available_duration = std::min<uint16_t>(ms / 100, UINT8_MAX - 1);
brake_available_full = ms >= ms_full;
} else {
brake_available_duration = UINT8_MAX;
brake_available_full = false;
}
}
bool brkAvailDurValid() const {
return brake_available_duration != UINT8_MAX;
}
float brkAvailDurSec() const {
if (brake_available_mux == BrkAvlMode::Unlimited) {
return INFINITY;
} else if (brkAvailDurValid()) {
if (brake_available_mux == BrkAvlMode::SecondsX2) {
return brake_available_duration * 2;
}
if (brake_available_mux == BrkAvlMode::MillisecondsX100) {
return brake_available_duration * 0.1f;
}
}
return NAN;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgBrakeReport2));
struct MsgBrakeReport3 {
static constexpr uint32_t ID = 0x311;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
uint8_t degraded_comms_dbw :1;
uint8_t degraded_comms_dbw_gateway :1;
uint8_t degraded_comms_dbw_steer :1;
uint8_t degraded_comms_dbw_thrtl :1;
uint8_t degraded_comms_dbw_gear :1;
uint8_t :1;
uint8_t degraded_control_performance :1;
uint8_t degraded_param_mismatch :1;
uint8_t degraded_comms_vehicle :1;
uint8_t :3;
uint8_t degraded_comms_actuator :1;
uint8_t degraded_comms_actuator_1 :1;
uint8_t degraded_comms_actuator_2 :1;
uint8_t :1;
uint8_t degraded_vehicle_speed :1;
uint8_t degraded_btsi_stuck_low :1;
uint8_t degraded_btsi_stuck_high :1;
uint8_t degraded_actuator_aeb_deny :1;
uint8_t degraded_actuator_1 :1;
uint8_t degraded_actuator_2 :1;
uint8_t :1;
uint8_t degraded_calibration :1;
uint8_t fault_comms_dbw :1;
uint8_t fault_comms_dbw_gateway :1;
uint8_t fault_comms_dbw_steer :1;
uint8_t fault_comms_dbw_thrtl :1;
uint8_t fault_comms_dbw_gear :1;
uint8_t :3;
uint8_t fault_comms_vehicle :1;
uint8_t :3;
uint8_t fault_comms_actuator :1;
uint8_t fault_comms_actuator_1 :1;
uint8_t fault_comms_actuator_2 :1;
uint8_t :1;
uint8_t fault_vehicle_speed :1;
uint8_t fault_actuator_acc_deny :1;
uint8_t fault_actuator_pedal_sensor :1;
uint8_t fault_bped_sensor_1 :1;
uint8_t fault_bped_sensor_2 :1;
uint8_t fault_bped_sensor_mismatch :1;
uint8_t fault_actuator_1 :1;
uint8_t fault_actuator_2 :1;
uint8_t :4;
uint8_t fault_control_performance :1;
uint8_t fault_param_mismatch :1;
uint8_t fault_param_limits :1;
uint8_t fault_calibration :1;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(8 == sizeof(MsgBrakeReport3));
struct MsgBrakeParamHash {
static constexpr uint32_t ID = 0x331;
static constexpr size_t PERIOD_MS = 5000;
static constexpr size_t TIMEOUT_MS = 17500;
uint32_t hash;
};
static_assert(4 == sizeof(MsgBrakeParamHash));
struct MsgThrtlCmd {
static constexpr size_t TIMEOUT_MS = 100;
enum class CmdType : uint8_t {
None = 0,
PedalRaw = 13,
Percent = 14,
};
uint16_t cmd; // 0.025 %
CmdType cmd_type :4;
uint8_t enable :1;
uint8_t clear :1;
uint8_t ignore :1;
uint8_t :1; // Launch
uint8_t rate_inc; // 10 %/s, 0 for default, 255 for no limit
uint8_t rate_dec; // 10 %/s, 0 for default, 255 for no limit
uint8_t :8;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCmdPercent(float percent, float inc_percent_s = 0, float dec_percent_s = 0) {
cmd = std::clamp<float>(percent / 0.025f, 0, UINT16_MAX);
if (inc_percent_s < 0 || std::isinf(inc_percent_s)) {
rate_inc = UINT8_MAX; // Unlimited
} else if (inc_percent_s > 0) {
rate_inc = std::clamp<float>(std::round(inc_percent_s / 10), 1, UINT8_MAX - 1);
} else {
rate_inc = 0; // Default
}
if (dec_percent_s < 0 || std::isinf(dec_percent_s)) {
rate_dec = UINT8_MAX; // Unlimited
} else if (dec_percent_s > 0) {
rate_dec = std::clamp<float>(std::round(dec_percent_s / 10), 1, UINT8_MAX - 1);
} else {
rate_dec = 0; // Default
}
}
float cmdPercent() const {
return cmd * 0.025f;
}
float cmdRateIncPercentS() const {
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<float>(rate_inc * 10, 100); // Minimum of 100 %/s
} else {
return INFINITY; // Unlimited
}
}
float cmdRateDecPercentS() const {
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<float>(rate_dec * 10, 100); // Minimum of 100 %/s
} else {
return INFINITY; // Unlimited
}
}
uint16_t cmdPercentU16() const {
constexpr uint16_t MAX = 100 / 0.025;
if (cmd < MAX) {
return (cmd * UINT16_MAX) / MAX;
}
return UINT16_MAX;
}
uint16_t cmdRateIncPercentSU16() const { // %/ms (0.001%/s)
if (rate_inc == 0) {
return 0; // Default
} else if (rate_inc < UINT8_MAX) {
return std::max<uint16_t>(rate_inc * (uint16_t)(10e-2 * 1e-3 * UINT16_MAX), (uint16_t)(100e-2 * 1e-3 * UINT16_MAX)); // Minimum of 100 %/s
} else {
return UINT16_MAX; // Unlimited
}
}
uint16_t cmdRateDecPercentSU16() const { // %/ms (0.001%/s)
if (rate_dec == 0) {
return 0; // Default
} else if (rate_dec < UINT8_MAX) {
return std::max<uint16_t>(rate_dec * (uint16_t)(10e-2 * 1e-3 * UINT16_MAX), (uint16_t)(100e-2 * 1e-3 * UINT16_MAX)); // Minimum of 100 %/s
} else {
return UINT16_MAX; // Unlimited
}
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgThrtlCmd));
struct MsgThrtlCmdRmt : public MsgThrtlCmd {
static constexpr uint32_t ID = 0x202;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgThrtlCmdRmt) == sizeof(MsgThrtlCmd));
struct MsgThrtlCmdUsr : public MsgThrtlCmd {
static constexpr uint32_t ID = 0x212;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgThrtlCmdUsr) == sizeof(MsgThrtlCmd));
struct MsgThrtlCmdUlc : public MsgThrtlCmd {
static constexpr uint32_t ID = 0x222;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgThrtlCmdUlc) == sizeof(MsgThrtlCmd));
struct MsgThrtlReport1 {
static constexpr uint32_t ID = 0x102;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t TIMEOUT_MS = 100;
typedef MsgThrtlCmd::CmdType CmdType;
uint16_t input :12; // 0.025 %, 4095=unknown
uint16_t :2;
uint8_t limiting_value :1;
uint8_t limiting_rate :1;
uint16_t cmd :12; // 0.025 %
CmdType cmd_type :4;
uint16_t output :12; // 0.025 %, 4095=unknown
uint8_t external_control :1;
uint8_t override_active :1;
uint8_t override_other :1;
uint8_t override_latched :1;
uint8_t ready :1;
uint8_t enabled :1;
uint8_t fault :1;
uint8_t timeout :1;
uint8_t bad_crc :1;
uint8_t bad_rc :1;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setPercent(float in_pc, float cmd_pc, float out_pc) {
cmd_type = CmdType::Percent;
if (std::isfinite(in_pc)) {
input = std::clamp<float>(in_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
input = UINT16_MAX >> 4;
}
if (std::isfinite(cmd_pc)) {
cmd = std::clamp<float>(cmd_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
cmd = UINT16_MAX >> 4;
}
if (std::isfinite(out_pc)) {
output = std::clamp<float>(out_pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
output = UINT16_MAX >> 4;
}
}
void setPedalRaw(float in_pc, float cmd_pc, float out_pc) {
setPercent(in_pc, cmd_pc, out_pc);
cmd_type = CmdType::PedalRaw;
}
void setPercentU16(uint16_t in_pc, uint16_t cmd_pc, uint16_t out_pc) {
cmd_type = CmdType::Percent;
constexpr uint16_t MAX = 100 / 0.025;
input = (in_pc * MAX) / UINT16_MAX;
cmd = (cmd_pc * MAX) / UINT16_MAX;
output = (out_pc * MAX) / UINT16_MAX;
}
void setPedalRawU16(uint16_t in_pc, uint16_t cmd_pc, uint16_t out_pc) {
setPercentU16(in_pc, cmd_pc, out_pc);
cmd_type = CmdType::PedalRaw;
}
void getPercent(float &in_pc, float &cmd_pc, float &out_pc) const {
if (input != UINT16_MAX >> 4) {
in_pc = input * 0.025f;
} else {
in_pc = NAN;
}
if (cmd != UINT16_MAX >> 4) {
cmd_pc = cmd * 0.025f;
} else {
cmd_pc = NAN;
}
if (output != UINT16_MAX >> 4) {
out_pc = output * 0.025f;
} else {
out_pc = NAN;
}
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgThrtlReport1));
struct MsgThrtlReport2 {
static constexpr uint32_t ID = 0x302;
static constexpr size_t PERIOD_MS = 200;
static constexpr size_t TIMEOUT_MS = 1000;
uint8_t degraded :1;
uint8_t degraded_cmd_type :1;
uint8_t degraded_comms :1;
uint8_t degraded_internal :1;
uint8_t degraded_vehicle :1;
uint8_t degraded_sensor :1;
uint8_t :1;
uint8_t :1;
uint8_t fault_power :1;
uint8_t fault_comms :1;
uint8_t fault_internal :1;
uint8_t fault_vehicle :1;
uint8_t fault_sensor :1;
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint16_t limit_value :10; // 0.1 %, 1023=unlimited
uint8_t :1;
CmdSrc cmd_src :3;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
limit_value = 0x3FF;
rc = save;
}
void setLimitValuePcU16(uint16_t pc, bool valid) {
if (valid) {
constexpr uint16_t MAX = 100 / 0.1;
limit_value = (pc * MAX) / UINT16_MAX;
} else {
limit_value = 0x3FF; // Unlimited
}
}
float getLimitValuePc() const {
if (limit_value != 0x3FF) {
return limit_value * 0.1f;
}
return INFINITY; // Unlimited
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgThrtlReport2));
struct MsgThrtlReport3 {
static constexpr uint32_t ID = 0x312;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
uint8_t degraded_comms_dbw :1;
uint8_t degraded_comms_dbw_gateway :1;
uint8_t degraded_comms_dbw_steer :1;
uint8_t degraded_comms_dbw_brake :1;
uint8_t degraded_comms_dbw_gear :1;
uint8_t :1;
uint8_t degraded_control_performance :1;
uint8_t degraded_param_mismatch :1;
uint8_t degraded_vehicle_speed :1;
uint8_t degraded_aped_feedback :1;
uint8_t degraded_actuator_pedal_sensor :1;
uint8_t :5;
uint8_t :7;
uint8_t degraded_calibration :1;
uint8_t fault_comms_dbw :1;
uint8_t fault_comms_dbw_gateway :1;
uint8_t fault_comms_dbw_steer :1;
uint8_t fault_comms_dbw_brake :1;
uint8_t fault_comms_dbw_gear :1;
uint8_t :3;
uint8_t fault_vehicle_speed :1;
uint8_t fault_aped_sensor_1 :1;
uint8_t fault_aped_sensor_2 :1;
uint8_t fault_aped_sensor_mismatch :1;
uint8_t fault_actuator_pedal_sensor :1;
uint8_t :3;
uint8_t :8;
uint8_t :4;
uint8_t fault_control_performance :1;
uint8_t fault_param_mismatch :1;
uint8_t fault_param_limits :1;
uint8_t fault_calibration :1;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(8 == sizeof(MsgThrtlReport3));
struct MsgThrtlParamHash {
static constexpr uint32_t ID = 0x332;
static constexpr size_t PERIOD_MS = 5000;
static constexpr size_t TIMEOUT_MS = 17500;
uint32_t hash;
};
static_assert(4 == sizeof(MsgThrtlParamHash));
struct MsgGearCmd {
static constexpr size_t TIMEOUT_MS = 0; // Event based
Gear cmd :4;
uint8_t :4;
uint8_t :8;
uint8_t :8;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
};
static_assert(4 == sizeof(MsgGearCmd));
struct MsgGearCmdRmt : public MsgGearCmd {
static constexpr uint32_t ID = 0x203;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgGearCmdRmt) == sizeof(MsgGearCmd));
struct MsgGearCmdUsr : public MsgGearCmd {
static constexpr uint32_t ID = 0x213;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgGearCmdUsr) == sizeof(MsgGearCmd));
struct MsgGearCmdUlc : public MsgGearCmd {
static constexpr uint32_t ID = 0x223;
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(sizeof(MsgGearCmdUlc) == sizeof(MsgGearCmd));
struct MsgGearReport1 {
static constexpr uint32_t ID = 0x103;
static constexpr size_t PERIOD_MIN = 20;
static constexpr size_t PERIOD_MS = 100;
static constexpr size_t PERIOD_MAX = 100;
static constexpr size_t TIMEOUT_MS = 350;
enum class Reject : uint8_t {
None = 0, // Not rejected
Fault = 1, // System in fault state
Unsupported = 2, // Unsupported gear command
ShiftInProgress = 3, // Shift in progress
Override = 4, // Override on brake, throttle, or steering
BrakeHold = 5, // Brake hold time depleted, stay in park
VehicleSpeed = 6, // Excessive vehicle speed
Vehicle = 7, // Rejected by vehicle (try pressing the brakes)
};
Gear gear :4;
Gear cmd :4;
Gear driver :4;
Reject reject :3;
uint8_t :1; // Future expansion of reject
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t power_latched :1;
uint8_t :3;
uint8_t external_control :1;
uint8_t override_active :1;
uint8_t override_other :1;
uint8_t :1; // override_latched
uint8_t ready :1;
uint8_t :1; // enabled
uint8_t fault :1;
uint8_t :1; // timeout
uint8_t bad_crc :1;
uint8_t :1; // bad_rc
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgGearReport1));
struct MsgGearReport2 {
static constexpr uint32_t ID = 0x303;
static constexpr size_t PERIOD_MS = 200;
static constexpr size_t TIMEOUT_MS = 1000;
uint8_t degraded :1;
uint8_t degraded_cmd_type :1;
uint8_t degraded_comms :1;
uint8_t degraded_internal :1;
uint8_t degraded_vehicle :1;
uint8_t degraded_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t fault_power :1;
uint8_t fault_comms :1;
uint8_t fault_internal :1;
uint8_t fault_vehicle :1;
uint8_t fault_actuator :1;
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t :3;
CmdSrc cmd_src :3;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgGearReport2));
struct MsgGearReport3 {
static constexpr uint32_t ID = 0x313;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
uint8_t degraded_comms_dbw :1;
uint8_t degraded_comms_dbw_gateway :1;
uint8_t degraded_comms_dbw_steer :1;
uint8_t degraded_comms_dbw_brake :1;
uint8_t degraded_comms_dbw_thrtl :1;
uint8_t :1;
uint8_t degraded_control_performance :1;
uint8_t degraded_param_mismatch :1;
uint8_t degraded_comms_vehicle :1;
uint8_t degraded_comms_vehicle_1 :1;
uint8_t degraded_comms_vehicle_2 :1;
uint8_t :1;
uint8_t degraded_comms_actuator :1;
uint8_t degraded_comms_actuator_1 :1;
uint8_t degraded_comms_actuator_2 :1;
uint8_t :1;
uint8_t degraded_vehicle_speed :1;
uint8_t degraded_gear_mismatch :1;
uint8_t :4;
uint8_t degraded_power :1;
uint8_t degraded_calibration :1;
uint8_t fault_comms_dbw :1;
uint8_t fault_comms_dbw_gateway :1;
uint8_t fault_comms_dbw_steer :1;
uint8_t fault_comms_dbw_brake :1;
uint8_t fault_comms_dbw_thrtl :1;
uint8_t :3;
uint8_t fault_comms_vehicle :1;
uint8_t fault_comms_vehicle_1 :1;
uint8_t fault_comms_vehicle_2 :1;
uint8_t :1;
uint8_t fault_comms_actuator :1;
uint8_t fault_comms_actuator_1 :1;
uint8_t fault_comms_actuator_2 :1;
uint8_t :1;
uint8_t fault_vehicle_speed :1;
uint8_t :7;
uint8_t :1;
uint8_t fault_actuator_config :1;
uint8_t :3;
uint8_t fault_param_mismatch :1;
uint8_t :1;
uint8_t fault_calibration :1;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(8 == sizeof(MsgGearReport3));
struct MsgMonitorCmd {
static constexpr uint32_t ID = 0x215;
static constexpr size_t TIMEOUT_MS = 250;
enum class CmdType : uint8_t {
None = 0,
ActivateTestFault = 1,
ClearTestFault = 2,
};
CmdType cmd_type :2;
uint8_t :6;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(2 == sizeof(MsgMonitorCmd));
struct MsgMonitorReport1 {
static constexpr uint32_t ID = 0x105;
static constexpr size_t PERIOD_MIN = 20;
static constexpr size_t PERIOD_MS = 100;
static constexpr size_t PERIOD_MAX = 100;
static constexpr size_t TIMEOUT_MS = 250;
enum class Fault : uint8_t {
Unknown = 0,
None = 1,
Fault = 2,
};
static constexpr Fault mergeFaults(Fault a, Fault b) {
if (a == Fault::Fault || b == Fault::Fault) {
return Fault::Fault;
}
if (a == Fault::Unknown || b == Fault::Unknown) {
return Fault::Unknown;
}
return Fault::None;
}
bool fault :1;
bool shutoff :1;
bool shutoff_on_motion :1;
bool stationary :1;
uint8_t :2;
Fault fault_test :2; // Test fault to verify shutoff action
Fault fault_system :2; // Any fault related to system enable/disable
Fault fault_steer :2; // Any fault related to steer control
Fault fault_brake :2; // Any fault related to brake control
Fault fault_thrtl :2; // Any fault related to throttle control
Fault fault_gear :2; // Any fault related to gear control
Fault fault_ulc :2; // Any fault related to the ULC
uint8_t :2;
Fault fault_vehicle_velocity :2; // Vehicle velocity measurement mismatch with OEM
uint8_t steer_cmd_match_oem :1;
uint8_t steer_cmd_match_dbw :1;
uint8_t brake_cmd_match_oem :1;
uint8_t brake_cmd_match_dbw :1;
uint8_t thrtl_cmd_match_oem :1;
uint8_t thrtl_cmd_match_dbw :1;
uint8_t gear_cmd_match_oem :1;
uint8_t gear_cmd_match_dbw :1;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(6 == sizeof(MsgMonitorReport1));
struct MsgMonitorReport2 {
static constexpr uint32_t ID = 0x305;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
using Fault = MsgMonitorReport1::Fault;
Fault fault_steer_feedback :2; // Steering wheel angle measurement mismatch with OEM
Fault fault_steer_input :2; // Steering column torque measurement mismatch with OEM
Fault fault_steer_param :2; // Steering parameter mismatch with DBW
Fault fault_steer_limit :2; // Steering limit calculation mismatch with DBW
Fault fault_steer_override :2; // Steering override calculation mismatch with DBW
Fault fault_steer_cmd :2; // Steering actuator command mismatch with OEM and DBW
Fault fault_steer_cmd_rate :2; // Steering actuator command rate faster than DBW and limit
Fault fault_steer_cmd_en :2; // Steering actuator command without matching DBW command enable
Fault fault_steer_cmd_sys :2; // Steering actuator command with DBW system disabled
Fault fault_steer_cmd_ovr :2; // Steering actuator command with override
uint8_t :4;
Fault fault_brake_feedback :2; // Brake actuator output torque/pressure measurement mismatch with OEM
Fault fault_brake_input :2; // Brake pedal input torque/pressure measurement mismatch with OEM
Fault fault_brake_param :2; // Brake parameter mismatch with DBW
Fault fault_brake_limit :2; // Brake limit calculation mismatch with DBW
Fault fault_brake_override :2; // Brake override calculation mismatch with DBW
Fault fault_brake_cmd :2; // Brake actuator command mismatch with OEM and DBW
Fault fault_brake_cmd_ulc :2; // Brake command generated by ULC without matching ULC command
Fault fault_brake_cmd_en :2; // Brake actuator command without matching DBW command enable
Fault fault_brake_cmd_sys :2; // Brake actuator command with DBW system disabled
Fault fault_brake_cmd_ovr :2; // Brake actuator command with override
uint8_t :4;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgMonitorReport2));
struct MsgMonitorReport3 {
static constexpr uint32_t ID = 0x315;
static constexpr size_t PERIOD_MS = 1000;
static constexpr size_t TIMEOUT_MS = 3500;
using Fault = MsgMonitorReport1::Fault;
Fault fault_thrtl_feedback :2; // Accelerator pedal output measurement mismatch with OEM
Fault fault_thrtl_input :2; // Accelerator pedal input measurement mismatch with OEM
Fault fault_thrtl_param :2; // Throttle parameter mismatch with DBW
Fault fault_thrtl_limit :2; // Throttle limit calculation mismatch with DBW
Fault fault_thrtl_override :2; // Throttle override calculation mismatch with DBW
Fault fault_thrtl_cmd :2; // Throttle actuator command mismatch with OEM and DBW
Fault fault_thrtl_cmd_ulc :2; // Throttle command generated by ULC without matching ULC command
Fault fault_thrtl_cmd_en :2; // Throttle actuator command without matching DBW command enable
Fault fault_thrtl_cmd_sys :2; // Throttle actuator command with DBW system disabled
Fault fault_thrtl_cmd_ovr :2; // Throttle actuator command with override
uint8_t :4;
Fault fault_gear_feedback :2; // Transmission gear measurement mismatch with OEM
Fault fault_gear_input :2; // Gear input selection measurement mismatch with OEM
Fault fault_gear_param :2; // Gear parameter mismatch with DBW
Fault fault_gear_override :2; // Gear override calculation mismatch with DBW
Fault fault_gear_cmd :2; // Gear actuator command mismatch with OEM and DBW
Fault fault_gear_cmd_ulc :2; // Gear command generated by ULC without matching ULC command
uint8_t :4;
uint8_t :8;
uint8_t :2;
Fault fault_system_param :2; // System parameter mismatch with DBW
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgMonitorReport3));
struct MsgMonitorThrtl {
static constexpr uint32_t ID = 0x2A7;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t TIMEOUT_MS = 100;
uint16_t pedal_pc :12; // 0.025 %
Quality pedal_qf :2;
uint8_t :2;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setPercent(float percent, Quality quality) {
pedal_pc = std::clamp<float>(percent / 0.025f, 0, UINT16_MAX >> 4);
pedal_qf = quality;
}
void setPercentU16(uint16_t percent, Quality quality) {
constexpr uint16_t MAX = 100 / 0.025;
pedal_pc = (percent * MAX) / UINT16_MAX;
pedal_qf = quality;
}
float getPercent() const {
return pedal_pc * 0.025f;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(4 == sizeof(MsgMonitorThrtl));
struct MsgSystemCmd {
static constexpr uint32_t ID = 0x216;
static constexpr size_t TIMEOUT_MS = 100;
enum class Cmd : uint8_t {
None = 0,
Enable = 1,
Disable = 2,
};
Cmd cmd :2;
uint8_t :2;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(2 == sizeof(MsgSystemCmd));
struct MsgSystemReport {
static constexpr uint32_t ID = 0x106;
static constexpr size_t PERIOD_MIN = 20;
static constexpr size_t PERIOD_MS = 100;
static constexpr size_t PERIOD_MAX = 100;
static constexpr size_t TIMEOUT_MS = 250;
enum class State : uint8_t {
Manual = 0, // Not ready
Ready = 1,
Active = 2,
Fault = 7,
};
enum class ReasonNotReady : uint8_t {
None = 0x00,
MissingReportSteer = 0x10,
MissingReportBrake = 0x11,
MissingReportThrtl = 0x12,
MissingReportGear = 0x13,
FaultSteer = 0x18,
FaultBrake = 0x19,
FaultThrtl = 0x1A,
FaultGear = 0x1B,
OverrideActiveSteer = 0x20,
OverrideActiveBrake = 0x21,
OverrideActiveThrtl = 0x22,
OverrideActiveGear = 0x23,
OverrideLatchedSteer = 0x24,
OverrideLatchedBrake = 0x25,
OverrideLatchedThrtl = 0x26,
OverrideOtherSteer = 0x28,
OverrideOtherBrake = 0x29,
OverrideOtherThrtl = 0x2A,
OverrideOtherGear = 0x2B,
NotReadySteer = 0x30,
NotReadyBrake = 0x31,
NotReadyThrtl = 0x32,
MissingCmdSteer = 0x38,
MissingCmdBrake = 0x39,
MissingCmdThrtl = 0x3A,
LockoutVehicleVelocity = 0xA0,
LockoutVehicleAccel = 0xA1,
LockoutGearReverse = 0xA2,
NotEnableCmdSteer = 0xC0,
NotEnableCmdBrake = 0xC1,
NotEnableCmdThrtl = 0xC2,
SystemReengageDelay = 0xF8,
SystemLockout = 0xFA,
SystemDisabled = 0xFE,
Unknown = 0xFF,
};
enum class ReasonDisengage : uint8_t {
None = 0x00,
LockoutVehicleVelocity = 0x10,
LockoutVehicleAccel = 0x11,
LockoutGearReverse = 0x12,
SteerCmdDisengage = 0x21,
SteerCmdInvalidCrc = 0x22,
SteerCmdInvalidRc = 0x23,
SteerCmdTimeout = 0x24,
SteerRptFault = 0x25,
SteerRptOverride = 0x26,
SteerRptDisengage = 0x27,
BrakeCmdDisengage = 0x41,
BrakeCmdInvalidCrc = 0x42,
BrakeCmdInvalidRc = 0x43,
BrakeCmdTimeout = 0x44,
BrakeRptFault = 0x45,
BrakeRptOverride = 0x46,
BrakeRptDisengage = 0x47,
ThrtlCmdDisengage = 0x61,
ThrtlCmdInvalidCrc = 0x62,
ThrtlCmdInvalidRc = 0x63,
ThrtlCmdTimeout = 0x64,
ThrtlRptFault = 0x65,
ThrtlRptOverride = 0x66,
ThrtlRptDisengage = 0x67,
GearRptFault = 0x85,
GearRptOverride = 0x86,
ExternalBrake = 0xA0,
SystemDisableCmd = 0xC0,
SystemDisableBtn = 0xC1,
Unknown = 0xFF,
};
static constexpr const char * reasonToString(ReasonNotReady x) {
switch (x) {
case ReasonNotReady::None: return "";
case ReasonNotReady::MissingReportSteer: return "MissingReportSteer";
case ReasonNotReady::MissingReportBrake: return "MissingReportBrake";
case ReasonNotReady::MissingReportThrtl: return "MissingReportThrtl";
case ReasonNotReady::MissingReportGear: return "MissingReportGear";
case ReasonNotReady::FaultSteer: return "FaultSteer";
case ReasonNotReady::FaultBrake: return "FaultBrake";
case ReasonNotReady::FaultThrtl: return "FaultThrtl";
case ReasonNotReady::FaultGear: return "FaultGear";
case ReasonNotReady::OverrideActiveSteer: return "OverrideActiveSteer";
case ReasonNotReady::OverrideActiveBrake: return "OverrideActiveBrake";
case ReasonNotReady::OverrideActiveThrtl: return "OverrideActiveThrtl";
case ReasonNotReady::OverrideActiveGear: return "OverrideActiveGear";
case ReasonNotReady::OverrideLatchedSteer: return "OverrideLatchedSteer";
case ReasonNotReady::OverrideLatchedBrake: return "OverrideLatchedBrake";
case ReasonNotReady::OverrideLatchedThrtl: return "OverrideLatchedThrtl";
case ReasonNotReady::OverrideOtherSteer: return "OverrideOtherSteer";
case ReasonNotReady::OverrideOtherBrake: return "OverrideOtherBrake";
case ReasonNotReady::OverrideOtherThrtl: return "OverrideOtherThrtl";
case ReasonNotReady::OverrideOtherGear: return "OverrideOtherGear";
case ReasonNotReady::NotReadySteer: return "NotReadySteer";
case ReasonNotReady::NotReadyBrake: return "NotReadyBrake";
case ReasonNotReady::NotReadyThrtl: return "NotReadyThrtl";
case ReasonNotReady::MissingCmdSteer: return "MissingCmdSteer";
case ReasonNotReady::MissingCmdBrake: return "MissingCmdBrake";
case ReasonNotReady::MissingCmdThrtl: return "MissingCmdThrtl";
case ReasonNotReady::LockoutVehicleVelocity: return "LockoutVehicleVelocity";
case ReasonNotReady::LockoutVehicleAccel: return "LockoutVehicleAccel";
case ReasonNotReady::LockoutGearReverse: return "LockoutGearReverse";
case ReasonNotReady::NotEnableCmdSteer: return "NotEnableCmdSteer";
case ReasonNotReady::NotEnableCmdBrake: return "NotEnableCmdBrake";
case ReasonNotReady::NotEnableCmdThrtl: return "NotEnableCmdThrtl";
case ReasonNotReady::SystemReengageDelay: return "SystemReengageDelay";
case ReasonNotReady::SystemLockout: return "SystemLockout";
case ReasonNotReady::SystemDisabled: return "SystemDisabled";
case ReasonNotReady::Unknown: default: return "Unknown";
}
}
static constexpr const char * reasonToString(ReasonDisengage x) {
switch (x) {
case ReasonDisengage::None: return "";
case ReasonDisengage::LockoutVehicleVelocity: return "LockoutVehicleVelocity";
case ReasonDisengage::LockoutVehicleAccel: return "LockoutVehicleAccel";
case ReasonDisengage::LockoutGearReverse: return "LockoutGearReverse";
case ReasonDisengage::SteerCmdDisengage: return "SteerCmdDisengage";
case ReasonDisengage::SteerCmdInvalidCrc: return "SteerCmdInvalidCrc";
case ReasonDisengage::SteerCmdInvalidRc: return "SteerCmdInvalidRc";
case ReasonDisengage::SteerCmdTimeout: return "SteerCmdTimeout";
case ReasonDisengage::SteerRptFault: return "SteerRptFault";
case ReasonDisengage::SteerRptOverride: return "SteerRptOverride";
case ReasonDisengage::SteerRptDisengage: return "SteerRptDisengage";
case ReasonDisengage::BrakeCmdDisengage: return "BrakeCmdDisengage";
case ReasonDisengage::BrakeCmdInvalidCrc: return "BrakeCmdInvalidCrc";
case ReasonDisengage::BrakeCmdInvalidRc: return "BrakeCmdInvalidRc";
case ReasonDisengage::BrakeCmdTimeout: return "BrakeCmdTimeout";
case ReasonDisengage::BrakeRptFault: return "BrakeRptFault";
case ReasonDisengage::BrakeRptOverride: return "BrakeRptOverride";
case ReasonDisengage::BrakeRptDisengage: return "BrakeRptDisengage";
case ReasonDisengage::ThrtlCmdDisengage: return "ThrtlCmdDisengage";
case ReasonDisengage::ThrtlCmdInvalidCrc: return "ThrtlCmdInvalidCrc";
case ReasonDisengage::ThrtlCmdInvalidRc: return "ThrtlCmdInvalidRc";
case ReasonDisengage::ThrtlCmdTimeout: return "ThrtlCmdTimeout";
case ReasonDisengage::ThrtlRptFault: return "ThrtlRptFault";
case ReasonDisengage::ThrtlRptOverride: return "ThrtlRptOverride";
case ReasonDisengage::ThrtlRptDisengage: return "ThrtlRptDisengage";
case ReasonDisengage::GearRptFault: return "GearRptFault";
case ReasonDisengage::GearRptOverride: return "GearRptOverride";
case ReasonDisengage::ExternalBrake: return "ExternalBrake";
case ReasonDisengage::SystemDisableCmd: return "SystemDisableCmd";
case ReasonDisengage::SystemDisableBtn: return "SystemDisableBtn";
case ReasonDisengage::Unknown: default: return "Unknown";
}
}
uint8_t inhibit :1; // Inhibit control for steer/brake/throttle/gear
uint8_t validate_cmd_crc_rc :1; // Parameter ValidateCmdCrcRc value
SystemSyncMode system_sync_mode :3; // Parameter SystemSyncMode value
uint8_t btn_enable :1;
uint8_t btn_disable :1;
uint8_t :1;
ReasonDisengage reason_disengage :8;
ReasonNotReady reason_not_ready :8;
uint16_t time_phase :10; // Milliseconds 0-999, 0x3FF for unknown
State state :3;
uint8_t :3;
uint8_t :6;
uint8_t lockout :1;
uint8_t override :1; // Any steer/brake/throttle/gear override
uint8_t ready :1; // All steer/brake/throttle ready, and gear not faulted
uint8_t enabled :1; // All steer/brake/throttle enabled, and gear not faulted (or any steer/brake/throttle enabled for Mode>=AllOrNone)
uint8_t fault :1; // Any steer/brake/throttle/gear fault
uint8_t /*timeout*/ :1;
uint8_t bad_crc :1; // Invalid CRC in MsgSystemCmd
uint8_t bad_rc :1; // Invalid rolling counter in MsgSystemCmd
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
time_phase = UINT16_MAX >> 6;
rc = save;
}
void setTimePhaseMs(size_t ms) {
time_phase = ms % 1000;
}
bool timePhaseValid() const {
return time_phase != UINT16_MAX >> 6;
}
size_t timePhaseMs() const {
if (timePhaseValid()) {
return time_phase;
}
return SIZE_MAX;
}
bool operator==(const MsgSystemReport& _other) const {
return memcmp(this, &_other, sizeof(*this)) == 0;
}
bool operator!=(const MsgSystemReport& _other) const {
return !(*this == _other);
}
bool needsUpdate(const MsgSystemReport& previous) const {
// Check for changes and ignore signals that always change
MsgSystemReport self = *this;
self.time_phase = previous.time_phase;
self.rc = previous.rc;
self.crc = previous.crc;
return self != previous;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgSystemReport));
struct MsgVehicleVelocity {
static constexpr uint32_t ID = 0x107;
static constexpr size_t PERIOD_MIN = 8;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t PERIOD_MAX = 50;
static constexpr size_t TIMEOUT_MS = 200;
enum class DirSrc : uint8_t {
None = 0, // Direction unknown
PRNDL = 1, // Transmission reverse gear sets negative direction
Sensor = 2, // Explicit wheel speed/direction sensors
};
int16_t veh_vel_brk :16; // 0.01 kph, measured by brakes
int16_t veh_vel_prpl :16; // 0.01 kph, measured by propulsion
uint8_t :8;
uint8_t :8;
DirSrc dir_src :2;
uint8_t :4;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setVelocityBrkKph(float kph) {
if (std::isfinite(kph)) {
veh_vel_brk = std::clamp<float>(kph * 100, -INT16_MAX, INT16_MAX);
} else {
veh_vel_brk = INT16_MIN;
}
}
bool velocityBrkValid() const {
return veh_vel_brk != INT16_MIN;
}
int16_t velocityBrkKphx100() const {
return veh_vel_brk;
}
float velocityBrkKph() const {
if (velocityBrkValid()) {
return veh_vel_brk * 0.01f;
}
return NAN; // Invalid
}
float velocityBrkMps() const {
if (velocityBrkValid()) {
return veh_vel_brk * (0.01f / 3.6f);
}
return NAN; // Invalid
}
void setVelocityPrplKph(float kph) {
if (std::isfinite(kph)) {
veh_vel_prpl = std::clamp<float>(kph * 100, -INT16_MAX, INT16_MAX);
} else {
veh_vel_prpl = INT16_MIN;
}
}
bool velocityPrplValid() const {
return veh_vel_prpl != INT16_MIN;
}
int16_t velocityPrplKphx100() const {
return veh_vel_prpl;
}
float velocityPrplKph() const {
if (velocityPrplValid()) {
return veh_vel_prpl * 0.01f;
}
return NAN; // Invalid
}
float velocityPrplMps() const {
if (velocityPrplValid()) {
return veh_vel_prpl * (0.01f / 3.6f);
}
return NAN; // Invalid
}
bool velocityValid() const {
return veh_vel_brk != INT16_MIN
|| veh_vel_prpl != INT16_MIN;
}
int16_t velocityKphx100() const {
if (veh_vel_brk != INT16_MIN) {
return veh_vel_brk;
}
if (veh_vel_prpl != INT16_MIN) {
return veh_vel_prpl;
}
return INT16_MIN; // Invalid
}
float velocityKph() const {
if (veh_vel_brk != INT16_MIN) {
return veh_vel_brk * 0.01f;
}
if (veh_vel_prpl != INT16_MIN) {
return veh_vel_prpl * 0.01f;
}
return NAN; // Invalid
}
float velocityMps() const {
if (veh_vel_brk != INT16_MIN) {
return veh_vel_brk * (0.01f / 3.6f);
}
if (veh_vel_prpl != INT16_MIN) {
return veh_vel_prpl * (0.01f / 3.6f);
}
return NAN; // Invalid
}
bool velocityZero() const {
if (veh_vel_brk == INT16_MIN) {
return veh_vel_prpl == 0;
}
if (veh_vel_prpl == INT16_MIN) {
return veh_vel_brk == 0;
}
return veh_vel_brk == 0
&& veh_vel_prpl == 0;
}
uint16_t speedKphx100() const {
if (veh_vel_brk != INT16_MIN) {
return std::abs(veh_vel_brk);
}
if (veh_vel_prpl != INT16_MIN) {
return std::abs(veh_vel_prpl);
}
return UINT16_MAX; // Invalid
}
float speedKph() const {
return std::abs(velocityKph());
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgVehicleVelocity));
struct MsgThrtlInfo {
static constexpr uint32_t ID = 0x109;
static constexpr size_t PERIOD_MIN = 8;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t PERIOD_MAX = 25;
static constexpr size_t TIMEOUT_MS = 200;
enum class OnePedalMode : uint8_t {
Unknown = 0,
Off = 1,
On = 2,
Fault = 3,
};
uint16_t accel_pedal_pc :12; // 0.025 %, 0x3FF=unknown
Quality accel_pedal_qf :2;
OnePedalMode one_pedal_drive :2;
uint16_t engine_rpm; // 0.25 RPM, 0xFFFF=unknown
uint8_t :8;
uint8_t /*gear_num*/ :8;
uint8_t :6;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
accel_pedal_qf = Quality::NoData;
accel_pedal_pc = UINT16_MAX >> 4;
one_pedal_drive = OnePedalMode::Unknown;
engine_rpm = UINT16_MAX;
rc = save;
}
void setAccelPedalPercent(float pc) {
if (std::isfinite(pc)) {
accel_pedal_pc = std::clamp<float>(pc / 0.025f, 0, (UINT16_MAX >> 4) - 1);
} else {
accel_pedal_pc = UINT16_MAX >> 4;
}
}
bool accelPedalPercentValid() const {
return accel_pedal_pc != UINT16_MAX >> 4;
}
bool accelPedalZero() const {
switch (accel_pedal_qf) {
case Quality::Ok:
case Quality::Partial:
return accel_pedal_pc == 0;
case Quality::NoData:
case Quality::Fault:
default:
return false;
}
}
float accelPedalPercent() const {
if (accelPedalPercentValid()) {
return accel_pedal_pc * 0.025f;
}
return NAN;
}
uint16_t accelPedalPercentU16() const {
if (accelPedalPercentValid()) {
constexpr uint16_t MAX = 100 / 0.025;
if (accel_pedal_pc < MAX) {
return (accel_pedal_pc * UINT16_MAX) / MAX;
}
return UINT16_MAX;
}
return 0;
}
void setEngineRpm(float rpm) {
if (std::isfinite(rpm)) {
engine_rpm = std::clamp<float>(rpm * 4, 0, UINT16_MAX - 1);
} else {
engine_rpm = UINT16_MAX >> 4;
}
}
bool engineRpmValid() const {
return engine_rpm != UINT16_MAX;
}
bool engineRpmZero() const {
return engine_rpm == 0;
}
float engineRpm() const {
if (engineRpmValid()) {
return engine_rpm * 0.25f;
}
return NAN;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgThrtlInfo));
struct MsgBrakeInfo {
static constexpr uint32_t ID = 0x10A;
static constexpr size_t PERIOD_MIN = 8;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t PERIOD_MAX = 25;
static constexpr size_t TIMEOUT_MS = 200;
uint16_t brake_torque_pedal :13; // 4 Nm, 0x1FFF for unknown
Quality brake_pedal_qf :2;
uint8_t :1;
uint16_t brake_torque_request :13; // 4 Nm, 0x1FFF for unknown
uint8_t abs_active :1;
uint8_t abs_enabled :1;
uint8_t esc_active :1;
uint16_t brake_torque_actual :13; // 4 Nm, 0x1FFF for unknown
uint8_t esc_enabled :1;
uint8_t trac_active :1;
uint8_t trac_enabled :1;
uint8_t brake_vacuum :6; // 0.016 bar, 0x3F for unknown
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
brake_pedal_qf = Quality::NoData;
brake_torque_pedal = UINT16_MAX >> 3;
brake_torque_request = UINT16_MAX >> 3;
brake_torque_actual = UINT16_MAX >> 3;
rc = save;
}
void setBrakeTorquePedalNm(uint16_t nm) { brake_torque_pedal = setBrakeTorqueNm(nm); }
void setBrakeTorquePedalNm(float nm) { brake_torque_pedal = setBrakeTorqueNm(nm); }
void setBrakeTorqueRequestNm(uint16_t nm) { brake_torque_request = setBrakeTorqueNm(nm); }
void setBrakeTorqueRequestNm(float nm) { brake_torque_request = setBrakeTorqueNm(nm); }
void setBrakeTorqueActualNm(uint16_t nm) { brake_torque_actual = setBrakeTorqueNm(nm); }
void setBrakeTorqueActualNm(float nm) { brake_torque_actual = setBrakeTorqueNm(nm); }
bool brakeTorquePedalValid() const { return brakeTorqueValid(brake_torque_pedal); }
bool brakeTorqueRequestValid() const { return brakeTorqueValid(brake_torque_request); }
bool brakeTorqueActualValid() const { return brakeTorqueValid(brake_torque_actual); }
float brakeTorquePedalNm() const { return brakeTorqueNm(brake_torque_pedal); }
float brakeTorqueRequestNm() const { return brakeTorqueNm(brake_torque_request); }
float brakeTorqueActualNm() const { return brakeTorqueNm(brake_torque_actual); }
void setBrakeVacuumBarX1000(uint16_t mbar) {
if (mbar != UINT16_MAX) {
brake_vacuum = std::clamp<uint16_t>(mbar / 16, 0, 0x3E);
} else {
brake_vacuum = 0x3F;
}
}
uint16_t brakeVacuumBarX1000() const {
return brake_vacuum != 0x3F ? brake_vacuum * 16 : UINT16_MAX;
}
float brakeVacuumBar() const {
return brake_vacuum != 0x3F ? brake_vacuum * 16e-3f : NAN;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
private:
static uint16_t setBrakeTorqueNm(uint16_t torque) {
if (torque != UINT16_MAX) {
return std::clamp<uint16_t>(torque / 4, 0, (UINT16_MAX >> 3) - 1);
} else {
return UINT16_MAX >> 3;
}
}
static uint16_t setBrakeTorqueNm(float torque) {
if (std::isfinite(torque)) {
return std::clamp<float>(torque / 4, 0, (UINT16_MAX >> 3) - 1);
} else {
return UINT16_MAX >> 3;
}
}
static bool brakeTorqueValid(uint16_t torque) {
return torque != UINT16_MAX >> 3;
}
static float brakeTorqueNm(uint16_t torque) {
if (brakeTorqueValid(torque)) {
return torque * 4;
}
return NAN;
}
};
static_assert(8 == sizeof(MsgBrakeInfo));
struct MsgUlcCmd {
static constexpr uint32_t ID = 0x284;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t TIMEOUT_MS = 100;
enum class CmdType : uint8_t {
None = 0,
Velocity = 1, // 0.0025 m/s
Accel = 2, // 0.0005 m/s^2
};
enum class CoastDecel : uint8_t {
UseBrakes = 0, // Use brakes to slow down
NoBrakes = 1, // Coast, don't use brakes
};
int16_t cmd :16; // Interpretation changes with cmd_type
CmdType cmd_type :3;
uint8_t :1;
uint8_t enable :1;
uint8_t clear :1;
uint8_t :2;
uint8_t enable_shift :1;
uint8_t enable_shift_park :1;
CoastDecel coast_decel :1;
uint8_t :5;
uint8_t :8;
uint8_t :8;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCmdVelocityMps(float velocity_m_s) {
cmd = std::clamp<float>(std::round(velocity_m_s / 0.0025f), -INT16_MAX, INT16_MAX);
}
void setCmdAccelMps(float accel_m_s) {
cmd = std::clamp<float>(std::round(accel_m_s / 0.0005f), -INT16_MAX, INT16_MAX);
}
float cmdVelocityMps() const {
return std::clamp(cmd * 0.0025f, -7.0f, 45.0f);
}
float cmdAccelMps() const {
return std::clamp(cmd * 0.0005f, -6.0f, 3.0f);
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgUlcCmd));
struct MsgUlcCfg {
static constexpr uint32_t ID = 0x285;
static constexpr size_t PERIOD_MS = 200;
static constexpr size_t TIMEOUT_MS = 1000;
uint8_t limit_accel; // 0.025 m/s^2, 0.3 to 3.0 m/s^2
uint8_t limit_decel; // 0.025 m/s^2, 0.3 to 6.0 m/s^2
uint8_t limit_jerk_throttle; // 0.1 m/s^3, 1.0 to 25.5 m/s^3
uint8_t limit_jerk_brake; // 0.1 m/s^3, 1.0 to 25.5 m/s^3
uint8_t :8;
uint8_t :8;
uint8_t :4;
uint8_t rc :4;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
bool operator==(const MsgUlcCfg& _other) const {
return memcmp(this, &_other, sizeof(*this)) == 0;
}
bool operator!=(const MsgUlcCfg& _other) const {
return !(*this == _other);
}
void setLimitAccelMps(float accel_m_s2) {
if (accel_m_s2 < 0 || std::isinf(accel_m_s2)) {
limit_accel = UINT8_MAX; // Maximum
} else if (accel_m_s2 > 0) {
limit_accel = std::clamp<float>(std::round(accel_m_s2 * 40), 1, UINT8_MAX - 1);
} else {
limit_accel = 0; // Default
}
}
void setLimitDecelMps(float decel_m_s2) {
if (decel_m_s2 < 0 || std::isinf(decel_m_s2)) {
limit_decel = UINT8_MAX; // Maximum
} else if (decel_m_s2 > 0) {
limit_decel = std::clamp<float>(std::round(decel_m_s2 * 40), 1, UINT8_MAX - 1);
} else {
limit_decel = 0; // Default
}
}
void setLimitJerkThrottleMps(float jerk_m_s3) {
if (jerk_m_s3 < 0 || std::isinf(jerk_m_s3)) {
limit_jerk_throttle = UINT8_MAX; // Maximum
} else if (jerk_m_s3 > 0) {
limit_jerk_throttle = std::clamp<float>(std::round(jerk_m_s3 * 10), 1, UINT8_MAX - 1);
} else {
limit_jerk_throttle = 0; // Default
}
}
void setLimitJerkBrakeMps(float jerk_m_s3) {
if (jerk_m_s3 < 0 || std::isinf(jerk_m_s3)) {
limit_jerk_brake = UINT8_MAX; // Maximum
} else if (jerk_m_s3 > 0) {
limit_jerk_brake = std::clamp<float>(std::round(jerk_m_s3 * 10), 1, UINT8_MAX - 1);
} else {
limit_jerk_brake = 0; // Default
}
}
float limitAccelMps() const {
if (limit_accel == 0) {
return 0; // Default
} else {
return std::clamp(limit_accel * 0.025f, 0.3f, 3.0f);
}
}
float limitDecelMps() const {
if (limit_decel == 0) {
return 0; // Default
} else {
return std::clamp(limit_decel * 0.025f, 0.3f, 6.0f);
}
}
float limitJerkThrottleMps() const {
if (limit_jerk_throttle == 0) {
return 0; // Default
} else {
return std::clamp(limit_jerk_throttle * 0.1f, 1.0f, 25.0f);
}
}
float limitJerkBrakeMps() const {
if (limit_jerk_brake == 0) {
return 0; // Default
} else {
return std::clamp(limit_jerk_brake * 0.1f, 1.0f, 25.0f);
}
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgUlcCfg));
struct MsgUlcReport {
static constexpr uint32_t ID = 0x280;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t TIMEOUT_MS = 500;
int16_t vel_ref :13; // 0.02 m/s,
MsgUlcCmd::CmdType cmd_type :3;
int16_t vel_meas :13; // 0.02 m/s
uint8_t override_active :1;
uint8_t override_latched :1;
uint8_t preempted :1;
int8_t accel_ref; // 0.05 m/s^2
int8_t accel_meas; // 0.05 m/s^2
uint8_t ready :1;
uint8_t enabled :1;
MsgUlcCmd::CoastDecel coast_decel :1;
uint8_t timeout :1;
uint8_t bad_crc :1;
uint8_t bad_rc :1;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setVelocityRefMps(float velocity_m_s) {
if (std::isfinite(velocity_m_s)) {
vel_ref = std::clamp<float>(velocity_m_s * 50, -INT16_MAX >> 3, INT16_MAX >> 3);
} else {
vel_ref = (uint16_t)0xF000;
}
}
bool velocityRefValid() const {
return (uint16_t)vel_ref != (uint16_t)0xF000;
}
float velocityRefMps() const {
if (velocityRefValid()) {
return vel_ref * 0.02f;
}
return NAN;
}
void setVelocityMeasMps(float velocity_m_s) {
if (std::isfinite(velocity_m_s)) {
vel_meas = std::clamp<float>(velocity_m_s * 50, -INT16_MAX >> 3, INT16_MAX >> 3);
} else {
vel_meas = (uint16_t)0xF000;
}
}
bool velocityMeasValid() const {
return (uint16_t)vel_meas != (uint16_t)0xF000;
}
float velocityMeasMps() const {
if (velocityMeasValid()) {
return vel_meas * 0.02f;
}
return NAN;
}
void setAccelRefMps(float accel_m_s2) {
if (std::isfinite(accel_m_s2)) {
accel_ref = std::clamp<float>(accel_m_s2 * 20, -INT8_MAX, INT8_MAX);
} else {
accel_ref = (int8_t)0x80;
}
}
bool accelRefValid() const {
return accel_ref != (int8_t)0x80;
}
float accelRefMps() const {
if (accelRefValid()) {
return accel_ref * 0.05f;
}
return NAN;
}
void setAccelMeasMps(float accel_m_s2) {
if (std::isfinite(accel_m_s2)) {
accel_meas = std::clamp<float>(accel_m_s2 * 20, -INT8_MAX, INT8_MAX);
} else {
accel_meas = (int8_t)0x80;
}
}
bool accelMeasValid() const {
return accel_meas != (int8_t)0x80;
}
int8_t accelMeasMpsx20() const {
return accel_meas;
}
float accelMeasMps() const {
if (accelMeasValid()) {
return accel_meas * 0.05f;
}
return NAN;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgUlcReport));
struct MsgAccel {
static constexpr uint32_t ID = 0x2A0;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t TIMEOUT_MS = 200;
int16_t x; // 0.01 m/s^2, forward positive, backward negative
int16_t y; // 0.01 m/s^2, left positive, right negative
int16_t z; // 0.01 m/s^2, up positive, down negative
uint8_t :6;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setAccelXMps2(float m_s2) { setAccelMps2(x, m_s2); }
void setAccelYMps2(float m_s2) { setAccelMps2(y, m_s2); }
void setAccelZMps2(float m_s2) { setAccelMps2(z, m_s2); }
bool accelXValid() const { return accelValid(x); }
bool accelYValid() const { return accelValid(y); }
bool accelZValid() const { return accelValid(z); }
float accelXMps2() const { return accelMps2(x); }
float accelYMps2() const { return accelMps2(y); }
float accelZMps2() const { return accelMps2(z); }
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
private:
static constexpr int16_t UNKNOWN = INT16_MIN;
static constexpr int16_t MAX = INT16_MAX;
static void setAccelMps2(int16_t &u, float m_s2) {
if (std::isfinite(m_s2)) {
u = std::clamp<float>(m_s2 * 100, -MAX, MAX);
} else {
u = UNKNOWN;
}
}
static bool accelValid(const int16_t &u) {
return u != UNKNOWN;
}
static float accelMps2(const int16_t &u) {
if (accelValid(u)) {
return u * 0.01f;
}
return NAN;
}
};
static_assert(8 == sizeof(MsgAccel));
struct MsgGyro {
static constexpr uint32_t ID = 0x2A1;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t TIMEOUT_MS = 200;
int16_t x; // 0.0002 rad/s, roll, right positive, left negative
int16_t y; // 0.0002 rad/s, pitch, down positive, up negative
int16_t z; // 0.0002 rad/s, yaw, left positive, right negative
uint8_t :6;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setGyroXRadS(float rad_s) { setGyroRadS(x, rad_s); }
void setGyroYRadS(float rad_s) { setGyroRadS(y, rad_s); }
void setGyroZRadS(float rad_s) { setGyroRadS(z, rad_s); }
bool gyroXValid() const { return gyroValid(x); }
bool gyroYValid() const { return gyroValid(y); }
bool gyroZValid() const { return gyroValid(z); }
float gyroXRadS() const { return gyroRadS(x); }
float gyroXDegS() const { return gyroDegS(x); }
float gyroYRadS() const { return gyroRadS(y); }
float gyroYDegS() const { return gyroDegS(y); }
float gyroZRadS() const { return gyroRadS(z); }
float gyroZDegS() const { return gyroDegS(z); }
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
private:
static constexpr int16_t UNKNOWN = INT16_MIN;
static constexpr int16_t MAX = INT16_MAX;
static void setGyroRadS(int16_t &u, float rad_s) {
if (std::isfinite(rad_s)) {
u = std::clamp<float>(rad_s * 5000, -MAX, MAX);
} else {
u = UNKNOWN;
}
}
static bool gyroValid(const int16_t &u) {
return u != UNKNOWN;
}
static float gyroRadS(const int16_t &u) {
if (gyroValid(u)) {
return u * 0.0002f;
}
return NAN;
}
static float gyroDegS(const int16_t &u) {
return gyroRadS(u) * (float)(180 / M_PI);
}
};
static_assert(8 == sizeof(MsgGyro));
struct MsgWheelSpeed {
static constexpr uint32_t ID = 0x2A4;
static constexpr size_t PERIOD_MS = 10;
static constexpr size_t TIMEOUT_MS = 100;
static constexpr int16_t UNKNOWN = INT16_MIN;
static constexpr int16_t MAX = INT16_MAX;
int16_t front_left; // 0.01 rad/s
int16_t front_right; // 0.01 rad/s
int16_t rear_left; // 0.01 rad/s
int16_t rear_right; // 0.01 rad/s
void setFrontLeftRadS(float rad_s) { setSpeedRadS(front_left, rad_s); }
void setFrontRightRadS(float rad_s) { setSpeedRadS(front_right, rad_s); }
void setRearLeftRadS(float rad_s) { setSpeedRadS(rear_left, rad_s); }
void setRearRightRadS(float rad_s) { setSpeedRadS(rear_right, rad_s); }
bool frontLeftValid() const { return speedValid(front_left); }
bool frontRightValid() const { return speedValid(front_right); }
bool rearLeftValid() const { return speedValid(rear_left); }
bool rearRightValid() const { return speedValid(rear_right); }
float frontLeftRadS() const { return speedRadS(front_left); }
float frontLeftDegS() const { return speedDegS(front_left); }
float frontRightRadS() const { return speedRadS(front_right); }
float frontRightDegS() const { return speedDegS(front_right); }
float rearLeftRadS() const { return speedRadS(rear_left); }
float rearLeftDegS() const { return speedDegS(rear_left); }
float rearRightRadS() const { return speedRadS(rear_right); }
float rearRightDegS() const { return speedDegS(rear_right); }
void reset() {
memset(this, 0x00, sizeof(*this));
}
private:
static void setSpeedRadS(int16_t &x, float rad_s) {
if (std::isfinite(rad_s)) {
x = std::clamp<float>(rad_s * 100, -MAX, MAX);
} else {
x = UNKNOWN;
}
}
static bool speedValid(const int16_t &x) {
return x != UNKNOWN;
}
static float speedRadS(const int16_t &x) {
if (speedValid(x)) {
return x * 0.01f;
}
return NAN;
}
static float speedDegS(const int16_t &x) {
return speedRadS(x) * (float)(180 / M_PI);
}
};
static_assert(8 == sizeof(MsgWheelSpeed));
struct MsgWheelPosition {
static constexpr uint32_t ID = 0x2A5;
static constexpr size_t PERIOD_MS = 20;
static constexpr size_t TIMEOUT_MS = 250;
int16_t front_left;
int16_t front_right;
int16_t rear_left;
int16_t rear_right;
void reset() {
memset(this, 0x00, sizeof(*this));
}
};
static_assert(8 == sizeof(MsgWheelPosition));
struct MsgMiscCmd {
static constexpr uint32_t ID = 0x2C0;
static constexpr size_t PERIOD_MS = 50;
static constexpr size_t TIMEOUT_MS = 250;
enum class PrkBrkCmd : uint8_t {
None = 0,
On = 1,
Off = 2,
};
enum class DoorSelect : uint8_t {
None = 0,
Left = 1,
Right = 2,
Trunk = 3,
};
enum class DoorCmd : uint8_t {
None = 0,
Open = 1,
Close = 2,
};
TurnSignal turn_signal_cmd :2;
PrkBrkCmd parking_brake_cmd :2;
DoorSelect door_select :2;
DoorCmd door_cmd :2;
uint8_t :8;
uint8_t :8;
uint8_t crc;
void reset() {
memset(this, 0x00, sizeof(*this));
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
};
static_assert(4 == sizeof(MsgMiscCmd));
struct MsgMiscReport1 {
static constexpr uint32_t ID = 0x2C4;
static constexpr size_t PERIOD_MS = 50;
static constexpr size_t TIMEOUT_MS = 200;
enum class PrkBrkStat : uint8_t {
Unknown = 0,
On = 1,
Off = 2,
Transition = 3,
};
TurnSignal turn_signal :2;
PrkBrkStat parking_brake :2;
uint8_t pasngr_detect :1;
uint8_t pasngr_airbag :1;
uint8_t buckle_driver :1;
uint8_t buckle_pasngr :1;
uint8_t door_driver :1;
uint8_t door_passenger :1;
uint8_t door_rear_left :1;
uint8_t door_rear_right :1;
uint8_t door_hood :1;
uint8_t door_trunk :1;
uint8_t :2;
uint8_t btn_ld_ok :1;
uint8_t btn_ld_up :1;
uint8_t btn_ld_down :1;
uint8_t btn_ld_left :1;
uint8_t btn_ld_right :1;
uint8_t :1;
uint8_t btn_rd_ok :1;
uint8_t btn_rd_up :1;
uint8_t btn_rd_down :1;
uint8_t btn_rd_left :1;
uint8_t btn_rd_right :1;
uint8_t :1;
uint8_t btn_cc_mode :1;
uint8_t btn_cc_on :1;
uint8_t btn_cc_off :1;
uint8_t btn_cc_res :1;
uint8_t btn_cc_cncl :1;
uint8_t btn_cc_on_off :1;
uint8_t btn_cc_res_cncl :1;
uint8_t btn_cc_res_inc :1;
uint8_t btn_cc_res_dec :1;
uint8_t btn_cc_set_inc :1;
uint8_t btn_cc_set_dec :1;
uint8_t btn_acc_gap_inc :1;
uint8_t btn_acc_gap_dec :1;
uint8_t btn_limit_on_off :1;
uint8_t btn_la_on_off :1;
uint8_t btn_apa :1;
uint8_t btn_media :1;
uint8_t btn_vol_inc :1;
uint8_t btn_vol_dec :1;
uint8_t btn_mute :1;
uint8_t btn_speak :1;
uint8_t btn_prev :1;
uint8_t btn_next :1;
uint8_t btn_call_start :1;
uint8_t btn_call_end :1;
uint8_t :1;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgMiscReport1));
struct MsgMiscReport2 {
static constexpr uint32_t ID = 0x2C5;
static constexpr size_t PERIOD_MS = 50;
static constexpr size_t TIMEOUT_MS = 200;
uint8_t outside_air_temp :8;
uint8_t cabin_air_temp :8;
uint8_t wiper_front :4;
uint8_t head_light_hi :2;
uint8_t :2;
uint8_t light_ambient :3;
uint8_t :5;
uint8_t :8;
uint8_t :8;
uint8_t :6;
uint8_t rc :2;
uint8_t crc;
void reset() {
uint8_t save = rc;
memset(this, 0x00, sizeof(*this));
rc = save;
}
void setCrc() {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
crc = crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validCrc() const {
static_assert(crc8(ID, MSG_NULL, offsetof(typeof(*this), crc)) != 0x00);
return crc == crc8(ID, this, offsetof(typeof(*this), crc));
}
bool validRc(uint8_t rc) const {
return rc != this->rc;
}
};
static_assert(8 == sizeof(MsgMiscReport2));
struct MsgReserved1 {
static constexpr uint32_t ID = 0x360;
static constexpr size_t TIMEOUT_MS = 500;
uint8_t reserved[8];
};
static_assert(8 == sizeof(MsgReserved1));
struct MsgReserved2 {
static constexpr uint32_t ID = 0x361;
static constexpr size_t TIMEOUT_MS = 5000;
uint8_t reserved[8];
};
static_assert(8 == sizeof(MsgReserved2));
struct MsgReservedDebug {
static constexpr uint32_t ID = 0x36F;
static constexpr size_t TIMEOUT_MS = 1000;
int16_t nom;
int16_t min;
int16_t max;
uint16_t ms :15;
uint16_t fault :1;
};
static_assert(8 == sizeof(MsgReservedDebug));
struct MsgTirePressure {
static constexpr uint32_t ID = 0x380;
static constexpr size_t PERIOD_MS = 500;
static constexpr size_t TIMEOUT_MS = 2500;
static constexpr uint16_t UNKNOWN = UINT16_MAX >> 4;
static constexpr uint16_t MAX = UNKNOWN - 1;
uint16_t front_left :12; // kPa
uint16_t front_right :12; // kPa
uint16_t rear_left :12; // kPa
uint16_t rear_right :12; // kPa
uint16_t spare :12; // kPa
uint16_t :4;
void setFrontLeftKPa(int kpa) { front_left = setPressureKPa(kpa); }
void setFrontRightKPa(int kpa) { front_right = setPressureKPa(kpa); }
void setRearLeftKPa(int kpa) { rear_left = setPressureKPa(kpa); }
void setRearRightKPa(int kpa) { rear_right = setPressureKPa(kpa); }
void setSpareKPa(int kpa) { spare = setPressureKPa(kpa); }
void setFrontLeftKPa(float kpa) { front_left = setPressureKPa(kpa); }
void setFrontRightKPa(float kpa) { front_right = setPressureKPa(kpa); }
void setRearLeftKPa(float kpa) { rear_left = setPressureKPa(kpa); }
void setRearRightKPa(float kpa) { rear_right = setPressureKPa(kpa); }
void setSpareKPa(float kpa) { spare = setPressureKPa(kpa); }
bool frontLeftValid() const { return pressureValid(front_left); }
bool frontRightValid() const { return pressureValid(front_right); }
bool rearLeftValid() const { return pressureValid(rear_left); }
bool rearRightValid() const { return pressureValid(rear_right); }
bool spareValid() const { return pressureValid(spare); }
float frontLeftKPa() const { return pressureKPa(front_left); }
float frontRightKPa() const { return pressureKPa(front_right); }
float rearLeftKPa() const { return pressureKPa(rear_left); }
float rearRightKPa() const { return pressureKPa(rear_right); }
float spareKPa() const { return pressureKPa(spare); }
void reset() {
memset(this, 0x00, sizeof(*this));
front_left = UNKNOWN;
front_right = UNKNOWN;
rear_left = UNKNOWN;
rear_right = UNKNOWN;
spare = UNKNOWN;
}
private:
static uint16_t setPressureKPa(int kpa) {
if (kpa >= 0) {
return std::clamp<int>(kpa, 0, MAX);
} else {
return UNKNOWN;
}
}
static uint16_t setPressureKPa(float kpa) {
if (std::isfinite(kpa)) {
return std::clamp<float>(kpa, 0, MAX);
} else {
return UNKNOWN;
}
}
static bool pressureValid(const uint16_t &x) {
return x != UNKNOWN;
}
static float pressureKPa(const uint16_t &x) {
if (pressureValid(x)) {
return x;
}
return NAN;
}
};
static_assert(8 == sizeof(MsgTirePressure));
#if 0
struct MsgReportGps1 {
static constexpr uint32_t ID = 0x06D;
static constexpr size_t PERIOD_MS = 1000;
int32_t latitude :31;
int32_t lat_valid :1;
int32_t longitude :31;
int32_t long_valid :1;
};
static_assert(8 == sizeof(MsgReportGps1));
struct MsgReportGps2 {
static constexpr uint32_t ID = 0x06E;
static constexpr size_t PERIOD_MS = 1000;
uint8_t utc_year :7;
uint8_t :1;
uint8_t utc_month :4;
uint8_t :4;
uint8_t utc_day :5;
uint8_t :3;
uint8_t utc_hours :5;
uint8_t :3;
uint8_t utc_minutes :6;
uint8_t :2;
uint8_t utc_seconds :6;
uint8_t :2;
uint8_t compass_dir :4;
uint8_t :4;
uint8_t pdop :5;
uint8_t fault :1;
uint8_t inferred :1;
uint8_t :1;
};
static_assert(8 == sizeof(MsgReportGps2));
struct MsgReportGps3 {
static constexpr uint32_t ID = 0x06F;
static constexpr size_t PERIOD_MS = 1000;
int16_t altitude;
uint16_t heading;
uint8_t speed;
uint8_t hdop :5;
uint8_t :3;
uint8_t vdop :5;
uint8_t :3;
uint8_t quality :3;
uint8_t num_sats :5;
};
static_assert(8 == sizeof(MsgReportGps3));
struct MsgReportFuelLevel {
static constexpr uint32_t ID = 0x072;
static constexpr size_t PERIOD_MS = 100;
int16_t fuel_level :11; // 0.18696 %
uint8_t :3;
uint16_t battery_hev :10; // 0.5 V
uint8_t battery_12v :8; // 0.0625 V
uint32_t odometer :24; // 0.1 km
uint8_t :8;
};
static_assert(8 == sizeof(MsgReportFuelLevel));
struct MsgReportSurround {
static constexpr uint32_t ID = 0x073;
static constexpr size_t PERIOD_MS = 200;
uint8_t l_cta_alert :1;
uint8_t l_cta_enabled :1;
uint8_t l_blis_alert :1;
uint8_t l_blis_enabled :1;
uint8_t r_cta_alert :1;
uint8_t r_cta_enabled :1;
uint8_t r_blis_alert :1;
uint8_t r_blis_enabled :1;
uint8_t sonar_00 :4;
uint8_t sonar_01 :4;
uint8_t sonar_02 :4;
uint8_t sonar_03 :4;
uint8_t sonar_04 :4;
uint8_t sonar_05 :4;
uint8_t sonar_06 :4;
uint8_t sonar_07 :4;
uint8_t sonar_08 :4;
uint8_t sonar_09 :4;
uint8_t sonar_10 :4;
uint8_t sonar_11 :4;
uint8_t :6;
uint8_t sonar_enabled :1;
uint8_t sonar_fault :1;
};
static_assert(8 == sizeof(MsgReportSurround));
// struct MsgReportBrakeInfo {
// static constexpr uint32_t ID = 0x074;
// static constexpr size_t PERIOD_MS = 20;
// static constexpr size_t TIMEOUT_MS = 200;
// uint16_t brake_torque_request :12;
// uint8_t hsa_stat :3;
// uint8_t stationary :1;
// uint16_t brake_torque_actual :12;
// uint8_t hsa_mode :2;
// uint8_t :2; // parking_brake
// int16_t wheel_torque :14;
// Quality bped_qf :2;
// int16_t accel_over_ground_est :10;
// uint8_t abs_active :1;
// uint8_t abs_enabled :1;
// uint8_t stab_active :1;
// uint8_t stab_enabled :1;
// uint8_t trac_active :1;
// uint8_t trac_enabled :1;
// };
// static_assert(8 == sizeof(MsgReportBrakeInfo));
// struct MsgReportThrottleInfo {
// static constexpr uint32_t ID = 0x075;
// static constexpr size_t PERIOD_MS = 10;
// uint16_t engine_rpm :16;
// uint16_t throttle_pc :10;
// uint8_t :4;
// Quality aped_qf :2;
// int8_t throttle_rate :8;
// uint8_t gear_num :5;
// uint8_t :3;
// uint8_t ign_stat :2;
// int16_t batt_curr :14;
// };
// static_assert(8 == sizeof(MsgReportThrottleInfo));
// typedef enum {
// GEAR_NUM_X = 0, // SNA, Unknown, Undefined
// GEAR_NUM_D01 = 1, // 1st (Drive)
// GEAR_NUM_D02 = 2, // 2nd (Drive)
// GEAR_NUM_D03 = 3, // 3rd (Drive)
// GEAR_NUM_D04 = 4, // 4th (Drive)
// GEAR_NUM_D05 = 5, // 5th (Drive)
// GEAR_NUM_D06 = 6, // 6th (Drive)
// GEAR_NUM_D07 = 7, // 7th (Drive)
// GEAR_NUM_D08 = 8, // 8th (Drive)
// GEAR_NUM_D09 = 9, // 9th (Drive)
// GEAR_NUM_D10 = 10, // 10th (Drive)
// // Room for expansion
// GEAR_NUM_N = 16, // Neutral (N)
// GEAR_NUM_R01 = 17, // 1st (Reverse)
// GEAR_NUM_R02 = 18, // 2nd (Reverse)
// // Room for expansion
// GEAR_NUM_P = 31, // Park (P)
// } GearNum; // Dataspeed defined number
// typedef enum {
// IGN_STAT_X = 0, // Unknown
// IGN_STAT_OFF = 1,
// IGN_STAT_ACC = 2, // Accessory
// IGN_STAT_RUN = 3,
// } IgnStat; // Dataspeed defined status
struct MsgReportDriverAssist {
static constexpr uint32_t ID = 0x079;
static constexpr size_t PERIOD_MS = 200;
uint8_t decel :8; // 0.0625 m/s^2
uint8_t decel_src :2;
uint8_t :1;
uint8_t fcw_enabled :1;
uint8_t fcw_active :1;
uint8_t aeb_enabled :1;
uint8_t aeb_precharge :1;
uint8_t aeb_active :1;
uint8_t :1;
uint8_t acc_enabled :1;
uint8_t acc_braking :1;
uint8_t :5;
};
static_assert(3 == sizeof(MsgReportDriverAssist));
typedef enum {
DECEL_SRC_NONE = 0,
DECEL_SRC_AEB = 1,
DECEL_SRC_ACC = 2,
} DecelSrc;
#endif
struct MsgEcuInfo {
static constexpr size_t PERIOD_MS = 200;
enum class Mux : uint8_t {
Version = 0x00,
CfgHash = 0x04,
MacAddr = 0x08,
License0 = 0x10, // Feature 0
License1 = 0x11, // Feature 1
License2 = 0x12, // Feature 2
License3 = 0x13, // Feature 3
License4 = 0x14, // Feature 4
License5 = 0x15, // Feature 5
License6 = 0x16, // Feature 6
License7 = 0x17, // Feature 7
LicenseDate0 = 0x40,
LicenseDate1 = 0x41,
BuildDate0 = 0x48,
BuildDate1 = 0x49,
VIN0 = 0x50,
VIN1 = 0x51,
VIN2 = 0x52,
Logging = 0x60,
};
Mux mux :8;
union {
struct {
uint8_t platform;
uint16_t major;
uint16_t minor;
uint16_t build;
} version;
struct {
uint8_t count_modified;
uint8_t count_configured;
uint8_t nvm_blank :1;
uint8_t nvm_write_pending :1;
uint8_t :6;
uint32_t hash;
} cfg;
struct {
uint8_t addr0;
uint8_t addr1;
uint8_t addr2;
uint8_t addr3;
uint8_t addr4;
uint8_t addr5;
uint8_t :8;
} mac;
struct {
uint8_t ready :1;
uint8_t enabled :1;
uint8_t trial :1;
uint8_t :5;
uint8_t :8;
uint8_t :8;
uint16_t trials_used;
uint16_t trials_left;
} license;
struct {
uint8_t date0;
uint8_t date1;
uint8_t date2;
uint8_t date3;
uint8_t date4;
uint8_t date5;
uint8_t date6;
} ldate0;
struct {
uint8_t date7;
uint8_t date8;
uint8_t date9;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t :8;
} ldate1;
struct {
uint8_t date0;
uint8_t date1;
uint8_t date2;
uint8_t date3;
uint8_t date4;
uint8_t date5;
uint8_t date6;
} bdate0;
struct {
uint8_t date7;
uint8_t date8;
uint8_t date9;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t :8;
} bdate1;
struct {
uint8_t vin00;
uint8_t vin01;
uint8_t vin02;
uint8_t vin03;
uint8_t vin04;
uint8_t vin05;
uint8_t vin06;
} vin0;
struct {
uint8_t vin07;
uint8_t vin08;
uint8_t vin09;
uint8_t vin10;
uint8_t vin11;
uint8_t vin12;
uint8_t vin13;
} vin1;
struct {
uint8_t vin14;
uint8_t vin15;
uint8_t vin16;
uint8_t :8;
uint8_t :8;
uint8_t :8;
uint8_t :8;
} vin2;
struct {
uint8_t fault :1;
uint8_t filesystem :1;
uint8_t :6;
uint8_t :8;
uint8_t :8;
uint32_t :8;
uint32_t filename :24; // "000000.dbw", 0xFFFFFF for None
bool validFilename() const {
return filename != 0xFFFFFFu;
}
} logging;
};
void reset() {
memset(this, 0x00, sizeof(*this));
}
};
static_assert(8 == sizeof(MsgEcuInfo));
struct MsgEcuInfoGateway : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C0; };
struct MsgEcuInfoSteer : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C1; };
struct MsgEcuInfoBrake : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C2; };
struct MsgEcuInfoThrottle : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C3; };
struct MsgEcuInfoShift : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C4; };
struct MsgEcuInfoBOO : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C5; };
struct MsgEcuInfoMonitor : public MsgEcuInfo { static constexpr uint32_t ID = 0x6C6; };
#pragma pack(pop) // Undo packing
// Verify that IDs are unique and in the desired order of priorities (unit test)
static constexpr std::array<uint32_t, 57> IDS {
// Primary reports
MsgSteerReport1::ID,
MsgBrakeReport1::ID,
MsgThrtlReport1::ID,
MsgGearReport1::ID,
MsgMonitorReport1::ID,
MsgSystemReport::ID,
// Primary sensors
MsgVehicleVelocity::ID,
MsgThrtlInfo::ID,
MsgBrakeInfo::ID,
// Commands (remote control)
MsgSteerCmdRmt::ID,
MsgBrakeCmdRmt::ID,
MsgThrtlCmdRmt::ID,
MsgGearCmdRmt::ID,
// Commands (user)
MsgSteerCmdUsr::ID,
MsgBrakeCmdUsr::ID,
MsgThrtlCmdUsr::ID,
MsgGearCmdUsr::ID,
MsgMonitorCmd::ID,
MsgSystemCmd::ID,
// Commands (ULC)
MsgBrakeCmdUlc::ID,
MsgThrtlCmdUlc::ID,
MsgGearCmdUlc::ID,
// ULC
MsgUlcReport::ID,
MsgUlcCmd::ID,
MsgUlcCfg::ID,
// Secondary sensors
MsgAccel::ID,
MsgGyro::ID,
MsgWheelSpeed::ID,
MsgWheelPosition::ID,
MsgMonitorThrtl::ID,
// Misc
MsgMiscCmd::ID,
MsgMiscReport1::ID,
MsgMiscReport2::ID,
// Secondary reports
MsgSteerReport2::ID,
MsgBrakeReport2::ID,
MsgThrtlReport2::ID,
MsgGearReport2::ID,
MsgMonitorReport2::ID,
MsgSteerReport3::ID,
MsgBrakeReport3::ID,
MsgThrtlReport3::ID,
MsgGearReport3::ID,
MsgMonitorReport3::ID,
MsgSteerParamHash::ID,
MsgBrakeParamHash::ID,
MsgThrtlParamHash::ID,
// Reserved
MsgReserved1::ID,
MsgReserved2::ID,
MsgReservedDebug::ID,
// Other sensors
MsgTirePressure::ID,
// ECU info
MsgEcuInfoGateway::ID,
MsgEcuInfoSteer::ID,
MsgEcuInfoBrake::ID,
MsgEcuInfoThrottle::ID,
MsgEcuInfoShift::ID,
MsgEcuInfoBOO::ID,
MsgEcuInfoMonitor::ID,
};
template <typename T, size_t N>
static constexpr bool _is_sorted_unique(const std::array<T, N> &arr) {
for (size_t i = 1; i < arr.size(); i++) {
if (arr[i-1] >= arr[i]) {
return false;
}
}
return true;
}
static_assert(_is_sorted_unique(IDS));
} // namespace ds_dbw_can